Checking for Interrupts

Introduction

It is considered good practice to allow C code to be interupted if it runs for an extended period.

R_CheckUserInterrupt() checks whether the user is trying to interrupt (using ctrl-c or similar) and will immediately abort the execution of the code.

R_CheckUserInterrupt() example

In the folowing example, when interrupted, the Rprintf() and return statements will not be executed. Control will immediately return to the user’s R session.

#include <R.h>
#include <Rinternals.h>
#include <unistd.h>  // for 'sleep()'

SEXP interruptable_sleep(void) {
  while (1) {
    R_CheckUserInterrupt();  // abort if user interrupts. no recovery.
    sleep(1);
  }
  Rprintf("Never get here!  Interrupt causes immediate exit!");
  return R_NilValue;
}
Click to show R code
code = r"(
#include <R.h>
#include <Rinternals.h>
#include <unistd.h>  // for 'sleep()'

SEXP interruptable_sleep(void) {
  while (1) {
    R_CheckUserInterrupt();  // abort if user interrupts. no recovery.
    sleep(1);
  }
  Rprintf("Never get here!  Interrupt causes immediate exit!");
  return R_NilValue;
}
)"

callme::compile(code)
interruptable_sleep()

Continuing C execution after the interrupt

You may also wish to continue execution of the C code after the interrupt (e.g. to tidy and free any resources).

In this example the Rprintf() statment and return will be executed following the interuption.

#include <R.h>
#include <Rinternals.h>
#include <unistd.h>  // for 'sleep()'
#include <stdbool.h>

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// As suggested by Simon Urbanek
// https://stat.ethz.ch/pipermail/r-devel/2011-April/060702.html
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static void check_interrupt_internal(void *dummy) {
  R_CheckUserInterrupt();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// this will call the above in a top-level context so it won't 
// longjmp-out of your context
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool check_interrupt(void) {
  return (R_ToplevelExec(check_interrupt_internal, NULL) == FALSE);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Your code goes here
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SEXP interruptable_sleep2(void) {
  while (1) {
    if (check_interrupt()) break;  // break out of while(). Keep executing
    sleep(1);
  }
  Rprintf("My sleep was interrupted!\n");
  return R_NilValue;
}
Click to show R code
code = r"(
#include <R.h>
#include <Rinternals.h>
#include <unistd.h>  // for 'sleep()'
#include <stdbool.h>

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// As suggested by Simon Urbanek
// https://stat.ethz.ch/pipermail/r-devel/2011-April/060702.html
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static void check_interrupt_internal(void *dummy) {
  R_CheckUserInterrupt();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// this will call the above in a top-level context so it won't 
// longjmp-out of your context
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool check_interrupt(void) {
  return (R_ToplevelExec(check_interrupt_internal, NULL) == FALSE);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Your code goes here
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SEXP interruptable_sleep2(void) {
  while (1) {
    if (check_interrupt()) break;  // break out of while(). Keep executing
    sleep(1);
  }
  Rprintf("My sleep was interrupted!\n");
  return R_NilValue;
}
)"

callme::compile(code)
interruptable_sleep2()