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/lstrings.h"
#include "support/os.h"
-#include <boost/bind.hpp>
+#include "support/bind.h"
#ifdef _WIN32
#include <QCoreApplication>
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 anon
-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)
{
+ for (int i = 0; i < MAX_PIPES; ++i) {
+ event_[i] = 0;
+ pipe_[i].handle = INVALID_HANDLE_VALUE;
+ }
ready_ = false;
openConnection();
}
-void LyXComm::pipeServer()
+bool LyXComm::pipeServer()
{
DWORD i;
+ DWORD error;
for (i = 0; i < MAX_PIPES; ++i) {
bool const is_outpipe = i >= MAX_CLIENTS;
// Manual-reset event, initial state = signaled
event_[i] = CreateEvent(NULL, TRUE, TRUE, NULL);
if (!event_[i]) {
+ error = GetLastError();
lyxerr << "LyXComm: Could not create event for pipe "
- << pipename.c_str() << "\nLyXComm: "
- << errormsg() << endl;
- closeHandles(i);
- return;
+ << pipename << "\nLyXComm: "
+ << errormsg(error) << endl;
+ return false;
}
pipe_[i].overlap.hEvent = event_[i];
PIPE_TIMEOUT, NULL);
if (pipe_[i].handle == INVALID_HANDLE_VALUE) {
+ error = GetLastError();
lyxerr << "LyXComm: Could not create pipe "
- << pipename.c_str() << "\nLyXComm: "
- << errormsg() << endl;
- closeHandles(i);
- return;
+ << pipename << "\nLyXComm: "
+ << errormsg(error) << endl;
+ return false;
}
- startPipe(i);
+ if (!startPipe(i))
+ return false;
pipe_[i].state = pipe_[i].pending_io ?
CONNECTING_STATE : (is_outpipe ? WRITING_STATE
: READING_STATE);
case CONNECTING_STATE:
// Pending connect operation
if (!success) {
+ error = GetLastError();
lyxerr << "LyXComm: "
- << errormsg() << endl;
- resetPipe(i, true);
+ << errormsg(error) << endl;
+ if (!resetPipe(i, true))
+ return false;
continue;
}
pipe_[i].state = is_outpipe ? WRITING_STATE
// Pending read operation
LASSERT(!is_outpipe, /**/);
if (!success || status == 0) {
- resetPipe(i);
+ if (!resetPipe(i, !success))
+ return false;
continue;
}
pipe_[i].nbytes = status;
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) {
- lyxerr << "LyXComm: " << errormsg() << endl;
+ 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:
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;
// The write operation completed successfully.
pipe_[i].iobuf.erase();
pipe_[i].pending_io = false;
- resetPipe(i);
+ if (!resetPipe(i))
+ return false;
continue;
}
- if (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.
- Sleep(100);
+ 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) {
+ if (!success) {
lyxerr << "LyXComm: Error sending message: "
<< pipe_[i].iobuf << "\nLyXComm: "
- << errormsg() << endl;
- resetPipe(i, true);
- } else
- resetPipe(i);
+ << 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 <= int(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.
+ DWORD const error = GetLastError();
lyxerr << "LyXComm: Could not connect pipe "
<< external_path(pipeName(index))
- << "\nLyXComm: " << errormsg() << endl;
- return;
+ << "\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.
+ DWORD const error = GetLastError();
lyxerr << "LyXComm: An error occurred while connecting pipe "
<< external_path(pipeName(index))
- << "\nLyXComm: " << errormsg() << endl;
+ << "\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)) {
+ DWORD const error = GetLastError();
lyxerr << "LyXComm: Could not disconnect pipe "
<< external_path(pipeName(index))
- << "\nLyXComm: " << errormsg() << endl;
+ << "\nLyXComm: " << errormsg(error) << endl;
// What to do now? Let's try whether re-creating the pipe helps.
close_handle = true;
}
PIPE_TIMEOUT, NULL);
if (pipe_[index].handle == INVALID_HANDLE_VALUE) {
+ DWORD const error = GetLastError();
lyxerr << "LyXComm: Could not reset pipe " << name
- << "\nLyXComm: " << errormsg() << endl;
- return;
+ << "\nLyXComm: " << errormsg(error) << endl;
+ return false;
}
}
- startPipe(index);
+ if (!startPipe(index))
+ return false;
pipe_[index].state = pipe_[index].pending_io ?
CONNECTING_STATE : (is_outpipe ? WRITING_STATE
: READING_STATE);
+ return true;
}
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");
// 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() << endl;
+ << "\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() << endl;
+ << "\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_) {
+ DWORD const error = GetLastError();
lyxerr << "LyXComm: Could not create pipe server thread"
- << "\nLyXComm: " << errormsg() << endl;
+ << "\nLyXComm: " << errormsg(error) << endl;
pipename_.erase();
CloseHandle(stopserver_);
CloseHandle(outbuf_mutex_);
SetEvent(stopserver_);
// Forcibly terminate the pipe server thread if it does
// not finish quickly.
- if (WaitForSingleObject(server_thread_, 200) != WAIT_OBJECT_0)
+ if (WaitForSingleObject(server_thread_, 200) != WAIT_OBJECT_0) {
TerminateThread(server_thread_, 0);
+ ready_ = false;
+ closeHandles();
+ }
CloseHandle(server_thread_);
ResetEvent(stopserver_);
CloseHandle(stopserver_);
ReleaseMutex(outbuf_mutex_);
} else {
// Something is fishy, better resetting the connection.
+ DWORD const error = GetLastError();
lyxerr << "LyXComm: Error sending message: " << msg
- << "\nLyXComm: " << errormsg()
- << "LyXComm: Resetting connection" << endl;
+ << "\nLyXComm: " << errormsg(error)
+ << "\nLyXComm: Resetting connection" << endl;
ReleaseMutex(outbuf_mutex_);
closeConnection();
- if (!checkStopServer())
- openConnection();
+ openConnection();
}
}
if (!write) {
theApp()->registerSocketCallback(fd,
- boost::bind(&LyXComm::read_ready, this));
+ bind(&LyXComm::read_ready, this));
}
return fd;
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)
{}
// 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 const fr(lyxaction.lookupFunc(cmd), arg);
+ 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:";