This snippet shows how you could use later to create an API that can be called on a background thread, to perform an arbitrary task on the main R thread, and return the result to the background thread. The background thread blocks until the result is returned from the main R thread.
Last active
July 10, 2017 22:19
-
-
Save jcheng5/c2ec5c0b025162ab2eddb4d7d42339f1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Rcpp.h> | |
#include <later_api.h> | |
#include <boost/function.hpp> | |
#include "tinythread.h" | |
// === Public API =============================== | |
template <class TOutput> | |
TOutput invoke(boost::function<TOutput ()> task); | |
// === Implementation =========================== | |
namespace { | |
template <class TOutput> | |
class Invoke { | |
public: | |
Invoke(boost::function<TOutput ()> task) : _task(task) { | |
} | |
TOutput operator ()() { | |
tthread::lock_guard<tthread::recursive_mutex> guard(_m); | |
later::later(mainThreadCallback, this, 0); | |
_cond.wait(_m); | |
return _result; | |
} | |
private: | |
// This method is run on the main thread; it does some threading | |
// boilerplate, and invokes the task. | |
void onMainThread() { | |
tthread::lock_guard<tthread::recursive_mutex> guard(_m); | |
this->_result = this->_task(); | |
_cond.notify_all(); | |
} | |
// This static method exists only because later::later needs a | |
// plain C function pointer, not a boost::function or anything | |
// fancy like that. Otherwise we'd pass the onMainThread() method | |
// directly to later::later, and get rid of this. | |
static void mainThreadCallback(void* data) { | |
Invoke<TOutput>* self = (Invoke<TOutput>*)data; | |
self->onMainThread(); | |
} | |
boost::function<TOutput ()> _task; | |
TOutput _result; | |
tthread::recursive_mutex _m; | |
tthread::condition_variable _cond; | |
}; | |
} // namespace | |
template <class TOutput> | |
TOutput invoke(boost::function<TOutput ()> task) { | |
return Invoke<TOutput>(task)(); | |
} | |
// === Example ================================== | |
#include <boost/bind.hpp> | |
using namespace Rcpp; | |
// The operation to perform on the main thread. You would replace | |
// this with code that invokes R code. | |
double negate(double val) { | |
return -val; | |
} | |
// Code to execute on the background thread | |
void background_test(void*) { | |
tthread::this_thread::sleep_for(tthread::chrono::seconds(5)); | |
// Invoke negate(10.0) from the R thread, and wait for the result | |
double result = invoke<double>(boost::bind(negate, 10.0)); | |
printf("%lf", result); | |
} | |
// [[Rcpp::export]] | |
void rcpp_hello_world() { | |
tthread::thread t(background_test, NULL); | |
t.detach(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment