Skip to content

Commit aa2db6b

Browse files
committed
Add proper synchronization for sketch launching and closing
Pressing Run button twice launched two sketch windows, but only one of them could be closed by Stop button. Pressing Stop had effect only after sketch VM was launched. Both of these issues are now fixed and buttons can handle a frustration-relieving session of aggressive clicking, leaving one sketch window if Run was the last button pressed or no window if Stop was the last button pressed.
1 parent e5833e8 commit aa2db6b

File tree

2 files changed

+100
-81
lines changed

2 files changed

+100
-81
lines changed

java/src/processing/mode/java/JavaEditor.java

Lines changed: 42 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ public class JavaEditor extends Editor {
4747
// Runner associated with this editor window
4848
private Runner runtime;
4949

50+
private boolean runtimeLaunchRequested;
51+
private final Object runtimeLock = new Object[0];
52+
5053
// Need to sort through the rest of these additions [fry]
5154

5255
protected final List<LineHighlight> breakpointedLines = new ArrayList<>();
@@ -1057,57 +1060,48 @@ public void handleRun() {
10571060
debugger.continueDebug();
10581061

10591062
} else {
1060-
prepareRun();
1061-
toolbar.activateRun();
1062-
new Thread(new Runnable() {
1063-
public void run() {
1064-
try {
1065-
//runtime = jmode.handleRun(sketch, JavaEditor.this);
1066-
runtime = jmode.handleLaunch(sketch, JavaEditor.this, false);
1067-
} catch (Exception e) {
1068-
EventQueue.invokeLater(() -> statusError(e));
1069-
}
1070-
}
1071-
}).start();
1063+
handleLaunch(false, false);
10721064
}
10731065
}
10741066

10751067

10761068
public void handlePresent() {
1077-
prepareRun();
1078-
toolbar.activateRun();
1079-
new Thread(new Runnable() {
1080-
public void run() {
1081-
try {
1082-
//runtime = jmode.handlePresent(sketch, JavaEditor.this);
1083-
runtime = jmode.handleLaunch(sketch, JavaEditor.this, true);
1084-
} catch (Exception e) {
1085-
EventQueue.invokeLater(() -> statusError(e));
1086-
}
1087-
}
1088-
}).start();
1069+
handleLaunch(true, false);
10891070
}
10901071

10911072

10921073
public void handleTweak() {
1093-
prepareRun();
1094-
//toolbar.activate(JavaToolbar.RUN);
1095-
toolbar.activateRun();
1074+
autoSave();
10961075

10971076
if (sketch.isModified()) {
1098-
toolbar.deactivateRun();
10991077
Messages.showMessage(Language.text("menu.file.save"),
11001078
Language.text("tweak_mode.save_before_tweak"));
11011079
return;
11021080
}
11031081

1104-
new Thread(new Runnable() {
1105-
public void run() {
1106-
try {
1107-
runtime = jmode.handleTweak(sketch, JavaEditor.this);
1108-
} catch (Exception e) {
1109-
EventQueue.invokeLater(() -> statusError(e));
1082+
handleLaunch(false, true);
1083+
}
1084+
1085+
protected void handleLaunch(boolean present, boolean tweak) {
1086+
prepareRun();
1087+
toolbar.activateRun();
1088+
synchronized (runtimeLock) {
1089+
runtimeLaunchRequested = true;
1090+
}
1091+
new Thread(() -> {
1092+
try {
1093+
synchronized (runtimeLock) {
1094+
if (runtimeLaunchRequested) {
1095+
runtimeLaunchRequested = false;
1096+
if (!tweak) {
1097+
runtime = jmode.handleLaunch(sketch, JavaEditor.this, present);
1098+
} else {
1099+
runtime = jmode.handleTweak(sketch, JavaEditor.this);
1100+
}
1101+
}
11101102
}
1103+
} catch (Exception e) {
1104+
EventQueue.invokeLater(() -> statusError(e));
11111105
}
11121106
}).start();
11131107
}
@@ -1122,23 +1116,24 @@ public void handleStop() {
11221116
debugger.stopDebug();
11231117

11241118
} else {
1125-
// toolbar.activate(JavaToolbar.STOP);
11261119
toolbar.activateStop();
11271120

11281121
try {
1129-
//jmode.handleStop();
1130-
if (runtime != null) {
1131-
runtime.close(); // kills the window
1132-
runtime = null;
1133-
// } else {
1134-
// System.out.println("runtime is null");
1122+
synchronized (runtimeLock) {
1123+
if (runtimeLaunchRequested) {
1124+
// Cancel the launch before the runtime was created
1125+
runtimeLaunchRequested = false;
1126+
}
1127+
if (runtime != null) {
1128+
// Cancel the launch after the runtime was created
1129+
runtime.close(); // kills the window
1130+
runtime = null;
1131+
}
11351132
}
11361133
} catch (Exception e) {
11371134
statusError(e);
11381135
}
11391136

1140-
// toolbar.deactivate(JavaToolbar.RUN);
1141-
// toolbar.deactivate(JavaToolbar.STOP);
11421137
toolbar.deactivateStop();
11431138
toolbar.deactivateRun();
11441139

@@ -1171,8 +1166,10 @@ public void handleContinue() {
11711166

11721167

11731168
public void onRunnerExiting(Runner runner) {
1174-
if (this.runtime == runner) {
1175-
deactivateRun();
1169+
synchronized (runtimeLock) {
1170+
if (this.runtime == runner) {
1171+
deactivateRun();
1172+
}
11761173
}
11771174
}
11781175

java/src/processing/mode/java/runner/Runner.java

Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public class Runner implements MessageConsumer {
6161
protected RunnerListener listener;
6262

6363
// Running remote VM
64-
protected VirtualMachine vm;
64+
protected volatile VirtualMachine vm;
6565
protected boolean vmReturnedError;
6666

6767
// Thread transferring remote error stream to our error stream
@@ -78,6 +78,9 @@ public class Runner implements MessageConsumer {
7878
protected PrintStream sketchErr;
7979
protected PrintStream sketchOut;
8080

81+
protected volatile boolean cancelled;
82+
protected final Object cancelLock = new Object[0];
83+
8184

8285
public Runner(JavaBuild build, RunnerListener listener) throws SketchException {
8386
this.listener = listener;
@@ -218,6 +221,13 @@ public boolean launchVirtualMachine(boolean present, String[] args) {
218221

219222
commandArgs.append(vmParams);
220223
commandArgs.append(sketchParams);
224+
225+
// Opportunistically quit if the launch was cancelled,
226+
// the next chance to cancel will be after connecting to the VM
227+
if (cancelled) {
228+
return false;
229+
}
230+
221231
launchJava(commandArgs.array());
222232

223233
AttachingConnector connector = (AttachingConnector)
@@ -249,7 +259,15 @@ public boolean launchVirtualMachine(boolean present, String[] args) {
249259
while (true) {
250260
try {
251261
Messages.log(getClass().getName() + " attempting to attach to VM");
252-
vm = connector.attach(arguments);
262+
synchronized (cancelLock) {
263+
vm = connector.attach(arguments);
264+
if (cancelled && vm != null) {
265+
// cancelled and connected to the VM, handle closing now
266+
Messages.log(getClass().getName() + " aborting, launch cancelled");
267+
close();
268+
return false;
269+
}
270+
}
253271
// vm = connector.attach(arguments);
254272
if (vm != null) {
255273
Messages.log(getClass().getName() + " attached to the VM");
@@ -523,29 +541,28 @@ protected void generateTrace() {
523541
//vm.setDebugTraceMode(debugTraceMode);
524542
// vm.setDebugTraceMode(VirtualMachine.TRACE_ALL);
525543
// vm.setDebugTraceMode(VirtualMachine.TRACE_NONE); // formerly, seems to have no effect
526-
527-
// Calling this seems to set something internally to make the
528-
// Eclipse JDI wake up. Without it, an ObjectCollectedException
529-
// is thrown on excReq.enable(). No idea why this works,
530-
// but at least exception handling has returned. (Suspect that it may
531-
// block until all or at least some threads are available, meaning
532-
// that the app has launched and we have legit objects to talk to).
533-
vm.allThreads();
534-
// The bug may not have been noticed because the test suite waits for
535-
// a thread to be available, and queries it by calling allThreads().
536-
// See org.eclipse.debug.jdi.tests.AbstractJDITest for the example.
537-
538-
EventRequestManager mgr = vm.eventRequestManager();
539-
// get only the uncaught exceptions
540-
ExceptionRequest excReq = mgr.createExceptionRequest(null, false, true);
541-
// System.out.println(excReq);
542-
// this version reports all exceptions, caught or uncaught
543-
// ExceptionRequest excReq = mgr.createExceptionRequest(null, true, true);
544-
// suspend so we can step
545-
excReq.setSuspendPolicy(EventRequest.SUSPEND_ALL);
546-
// excReq.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
547-
// excReq.setSuspendPolicy(EventRequest.SUSPEND_NONE); // another option?
548-
excReq.enable();
544+
try {
545+
// Calling this seems to set something internally to make the
546+
// Eclipse JDI wake up. Without it, an ObjectCollectedException
547+
// is thrown on excReq.enable(). No idea why this works,
548+
// but at least exception handling has returned. (Suspect that it may
549+
// block until all or at least some threads are available, meaning
550+
// that the app has launched and we have legit objects to talk to).
551+
vm.allThreads();
552+
// The bug may not have been noticed because the test suite waits for
553+
// a thread to be available, and queries it by calling allThreads().
554+
// See org.eclipse.debug.jdi.tests.AbstractJDITest for the example.
555+
556+
EventRequestManager mgr = vm.eventRequestManager();
557+
// get only the uncaught exceptions
558+
ExceptionRequest excReq = mgr.createExceptionRequest(null, false, true);
559+
// this version reports all exceptions, caught or uncaught
560+
// suspend so we can step
561+
excReq.setSuspendPolicy(EventRequest.SUSPEND_ALL);
562+
excReq.enable();
563+
} catch (VMDisconnectedException ignore) {
564+
return;
565+
}
549566

550567
Thread eventThread = new Thread() {
551568
public void run() {
@@ -851,19 +868,22 @@ protected SketchException findException(String message, ObjectReference or, Thre
851868

852869

853870
public void close() {
854-
// TODO make sure stop() has already been called to exit the sketch
871+
synchronized (cancelLock) {
872+
cancelled = true;
873+
874+
// TODO make sure stop() has already been called to exit the sketch
855875

856-
// TODO actually kill off the vm here
857-
if (vm != null) {
858-
try {
859-
vm.exit(0);
876+
// TODO actually kill off the vm here
877+
if (vm != null) {
878+
try {
879+
vm.exit(0);
860880

861-
} catch (com.sun.jdi.VMDisconnectedException vmde) {
862-
// if the vm has disconnected on its own, ignore message
863-
//System.out.println("harmless disconnect " + vmde.getMessage());
864-
// TODO shouldn't need to do this, need to do more cleanup
881+
} catch (com.sun.jdi.VMDisconnectedException vmde) {
882+
// if the vm has disconnected on its own, ignore message
883+
//System.out.println("harmless disconnect " + vmde.getMessage());
884+
// TODO shouldn't need to do this, need to do more cleanup
885+
}
865886
}
866-
vm = null;
867887
}
868888
}
869889

@@ -884,7 +904,9 @@ synchronized public void message(String s) {
884904
if (editor != null) {
885905
// editor.internalCloseRunner(); // [091124]
886906
// editor.handleStop(); // prior to 0192
887-
editor.internalCloseRunner(); // 0192
907+
java.awt.EventQueue.invokeLater(() -> {
908+
editor.internalCloseRunner(); // 0192
909+
});
888910
}
889911
return;
890912
}

0 commit comments

Comments
 (0)