This vignette explains the functionality of the RcppThread package:

Some background

Calling multi-threaded C++ code from R is a bit tricky because R is single-threaded. Using R's C API from multiple threads crashes your R session or causes other unexpected behavior. In particular, communication between your C++ code and R is problematic. We can neither check for user interruptions during long computations nor should we print messages to the R console.

It is possible to resolve this, but not without effort. RcppThread relieves us of that burden.

Thread safe communication with R

It is not safe to call R's C API from multiple threads. It is safe, however, to call it from the master thread. That's the idea behind RcppThread's checkUserInterrupt() and Rcout. They behave almost like their Rcpp versions, but only communicate with R when called from the master thread. Here's an example of their use with std::thread:

#include <RcppThread.h>
#include <thread>  // C++11 threads

// [[Rcpp::export]]
void pyjamaParty()
{
    // some work that will be done in separate threads
    auto work = [] {
        auto id = std::this_thread::get_id();
        std::this_thread::sleep_for(std::chrono::seconds(1));
        RcppThread::Rcout << id << " slept for one second" << std::endl;

        // Rcpp::checkUserInterrupt();      // R would crash
        RcppThread::checkUserInterrupt();  // does not crash

        std::this_thread::sleep_for(std::chrono::seconds(1));
        RcppThread::Rcout << id << " slept for another second" << std::endl;
    };

    // create two new threads
    std::thread t1(work);
    std::thread t2(work);

    // wait for threads to finish work
    t1.join();
    t2.join();
}

RcppThread::checkUserInterrupt() and RcppThread::Rcout are thread safe, no matter what threading framework is used. But be careful: they only communicate with R from the master thread. When called from another thread,

Because of this, the above example is neither interruptible nor does it print anything to the R console. Conveniently, RcppThread also provides two threading classes that resolve these issues, see below.

Remark

If you want to do some clean-up before interrupting, isInterrupted() returns a bool with the interruption status.

An R-friendly threading class

As of C++11, the standard template library provides the class std::thread for executing code in parallel. RcppThread's Thread class is an R-friendly wrapper to std::thread.

Instances of class Thread behave just like instances of std::thread, with one exception: Whenever other threads are doing some work, the master thread periodically synchronizes with R. When the user interrupts a threaded computation, any thread will stop as soon as it encounters a checkUserInterrupt().

Example
// [[Rcpp::export]]
void pyjamaParty2()
{
    // some work that will be done in separate threads
    auto work = [] {
        auto id = std::this_thread::get_id();
        std::this_thread::sleep_for(std::chrono::seconds(1));
        RcppThread::Rcout << id << " slept for one second" << std::endl;

        RcppThread::checkUserInterrupt();

        std::this_thread::sleep_for(std::chrono::seconds(1));
        RcppThread::Rcout << id << " slept for another second" << std::endl;
    };

    // create two new threads
    RcppThread::Thread t1(work);
    RcppThread::Thread t2(work);

    // wait for threads to finish work
    t1.join();
    t2.join();
}

Result after interrupting:

> pyjamaParty2()
139862637561600 slept for one second
139862645954304 slept for one second
Error in pyjamaParty2() : C++ call interrupted by user

A thread pool class

A thread pool is an abstraction for executing tasks in parallel. A thread pool consists of a fixed number of threads that wait for incoming tasks. Whenever a new task arrives, a waiting worker fetches the task and does the work.

Besides being easy to use, a thread pool is helpful when

The class ThreadPool implements the thread pool pattern using Thread objects and, thus, plays nicely with RcppThread::checkUserInterrupt() and RcppThread:Rcout.

Example
// [[Rcpp::export]]
void pool()
{
    // set up thread pool with 3 threads
    RcppThread::ThreadPool pool(3);

    // each tasks prints thread id
    auto task = [] () {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        RcppThread::checkUserInterrupt();
        RcppThread::Rcout <<
            "Task fetched by thread " << std::this_thread::get_id() << "\n";
    };

    // push 10 tasks to the pool
    for (int i = 0; i < 10; i++)
        pool.push(task);

    // wait for pool to finish work
    pool.join();
}

Result after interrupting:

> pool()
Task fetched by thread 140201245763328
Task fetched by thread 140201262548736
Task fetched by thread 140201254156032

Error in pool() : C++ call interrupted by user