2025-01-12 04:36:52 +08:00

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