Should System.exit be controlled by a Scope Local?
Ethan McCue
ethan at mccue.dev
Mon Feb 28 00:37:59 UTC 2022
K.
On Sun, Feb 27, 2022, 7:03 PM David Holmes <david.holmes at oracle.com> wrote:
> On 28/02/2022 8:20 am, Ethan McCue wrote:
> > My understanding is that when you System.exit all threads associated
> > with the JVM process are killed. That's what I meant by "nuclear
> > Thread.interrupt".
>
> The process is terminated, the threads are not individually "killed".
> All Thread.interrupt does is set a flag and unpark blocked threads (in
> some specific cases). There's really no comparison at all.
>
> David
> -----
>
> > It's the same issue as was raised about System.exit implicitly ending
> > control flow or implicitly closing open file handles - a process could
> > be relying on the behavior of implicitly killing all threads and not
> > have another cleanup mechanism
> >
> > On Sun, Feb 27, 2022, 5:16 PM David Holmes <david.holmes at oracle.com
> > <mailto:david.holmes at oracle.com>> wrote:
> >
> > 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
> > <mailto: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
> > <mailto: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
> > <mailto: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 <mailto:ethan at mccue.dev
> >>
> > >>>>> To: "core-libs-dev" <core-libs-dev at openjdk.java.net
> > <mailto: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
> > <
> 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
> > <https://grep.app/search?q=java.util.spi.ToolProvider>
> > >>>>
> > >>>
> > >>
> >
>
More information about the core-libs-dev
mailing list