X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FServer.cpp;h=b89e834f20408275b6200ec6de2a3b9ff1367fef;hb=0c7bd9a57f2a308bb9659200eda3b7e45f8d5d3c;hp=2bcd85ccc375150ff5c3b819aa9a030953a3a28b;hpb=2d09fff3746189929eddc592e2949861b84dac2c;p=lyx.git diff --git a/src/Server.cpp b/src/Server.cpp index 2bcd85ccc3..b89e834f20 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -34,30 +34,37 @@ 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 #include "Server.h" + +#include "DispatchResult.h" #include "FuncRequest.h" +#include "LyX.h" #include "LyXAction.h" -#include "LyXFunc.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 +#include "support/bind.h" + +#include #ifdef _WIN32 +#include #include #endif +#include #include #ifdef HAVE_SYS_STAT_H @@ -93,108 +100,125 @@ private: namespace { -char * errormsg() +string errormsg(DWORD const error) { 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(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(msgbuf); + LocalFree(msgbuf); + } else + message = "Unknown error"; + + return message; } +} // namespace anon -extern "C" { DWORD WINAPI pipeServerWrapper(void * arg) { LyXComm * lyxcomm = reinterpret_cast(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; } -} - -} // namespace anon 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) { + for (int i = 0; i < MAX_PIPES; ++i) { + event_[i] = 0; + pipe_[i].handle = INVALID_HANDLE_VALUE; + } ready_ = false; + deferred_loading_ = false; openConnection(); } -void LyXComm::pipeServer() +bool LyXComm::pipeServer() { 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)); - 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); - 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].iobuf.erase(); 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) { - 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 ? - CONNECTING_STATE : (i < MAX_PIPES ? READING_STATE - : WRITING_STATE); + CONNECTING_STATE : (is_outpipe ? WRITING_STATE + : READING_STATE); } // Add the stopserver_ event - event_[MAX_PIPES + 1] = stopserver_; + event_[MAX_PIPES] = stopserver_; // 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 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; - LASSERT(i >= 0 && i <= MAX_PIPES + 1, /**/); + LASSERT(i <= MAX_PIPES, /**/); // Check whether we were waked up for stopping the pipe server. - if (i == MAX_PIPES + 1) + if (i == MAX_PIPES) 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, @@ -204,36 +228,23 @@ void LyXComm::pipeServer() 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; } - 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 - LASSERT(i < MAX_PIPES, /**/); + LASSERT(!is_outpipe, /**/); if (!success || status == 0) { - resetPipe(i); + if (!resetPipe(i, !success)) + return false; continue; } pipe_[i].nbytes = status; @@ -242,11 +253,27 @@ void LyXComm::pipeServer() 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; } } @@ -256,9 +283,9 @@ void LyXComm::pipeServer() 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, - pipe_[i].pipebuf, PIPE_BUFSIZE - 1, + pipe_[i].readbuf, PIPE_BUFSIZE - 1, &pipe_[i].nbytes, &pipe_[i].overlap); if (success && pipe_[i].nbytes != 0) { @@ -268,91 +295,111 @@ void LyXComm::pipeServer() 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; } + success = error == ERROR_BROKEN_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: - 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(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; } - // 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. - writebuf_.erase(); + pipe_[i].iobuf.erase(); pipe_[i].pending_io = false; - resetPipe(i); + if (!resetPipe(i)) + return false; continue; } - if (!success && (GetLastError() == ERROR_IO_PENDING)) { + error = GetLastError(); + + if (success && error == ERROR_IO_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; } + success = error == ERROR_NO_DATA; + // 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; - 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]); + event_[i] = 0; } - if (pipe_[i].handle != INVALID_HANDLE_VALUE) + if (pipe_[i].handle != INVALID_HANDLE_VALUE) { CloseHandle(pipe_[i].handle); + pipe_[i].handle = INVALID_HANDLE_VALUE; + } } } @@ -367,23 +414,25 @@ bool LyXComm::event(QEvent * e) } -BOOL LyXComm::checkStopServer() +bool LyXComm::checkStopServer(DWORD timeout) { - return WaitForSingleObject(stopserver_, 0) == WAIT_OBJECT_0; + return WaitForSingleObject(stopserver_, timeout) == WAIT_OBJECT_0; } -void LyXComm::startPipe(DWORD index) +bool LyXComm::startPipe(DWORD index) { 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)) { - // 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()) { @@ -396,55 +445,64 @@ void LyXComm::startPipe(DWORD index) // Client is already connected, so signal an event. if (SetEvent(pipe_[index].overlap.hEvent)) break; + // fall through 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)) { - 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; } + + bool const is_outpipe = index >= MAX_CLIENTS; + 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); + pipe_[index].iobuf.erase(); 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) { - 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 ? - CONNECTING_STATE : (index < MAX_PIPES ? READING_STATE - : WRITING_STATE); + CONNECTING_STATE : (is_outpipe ? WRITING_STATE + : READING_STATE); + return true; } @@ -454,34 +512,60 @@ void LyXComm::openConnection() // If we are up, that's an error if (ready_) { - LYXERR0("LyXComm: Already connected"); + LYXERR(Debug::LYXSERVER, "LyXComm: Already connected"); 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; } - // 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)) { - 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); + 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(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(); + CloseHandle(stopserver_); + CloseHandle(outbuf_mutex_); return; } } @@ -502,7 +586,13 @@ void LyXComm::closeConnection() 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_); } @@ -510,11 +600,17 @@ void LyXComm::emergencyCleanup() { 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(outbuf_mutex_); } } @@ -523,19 +619,19 @@ void LyXComm::read_ready(DWORD inpipe) { // 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 - 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; - readbuf_[inpipe] = split(readbuf_[inpipe], cmd, '\n'); + pipe_[inpipe].iobuf = split(pipe_[inpipe].iobuf, cmd, '\n'); 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); @@ -549,7 +645,8 @@ void LyXComm::read_ready(DWORD inpipe) 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; } @@ -559,38 +656,38 @@ void LyXComm::send(string const & msg) return; if (!ready_) { - LYXERR0("LyXComm: Pipes are closed. Could not send " << msg); + lyxerr << "LyXComm: Pipes are closed. Could not send " + << msg << endl; 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 { - // 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(); - if (!checkStopServer()) - openConnection(); + openConnection(); } } string const LyXComm::pipeName(DWORD index) const { - return index < MAX_PIPES ? inPipeName() : outPipeName(); + return index < MAX_CLIENTS ? inPipeName() : outPipeName(); } @@ -600,6 +697,7 @@ string const LyXComm::pipeName(DWORD index) const LyXComm::LyXComm(string const &, Server *, ClientCallbackfct) {} + void LyXComm::openConnection() {} @@ -621,6 +719,7 @@ void LyXComm::endPipe(int & fd, string const & filename, bool write) void LyXComm::emergencyCleanup() {} + void LyXComm::read_ready() {} @@ -631,11 +730,11 @@ void LyXComm::send(string const & msg) #else // defined (HAVE_MKFIFO) - LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb) : pipename_(pip), client_(cli), clientcb_(ccb) { ready_ = false; + deferred_loading_ = false; openConnection(); } @@ -713,6 +812,12 @@ int LyXComm::startPipe(string const & file, bool write) 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; @@ -755,7 +860,7 @@ int LyXComm::startPipe(string const & file, bool write) if (!write) { theApp()->registerSocketCallback(fd, - boost::bind(&LyXComm::read_ready, this)); + bind(&LyXComm::read_ready, this)); } return fd; @@ -775,7 +880,7 @@ void LyXComm::endPipe(int & fd, string const & filename, bool write) << '\n' << strerror(errno) << endl; } - if (FileName(filename).removeFile() < 0) { + if (!FileName(filename).removeFile()) { lyxerr << "LyXComm: Could not remove pipe " << filename << '\n' << strerror(errno) << endl; } @@ -813,7 +918,6 @@ void LyXComm::read_ready() 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"); @@ -864,7 +968,8 @@ void LyXComm::send(string const & 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); @@ -879,6 +984,49 @@ void LyXComm::send(string const & msg) #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::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 { @@ -903,8 +1051,8 @@ void ServerCallback(Server * server, string const & 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) {} @@ -923,7 +1071,6 @@ Server::~Server() int compare(char const * a, char const * b, unsigned int len) { - using namespace std; return strncmp(a, b, len); } @@ -1025,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 - // connect to the lyxfunc in the single LyXView we + // connect to the lyxfunc in the single GuiView we // support currently. (Lgb) - func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg)); - string const rval = to_utf8(func_->getMessage()); + FuncRequest fr(lyxaction.lookupFunc(cmd), from_ascii(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; - if (func_->errorStat()) + if (dr.error()) buf = "ERROR:"; else buf = "INFO:";