Skip to content

Commit

Permalink
Update W32Service.stopService() to be more resilient to large dwWaitH…
Browse files Browse the repository at this point in the history
…int values (#1058)

-- make stopService(long timeout) public so that 30 seconds is not the only valid timeout
-- never wait more than the current timeout
-- if the service stops during the last sleep, do not throw an exception
-- use the preexisting timing logic in waitForNonPendingState to sleep fractions of the total dwWaitHint instead of the entire time
  • Loading branch information
keithharp authored and matthiasblaesing committed Jan 22, 2019
1 parent 9a1be16 commit 904a71e
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Next release (5.3.0)

Features
--------
* [#1058](https://github.com/java-native-access/jna/pull/1058): Add selectable timeout to stopService() and improve timeout handling - [@keithharp](https://github.com/keithharp).
* [#1050](https://github.com/java-native-access/jna/pull/1050): Add `c.s.j.p.win32.VersionHelpers` and supporting functions - [@dbwiddis](https://github.com/dbwiddis).

Bug Fixes
Expand Down
49 changes: 28 additions & 21 deletions contrib/platform/src/com/sun/jna/platform/win32/W32Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public void stopService() {
* @param timeout timeout in ms until the service must report to be
* stopped
*/
void stopService(long timeout) {
public void stopService(long timeout) {
long startTime = System.currentTimeMillis();
waitForNonPendingState();
// If the service is already stopped - return
Expand All @@ -236,22 +236,23 @@ void stopService(long timeout) {
// the previouos implementation queried the service status and
// failed if the application did not correctly update its state
while(status.dwCurrentState != Winsvc.SERVICE_STOPPED) {
try {
Thread.sleep( status.dwWaitHint );
long msRemainingBeforeTimeout = timeout - (System.currentTimeMillis() - startTime);

if (msRemainingBeforeTimeout < 0) {
throw new RuntimeException(String.format("Service stop exceeded timeout time of %d ms", timeout));
}

long dwWaitTime = Math.min(sanitizeWaitTime(status.dwWaitHint), msRemainingBeforeTimeout);

try {
Thread.sleep( dwWaitTime );
} catch (InterruptedException e){
throw new RuntimeException(e);
}
if(! Advapi32.INSTANCE.QueryServiceStatus(_handle, status)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
if((System.currentTimeMillis() - startTime) > timeout) {
throw new RuntimeException(String.format("Service stop exceeded timeout time of %d ms", timeout));
}
}
waitForNonPendingState();
if (queryStatus().dwCurrentState != Winsvc.SERVICE_STOPPED) {
throw new RuntimeException("Unable to stop the service");
}
}

/**
Expand Down Expand Up @@ -292,7 +293,22 @@ public void pauseService() {
}
}

/**
/**
* do not wait longer than the wait hint. A good interval is
* one-tenth the wait hint, but no less than 1 second and no
* more than 10 seconds.
*/
int sanitizeWaitTime(int dwWaitHint) {
int dwWaitTime = dwWaitHint / 10;

if (dwWaitTime < 1000)
dwWaitTime = 1000;
else if (dwWaitTime > 10000)
dwWaitTime = 10000;
return dwWaitTime;
}

/**
* Wait for the state to change to something other than a pending state.
*/
public void waitForNonPendingState() {
Expand All @@ -315,16 +331,7 @@ public void waitForNonPendingState() {
throw new RuntimeException("Timeout waiting for service to change to a non-pending state.");
}

// do not wait longer than the wait hint. A good interval is
// one-tenth the wait hint, but no less than 1 second and no
// more than 10 seconds.

int dwWaitTime = status.dwWaitHint / 10;

if (dwWaitTime < 1000)
dwWaitTime = 1000;
else if (dwWaitTime > 10000)
dwWaitTime = 10000;
int dwWaitTime = sanitizeWaitTime(status.dwWaitHint);

try {
Thread.sleep( dwWaitTime );
Expand Down

0 comments on commit 904a71e

Please sign in to comment.