[ https://issues.apache.org/jira/browse/GROOVY-11251?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Trevor Flynn updated GROOVY-11251: ---------------------------------- Description: When running groovysh from an application, there is no way to gracefully stop groovysh. Interrupting the run thread should always propagate up and cause the thread to terminate, but in this case the interrupt exception is caught silently and the console continues to run. Example: The console is created like so {code:java} IO groovyshIO = new IO(console1.getInputStream(), console1.getOut(), console1.getErr()); Binding binding = new Binding(getBinding().getVariables()); binding.setProperty("out", console1.getOut()); binding.setProperty("err", console1.getErr()); groovysh = new Groovysh( getClassLoader(), binding, groovyshIO, null, getCompilerConfiguration() ); shellThread = new Thread(() -> { while (running) { groovysh.run(""); } }); shellThread.start(); {code} To stop the console, the verbosity must be set to DEBUG to prevent the InteractiveShellRunner from swallowing the exception, and immediately after the interrupt, the running flag must be set to false. {code:java} running = false; //This only works in combination with closing the console //We must set the verbosity to debug to force the exception to get propagated Preferences.verbosity = IO.Verbosity.DEBUG; //Interrupt the thread shellThread.interrupt(); //I don't know why setting running to false is needed, since the error handler should already do this //But it IS needed groovysh.getRunner().setRunning(false); {code} This has the major disadvantage of dumping stack traces to the System output, and potentially running into a race condition with setting running to false. A more reliable approach when possible is to close the console streams (if the application has access to them to do so) {code:java} running = false; //Close input, output, and error streams console1.close(); //This only works in combination with closing the console //We must set the verbosity to debug to force the exception to get propagated Preferences.verbosity = IO.Verbosity.DEBUG; //I don't know why setting running to false is needed, since the error handler should already do this //But it IS needed groovysh.getRunner().setRunning(false); {code} Because the streams are closed, the runner will loop with IOExceptions until the verbosity change and running being set. This will always 'eventually' close groovysh. But this also has the issue where stack traces get printed to the System console. This brings up another issue too though, where groovysh not only does not exit on a thread interrupt, but also does not exit when the input/output streams are closed. This means that the runner will continue to request input as fast as possible, swallowing the IOExceptions. In summary, Proper behavior would be to gracefully exit on a thread interrupt, and on an IOException where the streams are closed. But if nothing else, on the thread interrupt. Please let me know if you have any questions. Thank you, Trevor Flynn was: When running groovysh from an application, there is no way to gracefully stop groovysh. Interrupting the run thread should always propagate up and cause the thread to terminate, but in this case the interrupt exception is caught silently and the console continues to run. Example: The console is created like so {code:java} IO groovyshIO = new IO(console1.getInputStream(), console1.getOut(), console1.getErr()); Binding binding = new Binding(getBinding().getVariables()); binding.setProperty("out", console1.getOut()); binding.setProperty("err", console1.getErr()); groovysh = new Groovysh( getClassLoader(), binding, groovyshIO, null, getCompilerConfiguration() ); shellThread = new Thread(() -> { while (running) { try { groovysh.run(""); } catch (Exception ex) { log.error("Error running groovy shell", ex); } } }); shellThread.start(); {code} To stop the console, the verbosity must be set to DEBUG to prevent the InteractiveShellRunner from swallowing the exception, and immediately after the interrupt, the running flag must be set to false. {code} running = false; //This only works in combination with closing the console //We must set the verbosity to debug to force the exception to get propagated Preferences.verbosity = IO.Verbosity.DEBUG; //Interrupt the thread shellThread.interrupt(); //I don't know why setting running to false is needed, since the error handler should already do this //But it IS needed groovysh.getRunner().setRunning(false); {code} This has the major disadvantage of dumping stack traces to the System output, and potentially running into a race condition with setting running to false. A more reliable approach when possible is to close the console streams (if the application has access to them to do so) {code} running = false; //Close input, output, and error streams console1.close(); //This only works in combination with closing the console //We must set the verbosity to debug to force the exception to get propagated Preferences.verbosity = IO.Verbosity.DEBUG; //I don't know why setting running to false is needed, since the error handler should already do this //But it IS needed groovysh.getRunner().setRunning(false); {code} Because the streams are closed, the runner will loop with IOExceptions until the verbosity change and running being set. This will always 'eventually' close groovysh. But this also has the issue where stack traces get printed to the System console. This brings up another issue too though, where groovysh not only does not exit on a thread interrupt, but also does not exit when the input/output streams are closed. This means that the runner will continue to request input as fast as possible, swallowing the IOExceptions. In summary, Proper behavior would be to gracefully exit on a thread interrupt, and on an IOException where the streams are closed. But if nothing else, on the thread interrupt. Please let me know if you have any questions. Thank you, Trevor Flynn > Interrupting run thread not possible > ------------------------------------ > > Key: GROOVY-11251 > URL: https://issues.apache.org/jira/browse/GROOVY-11251 > Project: Groovy > Issue Type: Bug > Components: Groovysh > Affects Versions: 4.0.15, 4.0.16 > Reporter: Trevor Flynn > Priority: Major > > When running groovysh from an application, there is no way to gracefully stop > groovysh. Interrupting the run thread should always propagate up and cause > the thread to terminate, but in this case the interrupt exception is caught > silently and the console continues to run. > Example: > The console is created like so > > {code:java} > IO groovyshIO = new IO(console1.getInputStream(), > console1.getOut(), console1.getErr()); > Binding binding = new Binding(getBinding().getVariables()); > binding.setProperty("out", console1.getOut()); > binding.setProperty("err", console1.getErr()); > groovysh = new Groovysh( > getClassLoader(), > binding, > groovyshIO, > null, > getCompilerConfiguration() > ); > shellThread = new Thread(() -> { > while (running) { > groovysh.run(""); > } > }); > shellThread.start(); > {code} > To stop the console, the verbosity must be set to DEBUG to prevent the > InteractiveShellRunner from swallowing the exception, and immediately after > the interrupt, the running flag must be set to false. > {code:java} > running = false; > > //This only works in combination with closing the console > //We must set the verbosity to debug to force the exception to > get propagated > Preferences.verbosity = IO.Verbosity.DEBUG; > //Interrupt the thread > shellThread.interrupt(); > //I don't know why setting running to false is needed, since the > error handler should already do this > //But it IS needed > groovysh.getRunner().setRunning(false); > {code} > This has the major disadvantage of dumping stack traces to the System output, > and potentially running into a race condition with setting running to false. > A more reliable approach when possible is to close the console streams (if > the application has access to them to do so) > {code:java} > running = false; > //Close input, output, and error streams > console1.close(); > //This only works in combination with closing the console > //We must set the verbosity to debug to force the exception to > get propagated > Preferences.verbosity = IO.Verbosity.DEBUG; > //I don't know why setting running to false is needed, since the > error handler should already do this > //But it IS needed > groovysh.getRunner().setRunning(false); > {code} > Because the streams are closed, the runner will loop with IOExceptions until > the verbosity change and running being set. This will always 'eventually' > close groovysh. But this also has the issue where stack traces get printed to > the System console. > This brings up another issue too though, where groovysh not only does not > exit on a thread interrupt, but also does not exit when the input/output > streams are closed. This means that the runner will continue to request input > as fast as possible, swallowing the IOExceptions. > In summary, > Proper behavior would be to gracefully exit on a thread interrupt, and on an > IOException where the streams are closed. But if nothing else, on the thread > interrupt. > Please let me know if you have any questions. > Thank you, > Trevor Flynn -- This message was sent by Atlassian Jira (v8.20.10#820010)