If some process executed by FS#readPipe lived for a while after closing stderr, FS#GobblerThread#run failed with an IllegalThreadStateException exception when accessing p.exitValue() for the process which is still alive. Add Process#waitFor calls to wait for the process completion. Bug: 528335 Change-Id: I87e0b6f9ad0b995dbce46ddfb877e33eaf3ae5a6 Signed-off-by: Dmitry Pavlenko <pavlenko@tmatesoft.com> Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>tags/v4.9.2.201712150930-r
cloneNonEmptyDirectory=Destination path "{0}" already exists and is not an empty directory | cloneNonEmptyDirectory=Destination path "{0}" already exists and is not an empty directory | ||||
closed=closed | closed=closed | ||||
collisionOn=Collision on {0} | collisionOn=Collision on {0} | ||||
commandClosedStderrButDidntExit=Command {0} closed stderr stream but didn''t exit within timeout {1} seconds | |||||
commandRejectedByHook=Rejected by "{0}" hook.\n{1} | commandRejectedByHook=Rejected by "{0}" hook.\n{1} | ||||
commandWasCalledInTheWrongState=Command {0} was called in the wrong state | commandWasCalledInTheWrongState=Command {0} was called in the wrong state | ||||
commitAlreadyExists=exists {0} | commitAlreadyExists=exists {0} | ||||
tagNameInvalid=tag name {0} is invalid | tagNameInvalid=tag name {0} is invalid | ||||
tagOnRepoWithoutHEADCurrentlyNotSupported=Tag on repository without HEAD currently not supported | tagOnRepoWithoutHEADCurrentlyNotSupported=Tag on repository without HEAD currently not supported | ||||
theFactoryMustNotBeNull=The factory must not be null | theFactoryMustNotBeNull=The factory must not be null | ||||
threadInterruptedWhileRunning="Current thread interrupted while running {0}" | |||||
timeIsUncertain=Time is uncertain | timeIsUncertain=Time is uncertain | ||||
timerAlreadyTerminated=Timer already terminated | timerAlreadyTerminated=Timer already terminated | ||||
tooManyCommands=Too many commands | tooManyCommands=Too many commands |
/***/ public String cloneNonEmptyDirectory; | /***/ public String cloneNonEmptyDirectory; | ||||
/***/ public String closed; | /***/ public String closed; | ||||
/***/ public String collisionOn; | /***/ public String collisionOn; | ||||
/***/ public String commandClosedStderrButDidntExit; | |||||
/***/ public String commandRejectedByHook; | /***/ public String commandRejectedByHook; | ||||
/***/ public String commandWasCalledInTheWrongState; | /***/ public String commandWasCalledInTheWrongState; | ||||
/***/ public String commitAlreadyExists; | /***/ public String commitAlreadyExists; | ||||
/***/ public String tagOnRepoWithoutHEADCurrentlyNotSupported; | /***/ public String tagOnRepoWithoutHEADCurrentlyNotSupported; | ||||
/***/ public String transactionAborted; | /***/ public String transactionAborted; | ||||
/***/ public String theFactoryMustNotBeNull; | /***/ public String theFactoryMustNotBeNull; | ||||
/***/ public String threadInterruptedWhileRunning; | |||||
/***/ public String timeIsUncertain; | /***/ public String timeIsUncertain; | ||||
/***/ public String timerAlreadyTerminated; | /***/ public String timerAlreadyTerminated; | ||||
/***/ public String tooManyCommands; | /***/ public String tooManyCommands; |
} | } | ||||
private static class GobblerThread extends Thread { | private static class GobblerThread extends Thread { | ||||
/* The process has 5 seconds to exit after closing stderr */ | |||||
private static final int PROCESS_EXIT_TIMEOUT = 5; | |||||
private final Process p; | private final Process p; | ||||
private final String desc; | private final String desc; | ||||
private final String dir; | private final String dir; | ||||
err.append((char) ch); | err.append((char) ch); | ||||
} | } | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
if (p.exitValue() != 0) { | |||||
setError(e, e.getMessage()); | |||||
if (waitForProcessCompletion(e) && p.exitValue() != 0) { | |||||
setError(e, e.getMessage(), p.exitValue()); | |||||
fail.set(true); | fail.set(true); | ||||
} else { | } else { | ||||
// ignore. command terminated faster and stream was just closed | // ignore. command terminated faster and stream was just closed | ||||
// or the process didn't terminate within timeout | |||||
} | } | ||||
} finally { | } finally { | ||||
if (err.length() > 0) { | |||||
setError(null, err.toString()); | |||||
if (waitForProcessCompletion(null) && err.length() > 0) { | |||||
setError(null, err.toString(), p.exitValue()); | |||||
if (p.exitValue() != 0) { | if (p.exitValue() != 0) { | ||||
fail.set(true); | fail.set(true); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
private void setError(IOException e, String message) { | |||||
@SuppressWarnings("boxing") | |||||
private boolean waitForProcessCompletion(IOException originalError) { | |||||
try { | |||||
if (!p.waitFor(PROCESS_EXIT_TIMEOUT, TimeUnit.SECONDS)) { | |||||
setError(originalError, MessageFormat.format( | |||||
JGitText.get().commandClosedStderrButDidntExit, | |||||
desc, PROCESS_EXIT_TIMEOUT), -1); | |||||
fail.set(true); | |||||
} | |||||
} catch (InterruptedException e) { | |||||
LOG.error(MessageFormat.format( | |||||
JGitText.get().threadInterruptedWhileRunning, desc), e); | |||||
} | |||||
return false; | |||||
} | |||||
private void setError(IOException e, String message, int exitCode) { | |||||
exception.set(e); | exception.set(e); | ||||
errorMessage.set(MessageFormat.format( | errorMessage.set(MessageFormat.format( | ||||
JGitText.get().exceptionCaughtDuringExcecutionOfCommand, | JGitText.get().exceptionCaughtDuringExcecutionOfCommand, | ||||
desc, dir, Integer.valueOf(p.exitValue()), message)); | |||||
desc, dir, Integer.valueOf(exitCode), message)); | |||||
} | } | ||||
} | } | ||||