Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System.exit does proper context hard exit. #10363

Merged
merged 12 commits into from
Jul 18, 2024
Merged
8 changes: 6 additions & 2 deletions engine/runner/src/main/java/org/enso/runner/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -880,8 +880,12 @@ private void runMain(
}
}
} catch (PolyglotException e) {
printPolyglotException(e, rootPkgPath);
throw exitFail();
if (e.isExit()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly we might close the context here to notify other language runtimes that the shutdown is happening... not very urgent.

doExit(e.getExitStatus());
} else {
printPolyglotException(e, rootPkgPath);
throw exitFail();
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5773,6 +5773,60 @@ class RuntimeServerTest
)
}

it should "return error when invoking System.exit" in {
val contextId = UUID.randomUUID()
val requestId = UUID.randomUUID()
val moduleName = "Enso_Test.Test.Main"
val metadata = new Metadata
val code =
"""import Standard.Base.System
|
|main =
| System.exit 42
|""".stripMargin.linesIterator.mkString("\n")
val contents = metadata.appendToCode(code)
val mainFile = context.writeMain(contents)

// create context
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
context.receive shouldEqual Some(
Api.Response(requestId, Api.CreateContextResponse(contextId))
)

// Set sources for the module
context.send(
Api.Request(requestId, Api.OpenFileRequest(mainFile, contents))
)
context.receive shouldEqual Some(
Api.Response(Some(requestId), Api.OpenFileResponse)
)

// push main
context.send(
Api.Request(
requestId,
Api.PushContextRequest(
contextId,
Api.StackItem.ExplicitCall(
Api.MethodPointer(moduleName, moduleName, "main"),
None,
Vector()
)
)
)
)
context.receiveNIgnoreStdLib(2) should contain theSameElementsAs Seq(
Api.Response(requestId, Api.PushContextResponse(contextId)),
Api.Response(
Api.ExecutionFailed(
contextId,
Api.ExecutionResult
.Failure("Exit was called with exit code 42.", None)
)
)
)
}

it should "return compiler warning unused variable" in {
val contextId = UUID.randomUUID()
val requestId = UUID.randomUUID()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,15 @@ public void shutdown() {
compiler.shutdown(shouldWaitForPendingSerializationJobs);
}

/**
* Perform <a
* href="https:/oracle/graal/blob/master/truffle/docs/Exit.md#context-exit">hard
* exit</a>.
*/
public void exit(int exitCode) {
environment.getContext().closeExited(null, exitCode);
}

private boolean shouldAssertionsBeEnabled() {
var envVar = environment.getEnvironment().get("ENSO_ENABLE_ASSERTIONS");
if (envVar != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.lang3.SystemUtils;
import org.enso.interpreter.EnsoLanguage;
import org.enso.interpreter.dsl.Builtin;
import org.enso.interpreter.node.expression.builtin.text.util.ExpectStringNode;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.atom.Atom;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.data.vector.ArrayLikeCoerceToArrayNode;
import org.enso.interpreter.service.error.ExitException;

public class System {

Expand Down Expand Up @@ -52,8 +52,15 @@ public static long nanoTime() {
@CompilerDirectives.TruffleBoundary
public static void exit(long code) {
var ctx = EnsoContext.get(null);
EnsoLanguage.get(null).disposeContext(ctx);
java.lang.System.exit((int) code);
if (ctx.isInteractiveMode()) {
// In interactive mode, the ExitException should be caught and handled by one of
// the instruments that should take care of proper context disposal.
throw new ExitException((int) code);
} else {
// While not in interactive mode, it is safe to directly call
// TruffleCOntext.exitContext
Akirathan marked this conversation as resolved.
Show resolved Hide resolved
ctx.exit((int) code);
Akirathan marked this conversation as resolved.
Show resolved Hide resolved
}
}

@Builtin.Specialize
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.enso.interpreter.service.error;

/** Thrown when {@code System.exit} call was called during execution. */
public class ExitException extends RuntimeException implements ServiceException {
Akirathan marked this conversation as resolved.
Show resolved Hide resolved
public ExitException(int code) {
super("Exit was called with exit code " + code + ".");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ create_process command arguments input redirect_in redirect_out redirect_err = @
@Builtin_Type
type System_Process_Result
Result exit_code stdout stderr

exit code = @Builtin_Method "System.exit"
Loading