[SI-6302] REPL Ctrl-C behaviour suggestion Created: 31/Aug/12  Updated: 19/Apr/15

Status: Open
Project: Scala Programming Language
Component/s: Repl / Interpreter
Affects Version/s: None
Fix Version/s: None

Type: Improvement Priority: Major
Reporter: Alex Cruise Assignee: Unassigned
Resolution: Unresolved Votes: 10
Labels: None


 Description   

From my scala-internals post (https://groups.google.com/d/msg/scala-internals/PqK6GkZhJK0/xjZw3wQ4wYEJ), verbatim:

Ideally, the function of Ctrl-C should depend on whether the REPL is waiting for user input, or running something in the foreground.

Waiting at a prompt, my preference would be:

  • If there's no text entered at the prompt, print a message like "(interrupt) type :quit <Enter> or Ctrl-D to exit"
  • it's handy to trap Ctrl-C here, because people often press it repeatedly without intending to completely exit their current activity--mysql client, I'm looking at you
  • If there is text entered, discard it and bring up a new, empty prompt--this is what bash does.

With a foreground task running, ideally Ctrl-C should kill the task and return control to the REPL. I guess this implies running each command in a separate thread from the REPL itself, so it can be forcibly stopped—but of course, this doesn't necessarily imply *starting a new thread for each command*—we could just keep one of them around to run user code in—until it dies, only then we start a new one.

As for the mechanics of stopping the task, so long as we're baking pie in the sky...

while (!thread.isDead) {
  thread.youShouldExitNow() // presumably this is a custom thread class
  thread.interrupt()
  Thread.sleep(n) // say 2-5 seconds
  if (!thread.isDead) {
    val yn = prompt "The thread hasn't exited yet. Should we try to forcibly stop it?"
    if (yn == "y") {
      thread.stop()
    } 
  }
}

If anything is running in the background, may FSM have mercy on its soul. Out of scope.

If we felt like being clever, we could rewrite any line that has a looping construct at the root of its tree to be breakable. If it's a method call that loops, well, too bad.



 Comments   
Comment by Alex Cruise [ 31/Aug/12 ]

Sigh, markup

Comment by Grzegorz Kossakowski [ 24/Sep/12 ]

Downgrading this to major. We cannot put on hold 2.10.0 release because of this ticket.

Comment by Chip Senkbeil [ 07/Sep/14 ]

I take it that this was never pursued. With JDK 8 (Oracle) officially disabling Thread.stop, the above would be trimmed down to just attempting an interrupt (hoping the code is interruptible). Cancelling a run doesn't seem to do anything to break out of long-running code.

Like the description says, you could rewrite looping constructs to be interruptible, but that doesn't do anything for external code being referenced inside the REPL.

Just seems like something that would be nice to have, but maybe it's too much to ask for.

Comment by Paul Phillips [ 07/Sep/14 ]

Nope, Thread.stop is still available. That refers to the "Thread.stop(Throwable)" version, but not the no argument form.

Comment by Chip Senkbeil [ 07/Sep/14 ]

I thought the no argument form was just a wrapper for Thread.stop(Throwable) with an argument of ThreadDeath?

In JDK 7, this was what I found when browsing JDK 1.7:

public final void stop()

{ stop(new ThreadDeath()); }
Comment by Paul Phillips [ 07/Sep/14 ]

I verified Thread.stop works on 1.8.0_20 before I commented.

Comment by Li Haoyi [ 19/Apr/15 ]

This is fixed in the Ammonite REPL

[info] Running ammonite.repl.Repl
Loading Ammonite Repl...
@ while(true) ()
 
Interrupted!
@

Generated at Tue Jun 19 03:08:41 CEST 2018 using JIRA 7.9.1#79001-sha1:60970b42586a2ec2760ed6cfe825b26961e62b9e.