216 lines
6.8 KiB
C
216 lines
6.8 KiB
C
|
// DO NOT include later.h directly from other packages; use later_api.h instead!
|
||
|
#ifndef _later_later_h
|
||
|
#define _later_later_h
|
||
|
|
||
|
#include <iostream>
|
||
|
|
||
|
#ifndef R_NO_REMAP
|
||
|
#define R_NO_REMAP
|
||
|
#endif
|
||
|
|
||
|
#ifndef STRICT_R_HEADERS
|
||
|
#define STRICT_R_HEADERS
|
||
|
#endif
|
||
|
|
||
|
#ifdef _WIN32
|
||
|
#ifndef _WIN32_WINNT
|
||
|
#define _WIN32_WINNT 0x0600 // so R <= 4.1 can find WSAPoll() on Windows
|
||
|
#endif
|
||
|
#include <winsock2.h>
|
||
|
#define WIN32_LEAN_AND_MEAN
|
||
|
// Taken from http://tolstoy.newcastle.edu.au/R/e2/devel/06/11/1242.html
|
||
|
// Undefine the Realloc macro, which is defined by both R and by Windows stuff
|
||
|
#undef Realloc
|
||
|
// Also need to undefine the Free macro
|
||
|
#undef Free
|
||
|
#include <windows.h>
|
||
|
#else // _WIN32
|
||
|
#include <poll.h>
|
||
|
#include <pthread.h>
|
||
|
#endif // _WIN32
|
||
|
|
||
|
#include <Rinternals.h>
|
||
|
|
||
|
// Needed for R_GetCCallable on R 3.3 and older; in more recent versions, this
|
||
|
// is included via Rinternals.h.
|
||
|
#include <R_ext/Rdynload.h>
|
||
|
|
||
|
|
||
|
|
||
|
namespace later {
|
||
|
|
||
|
// This is the version of the later API provided by this file. Ideally, this
|
||
|
// should match the version of the API provided by the later DLL that is
|
||
|
// installed on the user's system. However, since this file is compiled into
|
||
|
// other packages (like httpuv and promises), it is possible that there will
|
||
|
// be a mismatch. In the future we will be able to compare at runtime it to
|
||
|
// the result from apiVersion(), with:
|
||
|
//
|
||
|
// int (*dll_api_version)() = (int (*)()) R_GetCCallable("later", "apiVersion");
|
||
|
// if (LATER_H_API_VERSION != (*dll_api_version)()) { ... }
|
||
|
#define LATER_H_API_VERSION 3
|
||
|
#define GLOBAL_LOOP 0
|
||
|
|
||
|
|
||
|
// Gets the version of the later API that's provided by the _actually installed_
|
||
|
// version of later.
|
||
|
static int apiVersionRuntime() {
|
||
|
int (*dll_api_version)(void) = (int (*)(void)) R_GetCCallable("later", "apiVersion");
|
||
|
return (*dll_api_version)();
|
||
|
}
|
||
|
|
||
|
inline void later(void (*func)(void*), void* data, double secs, int loop_id) {
|
||
|
// This function works by retrieving the later::execLaterNative2 function
|
||
|
// pointer using R_GetCCallable the first time it's called (per compilation
|
||
|
// unit, since it's inline). execLaterNative2 is designed to be safe to call
|
||
|
// from any thread, but R_GetCCallable is only safe to call from R's main
|
||
|
// thread (otherwise you get stack imbalance warnings or worse). Therefore,
|
||
|
// we have to ensure that the first call to execLaterNative2 happens on the
|
||
|
// main thread. We accomplish this using a statically initialized object,
|
||
|
// in later_api.h. Therefore, any other packages wanting to call
|
||
|
// execLaterNative2 need to use later_api.h, not later.h.
|
||
|
//
|
||
|
// You may wonder why we used the filenames later_api.h/later.h instead of
|
||
|
// later.h/later_impl.h; it's because Rcpp treats $PACKAGE.h files
|
||
|
// specially by including them in RcppExports.cpp, and we definitely
|
||
|
// do not want the static initialization to happen there.
|
||
|
|
||
|
// The function type for the real execLaterNative2
|
||
|
typedef void (*elnfun)(void (*func)(void*), void*, double, int);
|
||
|
static elnfun eln = NULL;
|
||
|
if (!eln) {
|
||
|
// Initialize if necessary
|
||
|
if (func) {
|
||
|
// We're not initialized but someone's trying to actually schedule
|
||
|
// some code to be executed!
|
||
|
REprintf(
|
||
|
"Warning: later::execLaterNative2 called in uninitialized state. "
|
||
|
"If you're using <later.h>, please switch to <later_api.h>.\n"
|
||
|
);
|
||
|
}
|
||
|
eln = (elnfun)R_GetCCallable("later", "execLaterNative2");
|
||
|
}
|
||
|
|
||
|
// We didn't want to execute anything, just initialize
|
||
|
if (!func) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
eln(func, data, secs, loop_id);
|
||
|
}
|
||
|
|
||
|
inline void later(void (*func)(void*), void* data, double secs) {
|
||
|
later(func, data, secs, GLOBAL_LOOP);
|
||
|
}
|
||
|
|
||
|
static void later_fd_version_error(void (*func)(int *, void *), void *data, int num_fds, struct pollfd *fds, double secs, int loop_id) {
|
||
|
(void) func; (void) data; (void) num_fds; (void) fds; (void) secs; (void) loop_id;
|
||
|
Rf_error("later_fd called, but installed version of the 'later' package is too old; please upgrade 'later' to 1.4.1 or above");
|
||
|
}
|
||
|
|
||
|
inline void later_fd(void (*func)(int *, void *), void *data, int num_fds, struct pollfd *fds, double secs, int loop_id) {
|
||
|
// See above note for later()
|
||
|
|
||
|
// The function type for the real execLaterFdNative
|
||
|
typedef void (*elfdnfun)(void (*)(int *, void *), void *, int, struct pollfd *, double, int);
|
||
|
static elfdnfun elfdn = NULL;
|
||
|
if (!elfdn) {
|
||
|
// Initialize if necessary
|
||
|
if (func) {
|
||
|
// We're not initialized but someone's trying to actually schedule
|
||
|
// some code to be executed!
|
||
|
REprintf(
|
||
|
"Warning: later::execLaterFdNative called in uninitialized state. "
|
||
|
"If you're using <later.h>, please switch to <later_api.h>.\n"
|
||
|
);
|
||
|
}
|
||
|
if (apiVersionRuntime() >= 3) {
|
||
|
// Only later API version 3 supports execLaterFdNative
|
||
|
elfdn = (elfdnfun) R_GetCCallable("later", "execLaterFdNative");
|
||
|
} else {
|
||
|
// The installed version is too old and doesn't offer execLaterFdNative.
|
||
|
elfdn = later_fd_version_error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We didn't want to execute anything, just initialize
|
||
|
if (!func) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
elfdn(func, data, num_fds, fds, secs, loop_id);
|
||
|
}
|
||
|
|
||
|
inline void later_fd(void (*func)(int *, void *), void *data, int num_fds, struct pollfd *fds, double secs) {
|
||
|
later_fd(func, data, num_fds, fds, secs, GLOBAL_LOOP);
|
||
|
}
|
||
|
|
||
|
|
||
|
class BackgroundTask {
|
||
|
|
||
|
public:
|
||
|
BackgroundTask() {}
|
||
|
virtual ~BackgroundTask() {}
|
||
|
|
||
|
// Start executing the task
|
||
|
void begin() {
|
||
|
#ifndef _WIN32
|
||
|
pthread_attr_t attr;
|
||
|
pthread_attr_init(&attr);
|
||
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||
|
pthread_t t;
|
||
|
pthread_create(&t, &attr, BackgroundTask::task_main, this);
|
||
|
pthread_attr_destroy(&attr);
|
||
|
#else
|
||
|
HANDLE hThread = ::CreateThread(
|
||
|
NULL, 0,
|
||
|
BackgroundTask::task_main_win,
|
||
|
this,
|
||
|
0,
|
||
|
NULL
|
||
|
);
|
||
|
::CloseHandle(hThread);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
|
||
|
private:
|
||
|
static void* task_main(void* data) {
|
||
|
BackgroundTask* task = reinterpret_cast<BackgroundTask*>(data);
|
||
|
// TODO: Error handling
|
||
|
task->execute();
|
||
|
later(&BackgroundTask::result_callback, task, 0);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
#ifdef _WIN32
|
||
|
static DWORD WINAPI task_main_win(LPVOID lpParameter) {
|
||
|
task_main(lpParameter);
|
||
|
return 1;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static void result_callback(void* data) {
|
||
|
BackgroundTask* task = reinterpret_cast<BackgroundTask*>(data);
|
||
|
// TODO: Error handling
|
||
|
task->complete();
|
||
|
delete task;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
} // namespace later
|
||
|
|
||
|
#endif
|