Skip to content

Commit 78121ef

Browse files
committed
Merge branch 'master' into fix/textAreaNullPointer
2 parents b71266b + af1c807 commit 78121ef

File tree

9 files changed

+88
-37
lines changed

9 files changed

+88
-37
lines changed

core/src/main/java/edu/wpi/grip/core/GRIPCoreModule.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,15 @@
1616
import edu.wpi.grip.core.sources.MultiImageFileSource;
1717
import edu.wpi.grip.core.util.ExceptionWitness;
1818

19+
import java.util.logging.Level;
20+
import java.util.logging.Logger;
21+
1922
/**
2023
* A Guice {@link com.google.inject.Module} for GRIP's core package. This is where instances of {@link Pipeline},
2124
* {@link Palette}, {@link Project}, etc... are created.
2225
*/
2326
public class GRIPCoreModule extends AbstractModule {
27+
private final Logger logger = Logger.getLogger(GRIPCoreModule.class.getName());
2428

2529
private final EventBus eventBus = new EventBus(this::onSubscriberException);
2630

@@ -60,10 +64,20 @@ public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
6064
}
6165

6266
private void onSubscriberException(Throwable exception, SubscriberExceptionContext context) {
63-
eventBus.post(new UnexpectedThrowableEvent(exception, "An event subscriber threw an exception"));
67+
if (exception instanceof InterruptedException) {
68+
logger.log(Level.FINE, "EventBus Subscriber threw InterruptedException", exception);
69+
Thread.currentThread().interrupt();
70+
} else {
71+
eventBus.post(new UnexpectedThrowableEvent(exception, "An event subscriber threw an exception"));
72+
}
6473
}
6574

6675
private void onThreadException(Thread thread, Throwable exception) {
67-
eventBus.post(new UnexpectedThrowableEvent(exception, thread + " threw an exception"));
76+
if (exception instanceof InterruptedException) {
77+
logger.log(Level.FINE, "InterruptedException from thread " + thread, exception);
78+
Thread.currentThread().interrupt();
79+
} else {
80+
eventBus.post(new UnexpectedThrowableEvent(exception, thread + " threw an exception"));
81+
}
6882
}
6983
}

core/src/main/java/edu/wpi/grip/core/StartStoppable.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public interface StartStoppable {
2626
* @throws TimeoutException If the thread fails to stop in a timely manner
2727
* @throws IOException If cleaning up some system resource fails.
2828
*/
29-
void stop() throws TimeoutException, IOException;
29+
void stop() throws InterruptedException, TimeoutException, IOException;
3030

3131
/**
3232
* Used to indicate if the source is running or stopped

core/src/main/java/edu/wpi/grip/core/Step.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ private synchronized void runPerformIfPossible() {
143143

144144
try {
145145
this.operation.perform(inputSockets, outputSockets, data);
146-
} catch (Exception e) {
146+
} catch (RuntimeException e) {
147+
// We do not want to catch all exceptions, only runtime exceptions.
148+
// This is especially important when it comes to InterruptedExceptions
147149
final String operationFailedMessage = "The " + operation.getName() + " operation did not perform correctly.";
148150
logger.log(Level.WARNING, operationFailedMessage, e);
149151
witness.flagException(e, operationFailedMessage);

core/src/main/java/edu/wpi/grip/core/sources/CameraSource.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ public void start() throws IOException, IllegalStateException {
208208
} catch (TimeoutException | IOException e) {
209209
// TODO: This should use the ExceptionWitness once that has a UI component added for it
210210
eventBus.post(new UnexpectedThrowableEvent(e, "Camera Frame Grabber could not be stopped!"));
211+
} catch (InterruptedException e) {
212+
Thread.currentThread().interrupt();
211213
}
212214
}
213215
);
@@ -228,13 +230,13 @@ public void start() throws IOException, IllegalStateException {
228230
* @throws IOException If there is a problem stopping the Source
229231
* @throws IllegalStateException If the camera is already stopped.
230232
*/
231-
public void stop() throws TimeoutException, IOException {
233+
public void stop() throws InterruptedException, TimeoutException, IOException {
232234
synchronized (this) {
233235
if (frameThread.isPresent()) {
234236
final Thread ex = frameThread.get();
235237
ex.interrupt();
236238
try {
237-
ex.join(TimeUnit.SECONDS.toMillis(500));
239+
ex.join(TimeUnit.SECONDS.toMillis(10));
238240
if (ex.isAlive()) {
239241
throw new TimeoutException("Unable to terminate video feed from Web Camera");
240242
}
@@ -243,6 +245,7 @@ public void stop() throws TimeoutException, IOException {
243245
} catch (InterruptedException e) {
244246
Thread.currentThread().interrupt();
245247
logger.log(Level.WARNING, e.getMessage(), e);
248+
throw e;
246249
} finally {
247250
// This will always run even if a timeout exception occurs
248251
try {
@@ -266,7 +269,7 @@ public synchronized boolean isStarted() {
266269
}
267270

268271
@Subscribe
269-
public void onSourceRemovedEvent(SourceRemovedEvent event) throws TimeoutException, IOException {
272+
public void onSourceRemovedEvent(SourceRemovedEvent event) throws InterruptedException, TimeoutException, IOException {
270273
if (event.getSource() == this) {
271274
try {
272275
if (this.isStarted()) this.stop();

core/src/main/java/edu/wpi/grip/core/util/ExceptionWitness.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,16 @@ public interface Factory {
4848
/**
4949
* Indicates to the witness that an exception has occurred. This will also post an {@link ExceptionEvent} to the {@link EventBus}
5050
*
51-
* @param exception The exception that this is reporting
51+
* @param exception The exception that this is reporting.
52+
* If the Exception is an InterruptedException then this will not post an exception, instead,
53+
* it will set the threads interrupted state and return.
5254
* @param message Any additional details that should be associated with this message.
5355
*/
5456
public final void flagException(final Exception exception, final String message) {
57+
if (exception instanceof InterruptedException) {
58+
Thread.currentThread().interrupt();
59+
return;
60+
}
5561
isExceptionState.set(true);
5662
this.eventBus.post(new ExceptionEvent(origin, exception, message));
5763
}

ui/src/main/java/edu/wpi/grip/ui/Main.java

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -92,28 +92,35 @@ public void start(Stage stage) throws Exception {
9292
public final void onUnexpectedThrowableEvent(UnexpectedThrowableEvent event) {
9393
// Print throwable before showing the exception so that errors are in order in the console
9494
event.getThrowable().printStackTrace();
95-
// This should still use PlatformImpl
96-
PlatformImpl.runAndWait(() -> {
97-
// WARNING! Do not post any events from within this! It could result in a deadlock!
98-
synchronized (this.dialogLock) {
99-
try {
100-
// Don't create more than one exception dialog at the same time
101-
final ExceptionAlert exceptionAlert = new ExceptionAlert(root, event.getThrowable(), event.getMessage(), event.isFatal(), getHostServices());
102-
exceptionAlert.setInitialFocus();
103-
exceptionAlert.showAndWait();
104-
} catch (Throwable e) {
105-
// Well in this case something has gone very, very wrong
106-
// We don't want to create a feedback loop either.
107-
e.printStackTrace();
108-
System.exit(1); // Ensure we shut down the application if we get an exception
109-
}
110-
}
111-
});
11295

113-
if (event.isFatal()) {
114-
System.err.println("Original fatal exception");
115-
event.getThrowable().printStackTrace();
116-
System.exit(1);
96+
try {
97+
// If this is an interrupted exception then don't show an error.
98+
// We should exit as fast as possible.
99+
if (!(event.getThrowable() instanceof InterruptedException)) {
100+
// This should still use PlatformImpl
101+
PlatformImpl.runAndWait(() -> {
102+
// WARNING! Do not post any events from within this! It could result in a deadlock!
103+
synchronized (this.dialogLock) {
104+
try {
105+
// Don't create more than one exception dialog at the same time
106+
final ExceptionAlert exceptionAlert = new ExceptionAlert(root, event.getThrowable(), event.getMessage(), event.isFatal(), getHostServices());
107+
exceptionAlert.setInitialFocus();
108+
exceptionAlert.showAndWait();
109+
} catch (Throwable e) {
110+
// Well in this case something has gone very, very wrong
111+
// We don't want to create a feedback loop either.
112+
e.printStackTrace();
113+
System.exit(1); // Ensure we shut down the application if we get an exception
114+
}
115+
}
116+
});
117+
}
118+
} finally {
119+
if (event.isFatal()) {
120+
System.err.println("Original fatal exception");
121+
event.getThrowable().printStackTrace();
122+
System.exit(1);
123+
}
117124
}
118125
}
119126
}

ui/src/main/java/edu/wpi/grip/ui/components/StartStoppableButton.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ public interface Factory {
6464
// If this fails then an StartedStoppedEvent will not be posted
6565
} catch (TimeoutException | IOException e) {
6666
eventBus.post(new UnexpectedThrowableEvent(e, "Failed to stop"));
67+
} catch (InterruptedException e) {
68+
// Unfortunately we have to eat the exception here because we can't rethrow it
69+
Thread.currentThread().interrupt();
6770
}
6871
});
6972

ui/src/main/java/edu/wpi/grip/ui/util/GRIPPlatform.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public Runnable getAction() {
4949
* JavaFX application thread. If we are already on the JavaFX application thread
5050
* then this will be run immediately. Otherwise, it will be run as an event
5151
* inside of the event bus.
52+
* If
5253
* If {@link #runAsSoonAsPossible(Runnable)} is called within itself it will always run
5354
* immediately because the runnable will always be run in the JavaFX thread.
5455
*
@@ -69,25 +70,28 @@ public void runAsSoonAsPossible(Runnable action) {
6970
}
7071

7172
@Subscribe
72-
public void onJavaFXRunnerEvent(JavaFXRunnerEvent event) {
73+
public void onJavaFXRunnerEvent(JavaFXRunnerEvent event) throws InterruptedException {
7374
assert !Platform.isFxApplicationThread() : "This should never be run on the application thread. This can cause a deadlock!";
75+
final Thread callingThread = Thread.currentThread();
76+
if (Thread.interrupted()) {
77+
throw new InterruptedException("Interrupted in onJavaFXRunnerEvent");
78+
}
7479

7580
// queue on JavaFX thread and wait for completion
7681
final CountDownLatch doneLatch = new CountDownLatch(1);
7782
Platform.runLater(() -> {
7883
try {
79-
event.getAction().run();
84+
// If the calling thread was interrupted then don't run this event.
85+
if(!callingThread.isInterrupted()) {
86+
event.getAction().run();
87+
}
8088
} finally {
8189
doneLatch.countDown();
8290
}
8391
});
8492

85-
try {
86-
while (!doneLatch.await(500, TimeUnit.MILLISECONDS)) {
87-
logger.log(Level.WARNING, "POTENTIAL DEADLOCK!");
88-
}
89-
} catch (InterruptedException e) {
90-
// ignore exception
93+
while (!doneLatch.await(500, TimeUnit.MILLISECONDS)) {
94+
logger.log(Level.WARNING, "POTENTIAL DEADLOCK!");
9195
}
9296
}
9397

ui/src/test/java/edu/wpi/grip/ui/util/GRIPPlatformTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import java.util.logging.Logger;
1616

17+
import static org.junit.Assert.assertFalse;
1718
import static org.junit.Assert.assertTrue;
1819

1920
public class GRIPPlatformTest extends ApplicationTest {
@@ -76,4 +77,15 @@ public void testRunAsSoonAsPossibleDoesNotDeadlockWhenRunInsideItself() throws E
7677
waiter.await();
7778
}
7879

80+
@Test
81+
public void testRunAsSoonAsPossibleWillNotCallIfInterrupted() throws Exception {
82+
final boolean hasRun [] = {false};
83+
84+
Thread.currentThread().interrupt();
85+
platform.runAsSoonAsPossible(() -> {
86+
hasRun[0] = true;
87+
});
88+
assertFalse("runAsSoonAsPossible ran when interrupted", hasRun[0]);
89+
}
90+
7991
}

0 commit comments

Comments
 (0)