Use a Windows event to stop tinc when running as a service.
authorEtienne Dechamps <etienne@edechamps.fr>
Sat, 28 Jun 2014 14:19:11 +0000 (15:19 +0100)
committerEtienne Dechamps <etienne@edechamps.fr>
Sat, 28 Jun 2014 19:00:05 +0000 (20:00 +0100)
Currently, when the tinc service handler callback (which runs in a
separate thread) receives a service shutdown request, it calls
event_exit() to request the event loop to exit.

This approach has a few issues:

 - The event loop will only notice the exit request when the next event
   fires. This slows down tinc service shutdown. In some extreme cases
   (DeviceStandby enabled, long PingTimeout and no connections),
   shutdown can take ages.

 - Strictly speaking, because of the absence of memory barriers, there
   is no guarantee that the event loop will even notice an exit request
   coming from another thread. I suppose marking the "running" variable
   as "volatile" is supposed to alleviate that, but it's unclear whether
   that provides any guarantees with modern systems and compilers.

This commit fixes the issue by leveraging the new event loop Windows
interface, using a custom Windows event that is manually set when
shutdown is requested.

src/event.c
src/process.c

index 5e6908f58a2db5150c7bcb83c7e11d21fd8f5768..8f15ebeadd95a3e55fdaab74d812ca93fdd9cba2 100644 (file)
@@ -35,7 +35,7 @@ static const long READ_EVENTS = FD_READ | FD_ACCEPT | FD_CLOSE;
 static const long WRITE_EVENTS = FD_WRITE | FD_CONNECT;
 static DWORD event_count = 0;
 #endif
-static volatile bool running;
+static bool running;
 
 static int io_compare(const io_t *a, const io_t *b) {
 #ifndef HAVE_MINGW
index c1038bcd62165728a026742ab1034b44c40ca7ec..98f4d33a811d0e9c3d6edd8d1a2a3d3413a7d29b 100644 (file)
@@ -108,6 +108,8 @@ static bool install_service(void) {
        return true;
 }
 
+static io_t stop_io;
+
 DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
        switch(request) {
                case SERVICE_CONTROL_INTERROGATE:
@@ -124,16 +126,25 @@ DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
                        return ERROR_CALL_NOT_IMPLEMENTED;
        }
 
-       event_exit();
-       status.dwWaitHint = 30000;
+       status.dwWaitHint = 1000;
        status.dwCurrentState = SERVICE_STOP_PENDING;
        SetServiceStatus(statushandle, &status);
+       if (WSASetEvent(stop_io.event) == FALSE)
+               abort();
        return NO_ERROR;
 }
 
+static void stop_handler(void *data, int flags) {
+       event_exit();
+}
+
 VOID WINAPI run_service(DWORD argc, LPTSTR* argv) {
        extern int main2(int argc, char **argv);
 
+       io_add_event(&stop_io, stop_handler, NULL, WSACreateEvent());
+       if (stop_io.event == FALSE)
+               abort();
+
        status.dwServiceType = SERVICE_WIN32;
        status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
        status.dwWin32ExitCode = 0;
@@ -160,6 +171,9 @@ VOID WINAPI run_service(DWORD argc, LPTSTR* argv) {
                SetServiceStatus(statushandle, &status);
        }
 
+       if (WSACloseEvent(stop_io.event) == FALSE)
+               abort();
+       io_del(&stop_io);
        return;
 }