Should System.exit be controlled by a Scope Local?
David Holmes
david.holmes at oracle.com
Sun Feb 27 22:16:24 UTC 2022
On 28/02/2022 3:20 am, Ethan McCue wrote:
> I think continuations could work for the single threaded case, depending on
> their behavior with "finally" blocks. I'm sure there are more caveats once
> we add another thread to the mix though. System.exit is a nuclear
> Thread.interrupt, so replicating that behavior might be a bit daunting
What has Thread.interrupt got to do with System.exit ?
David
> private static final class ExitCode {
> volatile Integer code = null;
> }
>
> private final ScopeLocal<ExitCode> EXIT_CODE = ScopeLocal.newInstance();
>
> public void overridingExitBehavior(IntConsumer exit, Runnable run) {
> var exitCode = new ExitCode();
> ScopeLocal.with(EXIT_CODE, exitCode).run(() -> {
> // by whatever syntax
> var _ = inContinuation(run);
> if (exitCode.code != null) {
> exit.accept(code.exitCode)
> }
> });
> }
>
> public void exit(int status) {
> if (EXIT_CODE.isBound()) {
> EXIT_CODE.get().code = status;
> Continuation.yield();
> }
> else {
> Shutdown.exit(status);
> }
> }
>
>
>
> On Sun, Feb 27, 2022 at 10:41 AM Glavo <zjx001202 at gmail.com> wrote:
>
>> I think there is a problem with this: `System.exit` contains semantics to
>> interrupt the flow of control and exit, and if you implement it that way,
>> you might have some program abnormally execute parts of it that should
>> never be executed.
>>
>> Of course, using exceptions like this should solve part of the problem:
>>
>> class Exit extends Error {
>> final int exitCode;
>> public Exit(int exitCode) {
>> this.exitCode = exitCode;
>> }
>>
>> @Override
>> public synchronized Throwable fillInStackTrace() {
>> return this;
>> }
>> }
>>
>> try {
>> Runtime.getRuntime().overridingExitBehavior(exitCode -> {throw new
>> Exit(exitCode);}, ...);
>> } catch (Exit exit) {
>> ...
>> }
>>
>> However, the calling method may catch this exception unexpectedly, and
>> there may be unexpected behavior under multithreading.
>> Of course, this part of the problem also exists for the security manager.
>> But, if possible, it would be better to have a solution for these
>> situations.
>> (`Continuation` might help us?)
>>
>>
>>
>> On Sun, Feb 27, 2022 at 11:07 PM Ethan McCue <ethan at mccue.dev> wrote:
>>
>>> That undermines my point some, but I think the overall shape of the use
>>> case still makes sense
>>>
>>> On Sun, Feb 27, 2022 at 8:01 AM Remi Forax <forax at univ-mlv.fr> wrote:
>>>
>>>> Hi Ethan,
>>>> there is a far simpler solution, call org.apache.ivy.run(args, true)
>>>> instead of org.apache.ivy.main(args) in your tool provider.
>>>>
>>>> regards,
>>>> Rémi
>>>>
>>>> ----- Original Message -----
>>>>> From: "Ethan McCue" <ethan at mccue.dev>
>>>>> To: "core-libs-dev" <core-libs-dev at openjdk.java.net>
>>>>> Sent: Saturday, February 26, 2022 11:14:19 PM
>>>>> Subject: Should System.exit be controlled by a Scope Local?
>>>>
>>>>> I have a feeling this has been considered and I might just be
>>>> articulating
>>>>> the obvious - but:
>>>>>
>>>>> As called out in JEP 411, one of the remaining legitimate uses of the
>>>>> Security Manager is to intercept calls to System.exit. This seems
>>> like a
>>>>> decent use case for the Scope Local mechanism.
>>>>>
>>>>>
>>>>> public class Runtime {
>>>>> ...
>>>>> private final ScopeLocal<IntConsumer> EXIT =
>>>>> ScopeLocal.newInstance();
>>>>>
>>>>> ...
>>>>>
>>>>> public void overridingExitBehavior(IntConsumer exit, Runnable
>>>> run) {
>>>>> ScopeLocal.with(EXIT, exit).run(run);
>>>>> }
>>>>>
>>>>> ...
>>>>>
>>>>> public void exit(int status) {
>>>>> if (EXIT.isBound()) {
>>>>> EXIT.get().accept(status);
>>>>> }
>>>>> else {
>>>>> Shutdown.exit(status);
>>>>> }
>>>>> }
>>>>> }
>>>>>
>>>>>
>>>>> One of the likely minor benefits in the scope of things, but related
>>> to
>>>> the
>>>>> parts of the ecosystem I am doodling with so I'll mention it, is that
>>> it
>>>>> would become possible to wrap "naive" cli programs with the
>>> ToolProvider
>>>>> SPI without rewriting their code if this System.out, and System.err
>>> all
>>>>> became reliably configurable.
>>>>>
>>>>> For instance, Apache Ivy's CLI has a main class that looks like this
>>>>>
>>>>>
>>>>
>>> https://github.com/apache/ant-ivy/blob/424fa89419147f50a41b4bdc665d8ea92b5da516/src/java/org/apache/ivy/Main.java
>>>>>
>>>>> package org.apache.ivy;
>>>>>
>>>>> public final class Main {
>>>>> ...
>>>>>
>>>>> public static void main(String[] args) throws Exception {
>>>>> try {
>>>>> run(args, true);
>>>>> System.exit(0);
>>>>> } catch (ParseException ex) {
>>>>> System.err.println(ex.getMessage());
>>>>> System.exit(1);
>>>>> }
>>>>> }
>>>>> }
>>>>>
>>>>> Making these otherwise static parts of the system configurable would
>>>> enable
>>>>> a third party library to write
>>>>>
>>>>> public final class IvyToolProvider implements ToolProvider {
>>>>> @Override
>>>>> public String name() {
>>>>> return "ivy";
>>>>> }
>>>>>
>>>>> @Override
>>>>> public int run(PrintWriter out, PrintWriter err, String...
>>> args) {
>>>>> var exit = new AtomicInteger(0);
>>>>> Runtime.getRuntime().overridingExitBehavior(exit::set, ()
>>> -> {
>>>>> System.overridingOut(out, () -> {
>>>>> System.overridingErr(err, Main::main);
>>>>> }
>>>>> };
>>>>> return exit.get();
>>>>> }
>>>>> }
>>>>>
>>>>> Whether that would be enough to make it so that people other than
>>>> Christian
>>>>> Stein use the mechanism is anyone's guess, but might be worth a shot.
>>>>>
>>>>> https://grep.app/search?q=java.util.spi.ToolProvider
>>>>
>>>
>>
More information about the core-libs-dev
mailing list