]> git.lyx.org Git - lyx.git/blobdiff - src/Server.cpp
Merge branch 'master' of git.lyx.org:lyx
[lyx.git] / src / Server.cpp
index 4c92c89cbf52a879c672386305a4718a1bb991cf..c88ae8241a65e8664be701b8d81c2b7cd56fe5e2 100644 (file)
           received LyX will inform the client that it's listening its
           messages, and 'bye' will inform that lyx is closing.
 
           received LyX will inform the client that it's listening its
           messages, and 'bye' will inform that lyx is closing.
 
-          See development/server_monitor.c for an example client.
+          See development/lyxserver/server_monitor.cpp for an example client.
   Purpose: implement a client/server lib for LyX
 */
 
 #include <config.h>
 
 #include "Server.h"
   Purpose: implement a client/server lib for LyX
 */
 
 #include <config.h>
 
 #include "Server.h"
+
+#include "DispatchResult.h"
 #include "FuncRequest.h"
 #include "FuncRequest.h"
+#include "LyX.h"
 #include "LyXAction.h"
 #include "LyXAction.h"
-#include "LyXFunc.h"
 
 #include "frontends/Application.h"
 
 #include "support/debug.h"
 #include "support/FileName.h"
 
 #include "frontends/Application.h"
 
 #include "support/debug.h"
 #include "support/FileName.h"
+#include "support/filetools.h"
 #include "support/lassert.h"
 #include "support/lstrings.h"
 #include "support/os.h"
 
 #include "support/lassert.h"
 #include "support/lstrings.h"
 #include "support/os.h"
 
-#include <boost/bind.hpp>
+#include "support/bind.h"
+
+#include <iostream>
 
 #ifdef _WIN32
 
 #ifdef _WIN32
+#include <io.h>
 #include <QCoreApplication>
 #endif
 #include <QCoreApplication>
 #endif
+#include <QThread>
 
 #include <cerrno>
 #ifdef HAVE_SYS_STAT_H
 
 #include <cerrno>
 #ifdef HAVE_SYS_STAT_H
@@ -93,118 +100,125 @@ private:
 
 namespace {
 
 
 namespace {
 
-bool closing_ = false;
-
-void closing()
-{
-       closing_ = true;
-}
-
-
-char * errormsg()
+string errormsg(DWORD const error)
 {
        void * msgbuf;
 {
        void * msgbuf;
-       DWORD error = GetLastError();
-       FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
-                     FORMAT_MESSAGE_FROM_SYSTEM |
-                     FORMAT_MESSAGE_IGNORE_INSERTS,
-                     NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-                     (LPTSTR) &msgbuf, 0, NULL);
-       return static_cast<char *>(msgbuf);
+       string message;
+       if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                         FORMAT_MESSAGE_FROM_SYSTEM |
+                         FORMAT_MESSAGE_IGNORE_INSERTS,
+                         NULL, error,
+                         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                         (LPTSTR) &msgbuf, 0, NULL)) {
+               message = static_cast<char *>(msgbuf);
+               LocalFree(msgbuf);
+       } else
+               message = "Unknown error";
+
+       return message;
 }
 
 }
 
+} // namespace anon
 
 
-extern "C" {
 
 DWORD WINAPI pipeServerWrapper(void * arg)
 {
        LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg);
 
 DWORD WINAPI pipeServerWrapper(void * arg)
 {
        LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg);
-       lyxcomm->pipeServer();
+       if (!lyxcomm->pipeServer()) {
+               // Error exit; perform cleanup.
+               lyxcomm->ready_ = false;
+               lyxcomm->closeHandles();
+               CloseHandle(lyxcomm->server_thread_);
+               CloseHandle(lyxcomm->stopserver_);
+               CloseHandle(lyxcomm->outbuf_mutex_);
+               lyxerr << "LyXComm: Closing connection" << endl;
+       }
        return 1;
 }
 
        return 1;
 }
 
-}
-
-} // namespace anon
 
 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
 
 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
-       : pipename_(pip), client_(cli), clientcb_(ccb), stopserver_(0)
+       : stopserver_(0), pipename_(pip), client_(cli), clientcb_(ccb)
 {
 {
-       // Ask Qt to notify us on quit.
-       qAddPostRoutine(closing);
+       for (int i = 0; i < MAX_PIPES; ++i) {
+               event_[i] = 0;
+               pipe_[i].handle = INVALID_HANDLE_VALUE;
+       }
        ready_ = false;
        ready_ = false;
+       deferred_loading_ = false;
        openConnection();
 }
 
 
        openConnection();
 }
 
 
-void LyXComm::pipeServer()
+bool LyXComm::pipeServer()
 {
        DWORD i;
 {
        DWORD i;
+       DWORD error;
 
 
-       for (i = 0; i <= MAX_PIPES; ++i) {
-               DWORD open_mode;
+       for (i = 0; i < MAX_PIPES; ++i) {
+               bool const is_outpipe = i >= MAX_CLIENTS;
+               DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
+                                                  : PIPE_ACCESS_INBOUND;
                string const pipename = external_path(pipeName(i));
 
                string const pipename = external_path(pipeName(i));
 
-               if (i < MAX_PIPES) {
-                       open_mode = PIPE_ACCESS_INBOUND;
-                       readbuf_[i].erase();
-               } else {
-                       open_mode = PIPE_ACCESS_OUTBOUND;
-                       writebuf_.erase();
-               }
-
                // Manual-reset event, initial state = signaled
                event_[i] = CreateEvent(NULL, TRUE, TRUE, NULL);
                // Manual-reset event, initial state = signaled
                event_[i] = CreateEvent(NULL, TRUE, TRUE, NULL);
-
                if (!event_[i]) {
                if (!event_[i]) {
-                       LYXERR0("LyXComm: Could not create event for pipe "
-                               << pipename.c_str() << '\n' << errormsg());
-                       closeHandles(i);
-                       return;
+                       error = GetLastError();
+                       lyxerr << "LyXComm: Could not create event for pipe "
+                              << pipename << "\nLyXComm: "
+                              << errormsg(error) << endl;
+                       return false;
                }
 
                pipe_[i].overlap.hEvent = event_[i];
                }
 
                pipe_[i].overlap.hEvent = event_[i];
+               pipe_[i].iobuf.erase();
                pipe_[i].handle = CreateNamedPipe(pipename.c_str(),
                                open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
                pipe_[i].handle = CreateNamedPipe(pipename.c_str(),
                                open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
-                               MAX_PIPES, PIPE_BUFSIZE, PIPE_BUFSIZE,
+                               MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
                                PIPE_TIMEOUT, NULL);
 
                if (pipe_[i].handle == INVALID_HANDLE_VALUE) {
                                PIPE_TIMEOUT, NULL);
 
                if (pipe_[i].handle == INVALID_HANDLE_VALUE) {
-                       LYXERR0("LyXComm: Could not create pipe "
-                               << pipename.c_str() << '\n' << errormsg());
-                       closeHandles(i);
-                       return;
+                       error = GetLastError();
+                       lyxerr << "LyXComm: Could not create pipe "
+                              << pipename << "\nLyXComm: "
+                              << errormsg(error) << endl;
+                       return false;
                }
 
                }
 
-               startPipe(i);
+               if (!startPipe(i))
+                       return false;
                pipe_[i].state = pipe_[i].pending_io ?
                pipe_[i].state = pipe_[i].pending_io ?
-                       CONNECTING_STATE : (i < MAX_PIPES ? READING_STATE
-                                                         : WRITING_STATE);
+                       CONNECTING_STATE : (is_outpipe ? WRITING_STATE
+                                                      : READING_STATE);
        }
 
        // Add the stopserver_ event
        }
 
        // Add the stopserver_ event
-       event_[MAX_PIPES + 1] = stopserver_;
+       event_[MAX_PIPES] = stopserver_;
 
        // We made it!
        LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
        ready_ = true;
 
        // We made it!
        LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
        ready_ = true;
+       outbuf_.erase();
        DWORD status;
        bool success;
 
        while (!checkStopServer()) {
                // Indefinitely wait for the completion of an overlapped
                // read, write, or connect operation.
        DWORD status;
        bool success;
 
        while (!checkStopServer()) {
                // Indefinitely wait for the completion of an overlapped
                // read, write, or connect operation.
-               DWORD wait = WaitForMultipleObjects(MAX_PIPES + 2, event_,
+               DWORD wait = WaitForMultipleObjects(MAX_PIPES + 1, event_,
                                                    FALSE, INFINITE);
 
                // Determine which pipe instance completed the operation.
                i = wait - WAIT_OBJECT_0;
                                                    FALSE, INFINITE);
 
                // Determine which pipe instance completed the operation.
                i = wait - WAIT_OBJECT_0;
-               LASSERT(i >= 0 && i <= MAX_PIPES + 1, /**/);
+               LASSERT(i <= MAX_PIPES, /**/);
 
                // Check whether we were waked up for stopping the pipe server.
 
                // Check whether we were waked up for stopping the pipe server.
-               if (i == MAX_PIPES + 1)
+               if (i == MAX_PIPES)
                        break;
 
                        break;
 
+               bool const is_outpipe = i >= MAX_CLIENTS;
+
                // Get the result if the operation was pending.
                if (pipe_[i].pending_io) {
                        success = GetOverlappedResult(pipe_[i].handle,
                // Get the result if the operation was pending.
                if (pipe_[i].pending_io) {
                        success = GetOverlappedResult(pipe_[i].handle,
@@ -214,36 +228,23 @@ void LyXComm::pipeServer()
                        case CONNECTING_STATE:
                                // Pending connect operation
                                if (!success) {
                        case CONNECTING_STATE:
                                // Pending connect operation
                                if (!success) {
-                                       DWORD const err = GetLastError();
-                                       if (i == MAX_PIPES
-                                           && err == ERROR_IO_INCOMPLETE) {
-                                               // A reply on the output pipe
-                                               // has not been read, still.
-                                               // As we have only one instance
-                                               // for output, we risk a stalled
-                                               // pipe if no one reads it.
-                                               // So, if a reader doesn't
-                                               // appear within about 5 or 6
-                                               // seconds, we reset it.
-                                               static int count = 0;
-                                               Sleep(100);
-                                               if (++count == 50) {
-                                                       count = 0;
-                                                       resetPipe(i, true);
-                                               }
-                                       } else
-                                               LYXERR0("LyXComm: " << errormsg());
+                                       error = GetLastError();
+                                       lyxerr << "LyXComm: "
+                                              << errormsg(error) << endl;
+                                       if (!resetPipe(i, true))
+                                               return false;
                                        continue;
                                }
                                        continue;
                                }
-                               pipe_[i].state = i < MAX_PIPES ? READING_STATE
-                                                              : WRITING_STATE;
+                               pipe_[i].state = is_outpipe ? WRITING_STATE
+                                                           : READING_STATE;
                                break;
 
                        case READING_STATE:
                                // Pending read operation
                                break;
 
                        case READING_STATE:
                                // Pending read operation
-                               LASSERT(i < MAX_PIPES, /**/);
+                               LASSERT(!is_outpipe, /**/);
                                if (!success || status == 0) {
                                if (!success || status == 0) {
-                                       resetPipe(i);
+                                       if (!resetPipe(i, !success))
+                                               return false;
                                        continue;
                                }
                                pipe_[i].nbytes = status;
                                        continue;
                                }
                                pipe_[i].nbytes = status;
@@ -252,11 +253,27 @@ void LyXComm::pipeServer()
 
                        case WRITING_STATE:
                                // Pending write operation
 
                        case WRITING_STATE:
                                // Pending write operation
-                               LASSERT(i == MAX_PIPES, /**/);
-                               if (!success || status != writebuf_.length()) {
-                                       resetPipe(i);
-                                       continue;
+                               LASSERT(is_outpipe, /**/);
+                               // Let's see whether we have a reply
+                               if (!outbuf_.empty()) {
+                                       // Yep. Deliver it to all pipe
+                                       // instances if we get ownership
+                                       // of the mutex, otherwise we'll
+                                       // try again the next round.
+                                       DWORD result = WaitForSingleObject(
+                                                       outbuf_mutex_, 200);
+                                       if (result == WAIT_OBJECT_0) {
+                                               DWORD j = MAX_CLIENTS;
+                                               while (j < MAX_PIPES) {
+                                                       pipe_[j].iobuf = outbuf_;
+                                                       ++j;
+                                               }
+                                               outbuf_.erase();
+                                       }
+                                       ReleaseMutex(outbuf_mutex_);
                                }
                                }
+                               if (pipe_[i].iobuf.empty())
+                                       pipe_[i].pending_io = false;
                                break;
                        }
                }
                                break;
                        }
                }
@@ -266,9 +283,9 @@ void LyXComm::pipeServer()
                case READING_STATE:
                        // The pipe instance is connected to a client
                        // and is ready to read a request.
                case READING_STATE:
                        // The pipe instance is connected to a client
                        // and is ready to read a request.
-                       LASSERT(i < MAX_PIPES, /**/);
+                       LASSERT(!is_outpipe, /**/);
                        success = ReadFile(pipe_[i].handle,
                        success = ReadFile(pipe_[i].handle,
-                                       pipe_[i].pipebuf, PIPE_BUFSIZE - 1,
+                                       pipe_[i].readbuf, PIPE_BUFSIZE - 1,
                                        &pipe_[i].nbytes, &pipe_[i].overlap);
 
                        if (success && pipe_[i].nbytes != 0) {
                                        &pipe_[i].nbytes, &pipe_[i].overlap);
 
                        if (success && pipe_[i].nbytes != 0) {
@@ -278,91 +295,111 @@ void LyXComm::pipeServer()
                                continue;
                        }
 
                                continue;
                        }
 
-                       if (!success && GetLastError() == ERROR_IO_PENDING) {
+                       error = GetLastError();
+
+                       if (!success && error == ERROR_IO_PENDING) {
                                // The read operation is still pending.
                                pipe_[i].pending_io = true;
                                continue;
                        }
 
                                // The read operation is still pending.
                                pipe_[i].pending_io = true;
                                continue;
                        }
 
+                       success = error == ERROR_BROKEN_PIPE;
+
                        // Client closed connection (ERROR_BROKEN_PIPE) or
                        // an error occurred; in either case, reset the pipe.
                        // Client closed connection (ERROR_BROKEN_PIPE) or
                        // an error occurred; in either case, reset the pipe.
-                       if (GetLastError() != ERROR_BROKEN_PIPE) {
-                               LYXERR0("LyXComm: " << errormsg());
-                               if (!readbuf_[i].empty()) {
-                                       LYXERR0("LyXComm: truncated command: "
-                                               << readbuf_[i]);
-                                       readbuf_[i].erase();
+                       if (!success) {
+                               lyxerr << "LyXComm: " << errormsg(error) << endl;
+                               if (!pipe_[i].iobuf.empty()) {
+                                       lyxerr << "LyXComm: truncated command: "
+                                              << pipe_[i].iobuf << endl;
+                                       pipe_[i].iobuf.erase();
                                }
                                }
-                               resetPipe(i, true);
-                       } else
-                               resetPipe(i);
+                       }
+                       if (!resetPipe(i, !success))
+                               return false;
                        break;
 
                case WRITING_STATE:
                        break;
 
                case WRITING_STATE:
-                       if (i < MAX_PIPES) {
+                       if (!is_outpipe) {
                                // The request was successfully read
                                // from the client; commit it.
                                ReadReadyEvent * event = new ReadReadyEvent(i);
                                QCoreApplication::postEvent(this,
                                                static_cast<QEvent *>(event));
                                // Wait for completion
                                // The request was successfully read
                                // from the client; commit it.
                                ReadReadyEvent * event = new ReadReadyEvent(i);
                                QCoreApplication::postEvent(this,
                                                static_cast<QEvent *>(event));
                                // Wait for completion
-                               while (pipe_[i].nbytes && !checkStopServer())
-                                       Sleep(100);
+                               while (pipe_[i].nbytes && !checkStopServer(100))
+                                       ;
                                pipe_[i].pending_io = false;
                                pipe_[i].state = READING_STATE;
                                continue;
                        }
                                pipe_[i].pending_io = false;
                                pipe_[i].state = READING_STATE;
                                continue;
                        }
-                       // Let's see whether we have a reply.
-                       if (writebuf_.length() == 0) {
-                               // No, nothing to do.
-                               pipe_[i].pending_io = false;
-                               continue;
+
+                       // This is an output pipe instance. Initiate the
+                       // overlapped write operation or monitor its progress.
+
+                       if (pipe_[i].pending_io) {
+                               success = WriteFile(pipe_[i].handle,
+                                               pipe_[i].iobuf.c_str(),
+                                               pipe_[i].iobuf.length(),
+                                               &status,
+                                               &pipe_[i].overlap);
                        }
                        }
-                       // Yep, deliver it.
-                       success = WriteFile(pipe_[i].handle,
-                                       writebuf_.c_str(), writebuf_.length(),
-                                       &status, &pipe_[i].overlap);
 
 
-                       if (success && status == writebuf_.length()) {
+                       if (success && !pipe_[i].iobuf.empty()
+                           && status == pipe_[i].iobuf.length()) {
                                // The write operation completed successfully.
                                // The write operation completed successfully.
-                               writebuf_.erase();
+                               pipe_[i].iobuf.erase();
                                pipe_[i].pending_io = false;
                                pipe_[i].pending_io = false;
-                               resetPipe(i);
+                               if (!resetPipe(i))
+                                       return false;
                                continue;
                        }
 
                                continue;
                        }
 
-                       if (!success && (GetLastError() == ERROR_IO_PENDING)) {
+                       error = GetLastError();
+
+                       if (success && error == ERROR_IO_PENDING) {
                                // The write operation is still pending.
                                // The write operation is still pending.
+                               // We get here when a reader is started
+                               // well before a reply is ready, so delay
+                               // a bit in order to not burden the cpu.
+                               checkStopServer(100);
                                pipe_[i].pending_io = true;
                                continue;
                        }
 
                                pipe_[i].pending_io = true;
                                continue;
                        }
 
+                       success = error == ERROR_NO_DATA;
+
                        // Client closed connection (ERROR_NO_DATA) or
                        // an error occurred; in either case, reset the pipe.
                        // Client closed connection (ERROR_NO_DATA) or
                        // an error occurred; in either case, reset the pipe.
-                       if (GetLastError() != ERROR_NO_DATA) {
-                               LYXERR0("LyXComm: Error sending message: "
-                                       << writebuf_ << '\n' << errormsg());
-                               resetPipe(i, true);
-                       } else
-                               resetPipe(i);
+                       if (!success) {
+                               lyxerr << "LyXComm: Error sending message: "
+                                      << pipe_[i].iobuf << "\nLyXComm: "
+                                      << errormsg(error) << endl;
+                       }
+                       if (!resetPipe(i, !success))
+                               return false;
                        break;
                }
        }
 
        ready_ = false;
                        break;
                }
        }
 
        ready_ = false;
-       closeHandles(MAX_PIPES + 1);
+       closeHandles();
+       return true;
 }
 
 
 }
 
 
-void LyXComm::closeHandles(DWORD index)
+void LyXComm::closeHandles()
 {
 {
-       for (int i = 0; i <= index; ++i) {
+       for (int i = 0; i < MAX_PIPES; ++i) {
                if (event_[i]) {
                        ResetEvent(event_[i]);
                        CloseHandle(event_[i]);
                if (event_[i]) {
                        ResetEvent(event_[i]);
                        CloseHandle(event_[i]);
+                       event_[i] = 0;
                }
                }
-               if (pipe_[i].handle != INVALID_HANDLE_VALUE)
+               if (pipe_[i].handle != INVALID_HANDLE_VALUE) {
                        CloseHandle(pipe_[i].handle);
                        CloseHandle(pipe_[i].handle);
+                       pipe_[i].handle = INVALID_HANDLE_VALUE;
+               }
        }
 }
 
        }
 }
 
@@ -377,23 +414,25 @@ bool LyXComm::event(QEvent * e)
 }
 
 
 }
 
 
-BOOL LyXComm::checkStopServer()
+bool LyXComm::checkStopServer(DWORD timeout)
 {
 {
-       return WaitForSingleObject(stopserver_, 0) == WAIT_OBJECT_0 || closing_;
+       return WaitForSingleObject(stopserver_, timeout) == WAIT_OBJECT_0;
 }
 
 
 }
 
 
-void LyXComm::startPipe(DWORD index)
+bool LyXComm::startPipe(DWORD index)
 {
        pipe_[index].pending_io = false;
 {
        pipe_[index].pending_io = false;
+       pipe_[index].overlap.Offset = 0;
+       pipe_[index].overlap.OffsetHigh = 0;
 
        // Overlapped ConnectNamedPipe should return zero.
        if (ConnectNamedPipe(pipe_[index].handle, &pipe_[index].overlap)) {
 
        // Overlapped ConnectNamedPipe should return zero.
        if (ConnectNamedPipe(pipe_[index].handle, &pipe_[index].overlap)) {
-               // FIXME: What to do? Maybe the pipe server should be reset.
-               LYXERR0("LyXComm: Could not connect pipe "
-                       << external_path(pipeName(index)) << '\n'
-                       << errormsg());
-               return;
+               DWORD const error = GetLastError();
+               lyxerr << "LyXComm: Could not connect pipe "
+                      << external_path(pipeName(index))
+                      << "\nLyXComm: " << errormsg(error) << endl;
+               return false;
        }
 
        switch (GetLastError()) {
        }
 
        switch (GetLastError()) {
@@ -406,55 +445,64 @@ void LyXComm::startPipe(DWORD index)
                // Client is already connected, so signal an event.
                if (SetEvent(pipe_[index].overlap.hEvent))
                        break;
                // Client is already connected, so signal an event.
                if (SetEvent(pipe_[index].overlap.hEvent))
                        break;
+               // fall through
        default:
                // Anything else is an error.
        default:
                // Anything else is an error.
-               // FIXME: What to do? Maybe the pipe server should be reset.
-               LYXERR0("LyXComm: An error occurred while connecting pipe "
-                       << external_path(pipeName(index)) << '\n'
-                       << errormsg());
+               DWORD const error = GetLastError();
+               lyxerr << "LyXComm: An error occurred while connecting pipe "
+                      << external_path(pipeName(index))
+                      << "\nLyXComm: " << errormsg(error) << endl;
+               return false;
        }
        }
+
+       return true;
 }
 
 
 }
 
 
-void LyXComm::resetPipe(DWORD index, bool close_handle)
+bool LyXComm::resetPipe(DWORD index, bool close_handle)
 {
        // This method is called when an error occurs or when a client
        // closes the connection. We first disconnect the pipe instance,
        // then reconnect it, ready to wait for another client.
 
        if (!DisconnectNamedPipe(pipe_[index].handle)) {
 {
        // This method is called when an error occurs or when a client
        // closes the connection. We first disconnect the pipe instance,
        // then reconnect it, ready to wait for another client.
 
        if (!DisconnectNamedPipe(pipe_[index].handle)) {
-               LYXERR0("LyXComm: Could not disconnect pipe "
-                       << external_path(pipeName(index)) << '\n'
-                       << errormsg());
+               DWORD const error = GetLastError();
+               lyxerr << "LyXComm: Could not disconnect pipe "
+                      << external_path(pipeName(index))
+                      << "\nLyXComm: " << errormsg(error) << endl;
                // What to do now? Let's try whether re-creating the pipe helps.
                close_handle = true;
        }
                // What to do now? Let's try whether re-creating the pipe helps.
                close_handle = true;
        }
+
+       bool const is_outpipe = index >= MAX_CLIENTS;
+
        if (close_handle) {
        if (close_handle) {
-               DWORD const open_mode = index < MAX_PIPES ?
-                               PIPE_ACCESS_INBOUND : PIPE_ACCESS_OUTBOUND;
+               DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
+                                                  : PIPE_ACCESS_INBOUND;
                string const name = external_path(pipeName(index));
 
                CloseHandle(pipe_[index].handle);
 
                string const name = external_path(pipeName(index));
 
                CloseHandle(pipe_[index].handle);
 
+               pipe_[index].iobuf.erase();
                pipe_[index].handle = CreateNamedPipe(name.c_str(),
                                open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
                pipe_[index].handle = CreateNamedPipe(name.c_str(),
                                open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
-                               MAX_PIPES, PIPE_BUFSIZE, PIPE_BUFSIZE,
+                               MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
                                PIPE_TIMEOUT, NULL);
 
                if (pipe_[index].handle == INVALID_HANDLE_VALUE) {
                                PIPE_TIMEOUT, NULL);
 
                if (pipe_[index].handle == INVALID_HANDLE_VALUE) {
-                       LYXERR0("LyXComm: Could not reset pipe "
-                               << name << '\n' << errormsg());
-                       return;
+                       DWORD const error = GetLastError();
+                       lyxerr << "LyXComm: Could not reset pipe " << name
+                              << "\nLyXComm: " << errormsg(error) << endl;
+                       return false;
                }
                }
-               if (index == MAX_PIPES)
-                       writebuf_.erase();
-               else
-                       readbuf_[index].erase();
        }
        }
-       startPipe(index);
+
+       if (!startPipe(index))
+               return false;
        pipe_[index].state = pipe_[index].pending_io ?
        pipe_[index].state = pipe_[index].pending_io ?
-                       CONNECTING_STATE : (index < MAX_PIPES ? READING_STATE
-                                                             : WRITING_STATE);
+                       CONNECTING_STATE : (is_outpipe ? WRITING_STATE
+                                                      : READING_STATE);
+       return true;
 }
 
 
 }
 
 
@@ -464,34 +512,60 @@ void LyXComm::openConnection()
 
        // If we are up, that's an error
        if (ready_) {
 
        // If we are up, that's an error
        if (ready_) {
-               LYXERR0("LyXComm: Already connected");
+               LYXERR(Debug::LYXSERVER, "LyXComm: Already connected");
                return;
        }
                return;
        }
-       // We assume that we don't make it
-       ready_ = false;
 
        if (pipename_.empty()) {
                LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
                return;
        }
 
 
        if (pipename_.empty()) {
                LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
                return;
        }
 
-       // Check whether the pipe name is being used by some other program.
+       // Check whether the pipe name is being used by some other instance.
        if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) {
        if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) {
-               LYXERR0("LyXComm: Pipe " << external_path(inPipeName())
-                       << " already exists.\nMaybe another instance of LyX"
-                          " is using it.");
+               // Tell the running instance to load the files
+               if (run_mode == USE_REMOTE && loadFilesInOtherInstance()) {
+                       deferred_loading_ = true;
+                       pipename_.erase();
+                       return;
+               }
+               lyxerr << "LyXComm: Pipe " << external_path(inPipeName())
+                      << " already exists.\nMaybe another instance of LyX"
+                         " is using it." << endl;
+               pipename_.erase();
+               return;
+       }
+
+       // Mutex with no initial owner for synchronized access to outbuf_
+       outbuf_mutex_ = CreateMutex(NULL, FALSE, NULL);
+       if (!outbuf_mutex_) {
+               DWORD const error = GetLastError();
+               lyxerr << "LyXComm: Could not create output buffer mutex"
+                      << "\nLyXComm: " << errormsg(error) << endl;
                pipename_.erase();
                return;
        }
 
        // Manual-reset event, initial state = not signaled
        stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
                pipename_.erase();
                return;
        }
 
        // Manual-reset event, initial state = not signaled
        stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
+       if (!stopserver_) {
+               DWORD const error = GetLastError();
+               lyxerr << "LyXComm: Could not create stop server event"
+                      << "\nLyXComm: " << errormsg(error) << endl;
+               pipename_.erase();
+               CloseHandle(outbuf_mutex_);
+               return;
+       }
+
        server_thread_ = CreateThread(NULL, 0, pipeServerWrapper,
                                     static_cast<void *>(this), 0, NULL);
        if (!server_thread_) {
        server_thread_ = CreateThread(NULL, 0, pipeServerWrapper,
                                     static_cast<void *>(this), 0, NULL);
        if (!server_thread_) {
-               LYXERR0("LyXComm: Could not create pipe server thread\n"
-                       << errormsg());
+               DWORD const error = GetLastError();
+               lyxerr << "LyXComm: Could not create pipe server thread"
+                      << "\nLyXComm: " << errormsg(error) << endl;
                pipename_.erase();
                pipename_.erase();
+               CloseHandle(stopserver_);
+               CloseHandle(outbuf_mutex_);
                return;
        }
 }
                return;
        }
 }
@@ -508,11 +582,17 @@ void LyXComm::closeConnection()
        }
 
        if (!ready_) {
        }
 
        if (!ready_) {
-               LYXERR0("LyXComm: Already disconnected");
+               LYXERR(Debug::LYXSERVER, "LyXComm: Already disconnected");
                return;
        }
 
                return;
        }
 
-       emergencyCleanup();
+       SetEvent(stopserver_);
+       // Wait for the pipe server to finish
+       WaitForSingleObject(server_thread_, INFINITE);
+       CloseHandle(server_thread_);
+       ResetEvent(stopserver_);
+       CloseHandle(stopserver_);
+       CloseHandle(outbuf_mutex_);
 }
 
 
 }
 
 
@@ -520,11 +600,17 @@ void LyXComm::emergencyCleanup()
 {
        if (ready_) {
                SetEvent(stopserver_);
 {
        if (ready_) {
                SetEvent(stopserver_);
-               // Wait for the pipe server to finish
-               WaitForSingleObject(server_thread_, INFINITE);
+               // Forcibly terminate the pipe server thread if it does
+               // not finish quickly.
+               if (WaitForSingleObject(server_thread_, 200) != WAIT_OBJECT_0) {
+                       TerminateThread(server_thread_, 0);
+                       ready_ = false;
+                       closeHandles();
+               }
                CloseHandle(server_thread_);
                ResetEvent(stopserver_);
                CloseHandle(stopserver_);
                CloseHandle(server_thread_);
                ResetEvent(stopserver_);
                CloseHandle(stopserver_);
+               CloseHandle(outbuf_mutex_);
        }
 }
 
        }
 }
 
@@ -533,19 +619,19 @@ void LyXComm::read_ready(DWORD inpipe)
 {
        // Turn the pipe buffer into a C string
        DWORD const nbytes = pipe_[inpipe].nbytes;
 {
        // Turn the pipe buffer into a C string
        DWORD const nbytes = pipe_[inpipe].nbytes;
-       pipe_[inpipe].pipebuf[nbytes] = '\0';
+       pipe_[inpipe].readbuf[nbytes] = '\0';
 
 
-       readbuf_[inpipe] += rtrim(pipe_[inpipe].pipebuf, "\r");
+       pipe_[inpipe].iobuf += rtrim(pipe_[inpipe].readbuf, "\r");
 
        // Commit any commands read
 
        // Commit any commands read
-       while (readbuf_[inpipe].find('\n') != string::npos) {
+       while (pipe_[inpipe].iobuf.find('\n') != string::npos) {
                // split() grabs the entire string if
                // the delim /wasn't/ found. ?:-P
                string cmd;
                // split() grabs the entire string if
                // the delim /wasn't/ found. ?:-P
                string cmd;
-               readbuf_[inpipe] = split(readbuf_[inpipe], cmd, '\n');
+               pipe_[inpipe].iobuf = split(pipe_[inpipe].iobuf, cmd, '\n');
                cmd = rtrim(cmd, "\r");
                cmd = rtrim(cmd, "\r");
-               LYXERR(Debug::LYXSERVER, "LyXComm: status:" << nbytes
-                       << ", readbuf_:" << readbuf_[inpipe]
+               LYXERR(Debug::LYXSERVER, "LyXComm: nbytes:" << nbytes
+                       << ", iobuf:" << pipe_[inpipe].iobuf
                        << ", cmd:" << cmd);
                if (!cmd.empty())
                        clientcb_(client_, cmd);
                        << ", cmd:" << cmd);
                if (!cmd.empty())
                        clientcb_(client_, cmd);
@@ -559,7 +645,8 @@ void LyXComm::read_ready(DWORD inpipe)
 void LyXComm::send(string const & msg)
 {
        if (msg.empty()) {
 void LyXComm::send(string const & msg)
 {
        if (msg.empty()) {
-               LYXERR0("LyXComm: Request to send empty string. Ignoring.");
+               lyxerr << "LyXComm: Request to send empty string. Ignoring."
+                      << endl;
                return;
        }
 
                return;
        }
 
@@ -569,38 +656,38 @@ void LyXComm::send(string const & msg)
                return;
 
        if (!ready_) {
                return;
 
        if (!ready_) {
-               LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
+               lyxerr << "LyXComm: Pipes are closed. Could not send "
+                      << msg << endl;
                return;
        }
 
                return;
        }
 
-       // Wait a couple of secs for completion of a previous write operation.
-       for (int count = 0; writebuf_.length() && count < 20; ++count)
-               Sleep(100);
-
-       if (!writebuf_.length()) {
-               writebuf_ = msg;
-               // Tell the pipe server he has a job to do.
-               SetEvent(pipe_[MAX_PIPES].overlap.hEvent);
+       // Request ownership of the outbuf_mutex_
+       DWORD result = WaitForSingleObject(outbuf_mutex_, PIPE_TIMEOUT);
+
+       if (result == WAIT_OBJECT_0) {
+               // If a client doesn't care to read a reply (JabRef is one
+               // such client), the output buffer could grow without limit.
+               // So, we empty it when its size is larger than PIPE_BUFSIZE.
+               if (outbuf_.size() > PIPE_BUFSIZE)
+                       outbuf_.erase();
+               outbuf_ += msg;
+               ReleaseMutex(outbuf_mutex_);
        } else {
        } else {
-               // Nope, output pipe is still busy. Most likely, a bad client
-               // did not care to read the answer (JabRef is one such client).
-               // Let's do a reset, otherwise the output pipe could remain
-               // stalled if the pipe server failed to reset it.
-               // This will remedy the output pipe stall, but the client will
-               // get a broken pipe error.
-               LYXERR0("LyXComm: Error sending message: " << msg
-                       << "\nLyXComm: Output pipe is stalled\n"
-                          "LyXComm: Resetting connection");
+               // Something is fishy, better resetting the connection.
+               DWORD const error = GetLastError();
+               lyxerr << "LyXComm: Error sending message: " << msg
+                      << "\nLyXComm: " << errormsg(error)
+                      << "\nLyXComm: Resetting connection" << endl;
+               ReleaseMutex(outbuf_mutex_);
                closeConnection();
                closeConnection();
-               if (!checkStopServer())
-                       openConnection();
+               openConnection();
        }
 }
 
 
 string const LyXComm::pipeName(DWORD index) const
 {
        }
 }
 
 
 string const LyXComm::pipeName(DWORD index) const
 {
-       return index < MAX_PIPES ? inPipeName() : outPipeName();
+       return index < MAX_CLIENTS ? inPipeName() : outPipeName();
 }
 
 
 }
 
 
@@ -610,6 +697,7 @@ string const LyXComm::pipeName(DWORD index) const
 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
 {}
 
 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
 {}
 
+
 void LyXComm::openConnection()
 {}
 
 void LyXComm::openConnection()
 {}
 
@@ -631,6 +719,7 @@ void LyXComm::endPipe(int & fd, string const & filename, bool write)
 void LyXComm::emergencyCleanup()
 {}
 
 void LyXComm::emergencyCleanup()
 {}
 
+
 void LyXComm::read_ready()
 {}
 
 void LyXComm::read_ready()
 {}
 
@@ -641,11 +730,11 @@ void LyXComm::send(string const & msg)
 
 #else // defined (HAVE_MKFIFO)
 
 
 #else // defined (HAVE_MKFIFO)
 
-
 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
        : pipename_(pip), client_(cli), clientcb_(ccb)
 {
        ready_ = false;
 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
        : pipename_(pip), client_(cli), clientcb_(ccb)
 {
        ready_ = false;
+       deferred_loading_ = false;
        openConnection();
 }
 
        openConnection();
 }
 
@@ -723,6 +812,12 @@ int LyXComm::startPipe(string const & file, bool write)
                        if (fd >= 0) {
                                // Another LyX instance is using it.
                                ::close(fd);
                        if (fd >= 0) {
                                // Another LyX instance is using it.
                                ::close(fd);
+                               // Tell the running instance to load the files
+                               if (run_mode == USE_REMOTE && loadFilesInOtherInstance()) {
+                                       deferred_loading_ = true;
+                                       pipename_.erase();
+                                       return -1;
+                               }
                        } else if (errno == ENXIO) {
                                // No process is reading from the other end.
                                stalepipe = true;
                        } else if (errno == ENXIO) {
                                // No process is reading from the other end.
                                stalepipe = true;
@@ -765,7 +860,7 @@ int LyXComm::startPipe(string const & file, bool write)
 
        if (!write) {
                theApp()->registerSocketCallback(fd,
 
        if (!write) {
                theApp()->registerSocketCallback(fd,
-                       boost::bind(&LyXComm::read_ready, this));
+                       bind(&LyXComm::read_ready, this));
        }
 
        return fd;
        }
 
        return fd;
@@ -785,7 +880,7 @@ void LyXComm::endPipe(int & fd, string const & filename, bool write)
                       << '\n' << strerror(errno) << endl;
        }
 
                       << '\n' << strerror(errno) << endl;
        }
 
-       if (FileName(filename).removeFile() < 0) {
+       if (!FileName(filename).removeFile()) {
                lyxerr << "LyXComm: Could not remove pipe " << filename
                       << '\n' << strerror(errno) << endl;
        }
                lyxerr << "LyXComm: Could not remove pipe " << filename
                       << '\n' << strerror(errno) << endl;
        }
@@ -823,7 +918,6 @@ void LyXComm::read_ready()
        int status;
        // the single = is intended here.
        while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
        int status;
        // the single = is intended here.
        while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
-
                if (status > 0) {
                        charbuf[status] = '\0'; // turn it into a c string
                        read_buffer_ += rtrim(charbuf, "\r");
                if (status > 0) {
                        charbuf[status] = '\0'; // turn it into a c string
                        read_buffer_ += rtrim(charbuf, "\r");
@@ -874,7 +968,8 @@ void LyXComm::send(string const & msg)
 
        LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
 
 
        LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
 
-       if (pipename_.empty()) return;
+       if (pipename_.empty())
+               return;
 
        if (!ready_) {
                LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
 
        if (!ready_) {
                LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
@@ -889,6 +984,49 @@ void LyXComm::send(string const & msg)
 
 #endif // defined (HAVE_MKFIFO)
 
 
 #endif // defined (HAVE_MKFIFO)
 
+namespace {
+
+struct Sleep : QThread
+{
+       static void millisec(unsigned long ms)
+       {
+               QThread::usleep(ms * 1000);
+       }
+};
+
+} // namespace anon
+
+
+bool LyXComm::loadFilesInOtherInstance()
+{
+       int pipefd;
+       int loaded_files = 0;
+       FileName const pipe(inPipeName());
+       vector<string>::iterator it = theFilesToLoad().begin();
+       while (it != theFilesToLoad().end()) {
+               FileName fname = fileSearch(string(), os::internal_path(*it),
+                                               "lyx", may_not_exist);
+               if (fname.empty()) {
+                       ++it;
+                       continue;
+               }
+               // Wait a while to allow time for the other
+               // instance to reset the connection
+               Sleep::millisec(200);
+               pipefd = ::open(pipe.toFilesystemEncoding().c_str(), O_WRONLY);
+               if (pipefd < 0)
+                       break;
+               string const cmd = "LYXCMD:pipe:file-open:" +
+                                       fname.absFileName() + '\n';
+               if (::write(pipefd, cmd.c_str(), cmd.length()) < 0)
+                       LYXERR0("Cannot write to pipe!");
+               ::close(pipefd);
+               ++loaded_files;
+               it = theFilesToLoad().erase(it);
+       }
+       return loaded_files > 0;
+}
+
 
 string const LyXComm::inPipeName() const
 {
 
 string const LyXComm::inPipeName() const
 {
@@ -913,8 +1051,8 @@ void ServerCallback(Server * server, string const & msg)
        server->callback(msg);
 }
 
        server->callback(msg);
 }
 
-Server::Server(LyXFunc * f, string const & pipes)
-       : numclients_(0), func_(f), pipes_(pipes, this, &ServerCallback)
+Server::Server(string const & pipes)
+       : numclients_(0), pipes_(pipes, this, &ServerCallback)
 {}
 
 
 {}
 
 
@@ -933,7 +1071,6 @@ Server::~Server()
 
 int compare(char const * a, char const * b, unsigned int len)
 {
 
 int compare(char const * a, char const * b, unsigned int len)
 {
-       using namespace std;
        return strncmp(a, b, len);
 }
 
        return strncmp(a, b, len);
 }
 
@@ -1035,17 +1172,20 @@ void Server::callback(string const & msg)
                        // The correct solution would be to have a
                        // specialized (non-gui) BufferView. But how do
                        // we do it now? Probably we should just let it
                        // The correct solution would be to have a
                        // specialized (non-gui) BufferView. But how do
                        // we do it now? Probably we should just let it
-                       // connect to the lyxfunc in the single LyXView we
+                       // connect to the lyxfunc in the single GuiView we
                        // support currently. (Lgb)
 
                        // support currently. (Lgb)
 
-                       func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
-                       string const rval = to_utf8(func_->getMessage());
+                       FuncRequest fr(lyxaction.lookupFunc(cmd), arg);
+                       fr.setOrigin(FuncRequest::LYXSERVER);
+                       DispatchResult dr;
+                       theApp()->dispatch(fr, dr);
+                       string const rval = to_utf8(dr.message());
 
                        // all commands produce an INFO or ERROR message
                        // in the output pipe, even if they do not return
                        // anything. See chapter 4 of Customization doc.
                        string buf;
 
                        // all commands produce an INFO or ERROR message
                        // in the output pipe, even if they do not return
                        // anything. See chapter 4 of Customization doc.
                        string buf;
-                       if (func_->errorStat())
+                       if (dr.error())
                                buf = "ERROR:";
                        else
                                buf = "INFO:";
                                buf = "ERROR:";
                        else
                                buf = "INFO:";