Ignore user interrupts

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Ignore user interrupts

Sharpie
Is there a way to suspend user interrupts for the duration of a function call? There is a point in one of my packages where values are being written to a Filehash database. If the user is unlucky enough to send an interrupt while this code is active, then they have to:

  - Hunt down a lock file and dispose of it before the package will work again.

  - Possibly trash the Filehash database and start over if the interrupt caused R to leave the database file in a corrupted state.

Is there something analogous to the C macros BEGIN_SUSPEND_INTERRUPTS/END_SUSPEND_INTERRUPTS that I can use at the R level to delay interrupts until after critical code has executed?

If not, would it work to move the function call into a C function that uses `eval` inside a block protected by BEGIN_SUSPEND_INTERRUPTS?

-Charlie
Charlie Sharpsteen
Undergraduate-- Environmental Resources Engineering
Humboldt State University
Reply | Threaded
Open this post in threaded view
|

Re: Ignore user interrupts

Sharpie
Sharpie wrote
If not, would it work to move the function call into a C function that uses `eval` inside a block protected by BEGIN_SUSPEND_INTERRUPTS?
Just to clarify, if there is no functionality at the R level for evaluating an expression without interrupts, would it be possible to create it by defining a function:

evalWithoutInterrupts <- function(expr, envir = parent.frame())
{
  .Call(do_evalWithoutInterrupts, expr, envir)
}


With a C-level implemention:

SEXPR do_evalWithoutInterrupts(SEXP expr, SEXP envir)
{
  SEXP result;

  BEGIN_SUSPEND_INTERRUPTS{
    result = eval(expr, envir);
  }END_SUSPEND_INTERRUPTS;

  return result;
}


Would that be a correct way to tackle the problem?

-Charlie
Charlie Sharpsteen
Undergraduate-- Environmental Resources Engineering
Humboldt State University
Reply | Threaded
Open this post in threaded view
|

Re: Ignore user interrupts

Sharpie
Sharpie wrote
evalWithoutInterrupts <- function(expr, envir = parent.frame())
{
  .Call(do_evalWithoutInterrupts, expr, envir)
}


With a C-level implemention:

SEXPR do_evalWithoutInterrupts(SEXP expr, SEXP envir)
{
  SEXP result;

  BEGIN_SUSPEND_INTERRUPTS{
    result = eval(expr, envir);
  }END_SUSPEND_INTERRUPTS;

  return result;
}
Some more info, and a possible bug:

This approach appears to work if I change `evalWithoutInterrupts` so that it invokes `substitute` on `expr` before passing to the C code:


    evalWithoutInterrupts <- function(expr, envir = parent.frame())
    {
      .Call(do_evalWithoutInterrupts, substitute(expr), envir)
    }


For example, I can do the following (using OS X, R 2.14.1 in Terminal.app):

    eval({for(i in 1:10000000){log(i)};message("Hello, world!")})
    evalWithoutInterrupts({for(i in 1:10000000){log(i)};message("Hello, world!")})

The `eval` call can be interrupted by CTRL-C as normal. However, with the `evalWithoutInterrupts` call, I can tap on CTRL-C and the loop will still execute, "Hello, world!" will be printed and then the interrupt will be registered:

    > evalWithoutInterrupts({for(i in 1:10000000){log(i)};message("Hello, world!")})
    ^C^C^C^C^CHello, world!

    >


The only odd thing I came across was when I tried to test this function using `Sys.sleep` instead of a long loop:

    evalWithoutInterrupts(Sys.sleep(3);message("Hello, world!")})

The call can be interrupted immediately by CTRL-C. Some poking in GDB reveals that the call chain passes from my C function `evalWithoutInterrupts` to `do_syssleep` an finally to `Rf_onintr` through `R_checkActivity`:

Breakpoint 1, Rf_onintr () at errors.c:123
123    if (R_interrupts_suspended) {
(gdb) bt  
#0  Rf_onintr () at errors.c:123
#1  0x00000001001ced41 in R_SelectEx (n=1, readfds=0x10038cdc0, writefds=0x0, exceptfds=0x0, timeout=0x7fff5fbf8b60, intr=0) at sys-std.c:127
#2  0x00000001001cf109 in R_checkActivityEx (usec=3000000, ignore_stdin=1, intr=0) at sys-std.c:329
#3  0x00000001001cf14b in R_checkActivity (usec=3000000, ignore_stdin=1) at sys-std.c:338
#4  0x00000001001d0fbb in do_syssleep (call=0x1025bb200, op=0x10086d0d8, args=0x1033679b8, rho=0x1033679f0) at sys-std.c:1299
#5  0x00000001000c7608 in bcEval (body=0x1025ba878, rho=0x1033679f0, useCache=TRUE) at eval.c:4445
#6  0x00000001000bcb69 in Rf_eval (e=0x1025ba878, rho=0x1033679f0) at eval.c:401
#7  0x00000001000bddd7 in Rf_applyClosure (call=0x103366e38, op=0x1025ba8e8, arglist=0x103367a60, rho=0x100877ea8, suppliedenv=0x100877ee0) at eval.c:840
#8  0x00000001000bd22e in Rf_eval (e=0x103366e38, rho=0x100877ea8) at eval.c:515
#9  0x00000001000bf879 in do_begin (call=0x103366ee0, op=0x10084e6e0, args=0x103366dc8, rho=0x100877ea8) at eval.c:1422
#10 0x00000001000bcf40 in Rf_eval (e=0x103366ee0, rho=0x100877ea8) at eval.c:471
#11 0x0000000102fc736d in evalWithoutInterrupts ()


However, at this point the variable `R_interrupts_suspended` is not set to `TRUE` as I would expect due to the `BEGIN_SUSPEND_INTERRUPTS` block in `evalWithoutInterrupts`:

(gdb) p R_interrupts_suspended
$1 = FALSE


Bug?

-Charlie
Charlie Sharpsteen
Undergraduate-- Environmental Resources Engineering
Humboldt State University
Reply | Threaded
Open this post in threaded view
|

Re: Ignore user interrupts

luke-tierney
On Thu, 26 Jan 2012, Sharpie wrote:

>
> Sharpie wrote
>>
>> evalWithoutInterrupts <- function(expr, envir = parent.frame())
>> {
>>   .Call(do_evalWithoutInterrupts, expr, envir)
>> }
>>
>>
>> With a C-level implemention:
>>
>> SEXPR do_evalWithoutInterrupts(SEXP expr, SEXP envir)
>> {
>>   SEXP result;
>>
>>   BEGIN_SUSPEND_INTERRUPTS{
>>     result = eval(expr, envir);
>>   }END_SUSPEND_INTERRUPTS;
>>
>>   return result;
>> }
>>
>
> Some more info, and a possible bug:
>
> This approach appears to work if I change `evalWithoutInterrupts` so that it
> invokes `substitute` on `expr` before passing to the C code:
>
>
>    evalWithoutInterrupts <- function(expr, envir = parent.frame())
>    {
>      .Call(do_evalWithoutInterrupts, substitute(expr), envir)
>    }
>
>
> For example, I can do the following (using OS X, R 2.14.1 in Terminal.app):
>
>    eval({for(i in 1:10000000){log(i)};message("Hello, world!")})
>    evalWithoutInterrupts({for(i in 1:10000000){log(i)};message("Hello,
> world!")})
>
> The `eval` call can be interrupted by CTRL-C as normal. However, with the
> `evalWithoutInterrupts` call, I can tap on CTRL-C and the loop will still
> execute, "Hello, world!" will be printed and then the interrupt will be
> registered:
>
>    > evalWithoutInterrupts({for(i in 1:10000000){log(i)};message("Hello,
> world!")})
>    ^C^C^C^C^CHello, world!
>
>    >
>
>
> The only odd thing I came across was when I tried to test this function
> using `Sys.sleep` instead of a long loop:
>
>    evalWithoutInterrupts(Sys.sleep(3);message("Hello, world!")})
>
> The call can be interrupted immediately by CTRL-C. Some poking in GDB
> reveals that the call chain passes from my C function
> `evalWithoutInterrupts` to `do_syssleep` an finally to `Rf_onintr` through
> `R_checkActivity`:
>
> Breakpoint 1, Rf_onintr () at errors.c:123
> 123    if (R_interrupts_suspended) {
> (gdb) bt
> #0  Rf_onintr () at errors.c:123
> #1  0x00000001001ced41 in R_SelectEx (n=1, readfds=0x10038cdc0,
> writefds=0x0, exceptfds=0x0, timeout=0x7fff5fbf8b60, intr=0) at
> sys-std.c:127
> #2  0x00000001001cf109 in R_checkActivityEx (usec=3000000, ignore_stdin=1,
> intr=0) at sys-std.c:329
> #3  0x00000001001cf14b in R_checkActivity (usec=3000000, ignore_stdin=1) at
> sys-std.c:338
> #4  0x00000001001d0fbb in do_syssleep (call=0x1025bb200, op=0x10086d0d8,
> args=0x1033679b8, rho=0x1033679f0) at sys-std.c:1299
> #5  0x00000001000c7608 in bcEval (body=0x1025ba878, rho=0x1033679f0,
> useCache=TRUE) at eval.c:4445
> #6  0x00000001000bcb69 in Rf_eval (e=0x1025ba878, rho=0x1033679f0) at
> eval.c:401
> #7  0x00000001000bddd7 in Rf_applyClosure (call=0x103366e38, op=0x1025ba8e8,
> arglist=0x103367a60, rho=0x100877ea8, suppliedenv=0x100877ee0) at eval.c:840
> #8  0x00000001000bd22e in Rf_eval (e=0x103366e38, rho=0x100877ea8) at
> eval.c:515
> #9  0x00000001000bf879 in do_begin (call=0x103366ee0, op=0x10084e6e0,
> args=0x103366dc8, rho=0x100877ea8) at eval.c:1422
> #10 0x00000001000bcf40 in Rf_eval (e=0x103366ee0, rho=0x100877ea8) at
> eval.c:471
> #11 0x0000000102fc736d in evalWithoutInterrupts ()
>
>
> However, at this point the variable `R_interrupts_suspended` is not set to
> `TRUE` as I would expect due to the `BEGIN_SUSPEND_INTERRUPTS` block in
> `evalWithoutInterrupts`:
>
> (gdb) p R_interrupts_suspended
> $1 = FALSE
>
>
> Bug?

No.

The interrupt managemant we have now is intended for the C level to
make sure interrupts can only occur where they are safe for the C
code, and at this point in sleep they are and so do_syssleep enables
them.

There is a need for interrupt management at the R level but getting it
right is not easy.  It isn't just a matter of suspending them, but
suspending and and re-enabling them where they are safe. Some code
that would potentially hang needs to enable them -- typically these
are operations that could signal other sorts of errors, like read
errors, timeouts, etc. and code that needs to ensure cleanup code is
run would need to catch those as well. (Interrupts are catchable as
"interrupt" conditions by the way).  There is some literature on this
(in particular an article "Asynchronous Exceptions in Haskell") that I
need to study more before implementing something at the R level.

luke

>
> -Charlie
>
>
> -----
> Charlie Sharpsteen
> Undergraduate-- Environmental Resources Engineering
> Humboldt State University
> --
> View this message in context: http://r.789695.n4.nabble.com/Ignore-user-interrupts-tp4321252p4331653.html
> Sent from the R devel mailing list archive at Nabble.com.
>
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>

--
Luke Tierney
Chair, Statistics and Actuarial Science
Ralph E. Wareham Professor of Mathematical Sciences
University of Iowa                  Phone:             319-335-3386
Department of Statistics and        Fax:               319-335-3017
    Actuarial Science
241 Schaeffer Hall                  email:   [hidden email]
Iowa City, IA 52242                 WWW:  http://www.stat.uiowa.edu

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Ignore user interrupts

Sharpie
On Fri, Jan 27, 2012 at 6:25 AM,  <[hidden email]> wrote:

> On Thu, 26 Jan 2012, Sharpie wrote:
>> The only odd thing I came across was when I tried to test this function
>> using `Sys.sleep` instead of a long loop:
>>
>>   evalWithoutInterrupts(Sys.sleep(3);message("Hello, world!")})
>>
>> The call can be interrupted immediately by CTRL-C. Some poking in GDB
>> reveals that the call chain passes from my C function
>> `evalWithoutInterrupts` to `do_syssleep` an finally to `Rf_onintr` through
>> `R_checkActivity`:

...

>> However, at this point the variable `R_interrupts_suspended` is not set to
>> `TRUE` as I would expect due to the `BEGIN_SUSPEND_INTERRUPTS` block in
>> `evalWithoutInterrupts`:
>>
>> (gdb) p R_interrupts_suspended
>> $1 = FALSE
>>
>>
>> Bug?
>
>
> No.
>
> The interrupt managemant we have now is intended for the C level to
> make sure interrupts can only occur where they are safe for the C
> code, and at this point in sleep they are and so do_syssleep enables
> them.


Thanks for the confirmation Luke---I figured Sys.sleep was somehow
explicitly ignoring the suspended state of interrupts. However, it
seems possible that a situation could arise where R needs to sleep
while waiting for some information needed to finish a critical
operation. I agree that this sounds like poorly written code, but it
would be nice to have a "do not disturb" sign that sets up a context
where all code will ignore SIGINTs generated by the user---even if
that code is just napping for a few seconds.

If someone really wants to halt a critical section of code, they
should need a signal like SIGTERM or SIGKILL that expresses the
severity of such an action.


> There is a need for interrupt management at the R level but getting it
> right is not easy.  It isn't just a matter of suspending them, but
> suspending and and re-enabling them where they are safe. Some code
> that would potentially hang needs to enable them -- typically these
> are operations that could signal other sorts of errors, like read
> errors, timeouts, etc. and code that needs to ensure cleanup code is
> run would need to catch those as well.


The only thing I am worried about are user-generated interrupts. I
have had a few bug reports that are the result of users just being
unlucky enough to signal an interrupt while a filehash operation was
running. I am pretty confident that these operations either complete
or throw an error, so it is very helpful to delay the processing of
SIGINT for a few microseconds until the filehash ops complete.


> (Interrupts are catchable as "interrupt" conditions by the way).


I tried playing around with `tryCatch` before turning my attention to
BEGIN_SUSPEND_INTERRUPTS and didn't have much success. I could not
find any way to create an interrupt handler that returned control to
the interrupted function call at the point where the interrupt was
signaled. For example, the following catches warnings and ignores
them:

    withCallingHandlers(expr, warning = function(w)
invokeRestart("muffleWarning"))

I am looking for a similar restart handler that traps interrupts and
continues on with the original function call as if nothing happened
(or maybe keeps track of the rate at which interrupts are being
signaled and finally breaks out if the user seems insistent).


-Charlie

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Charlie Sharpsteen
Undergraduate-- Environmental Resources Engineering
Humboldt State University