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