Should System.exit be controlled by a Scope Local?
David Holmes
david.holmes at oracle.com
Mon Feb 28 00:02:47 UTC 2022
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