A non-runaway-REPL?

260 views
Skip to first unread message

Haoyi Li

unread,
Jan 18, 2015, 3:10:04 PM1/18/15
to scala-internals
The Scala REPL is inferior to a bunch of other REPLs (Bash, Ruby, Python, ...) in that it does not handle Ctrl-C gracefully. The whole process dies, and you lose all your work, instead usually the last-command exiting and only rarely the whole-process dying

It turns out that solving the runaway-REPL problem is relatively easy:

sun.misc.Signal.handle(new Signal("INT"), new sun.misc.SignalHandler () {
  def handle(sig: Signal) {
      mainThread.stop()
  }
})

This is all that is required at the top of the program. The REPL's own try-catch error recovery is sufficient to do the rest.

Notably, this uses sun.misc.* functionality, which I'm not sure is available on other JVMs. I'd guess they also provide similar signal-handling functionality (speculation). This also makes use of the @Deprecated Thread#stop() method, which apparently is unsafe. Also notably, the groovysh/jython REPLs shares the same behavior as Scala, in forcing you to terminate the process if you accidentally infinite loop. 

However, I think that despite the fact that Thread#stop() is unsafe, it is no more unsafe than all the other things that the JVM provides (Runtime#exec anyone?). Furthermore, I think that a REPL providing a best-effort cleanup is still worth it, even if there are no guarantees: even trying to kill a separate process on your own machine can lead to the same problems that they deprecated Thread#stop() for! Similarly, you can easily get your Bash shell or OS into a state that it's unrecoverable and you need to re-boot. 

I'm sure many of us make use of Bash's best-effort process-killing and are happy that a single bad ls doesn't force us to terminate and re-start our SSH session, even though most of us have had my bash-prompt force-killed due to process/system damage when it's really severe.

Is there any interest in putting this kind of thing into the main Scala distribution? I I could easily put this functionality into my own custom REPL (and I have here) if people think using the deprecated Thread#stop is too sketchy. But I do think that having the REPL not blow up you when you make a mistake would be of value to a lot of people, and would be willing to do the legwork necessary to test this on various platforms and JVMs. Worst come to worst we use reflection to load the thing and just no-op otherwise.

Thoughts?

Som Snytt

unread,
Jan 18, 2015, 3:43:42 PM1/18/15
to scala-internals
I just hit this again recently while testing something that was known to hang.

I think there is a ticket for better ctl-c handling.

It could try the usual things: interrupt the thread, see if it worked, then escalate.

And if it finally reports, "I seem to be totally borked. Abandon session? [y]es, yes and [r]eplay, or [n]o" like it does with a slain compiler.

Maybe it could also use a "safe" mode where it forks a JVM. While we're at it, it's time for a debug command.

How many days 'til Christmas?


--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Adriaan Moors

unread,
Jan 18, 2015, 4:08:38 PM1/18/15
to scala-i...@googlegroups.com
I would love better ctrl-c handling! Definitely hit this as well from time to time. 

Ideally, we'd degrade gracefully on determining reflectively that sun.miss is miscing. Should also make sure not to break sbt/spark notebook, and any other IDEs building on the repl (I don't see how it would break anything, though). 

Haoyi Li

unread,
Jan 18, 2015, 4:14:25 PM1/18/15
to scala-internals
"I seem to be totally borked. Abandon session? [y]es, yes and [r]eplay, or [n]o" like it does with a slain compiler.

There's no reason that a slain compiler should make you lose your session. But that's a battle for another day =P Definitely will take more than a 10-line patch to fix.

Haoyi Li

unread,
Jan 18, 2015, 7:02:16 PM1/18/15
to scala-internals, Adriaan Moors
Adriaan how does one build a dist of the Scala repo locally as fast as possible (e.g. without running partest) so I can poke around at things (e.g. the scala command line launcher) manually? I've been reading your commit messages and they all say different things ^_^ Given the amount of time you've spent in build.xml you must surely be the expert by now....

I especially like this one

Inline image 1

Jason Zaugg

unread,
Jan 18, 2015, 7:31:04 PM1/18/15
to scala-i...@googlegroups.com, Adriaan Moors
On Mon, Jan 19, 2015 at 10:01 AM, Haoyi Li <haoy...@gmail.com> wrote:
Adriaan how does one build a dist of the Scala repo locally as fast as possible (e.g. without running partest) so I can poke around at things (e.g. the scala command line launcher) manually?

% ant all.clean build && ./build/pack/bin/scala

-jason

Haoyi Li

unread,
Jan 18, 2015, 8:18:02 PM1/18/15
to scala-internals, Adriaan Moors
Cool thanks Jason

--

Haoyi Li

unread,
Jan 18, 2015, 8:49:03 PM1/18/15
to scala-internals, Adriaan Moors
So here's what I have so far:


It works on my machine. This includes the sketchy resetting-signalhandler-via-reflection thing to make sure we properly give up control when the REPL closes, e.g. when handing over control back to the SBT REPL. As far as I can tell, it does exactly what it should.

I haven't managed to test it on anything more than my OSX machine right now, but I can probably do Windows/Ubuntu without too much difficult and Java 6/8. If there's in interest, I could get J9 and Avian to test it on as well. Not sure how I'd test it with ScalaIDE or IntelliJ.

I left a flag in the source code to disable it if necessary, not sure if you guys have a standard practice around these things or whether it should hang around off-by-default for some incubation period before being turned on.

WDYT? If there's interest I can polish it up (formatting, doc-comments, testing, etc.) and send a PR your way.


Jason Zaugg

unread,
Jan 18, 2015, 10:00:00 PM1/18/15
to scala-i...@googlegroups.com, Adriaan Moors
On Mon, Jan 19, 2015 at 11:48 AM, Haoyi Li <haoy...@gmail.com> wrote:

WDYT? If there's interest I can polish it up (formatting, doc-comments, testing, etc.) and send a PR your way.

For those interested in following the discussion, we've been discussing this feature in the GitHub comments of https://github.com/lihaoyi/scala/commit/6861761aa0ef3f4ed737af51fcbc0205da1f9781.

-jason

Dmitry Petrashko

unread,
Jan 26, 2015, 3:05:59 PM1/26/15
to scala-i...@googlegroups.com


On Sunday, 18 January 2015 21:10:04 UTC+1, Haoyi Li wrote:

However, I think that despite the fact that Thread#stop() is unsafe, it is no more unsafe than all the other things that the JVM provides (Runtime#exec anyone?).
 
Thoughts?
It's one of the unsafiest methods you can imagine.
Why? becouse it's the first @deprecated method in JVM ever de-implemented.
As of JDK8 It now just throws UnsupportedOperationException.

---
Dmitry

Erik Bruchez

unread,
Jan 26, 2015, 3:37:47 PM1/26/15
to scala-i...@googlegroups.com
> As of JDK8 It now just throws UnsupportedOperationException.

That doesn't seem to be correct. It seems that Thread.stop(Throwable
obj) throws UnsupportedOperationException, yes, but Thread.stop()
works as before.

http://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#stop--

See also:

http://stackoverflow.com/questions/23317332/alternate-to-thread-stop-in-java-8

-Erik

Som Snytt

unread,
Jan 26, 2015, 3:49:49 PM1/26/15
to scala-internals
Still, this is the quote of the day.
 
It's one of the unsafiest methods you can imagine.
 

Haoyi Li

unread,
Jan 26, 2015, 4:17:33 PM1/26/15
to scala-internals
It's one of the unsafiest methods you can imagine.
> Why? becouse it's the first @deprecated method in JVM ever de-implemented.
> As of JDK8 It now just throws UnsupportedOperationException.

Only the non-threaddeath version; the threaddeath version still works great

Viktor Klang

unread,
Jan 26, 2015, 4:24:56 PM1/26/15
to scala-i...@googlegroups.com
Can't even be clever :(

scala> Thread.currentThread.stop(new ThreadDeath)
warning: there was one deprecation warning; re-run with -deprecation for details
java.lang.UnsupportedOperationException
  at java.lang.Thread.stop(Thread.java:868)
  ... 33 elided
Cheers,

Haoyi Li

unread,
Jan 26, 2015, 5:12:08 PM1/26/15
to scala-internals
<rant>

I still don't understand why they'd take away thread stop because of problems with locks and mutable state. Shouldn't you take away the locks and mutable state instead? It's OO gone mad.

The problems everyone keeps saying thread-stop causes are problems that people have had with unix systems since forever. Yet life goes on. Could you imagine if bash didn't let you kill a program because you may leave the filesystem in an inconsistent state?

"Oops, my program has a bug. Plz stop"

"""
Sorry I can't do that Dave. You're going to have to pay Amazon $$$ until the end of time. 

I've still got the greatest enthusiasm and confidence in the mission
"""
</rant>
Reply all
Reply to author
Forward
0 new messages