[Rd] Interrupting C++ code execution

Simon Urbanek simon.urbanek at r-project.org
Mon Apr 25 18:23:26 CEST 2011


On Apr 25, 2011, at 11:09 AM, schattenpflanze at arcor.de wrote:

> Thank you for your response, Simon.
> 
>>> 1. Calling R_CheckUserInterrupt() interrupts immediately, so I have
>>> no possibility to exit my code gracefully. In particular, I suppose
>>> that objects created on the heap (e.g., STL containers) are not
>>> destructed properly.
>> In general, you're responsible for the cleanup. See R-devel archives
>> for discussion on the interactions of C++ and R error handling.
>> Generally, you should not use local objects and you should use
>> on.exit to make sure you clean up.
> I am using Rcpp (Rcpp-modules, to be precise). This means, I do actually not write any R code. Moreover, the C++ code does not use the R API. My C++ functions are 'exposed' to R via Rcpp, which creates suitable S4 classes. Rcpp does the exception handling.
> In particular, there is no obvious possibility for me to add an 'on.exit' statement to a particular exposed C++ method.
> 
>> Generally, you should not use local objects
> We are talking about large amounts of code, dozens of nested function calls, and even external libraries. So "not using local objects" is definitely no option.
> 

But that would imply that the library calls R! Note that we're talking about the stack at the point of R API call, so you can do what you want until you cal R API. At the moment you touch R API you should have no local C++ objects on the stack (all the way down) - that's what I meant. 


>>> 2. Calling R_CheckUserInterrupt() within a parallel OpenMP loop
>>> causes memory corruptions. Even if I do so within a critical
>>> section, it usually results in segfaults, crashes, or invalid
>>> variable contents afterwards. I suppose this is due to the threads
>>> not being destroyed properly. Since most of the time critical
>>> computations are done in parallel, this means I can hardly
>>> interrupt anything.
>> As you know R is not thread-safe so you cannot call any R API from a
>> thread - including OMP threads - so obviously you can't call
>> R_CheckUserInterrupt().
> That is very interesting. Not being thread safe does not necessarily imply that a function cannot be called from within a thread (as long as it is not done concurrently from several threads). In particular, the main program itself is also a thread, isn't it?

Yes, but each thread has a separate stack, and you can only enter R with the same stack you left (because the stack will be restored to the state of the calling context).


> Since no cleanup is done, however, it is now clear that calling R_CheckUserInterrupt() _anywhere_ in my program, parallel section or not, is a bad idea.
> 
>> Since you're using threads the safe way is to
>> perform your computations on a separate thread and let R handle
>> events so that you can abort your computation thread as part of
>> on.exit.
> Starting the computations in a separate thread is a nice idea. I could then call R_CheckUserInterrupt() every x milliseconds in the function which dispatches the worker thread. Unfortunately, I see no obvious way of adding an "on.exit" statement to an Rcpp module method. So I would probably have to call an R function from C++ (e.g., using RInside) which contains the on.exit statement, which in turn calls again a C++ function setting a global 'abort' flag and waits for the threads to be terminated. Hmmm.
> 
> How does on.exit work?

It sets the conexit object of the current context structure to the closure to be evaluated when the context is left. endcontext() then simply evaluates that closure when the context is left.


> Could I mimic that behaviour directly in C++?
> 

Unfortunately there is no C-level onexit hook and the internal structure of RCNTXT is not revealed to packages. So AFAICS the closest you can get is to use eval to call on.exit().

However, I think it would be useful to have a provision for creating a context with a C-level hook - the question is whether the others have the feeling that it's going to a too low level ...


>>> Having a function similar to R_CheckUserInterrupt() but returning a
>>> boolean variable (has an interrupt occurred or not?) would solve
>>> these problems. Is there a way to find out about user interrupt
>>> requests (the user pressing ctrl+c or maybe a different set of
>>> keys) without interrupting immediately?
>> Checking for interrupts may involve running the OS event loop (to
>> allow the user to interact with R) and thus is not guaranteed to
>> return.
> I see.
> 
>> There is no general solution - if you're worried only about
>> your, local code, then on unix, for example, you could use custom
>> signal handlers to set a flag and co-operatively interrupt your
>> program. On Windows there is the UserBreak flag which can be set by a
>> separate thread and thus you may check on it. That said, all this is
>> very much platform-specific.
> Being able to set a flag is all I need and would be the perfect solution imho. However, I do not yet see how I could achieve that.
> 

It is GUI-specific, unfortunately. AFAIR the Windows GUI does that because it's running on a separate thread. I think the X11-based GUIs use fds so the are synchronous and on OS X runs the OS loop inside the R event loop - so, again, synchronous.


> How can I write a signal handler within C++ code which does not create a GUI and has no dedicated event dispatching thread?

That's simple just use signal() to register your handler.


> Would it be possible to use, e.g., a Qt keyboard event handler within the C++ code? Would a keyboard event be visible to such an event handler? Is it not intercepted by R / the terminal window / the OS?
> 

Meshing R's loop, GUI loop and your own code will be a nightmare. For example, one problem is that if you are running the GUI loop and it triggers an event that R would otherwise handle (e.g. resizing plot window) you're in trouble since you can't let R do anything...


> Does any existing R package contain signal handlers?
> 

I'm not sure - I would definitely not recommend that to be used in packages since it's platform-dependent and changes the semantics of signals defined by R. But you can play with it ;).

Cheers,
Simo



More information about the R-devel mailing list