1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-23 17:05:26 +02:00

Bug 574131: Do not concurrently call Spawner#destroy()

As the Object#wait() voids the synchronization, more threads can call
destroy() before the Reaper thread has identified that the process
exited or the timeout occured.
The change ensures that only one call actually raises the signal while
the others are NOP.

Contributed by STMicroelectronics

Change-Id: I64722b17138582a76bb9cf604a6b0c14685f1720
Signed-off-by: Torbjörn Svensson <torbjorn.svensson@st.com>
This commit is contained in:
Torbjörn SVENSSON 2021-10-18 16:53:24 +02:00 committed by Torbjörn Svensson
parent cd73469b01
commit 4a7bd0d1b4

View file

@ -66,12 +66,17 @@ public class Spawner extends Process {
int pid = 0;
int status;
final IChannel[] fChannels = { null, null, null };
boolean isDone;
OutputStream out;
InputStream in;
InputStream err;
private PTY fPty;
private static enum State {
RUNNING, DESTROYING, DONE
}
private State fState = State.RUNNING;
/**
* @deprecated Do not use this method it splits command line arguments on whitespace with no regard to quoting rules. See Bug 573677
*/
@ -223,7 +228,7 @@ public class Spawner extends Process {
**/
@Override
public synchronized int waitFor() throws InterruptedException {
while (!isDone) {
while (fState != State.DONE) {
wait();
}
@ -245,7 +250,7 @@ public class Spawner extends Process {
**/
@Override
public synchronized int exitValue() {
if (!isDone) {
if (fState != State.DONE) {
throw new IllegalThreadStateException("Process not Terminated"); //$NON-NLS-1$
}
return status;
@ -260,39 +265,50 @@ public class Spawner extends Process {
**/
@Override
public synchronized void destroy() {
// Sends the TERM
terminate();
switch (fState) {
case RUNNING:
fState = State.DESTROYING;
// Close the streams on this side.
//
// We only close the streams that were
// never used by any client.
// So, if the stream was not created yet,
// we create it ourselves and close it
// right away, so as to release the pipe.
// Note that even if the stream was never
// created, the pipe has been allocated in
// native code, so we need to create the
// stream and explicitly close it.
//
// We don't close streams the clients have
// created because we don't know when the
// client will be finished using them.
// It is up to the client to close those
// streams.
//
// But 345164
closeUnusedStreams();
// Sends the TERM
terminate();
// Grace before using the heavy gone.
if (!isDone) {
try {
wait(1000);
} catch (InterruptedException e) {
// Close the streams on this side.
//
// We only close the streams that were
// never used by any client.
// So, if the stream was not created yet,
// we create it ourselves and close it
// right away, so as to release the pipe.
// Note that even if the stream was never
// created, the pipe has been allocated in
// native code, so we need to create the
// stream and explicitly close it.
//
// We don't close streams the clients have
// created because we don't know when the
// client will be finished using them.
// It is up to the client to close those
// streams.
//
// But 345164
closeUnusedStreams();
// Grace before using the heavy gun.
if (fState != State.DONE) {
try {
wait(1000);
} catch (InterruptedException e) {
}
}
}
if (!isDone) {
kill();
if (fState != State.DONE) {
kill();
}
break;
case DESTROYING:
case DONE:
// Nothing to do
break;
}
}
@ -563,7 +579,7 @@ public class Spawner extends Process {
// Sync with spawner and notify when done.
status = waitFor(pid);
synchronized (Spawner.this) {
isDone = true;
fState = Spawner.State.DONE;
Spawner.this.notifyAll();
}
}