Skip to content

Commit

Permalink
Clear interrupt handler after each test case
Browse files Browse the repository at this point in the history
Test cases usually set a timing event, which could interfere with running other test cases.

Also, distinguish between evaluation requests that can be interrupted or not.
  • Loading branch information
fniephaus committed Dec 20, 2023
1 parent 32785a4 commit ee78f9c
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ protected static TestResult runTestCase(final TestRequest request) {
throw new RuntimeException("Test was interrupted");
} catch (final ExecutionException e) {
return TestResult.fromException("failed with an error", e.getCause());
} finally {
image.interrupt.clear();
}
}

Expand All @@ -195,7 +197,6 @@ protected static <T, R> R runWithTimeout(final T argument, final Function<T, R>
} finally {
testWithImageIsActive = false;
}

});
try {
return future.get(timeout, TimeUnit.SECONDS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public boolean isMemberReadable(@SuppressWarnings("unused") final String member)

@ExportMessage
public Object getMembers(@SuppressWarnings("unused") final boolean includeInternal) {
return image.evaluate("Smalltalk globals keys collect: [:ea | ea asString]");
return image.evaluateUninterruptably("Smalltalk globals keys collect: [:ea | ea asString]");
}

@ExportMessage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,19 @@ public Object evaluate(final String sourceCode) {
return getDoItContextNode(sourceCode, false).getCallTarget().call();
}

@TruffleBoundary
public Object evaluateUninterruptably(final String sourceCode) {
final boolean wasActive = interrupt.isActive();
interrupt.deactivate();
try {
return evaluate(sourceCode);
} finally {
if (wasActive) {
interrupt.activate();
}
}
}

@TruffleBoundary
public Object lookup(final String member) {
return smalltalk.send(this, "at:ifAbsent:", asByteSymbol(member), NilObject.SINGLETON);
Expand Down Expand Up @@ -292,7 +305,7 @@ public DoItRootNode getDoItContextNode(final ParsingRequest request) {
sourceCode = String.format("[ :%s | %s ]", String.join(" :", request.getArgumentNames()), source.getCharacters().toString());
}
}
return DoItRootNode.create(this, language, evaluate(sourceCode));
return DoItRootNode.create(this, language, evaluateUninterruptably(sourceCode));
}

private static boolean isFileInFormat(final Source source) {
Expand Down Expand Up @@ -358,21 +371,21 @@ private ExecuteTopLevelContextNode getDoItContextNode(final String source, final
*/
public ContextObject getInteropExceptionThrowingContext() {
if (interopExceptionThrowingContextPrototype == null) {
assert evaluate("Interop") != NilObject.SINGLETON : "Interop class must be present";
final CompiledCodeObject onDoMethod = (CompiledCodeObject) evaluate("BlockClosure>>#on:do:");
assert evaluateUninterruptably("Interop") != NilObject.SINGLETON : "Interop class must be present";
final CompiledCodeObject onDoMethod = (CompiledCodeObject) evaluateUninterruptably("BlockClosure>>#on:do:");
interopExceptionThrowingContextPrototype = ContextObject.create(this, onDoMethod.getSqueakContextSize());
interopExceptionThrowingContextPrototype.setCodeObject(onDoMethod);
interopExceptionThrowingContextPrototype.setReceiver(NilObject.SINGLETON);
/*
* Need to catch all exceptions here. Otherwise, the contexts sender is used to find the
* next handler context (see Context>>#nextHandlerContext).
*/
interopExceptionThrowingContextPrototype.atTempPut(0, evaluate("Exception"));
interopExceptionThrowingContextPrototype.atTempPut(0, evaluateUninterruptably("Exception"));
/*
* Throw Error and Halt as interop, ignore warnings, handle all other exceptions the
* usual way via UndefinedObject>>#handleSignal:.
*/
interopExceptionThrowingContextPrototype.atTempPut(1, evaluate(
interopExceptionThrowingContextPrototype.atTempPut(1, evaluateUninterruptably(
"[ :e | ((e isKindOf: Error) or: [ e isKindOf: Halt ]) ifTrue: [ Interop throwException: e \"rethrow as interop\" ] ifFalse: [(e isKindOf: Warning) ifTrue: [ e resume \"ignore\" ] " +
"ifFalse: [ nil handleSignal: e \"handle the usual way\" ] ] ]"));
interopExceptionThrowingContextPrototype.atTempPut(2, BooleanObject.TRUE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,18 +159,22 @@ public void resetTrigger() {
* TESTING
*/

public void clear() {
nextWakeupTick = 0;
interruptPending = false;
pendingFinalizationSignals = false;
clearWeakPointersQueue();
semaphoresToSignal.clear();
}

public void reset() {
CompilerAsserts.neverPartOfCompilation("Resetting interrupt handler only supported for testing purposes");
isActive = true;
nextWakeupTick = 0;
if (interruptChecks != null) {
interruptChecks.cancel(true);
}
shutdown();
interruptPending = false;
pendingFinalizationSignals = false;
clearWeakPointersQueue();
semaphoresToSignal.clear();
clear();
}

private void clearWeakPointersQueue() {
Expand Down

0 comments on commit ee78f9c

Please sign in to comment.