You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2025/05/15 14:58:51 UTC
(commons-daemon) branch master updated: Arrange the timeout logic and values (#238)
This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-daemon.git
The following commit(s) were added to refs/heads/master by this push:
new f0b4524 Arrange the timeout logic and values (#238)
f0b4524 is described below
commit f0b4524ac6981cc6b50d69612ab124c14d2f7d86
Author: Jean-Frederic Clere <jf...@gmail.com>
AuthorDate: Thu May 15 16:58:45 2025 +0200
Arrange the timeout logic and values (#238)
Change ONE_MINUTE to ONE_MINUTE_AS_MILLIS and ONE_MINUTE_SEC to ONE_MINUTE_AS_SEC.
Don't allow to install service with negative StopTimeout value.
Arrange explaination for StopTimeout
Use ERROR and display the already waited time in case StopTimeout is too small.
---
src/native/windows/apps/prunsrv/prunsrv.c | 191 ++++++++++++++++++++++++------
src/native/windows/src/javajni.c | 4 +-
src/native/windows/src/rprocess.c | 16 +++
src/site/xdoc/procrun.xml | 12 +-
4 files changed, 181 insertions(+), 42 deletions(-)
diff --git a/src/native/windows/apps/prunsrv/prunsrv.c b/src/native/windows/apps/prunsrv/prunsrv.c
index ce01b88..771b87f 100644
--- a/src/native/windows/apps/prunsrv/prunsrv.c
+++ b/src/native/windows/apps/prunsrv/prunsrv.c
@@ -41,7 +41,8 @@
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
-#define ONE_MINUTE (60 * 1000)
+#define ONE_MINUTE_AS_MILLIS (60 * 1000)
+#define ONE_MINUTE_AS_SEC 60
#ifdef _WIN64
#define KREG_WOW6432 KEY_WOW64_32KEY
@@ -258,6 +259,8 @@ static BOOL gSignalValid = TRUE;
static APXJAVA_THREADARGS gRargs;
static APXJAVA_THREADARGS gSargs;
+static DWORD stopCalledTime = 0; /* Not stop not started */
+
DWORD WINAPI eventThread(LPVOID lpParam)
{
DWORD dwRotateCnt = SO_LOGROTATE;
@@ -289,6 +292,21 @@ DWORD WINAPI eventThread(LPVOID lpParam)
UNREFERENCED_PARAMETER(lpParam);
}
+/* Calculate how much time we already spent stopping the service */
+static DWORD waitedSinceStopCmd()
+{
+ DWORD now = 0;
+ DWORD waited = 0;
+ now = GetTickCount();
+ if (now >= stopCalledTime)
+ waited = now - stopCalledTime;
+ else {
+ /* we have wrapped to zero */
+ waited = (0xFFFFFFFF - stopCalledTime) + now;
+ }
+ return waited;
+}
+
/* redirect console stdout/stderr to files
* so that Java messages can get logged
* If stderrfile is not specified it will
@@ -777,6 +795,12 @@ static BOOL docmdInstallService(LPAPXCMDLINE lpCmdline)
apxLogWrite(APXLOG_MARK_ERROR "Unable to open the Service Manager.");
return FALSE;
}
+
+ /* Check the stop timeout value */
+ if (SO_STOPTIMEOUT > 0x7FFFFFFF) {
+ apxLogWrite(APXLOG_MARK_ERROR "StopTimeout can't be lower that 0");
+ return FALSE;
+ }
/* Check the startup mode */
if (ST_STARTUP & APXCMDOPT_FOUND) {
if (lstrcmpiW(SO_STARTUP, PRSRV_AUTO) == 0) {
@@ -940,6 +964,7 @@ static BOOL docmdStopService(LPAPXCMDLINE lpCmdline)
return FALSE;
}
+ stopCalledTime = GetTickCount();
SetLastError(ERROR_SUCCESS);
/* Open the service */
if (apxServiceOpen(hService, lpCmdline->szApplication,
@@ -951,15 +976,34 @@ static BOOL docmdStopService(LPAPXCMDLINE lpCmdline)
NULL);
if (!rv) {
/* Wait for the timeout if any */
- int timeout = SO_STOPTIMEOUT;
- if (timeout) {
- int i;
- for (i = 0; i < timeout; i++) {
- rv = apxServiceCheckStop(hService);
- apxLogWrite(APXLOG_MARK_DEBUG "apxServiceCheck returns %d.", rv);
- if (rv)
- break;
- }
+ int timeout = SO_STOPTIMEOUT;
+ int waited = waitedSinceStopCmd();
+ int i;
+ if (!timeout) {
+ /* waiting for ever doesn't look OK here */
+ timeout = ONE_MINUTE_AS_SEC;
+ }
+ apxLogWrite(APXLOG_MARK_DEBUG "docmdStopService Waited %d timeout %d", waited, timeout*1000);
+ if (timeout*1000 > waited) {
+ /*
+ * it took waited to send the command, the service starts
+ * counting the timeout once it receives the STOP command
+ * that is probably after waited but that depends on the
+ * box we are using, we add 1 second just to be sure.
+ */
+ timeout = timeout*1000 + waited + 1000;
+ } else {
+ apxLogWrite(APXLOG_MARK_DEBUG "docmdStopService timeout %d too small.", timeout*1000);
+ timeout = 1000; /* we might wait 1 s too much but we check if we have already stopped */
+ }
+ apxLogWrite(APXLOG_MARK_DEBUG "docmdStopService estimated timeout %d milliseconds.", timeout);
+ /* the SO_STOPTIMEOUT applies to the stop command and to the time service needs to stop */
+ for (i = 0; i < timeout; i=i+1000) {
+ /* apxServiceCheckStop waits 1000 ms */
+ rv = apxServiceCheckStop(hService);
+ apxLogWrite(APXLOG_MARK_DEBUG "apxServiceCheck returns %d.", rv);
+ if (rv)
+ break;
}
}
if (rv)
@@ -1223,16 +1267,26 @@ static DWORD WINAPI serviceStop(LPVOID lpParameter)
BOOL wait_to_die = FALSE;
DWORD timeout = SO_STOPTIMEOUT * 1000;
DWORD dwCtrlType = (DWORD)((BYTE *)lpParameter - (BYTE *)0);
+ DWORD waited = 0;
apxLogWrite(APXLOG_MARK_INFO "Stopping service...");
+ stopCalledTime = GetTickCount();
+ if (dwCtrlType == SERVICE_CONTROL_SHUTDOWN)
+ timeout = MIN(timeout, apxGetMaxServiceTimeout(gPool));
+ if (!timeout) {
+ /* Use 1 minute default */
+ timeout = ONE_MINUTE_AS_MILLIS;
+ }
+ apxLogWrite(APXLOG_MARK_INFO "Stopping service...timeout %d", timeout);
+ /* give a hint for shutdown time */
+ reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, timeout);
if (IS_INVALID_HANDLE(gWorker)) {
apxLogWrite(APXLOG_MARK_INFO "Worker is not defined.");
return TRUE; /* Nothing to do */
}
- if (timeout > 0x7FFFFFFF)
- timeout = INFINITE; /* If the timeout was '-1' wait forewer */
if (_jni_shutdown) {
+ apxLogWrite(APXLOG_MARK_INFO "Stopping service...timeout %d _jni_shutdown", timeout);
if (!IS_VALID_STRING(SO_STARTPATH) && IS_VALID_STRING(SO_STOPPATH)) {
/* If the Working path is specified change the current directory
* but only if the start path wasn't specified already.
@@ -1269,16 +1323,14 @@ static DWORD WINAPI serviceStop(LPVOID lpParameter)
}
else {
if (lstrcmpA(_jni_sclass, "java/lang/System") == 0) {
- reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 20 * 1000);
+ /* report progress */
+ reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, timeout);
apxLogWrite(APXLOG_MARK_DEBUG "Forcing Java JNI System.exit() worker to finish...");
return 0;
}
else {
apxLogWrite(APXLOG_MARK_DEBUG "Waiting for Java JNI stop worker to finish for %s:%s...", _jni_sclass, _jni_smethod);
- if (!timeout)
- apxJavaWait(hWorker, INFINITE, FALSE);
- else
- apxJavaWait(hWorker, timeout, FALSE);
+ apxJavaWait(hWorker, timeout, FALSE);
apxLogWrite(APXLOG_MARK_DEBUG "Java JNI stop worker finished.");
}
}
@@ -1351,10 +1403,7 @@ static DWORD WINAPI serviceStop(LPVOID lpParameter)
goto cleanup;
} else {
apxLogWrite(APXLOG_MARK_DEBUG "Waiting for stop worker to finish...");
- if (!timeout)
- apxHandleWait(hWorker, INFINITE, FALSE);
- else
- apxHandleWait(hWorker, timeout, FALSE);
+ apxHandleWait(hWorker, timeout, FALSE);
apxLogWrite(APXLOG_MARK_DEBUG "Stop worker finished.");
}
wait_to_die = TRUE;
@@ -1375,12 +1424,21 @@ cleanup:
CloseHandle(gSignalThread);
gSignalEvent = NULL;
}
- if (wait_to_die && !timeout)
- timeout = 300 * 1000; /* Use the 5 minute default shutdown */
- if (dwCtrlType == SERVICE_CONTROL_SHUTDOWN)
- timeout = MIN(timeout, apxGetMaxServiceTimeout(gPool));
- reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, timeout);
+ /* We already waited here timeout is msec */
+ waited = waitedSinceStopCmd();
+ apxLogWrite(APXLOG_MARK_DEBUG "Waited %d timeout (%d)", waited, timeout);
+ if (timeout > waited) {
+ timeout = timeout - waited;
+ /* renew the hint message */
+ reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, timeout);
+ } else {
+ /* something is wrong, the timeout is too small */
+ apxLogWrite(APXLOG_MARK_DEBUG "Waiting more than the specified timeout (%d)", timeout);
+ /* tell we fail */
+ reportServiceStatus(SERVICE_STOP_PENDING, ERROR_SERVICE_REQUEST_TIMEOUT, 0);
+ }
+
if (timeout) {
FILETIME fts, fte;
@@ -1609,11 +1667,14 @@ void WINAPI service_ctrl_handler(DWORD dwCtrlCode)
case SERVICE_CONTROL_STOP:
apxLogWrite(APXLOG_MARK_INFO "Service SERVICE_CONTROL_STOP signalled.");
_exe_shutdown = TRUE;
- if (SO_STOPTIMEOUT > 0) {
+ stopCalledTime = GetTickCount();
+ /* hint for shutdown time */
+ if (SO_STOPTIMEOUT) {
reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, SO_STOPTIMEOUT * 1000);
}
else {
- reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 3 * 1000);
+ /* Use 1 minute default */
+ reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, ONE_MINUTE_AS_MILLIS);
}
/* Stop the service asynchronously */
stopThread = CreateThread(NULL, 0,
@@ -1838,9 +1899,13 @@ void WINAPI serviceMain(DWORD argc, LPTSTR *argv)
SetConsoleCtrlHandler((PHANDLER_ROUTINE)console_handler, TRUE);
if (SO_STOPTIMEOUT) {
- /* we have a stop timeout */
+ /* we wait until the service has stopped or crashed, printing a debug message every 2 seconds */
+ /* we also warn in case the service doesn't stop before the stop timeout */
BOOL bLoopWarningIssued = FALSE;
+ int waited = 0;
+ int timeout = SO_STOPTIMEOUT;
do {
+ DWORD count = 0;
/* wait 2 seconds */
DWORD rv = apxHandleWait(gWorker, 2000, FALSE);
if (rv == WAIT_OBJECT_0 && !_exe_shutdown) {
@@ -1848,12 +1913,31 @@ void WINAPI serviceMain(DWORD argc, LPTSTR *argv)
apxLogWrite(APXLOG_MARK_WARN "Start method returned before stop method was called. This should not happen. Using loop with a fixed sleep of 2 seconds waiting for stop method to be called.");
bLoopWarningIssued = TRUE;
}
+ /* XXXX apart error are we getting there??? */
Sleep(2000);
+ count = count + 2;
+ if (count >= SO_STOPTIMEOUT) {
+ apxLogWrite(APXLOG_MARK_WARN "waited %d sec, Timeout reached!" , count);
+ break;
+ }
}
+ if (rv == WAIT_OBJECT_0)
+ apxLogWrite(APXLOG_MARK_WARN "Worker has crashed or stopped!");
+ else
+ apxLogWrite(APXLOG_MARK_DEBUG "waiting until Worker is done...");
} while (!_exe_shutdown);
- apxLogWrite(APXLOG_MARK_DEBUG "waiting %d sec... shutdown: %d", SO_STOPTIMEOUT, _exe_shutdown);
- apxHandleWait(gWorker, SO_STOPTIMEOUT*1000, FALSE);
+
+ /* calculate remaing timeout */
+ waited = waitedSinceStopCmd();
+ if (timeout*1000 > waited) {
+ timeout = timeout*1000 - waited;
+ } else {
+ timeout = 1000; /* 1000 ms in the worse case */
+ }
+ apxLogWrite(APXLOG_MARK_DEBUG "waiting %d milliseconds... shutdown: %d", timeout, _exe_shutdown);
+ apxHandleWait(gWorker, timeout, FALSE);
} else {
+ apxLogWrite(APXLOG_MARK_DEBUG "waiting until Worker is done...");
apxHandleWait(gWorker, INFINITE, FALSE);
}
apxLogWrite(APXLOG_MARK_DEBUG "Worker finished.");
@@ -1863,21 +1947,52 @@ void WINAPI serviceMain(DWORD argc, LPTSTR *argv)
goto cleanup;
}
if (gShutdownEvent) {
+ int waited = waitedSinceStopCmd();
+ int timeout = SO_STOPTIMEOUT;
+ BOOL btimeoutelapsed = FALSE;
/* Ensure that shutdown thread exits before us */
apxLogWrite(APXLOG_MARK_DEBUG "Waiting for ShutdownEvent.");
- reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, ONE_MINUTE);
- WaitForSingleObject(gShutdownEvent, ONE_MINUTE);
+ reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, ONE_MINUTE_AS_MILLIS);
+ WaitForSingleObject(gShutdownEvent, ONE_MINUTE_AS_MILLIS);
apxLogWrite(APXLOG_MARK_DEBUG "ShutdownEvent signaled.");
CloseHandle(gShutdownEvent);
gShutdownEvent = NULL;
+ /* calculate the next timeout here timeout is sec */
+ if (!timeout)
+ timeout = ONE_MINUTE_AS_SEC;
+ apxLogWrite(APXLOG_MARK_DEBUG "Waited %d timeout (%d)", waited, timeout*1000);
+ if (timeout*1000 > waited) {
+ timeout = timeout*1000 - waited;
+ } else {
+ /* something is wrong, the timeout is too small */
+ apxLogWrite(APXLOG_MARK_ERROR "Waiting more than the specified timeout (%d) already waited for %d", timeout, waited/1000);
+ timeout = ONE_MINUTE_AS_MILLIS;
+ btimeoutelapsed = TRUE;
+ }
+
/* This will cause to wait for all threads to exit
*/
- apxLogWrite(APXLOG_MARK_DEBUG "Waiting 1 minute for all threads to exit.");
- reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, ONE_MINUTE);
- apxDestroyJvm(ONE_MINUTE);
- /* if we are not using JAVA apxDestroyJvm does nothing, check the chid processes in case they hang */
+ apxLogWrite(APXLOG_MARK_DEBUG "Waiting %d milliseconds for all threads to exit.", timeout);
+ reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, ONE_MINUTE_AS_MILLIS);
+ if (!apxDestroyJvm(timeout)) {
+ /* if we are not using JAVA apxDestroyJvm does nothing, check the chid processes in case they hang */
+ apxLogWrite(APXLOG_MARK_DEBUG "apxDestroyJvm did nothing or failed");
+ for (;;) {
+ if (!apxProcessTerminateChild( GetCurrentProcessId(), TRUE)) {
+ /* Just print the children processes once for debugging */
+ if (btimeoutelapsed)
+ break;
+ waited = waitedSinceStopCmd();
+ if (waited >= timeout)
+ break; /* Done */
+ Sleep(1000);
+ } else {
+ break;
+ }
+ }
+ }
apxProcessTerminateChild( GetCurrentProcessId(), FALSE); /* FALSE kills! */
}
else {
@@ -1887,7 +2002,7 @@ void WINAPI serviceMain(DWORD argc, LPTSTR *argv)
*/
apxLogWrite(APXLOG_MARK_DEBUG "Waiting for all threads to exit.");
apxDestroyJvm(INFINITE);
- reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
+ reportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, ONE_MINUTE_AS_MILLIS);
}
apxLogWrite(APXLOG_MARK_DEBUG "JVM destroyed.");
reportServiceStatusStopped(apxGetVmExitCode());
diff --git a/src/native/windows/src/javajni.c b/src/native/windows/src/javajni.c
index 1aad2e1..e98630e 100644
--- a/src/native/windows/src/javajni.c
+++ b/src/native/windows/src/javajni.c
@@ -486,8 +486,10 @@ apxDestroyJvm(DWORD dwTimeout)
CloseHandle(hWaiter);
return rv;
}
- else
+ else {
+ apxLogWrite(APXLOG_MARK_DEBUG "apxDestroyJvm No JVM so Done");
return FALSE;
+ }
}
static BOOL __apxIsJava9()
diff --git a/src/native/windows/src/rprocess.c b/src/native/windows/src/rprocess.c
index 46ca36f..3e23883 100644
--- a/src/native/windows/src/rprocess.c
+++ b/src/native/windows/src/rprocess.c
@@ -408,6 +408,22 @@ BOOL apxProcessTerminateChild(DWORD dwProcessId, BOOL dryrun)
apxLogWrite(APXLOG_MARK_DEBUG "Process ID: 0x%08X (%d) Termination failed!", treeProcessId[i], treeProcessId[i]);
}
}
+ } else {
+ /* Check if we have child processes and return FALSE if not */
+ HANDLE hProcess;
+ for (i=0; i<maxProcessId; i++) {
+ if (treeProcessId[i] == 0) {
+ break; /* Done */
+ }
+ hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, treeProcessId[i]);
+ if(hProcess != NULL) {
+ apxLogWrite(APXLOG_MARK_DEBUG "Process ID: 0x%08X (%d) Still here!", treeProcessId[i], treeProcessId[i]);
+ CloseHandle(hProcess);
+ CloseHandle(hProcessSnap);
+ return(FALSE);
+ }
+ }
+
}
CloseHandle(hProcessSnap);
diff --git a/src/site/xdoc/procrun.xml b/src/site/xdoc/procrun.xml
index 13213d1..f42a6a0 100644
--- a/src/site/xdoc/procrun.xml
+++ b/src/site/xdoc/procrun.xml
@@ -530,9 +530,15 @@ will add the new value(s) to any existing value(s).
</tr>
<tr>
<td>--StopTimeout</td>
- <td>No Timeout</td>
- <td>Defines the timeout in seconds that procrun waits for service to
- exit gracefully.</td>
+ <td>60 seconds</td>
+ <td>Defines the timeout in seconds that procrun waits for service
+ to stop.
+ <b>The timeout applies to the stop command and to the time service needs to stop</b>
+ Negative values are not allowed.
+ Note: Make sure you use a value big enough to give time for
+ procrun to stop the service. Once the time out is elapsed,
+ procrun will try to kill the whole process tree the service
+ has created.</td>
</tr>
<tr>
<td>--LogPath</td>