Utilities for Delaying Function Execution

Executes arbitrary R or C functions some time after the current time, after the R execution stack has emptied.


Build Status

Schedule an R function or formula to run after a specified period of time. Similar to JavaScript's setTimeout function. Like JavaScript, R is single-threaded so there's no guarantee that the operation will run exactly at the requested time, only that at least that much time will elapse.

To avoid bugs due to reentrancy, by default, scheduled operations only run when there is no other R code present on the execution stack; i.e., when R is sitting at the top-level prompt. You can force past-due operations to run at a time of your choosing by calling later::run_now().

The mechanism used by this package is inspired by Simon Urbanek's background package and similar code in Rhttpd.

Installation

remotes::install_github("r-lib/later")

Usage from R

Pass a function (in this case, delayed by 5 seconds):

later::later(function() {
  print("Got here!")
}, 5)

Or a formula (in this case, run as soon as control returns to the top-level):

later::later(~print("Got here!"))

Usage from C++

You can also call later::later from C++ code in your own packages, to cause your own C-style functions to be called back. This is safe to call from either the main R thread or a different thread; in both cases, your callback will be invoked from the main R thread.

later::later is accessible from later_api.h and its prototype looks like this:

void later(void (*func)(void*), void* data, double secs)

The first argument is a pointer to a function that takes one void* argument and returns void. The second argument is a void* that will be passed to the function when it's called back. And the third argument is the number of seconds to wait (at a minimum) before invoking.

To use the C++ interface, you'll need to add later to your DESCRIPTION file under both LinkingTo and Imports, and also make sure that your NAMESPACE file has an import(later) entry.

Background tasks

Finally, this package also offers a higher-level C++ helper class to make it easier to execute tasks on a background thread. It is also available from later_api.h and its public/protected interface looks like this:

class BackgroundTask {
 
public:
  BackgroundTask();
  virtual ~BackgroundTask();
 
  // Start executing the task   
  void begin();
 
protected:
  // The task to be executed on the background thread. 
  // Neither the R runtime nor any R data structures may be 
  // touched from the background thread; any values that need 
  // to be passed into or out of the Execute method must be 
  // included as fields on the Task subclass object. 
  virtual void execute() = 0;
  
  // A short task that runs on the main R thread after the 
  // background task has completed. It's safe to access the 
  // R runtime and R data structures from here. 
  virtual void complete() = 0;
} 

Create your own subclass, implementing a custom constructor plus the execute and complete methods.

It's critical that the code in your execute method not mutate any R data structures, call any R code, or cause any R allocations, as it will execute in a background thread where such operations are unsafe. You can, however, perform such operations in the constructor (assuming you perform construction only from the main R thread) and complete method. Pass values between the constructor and methods using fields.

#include <Rcpp.h>
#include <later_api.h>
 
class MyTask : public later::BackgroundTask {
public:
  MyTask(Rcpp::NumericVector vec) :
    inputVals(Rcpp::as<std::vector<double> >(vec)) {
  }
 
protected:
  void execute() {
    double sum = 0;
    for (std::vector<double>::const_iterator it = inputVals.begin();
      it != inputVals.end();
      it++) {
      
      sum += *it;
    }
    result = sum / inputVals.size();
  }
  
  void complete() {
    Rprintf("Result is %f\n", result);
  }
 
private:
  std::vector<double> inputVals;
  double result;
};

To run the task, new up your subclass and call begin(), e.g. (new MyTask(vec))->begin(). There's no need to keep track of the pointer; the task object will delete itself when the task is complete.

// [[Rcpp::export]]
void asyncMean(Rcpp::NumericVector data) {
  (new MyTask(data))->begin();
}

It's not very useful to execute tasks on background threads if you can't get access to the results back in R. We'll soon be introducing a complementary R package that provides a suitable "promise" or "future" abstraction.

News

later 0.8.0

  • Fixed issue #77: On some platforms, the system's C library has support for C11-style threads, but there is no threads.h header file. In this case, later's configure script tried to use the tinycthread, but upon linking, there were function name conflicts between tinycthread and the system's C library. Later no longer tries to use the system's threads.h, and the functions in tinycthread were renamed so that they do not accidentally link to the system C library's C11-style thread functions. PR #79

  • Added all argument to run_now(); defaults to TRUE, but if set to FALSE, then run_now will run at most one later operation before returning. PR #75

  • Fixed issue #74: Using later with R at the terminal on POSIX could cause 100% CPU. This was caused by later accidentally provoking R to call its input handler continuously. PR #76

  • Fixed issue #73: Linking later on ARM failed because boost::atomic requires the -lboost_atomic flag. Now later tries to use std::atomic when available (when the compiler supports C++11), and falls back to boost::atomic if not. PR #80

later 0.7.5

  • Fixed issue where the order of callbacks scheduled by native later::later could be nondeterministic if they are scheduled too quickly. This was because callbacks were sorted by the time at which they come due, which could be identical. Later now uses the order of insertion as a tiebreaker. PR #69

later 0.7.4

  • Fixed issue #45 and #63: glibc 2.28 and musl (used on Arch and Alpine Linux) added support for C11-style threads.h, which masked functions from the tinycthread library used by later. Later now detects support for threads.h and uses it if available; otherwise it uses tinycthread. PR #64

later 0.7.3

  • Fixed issue #57: If a user interrupt occurred when later (internally) called sys.nframe(), the R process would crash. PR #58

later 0.7.2

  • Fixed issue #48: Occasional timedwait errors from later::run_now. Thanks, @vnijs! PR #49

  • Fixed a build warning on OS X 10.11 and earlier. PR #54

later 0.7.1

  • Fixed issue #39: Calling the C++ function later::later() from a different thread could cause an R GC event to occur on that thread, leading to memory corruption. PR #40

  • Decrease latency of repeated top-level execution.

later 0.7 (unreleased)

  • Fixed issue #22: GC events could cause an error message: Error: unimplemented type 'integer' in 'coerceToInteger'. PR #23

  • Fixed issues #25, #29, and #31: If errors occurred when callbacks were executed by R's input handler (as opposed to by run_now()), then they would not be properly handled by R and put the terminal in a problematic state. PR #33

  • Fixed issue #37: High CPU usage on Linux. PR #38

  • Fixed issue #36: Failure to build on OS X <=10.12 (thanks @mingwandroid). PR #21

later 0.6

  • Fix a hang on address sanitized (ASAN) builds of R. Issue #16, PR #17

  • The run_now() function now takes a timeoutSecs argument. If no tasks are ready to run at the time run_now(timeoutSecs) is invoked, we will wait up to timeoutSecs for one to become ready. The default value of 0 means run_now() will return immediately if no tasks are ready, which is the same behavior as in previous releases. PR #19

  • The run_now() function used to return only when it was unable to find any more tasks that were due. This means that if tasks were being scheduled at an interval faster than the tasks are executed, run_now() would never return. This release changes that behavior so that a timestamp is taken as run_now() begins executing, and only tasks whose timestamps are earlier or equal to it are run. PR #18

  • Fix compilation errors on Solaris. Reported by Brian Ripley. PR #20

later 0.5

  • Fix a hang on Fedora 25+ which prevented the package from being installed successfully. Reported by @lepennec. Issue #7, PR #10

  • Fixed issue #12: When an exception occurred in a callback function, it would cause future callbacks to not execute. PR #13

  • Added next_op_secs() function to report the number of seconds before the next scheduled operation. PR #15

later 0.4

  • Add loop_empty() function, which returns TRUE if there are currently no callbacks that are scheduled to execute in the present or future.

  • On POSIX platforms, fix an issue where socket connections hang when written to/read from while a later callback is scheduled. The fix required stopping the input handler from being called in several spurious situations: 1) when callbacks are already being run, 2) when R code is busy executing (we used to try as often as possible, now we space it out a bit), and 3) when all the scheduled callbacks are in the future. To accomplish this, we use a background thread that acts like a timer to poke the file descriptor whenever the input handler needs to be run--similar to what we already do for Windows. Issue #4

  • On all platforms, don't invoke callbacks if callbacks are already being invoked (unless explicitly requested by a caller to run_now()).

later 0.3

Initial release.

Reference manual

It appears you don't have a PDF plugin for this browser. You can click here to download the reference manual.

install.packages("later")

0.8.0 by Joe Cheng, 2 months ago


https://github.com/r-lib/later


Report a bug at https://github.com/r-lib/later/issues


Browse source code at https://github.com/cran/later


Authors: Joe Cheng [aut, cre] , RStudio [cph] , Winston Chang [aut] , Marcus Geelnard [ctb, cph] (TinyCThread library , https://tinycthread.github.io/) , Evan Nemerson [ctb, cph] (TinyCThread library , https://tinycthread.github.io/)


Documentation:   PDF Manual  


GPL (>= 2) license


Imports Rcpp, rlang

Suggests knitr, rmarkdown, testthat

Linking to Rcpp, BH


Imported by eplusr, fiery, httpuv, pagedown, pool, promises, shiny.

Suggested by blogdown, servr.


See at CRAN