Skip to content

Commit

Permalink
Resolve SIGINT-triggered console assertion
Browse files Browse the repository at this point in the history
In the R3 console executable, SIGINT (Ctrl-C) can happen at any moment,
and sets a flag that Rebol can read at a later time when it is ready
for it.  Then when it is "ready for it", it uses longjmp to hop up to
a handler to take control from whatever was going on.

Problem is, in the API it considered itself "ready for it" during
printing out of values through the value-printing API.  The host main
was not prepared for an error to occur outside of evaluation, and
if after a Ctrl-C happened it got to any printing before enough
evaluation steps signaled to process the signal, it would assert.

This adds a containing trap--even outside of any evaluation--which
should resolve this assert for now (though it points to the idea that
the API should have a solid story for interactions with interrupts).
  • Loading branch information
hostilefork committed Jan 14, 2016
1 parent 95abcd6 commit ed0ea34
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/core/c-do.c
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,8 @@ REBOOL Do_Signals_Throws(REBVAL *out)
REBCNT sigs;
REBCNT mask;

assert(Saved_State || PG_Boot_Phase < BOOT_MEZZ);

// Accumulate evaluation counter and reset countdown:
if (Eval_Count <= 0) {
//Debug_Num("Poll:", (REBINT) Eval_Cycles);
Expand Down
5 changes: 5 additions & 0 deletions src/core/c-port.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ REBOOL Wait_Ports(REBARR *ports, REBCNT timeout, REBOOL only)
REBCNT wt = 1;
REBCNT res = (timeout >= 1000) ? 0 : 16; // OS dependent?

// Waiting opens the doors to pressing Ctrl-C, which may get this code
// to throw an error. There needs to be a state to catch it.
//
assert(Saved_State != NULL);

while (wt) {
if (GET_SIGNAL(SIG_HALT)) {
CLR_SIGNAL(SIG_HALT);
Expand Down
46 changes: 45 additions & 1 deletion src/os/host-main.c
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,10 @@ void Host_Repl(int *exit_status, REBVAL *out, REBOOL at_breakpoint) {

do_result = Do_String(exit_status, out, input, at_breakpoint);

// NOTE: Although the operation has finished at this point, it may
// be that a Ctrl-C set up a pending FAIL, which will be triggered
// during output below. See the PUSH_UNHALTABLE_TRAP in the caller.

if (do_result == -1) {
//
// If we're inside a breakpoint, this actually means "resume",
Expand Down Expand Up @@ -1008,18 +1012,58 @@ int main(int argc, char **argv_ansi)
goto cleanup_and_exit; // exit status is set...

#if !defined(ENCAP)
// Console line input loop (just an example, can be improved):
//
// Call the console line input loop function if necessary
//
if (
!(Main_Args.options & RO_CGI)
&& (
!Main_Args.script // no script was provided
|| Main_Args.options & RO_HALT // --halt option
)
) {
struct Reb_State state;
REBCON *error;

REBVAL value;
VAL_INIT_WRITABLE_DEBUG(&value);

push_trap:
//
// The R3-Alpha host kit did not have a policy articulated on dealing
// with the interrupt nature of the SIGINT signals sent by Ctrl-C
//
// https://en.wikipedia.org/wiki/Unix_signal
//
// Guarding against errors being longjmp'd when an evaluation is in
// effect isn't the only time these signals are processed. Rebol's
// Process_Signals currently happens during I/O, such as printing.
// As a consequence, a Ctrl-C can be picked up and then triggered
// during an Out_Value, jumping the stack from there.
//
// This means a top-level trap must be run, even though no eval is
// in effect. The most convenient place to do this is here, outside
// the REPL call that has the I/O.
//
PUSH_UNHALTABLE_TRAP(&error, &state);

// The first time through the following code 'error' will be NULL, but...
// `fail` can longjmp here, so 'error' won't be NULL *if* that happens!

if (error) {
//
// If a HALT happens and manages to get here, just go set up the
// trap again and call into the REPL again. (It wasn't an
// evaluation error because those have their own traps, it was a
// halt that happened during output.)
//
assert(ERR_NUM(error) == RE_HALT);
goto push_trap;
}

Host_Repl(&exit_status, &value, FALSE);

DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);
}
else
exit_status = 0; // "success"
Expand Down

0 comments on commit ed0ea34

Please sign in to comment.