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"
+
+#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 <boost/bind.hpp>
+#include <iostream>
#ifdef _WIN32
-#include <QCoreApplication>
+# include <io.h>
+# include <QCoreApplication>
+#else
+# ifdef HAVE_UNISTD_H
+# include <unistd.h>
+# endif
#endif
+#include <QThread>
#include <cerrno>
#ifdef HAVE_SYS_STAT_H
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<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
-extern "C" {
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;
}
-}
-
-} // namespace anon
LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
- : pipename_(pip), client_(cli), clientcb_(ccb), stopserver_(0)
+ : stopserver_(0),
+ ready_(false), pipename_(pip), client_(cli), clientcb_(ccb),
+ deferred_loading_(false)
{
- ready_ = false;
+ for (int i = 0; i < MAX_PIPES; ++i) {
+ event_[i] = 0;
+ pipe_[i].handle = INVALID_HANDLE_VALUE;
+ }
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;
- DWORD status;
- bool success;
+ outbuf_.erase();
+ DWORD status = 0;
+ bool success = false;
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,
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;
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;
}
}
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) {
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<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;
}
- // 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 || error == NO_ERROR)) {
// 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;
+ case CONNECTING_STATE:
+ LYXERR0("Wrong pipe state");
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;
+ }
}
}
}
-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()) {
// 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;
}
// 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<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();
+ CloseHandle(stopserver_);
+ CloseHandle(outbuf_mutex_);
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_);
}
{
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_);
}
}
{
// 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);
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;
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();
}
LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
{}
+
void LyXComm::openConnection()
{}
void LyXComm::emergencyCleanup()
{}
+
void LyXComm::read_ready()
{}
#else // defined (HAVE_MKFIFO)
-
LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
- : pipename_(pip), client_(cli), clientcb_(ccb)
+ : infd_(-1), outfd_(-1),
+ ready_(false), pipename_(pip), client_(cli), clientcb_(ccb),
+ deferred_loading_(false)
{
- ready_ = false;
openConnection();
}
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;
}
if (!write) {
- theApp()->registerSocketCallback(fd,
- boost::bind(&LyXComm::read_ready, this));
+ // Make sure not to call read_ready after destruction.
+ weak_ptr<void> tracker = tracker_.p();
+ theApp()->registerSocketCallback(fd, [this, tracker](){
+ if (!tracker.expired())
+ read_ready();
+ });
}
return fd;
<< '\n' << strerror(errno) << endl;
}
- if (FileName(filename).removeFile() < 0) {
+ if (!FileName(filename).removeFile()) {
lyxerr << "LyXComm: Could not remove pipe " << filename
<< '\n' << strerror(errno) << endl;
}
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");
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);
#endif // defined (HAVE_MKFIFO)
+namespace {
+
+struct Sleep : QThread
+{
+ static void millisec(unsigned long ms)
+ {
+ QThread::usleep(ms * 1000);
+ }
+};
+
+} // namespace
+
+
+bool LyXComm::loadFilesInOtherInstance() const
+{
+ int pipefd;
+ FileName const pipe(inPipeName());
+
+ if (theFilesToLoad().empty()) {
+ LYXERR0("LyX is already running in another instance\n"
+ "and 'use single instance' is active.");
+ // Wait a while for the other instance to reset the connection
+ Sleep::millisec(200);
+ pipefd = ::open(pipe.toFilesystemEncoding().c_str(), O_WRONLY);
+ if (pipefd >= 0) {
+ string const cmd = "LYXCMD:pipe:window-raise\n";
+ if (::write(pipefd, cmd.c_str(), cmd.length()) < 0)
+ LYXERR0("Cannot communicate with running instance!");
+ ::close(pipefd);
+ }
+ return true;
+ }
+
+ int loaded_files = 0;
+ 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
{
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)
{}
int compare(char const * a, char const * b, unsigned int len)
{
- using namespace std;
return strncmp(a, b, len);
}
<< client << " said goodbye");
} else {
LYXERR(Debug::LYXSERVER,
- "Server: ignoring bye messge from unregistered client" << client);
+ "Server: ignoring bye message from unregistered client" << client);
}
} else {
LYXERR0("Server: Undefined server command " << cmd << '.');
// 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_utf8(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:";