Kill engines that do not respond to 'quit' in time
authorH.G.Muller <hgm@hgm-xboard.(none)>
Tue, 18 Jun 2019 10:48:20 +0000 (12:48 +0200)
committerH.G.Muller <hgm@hgm-xboard.(none)>
Tue, 18 Jun 2019 11:47:21 +0000 (13:47 +0200)
If an engine process is still alive 50ms after the adapter received a
'quit' command, it will now be killed. This is done by the GUI thread
(which now also causes the adapter to exit), because the engine thread
might be tied up in listening to an engine that has no intention to
respond to 'stop'. (Which would be a necessary condition for the engine
thread to relay the queued 'quit' command.)

UCI2WB.c

index 10b1f6d..4ca1598 100644 (file)
--- a/UCI2WB.c
+++ b/UCI2WB.c
 #  include <io.h>\r
    HANDLE process;\r
    DWORD thread_id;\r
 #  include <io.h>\r
    HANDLE process;\r
    DWORD thread_id;\r
+   void Bury(int s) { if(WaitForSingleObject(process, 1000*s+50) != WAIT_OBJECT_0) TerminateProcess(process, 0); }\r
 #else\r
 #  include <pthread.h>\r
 #  include <signal.h>\r
 #  include <unistd.h>\r
 #  define NO_ERROR 0\r
 #  include <sys/time.h>\r
 #else\r
 #  include <pthread.h>\r
 #  include <signal.h>\r
 #  include <unistd.h>\r
 #  define NO_ERROR 0\r
 #  include <sys/time.h>\r
+#  include <sys/wait.h>\r
    int GetTickCount() // with thanks to Tord\r
    { struct timeval t; gettimeofday(&t, NULL); return t.tv_sec*1000 + t.tv_usec/1000; }\r
 //#  include <unistd.h>\r
    int Sleep(int msec) { return usleep(1000*msec); }\r
    int GetTickCount() // with thanks to Tord\r
    { struct timeval t; gettimeofday(&t, NULL); return t.tv_sec*1000 + t.tv_usec/1000; }\r
 //#  include <unistd.h>\r
    int Sleep(int msec) { return usleep(1000*msec); }\r
+   int pid; void Bury(int msec) { Sleep(msec+50); if(waitpid(-1, NULL, WNOHANG) <= 0) kill(pid, SIGKILL); }\r
 #endif\r
 #include <fcntl.h>\r
 #include <string.h>\r
 #endif\r
 #include <fcntl.h>\r
 #include <string.h>\r
@@ -52,10 +55,9 @@ int statDepth, statScore, statNodes, statTime, currNr, size, collect, nr, sm, in
 char currMove[20], moveMap[500][10], /* for analyze mode */ canPonder[20], threadOpt[20], varList[8000], anaOpt[20], checkOptions[8192] = "Ponder";\r
 char pvs[99][999], board[100];  // XQ board for UCCI\r
 char *nameWord = "name ", *valueWord = "value ", *wTime = "w", *bTime = "b", *wInc = "winc", *bInc = "binc", newGame; // keywords that differ in UCCI\r
 char currMove[20], moveMap[500][10], /* for analyze mode */ canPonder[20], threadOpt[20], varList[8000], anaOpt[20], checkOptions[8192] = "Ponder";\r
 char pvs[99][999], board[100];  // XQ board for UCCI\r
 char *nameWord = "name ", *valueWord = "value ", *wTime = "w", *bTime = "b", *wInc = "winc", *bInc = "binc", newGame; // keywords that differ in UCCI\r
-int unit = 1, drawOffer, scores[99], mpvSP, maxDepth, ponderAlways, newCnt, priority;\r
+int unit = 1, drawOffer, scores[99], mpvSP, maxDepth, ponderAlways, newCnt, priority, killDelay;\r
 \r
 FILE *toE, *fromE, *fromF;\r
 \r
 FILE *toE, *fromE, *fromF;\r
-int pid;\r
 \r
 char *strcasestr (char *p, char *q) { while(*p) { char *r=p++, *s=q; while(tolower(*r++) == tolower(*s) && *s) s++; if(!*s) return p-1; } return NULL; }\r
 \r
 \r
 char *strcasestr (char *p, char *q) { while(*p) { char *r=p++, *s=q; while(tolower(*r++) == tolower(*s) && *s) s++; if(!*s) return p-1; } return NULL; }\r
 \r
@@ -533,7 +535,7 @@ GUI2Engine()
        p = line; while(qEnd < queue+10000 && (*qEnd++ = *p++) != '\n') {}\r
        Sync(WAKEUP);\r
        // when 'stop' doesn't catch engine's attention in reasonable time, so the GUI might kill us:\r
        p = line; while(qEnd < queue+10000 && (*qEnd++ = *p++) != '\n') {}\r
        Sync(WAKEUP);\r
        // when 'stop' doesn't catch engine's attention in reasonable time, so the GUI might kill us:\r
-       if(!strcmp(command, "quit")) { Sleep(500); EPRINT((f, "quit\n")); } // make sure 'quit' is still sent\r
+       if(!strcmp(command, "quit")) { Bury(killDelay); exit(0); } // kill the engine and exit\r
       }\r
     }\r
 }\r
       }\r
     }\r
 }\r
@@ -632,7 +634,7 @@ DoCommand ()
            if(sc == 's') EPRINT((f, "# gameover %s\n", line[8] == '/' ? "draw" : (line[7] == '0') == mySide ? "win" : "lose"))\r
            computer = NONE;\r
        }\r
            if(sc == 's') EPRINT((f, "# gameover %s\n", line[8] == '/' ? "draw" : (line[7] == '0') == mySide ? "win" : "lose"))\r
            computer = NONE;\r
        }\r
-       else if(!strcmp(command, "quit"))   { EPRINT((f, "# quit\n")) fflush(toE), exit(atoi(line+4)); }\r
+       else if(!strcmp(command, "quit"))   { EPRINT((f, "# quit\n")) fflush(toE); }\r
        else printf("Error (unknown command): %s\n", command);\r
 \r
        fflush(stdout);\r
        else printf("Error (unknown command): %s\n", command);\r
 \r
        fflush(stdout);\r
@@ -704,7 +706,6 @@ StartEngine(char *cmdLine, char *dir)
   CloseHandle(hChildStdoutWr);\r
 \r
   process = piProcInfo.hProcess;\r
   CloseHandle(hChildStdoutWr);\r
 \r
   process = piProcInfo.hProcess;\r
-  pid = piProcInfo.dwProcessId;\r
   fromE = (FILE*) _fdopen( _open_osfhandle((long)hChildStdoutRd, _O_TEXT|_O_RDONLY), "r");\r
   toE   = (FILE*) _fdopen( _open_osfhandle((long)hChildStdinWr, _O_WRONLY), "w");\r
 #else\r
   fromE = (FILE*) _fdopen( _open_osfhandle((long)hChildStdoutRd, _O_TEXT|_O_RDONLY), "r");\r
   toE   = (FILE*) _fdopen( _open_osfhandle((long)hChildStdinWr, _O_WRONLY), "w");\r
 #else\r