From 8d98b4e0d1d83b9a170dd8dccd86451c1c104419 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 2 Mar 2021 20:48:01 +0200 Subject: Windows: Implement IPC with mailslots --- src/ipc.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 170 insertions(+), 34 deletions(-) (limited to 'src/ipc.c') diff --git a/src/ipc.c b/src/ipc.c index 8fdc3bd5..6c528468 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -57,37 +57,31 @@ void init_Ipc(const char *runDir) { signal(SIGUSR1, SIG_IGN); } -void deinit_Ipc(void) { - iIpc *d = &ipc_; - signal(SIGUSR1, SIG_IGN); +static void doStopListening_Ipc_(iIpc *d) { if (d->isListening) { remove(lockFilePath_(d)); + d->isListening = iFalse; } - deinit_String(&d->dir); } -static void handleUserSignal_(int sig) { - iIpc *d = &ipc_; - iAssert(sig == SIGUSR1); - iUnused(sig); - const char *path = inputFilePath_(d, 0); - iFile *f = newCStr_File(path); +iProcessId check_Ipc(void) { + const iIpc *d = &ipc_; + iProcessId pid = 0; + iFile *f = newCStr_File(lockFilePath_(d)); if (open_File(f, readOnly_FileMode)) { - iString *cmds = new_String(); - initBlock_String(cmds, collect_Block(readAll_File(f))); - iRangecc line = iNullRange; - while (nextSplit_Rangecc(range_String(cmds), "\n", &line)) { - postCommand_App(cstr_Rangecc(line)); + const iBlock *running = collect_Block(readAll_File(f)); + close_File(f); + pid = atoi(constData_Block(running)); + if (!exists_Process(pid)) { + pid = 0; + remove(cstr_String(path_File(f))); /* Stale. */ } - delete_String(cmds); } iRelease(f); - remove(path); + return pid; } -void listen_Ipc(void) { - iIpc *d = &ipc_; - signal(SIGUSR1, handleUserSignal_); +static void doListen_Ipc_(iIpc *d) { iFile *f = newCStr_File(lockFilePath_(d)); if (open_File(f, writeOnly_FileMode)) { printf_Stream(stream_File(f), "%u", currentId_Process()); @@ -96,24 +90,41 @@ void listen_Ipc(void) { iRelease(f); } -iProcessId check_Ipc(void) { - const iIpc *d = &ipc_; - iProcessId pid = 0; - iFile *f = newCStr_File(lockFilePath_(d)); +static void postCommands_Ipc_(const iBlock *cmds) { + iRangecc line = iNullRange; + while (nextSplit_Rangecc(range_Block(cmds), "\n", &line)) { + postCommand_App(cstr_Rangecc(line)); + } +} + +/*----------------------------------------------------------------------------------------------*/ +#if !defined (iPlatformMsys) + +void deinit_Ipc(void) { + iIpc *d = &ipc_; + signal(SIGUSR1, SIG_IGN); + doStopListening_Ipc_(d); + deinit_String(&d->dir); +} + +static void handleUserSignal_(int sig) { + iIpc *d = &ipc_; + iAssert(sig == SIGUSR1); + iUnused(sig); + const char *path = inputFilePath_(d, 0); + iFile *f = newCStr_File(path); if (open_File(f, readOnly_FileMode)) { - const iBlock *running = collect_Block(readAll_File(f)); - close_File(f); - pid = atoi(constData_Block(running)); - if (!exists_Process(pid)) { - pid = 0; - remove(cstr_String(path_File(f))); /* Stale. */ - } + postCommands_Ipc_(collect_Block(readAll_File(f))); } iRelease(f); - return pid; + remove(path); } -/*----------------------------------------------------------------------------------------------*/ +void listen_Ipc(void) { + iIpc *d = &ipc_; + signal(SIGUSR1, handleUserSignal_); + doListen_Ipc_(d); +} iDeclareType(IpcResponse) @@ -206,5 +217,130 @@ iString *communicate_Ipc(const iString *command) { } void signal_Ipc(iProcessId pid) { - kill(pid, SIGUSR1); + if (kill(pid, SIGUSR1)) { + printf("kill failed: %s\n", strerror(errno)); + fflush(stdout); + } +} + +#endif +/*----------------------------------------------------------------------------------------------*/ +#if defined (iPlatformMsys) +/* Windows doesn't have user signals, so we'll use one of the simpler native + Win32 IPC APIs: mailslots. */ + +#include + +#define WIN32_LEAN_AND_MEAN +#include + +static iThread *listenThread_; +static HANDLE listenSlot_; + +static iThreadResult readSlotThread_Ipc_(iThread *thd) { + iIpc *d = &ipc_; + DWORD msgSize; + while (d->isListening) { + BOOL ok = GetMailslotInfo(listenSlot_, NULL, &msgSize, NULL, NULL); + if (msgSize == MAILSLOT_NO_MESSAGE) { + sleep_Thread(0.333); + continue; + } + if (!ok) break; + /* Read the message.*/ + DWORD readBytes = 0; + iBlock *msg = new_Block(msgSize); + if (ReadFile(listenSlot_, data_Block(msg), size_Block(msg), &readBytes, NULL)) { + postCommands_Ipc_(msg); + } + delete_Block(msg); + } + return 0; +} + +static const char *slotName_(int pid) { + return format_CStr("\\\\.\\mailslot\\fi.skyjake.Lagrange\\%u", pid); +} + +void deinit_Ipc(void) { + iIpc *d = &ipc_; + doStopListening_Ipc_(d); + CloseHandle(listenSlot_); + if (listenThread_) { + join_Thread(listenThread_); + iRelease(listenThread_); + } + deinit_String(&d->dir); +} + +void listen_Ipc(void) { + iIpc *d = &ipc_; + /* Create a mailslot for listening. */ + listenSlot_ = CreateMailslotA(slotName_(currentId_Process()), 0, 1000, NULL); + listenThread_ = new_Thread(readSlotThread_Ipc_); + doListen_Ipc_(d); + start_Thread(listenThread_); +} + +iBool write_Ipc(iProcessId pid, const iString *input, enum iIpcWrite type) { + iUnused(type); + HANDLE slot = CreateFile(slotName_(pid), + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (slot == INVALID_HANDLE_VALUE) { + return iFalse; + } + DWORD writeBytes = 0; + iBool ok = + WriteFile( + slot, constData_Block(utf8_String(input)), size_String(input), &writeBytes, NULL) && + writeBytes == size_String(input); + CloseHandle(slot); + return ok; +} + +iString *communicate_Ipc(const iString *command) { + iProcessId pid = check_Ipc(); + if (!pid) { + return NULL; + } + /* Open a mailslot for the response. */ + HANDLE responseSlot = CreateMailslotA(slotName_(currentId_Process()), 0, 1000, NULL); + /* Write the commands. */ + if (!write_Ipc(pid, command, command_IpcWrite)) { + CloseHandle(responseSlot); + return NULL; + } + /* Read the response. */ + iString *output = NULL; + DWORD msgSize = 0; + iTime startTime; + for (initCurrent_Time(&startTime); elapsedSeconds_Time(&startTime) < 2; ) { + if (!GetMailslotInfo(responseSlot, NULL, &msgSize, NULL, NULL)) { + break; + } + if (msgSize == MAILSLOT_NO_MESSAGE) { + sleep_Thread(0.1); + continue; + } + iBlock *resp = new_Block(msgSize); + DWORD bytesRead = 0; + ReadFile(responseSlot, data_Block(resp), msgSize, &bytesRead, NULL); + output = newBlock_String(resp); + delete_Block(resp); + break; + } + CloseHandle(responseSlot); + return output; +} + +void signal_Ipc(iProcessId pid) { + /* The write to the mailslot will trigger a read. */ + iUnused(pid); } + +#endif \ No newline at end of file -- cgit v1.2.3