Until now I've only implemented synchronous node-addon-api methods, i.e., a JavaScript function makes a call, work is done, and the addon returns. I have big gaps in knowledge when it comes to the inner workings of v8, libuv, and node, so please correct any obvious misconceptions.
The goal is to call a JavaScript callback when C++ garbage collection callbacks are called from v8. I originally just called the JavaScript callback from the v8 garbage collection callback but that ended up with a segv after a couple calls. It seems that just making a call into JavaScript while being called from a v8 callback has some problems (v8 docs the callbacks shouldn't allocate objects). So I looked around and found a Nan-based example that uses libuv and Nan's AsyncResource to make the callback. The following approach works using node-nan:
NAN_GC_CALLBACK(afterGC) {
uint64_t et = uv_hrtime() - gcStartTime;
// other bookkeeping for GCData_t raw.
if (doCallbacks) {
uv_async_t* async = new uv_async_t;
GCData_t* data = new GCData_t;
*data = raw;
data->gcTime = et;
async->data = data;
uv_async_init(uv_default_loop(), async, asyncCB);
uv_async_send(async);
}
}
class GCResponseResource : public Nan::AsyncResource {
public:
GCResponseResource(Local<Function> callback_)
: Nan::AsyncResource("nan:gcstats.DeferredCallback") {
callback.Reset(callback_);
}
~GCResponseResource() {
callback.Reset();
}
Nan::Persistent<Function> callback;
};
static GCResponseResource* asyncResource;
static void closeCB(uv_handle_t *handle) {
delete handle;
}
static void asyncCB(uv_async_t *handle) {
Nan::HandleScope scope;
GCData_t* data = static_cast<GCData_t*>(handle->data);
Local<Object> obj = Nan::New<Object>();
Nan::Set(obj, Nan::New("gcCount").ToLocalChecked(),
Nan::New<Number>((data->gcCount));
Nan::Set(obj, Nan::New("gcTime").ToLocalChecked(),
Nan::New<Number>(data->gcTime));
Local<Object> counts = Nan::New<v8::Object>();
for (int i = 0; i < maxTypeCount; i++) {
if (data->typeCounts[i] != 0) {
Nan::Set(counts, i, Nan::New<Number>(data->typeCounts[i]));
}
}
Nan::Set(obj, Nan::New("gcTypeCounts").ToLocalChecked(), counts);
Local<Value> arguments[] = {obj};
Local<Function> callback = Nan::New(asyncResource->callback);
v8::Local<v8::Object> target = Nan::New<v8::Object>();
asyncResource->runInAsyncScope(target, callback, 1, arguments);
delete data;
uv_close((uv_handle_t*) handle, closeCB);
}
My question is how would I do this using the node-addon-api instead of nan?
It's not clear to me what the node-addon-api equivalent of uv_async_init, uv_async_send, etc are. This is partially because it's not clear to me what underlying N-API (as opposed to node-addon-api) functions are required.
I have been unable to find an example like this. The callback example is completely synchronous. The async pi example uses a worker thread to perform a task but that seems overkill compared to the approach in the nan-based code using the uv primitives.
Your example is not really asynchronous, because the GC callbacks run in the main thread. However when the JS world is stopped because of the GC, this does not mean that it is stopped in a way allowing a callback to run - as the GC can stop it in the middle of a function.
You need a ThreadSafeFunction to do this. Look here for an example:
https://github.com/nodejs/node-addon-api/blob/main/doc/threadsafe_function.md
Related
I'm attempting to write a C++ "wrapper" for the POSIX timer system on Linux, so that my C++ program can set timeouts for things (such as waiting for a message to arrive over the network) using the system clock, without dealing with POSIX's ugly C interface. It seems to work most of the time, but occasionally my program will segfault after several minutes of running successfully. The problem seems to be that my LinuxTimerManager object (or one of its member objects) gets its memory corrupted, but unfortunately the problem refuses to appear if I run the program under Valgrind, so I'm stuck staring at my code to try to figure out what's wrong with it.
Here's the core of my timer-wrapper implementation:
LinuxTimerManager.h:
namespace util {
using timer_id_t = int;
class LinuxTimerManager {
private:
timer_id_t next_id;
std::map<timer_id_t, timer_t> timer_handles;
std::map<timer_id_t, std::function<void(void)>> timer_callbacks;
std::set<timer_id_t> cancelled_timers;
friend void timer_signal_handler(int signum, siginfo_t* info, void* ucontext);
public:
LinuxTimerManager();
timer_id_t register_timer(const int delay_ms, std::function<void(void)> callback);
void cancel_timer(const timer_id_t timer_id);
};
void timer_signal_handler(int signum, siginfo_t* info, void* ucontext);
}
LinuxTimerManager.cpp:
namespace util {
LinuxTimerManager* tm_instance;
LinuxTimerManager::LinuxTimerManager() : next_id(0) {
tm_instance = this;
struct sigaction sa = {0};
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timer_signal_handler;
sigemptyset(&sa.sa_mask);
int success_flag = sigaction(SIGRTMIN, &sa, NULL);
assert(success_flag == 0);
}
void timer_signal_handler(int signum, siginfo_t* info, void* ucontext) {
timer_id_t timer_id = info->si_value.sival_int;
auto cancelled_location = tm_instance->cancelled_timers.find(timer_id);
//Only fire the callback if the timer is not in the cancelled set
if(cancelled_location == tm_instance->cancelled_timers.end()) {
tm_instance->timer_callbacks.at(timer_id)();
} else {
tm_instance->cancelled_timers.erase(cancelled_location);
}
tm_instance->timer_callbacks.erase(timer_id);
timer_delete(tm_instance->timer_handles.at(timer_id));
tm_instance->timer_handles.erase(timer_id);
}
timer_id_t LinuxTimerManager::register_timer(const int delay_ms, std::function<void(void)> callback) {
struct sigevent timer_event = {0};
timer_event.sigev_notify = SIGEV_SIGNAL;
timer_event.sigev_signo = SIGRTMIN;
timer_event.sigev_value.sival_int = next_id;
timer_t timer_handle;
int success_flag = timer_create(CLOCK_REALTIME, &timer_event, &timer_handle);
assert(success_flag == 0);
timer_handles[next_id] = timer_handle;
timer_callbacks[next_id] = callback;
struct itimerspec timer_spec = {0};
timer_spec.it_interval.tv_sec = 0;
timer_spec.it_interval.tv_nsec = 0;
timer_spec.it_value.tv_sec = 0;
timer_spec.it_value.tv_nsec = delay_ms * 1000000;
timer_settime(timer_handle, 0, &timer_spec, NULL);
return next_id++;
}
void LinuxTimerManager::cancel_timer(const timer_id_t timer_id) {
if(timer_handles.find(timer_id) != timer_handles.end()) {
cancelled_timers.emplace(timer_id);
}
}
}
When my program crashes, the segfault always comes from timer_signal_handler(), usually the lines tm_instance->timer_callbacks.erase(timer_id) or tm_instance->timer_handles.erase(timer_id). The actual segfault is thrown from somewhere deep in the std::map implementation (i.e. stl_tree.h).
Could my memory corruption be caused by a race condition between different timer signals modifying the same LinuxTimerManager? I thought only one timer signal was delivered at a time, but maybe I misunderstood the man pages. Is it just generally unsafe to make a Linux signal handler modify a complex C++ object like std::map?
The signal can occur in the middle of e.g. malloc or free and thus most calls which do interesting things with containers could result in reentering the memory allocation support while its data structures are in an arbitrary state. (As pointed out in the comments, most functions are not safe to call in asynchronous signal handlers. malloc and free are just examples.) Reentering a component in this fashion leads to pretty much arbitrary failure.
Libraries cannot be made safe against this behavior without blocking signals for the entire process during any operations within the library. Doing that is prohibitively expensive, both in the overhead of managing the signal mask and in the amount of time signals would be blocked. (It has to be for the entire process as a signal handler should not block on locks. If a thread handling a signal calls into a library protected by mutexes while another thread holds a mutex the signal handler needs, the handler will block. It is very hard to avoid deadlock when this can happen.)
Designs which work around this typically have a thread which listens for specific event and then does the processing. You have to use semaphores to synchronize between the thread and the signal handler.
I try to develop an async Node.js addon that works with an Isolate.
Consider this example code:
struct Work {
uv_work_t request;
Persistent<Function> callback;
Isolate * isolate;
};
// called in worker thread
static void WorkAsync(uv_work_t *req)
{
Work *work = static_cast<Work *>(req->data);
HeapStatistics stats;
work->isolate->GetHeapStatistics(&stats);
// ... do other stuff ...
}
// called by in main thread
static void WorkAsyncComplete(uv_work_t *req, int status)
{
Isolate * isolate = Isolate::GetCurrent();
v8::HandleScope handleScope(isolate);
Work *work = static_cast<Work *>(req->data);
// ... do other stuff ...
work->callback.Reset();
delete work;
}
void RunAsync(const v8::FunctionCallbackInfo<v8::Value>&args) {
Isolate* isolate = args.GetIsolate();
Work * work = new Work();
work->request.data = work;
work->isolate = isolate;
Local<Function> callback = Local<Function>::Cast(args[0]);
work->callback.Reset(isolate, callback);
// kick of the worker thread
uv_queue_work(uv_default_loop(), &work->request, WorkAsync, WorkAsyncComplete);
args.GetReturnValue().Set(Undefined(isolate));
}
void setup(Handle <Object> exports, Handle<Object> module) {
NODE_SET_METHOD(exports, "run", RunAsync);
}
NODE_MODULE(addon, setup)
My question: is it safe to pass a pointer to current Isolate to a worker thread (for read-only purposes)?
Thanks!
Answered by Scott Frees (the author of C++ and Node.js Integration ebook: https://scottfrees.com/ebooks/nodecpp/):
To be honest, I've never tried to do that. It seems to me that it should be fine - as long as you are only making calls on the isolate and not actually modifying anything within it.
Generally, if you try to do anything in violation with the V8 threading model, it will generate an exception - so if it's not, then I'd assume it is ok.
Yes it's possible. However, you are the responsable of the safety. You need to be sure that Isolate is leaved from the parent thread and unlocked. Then it can be used (Entred) in the worker thread.
I need in my Native code to raise an asynchronous event to C#,I have used as bridge CLI ,actually I have gone though sending a pointer to function from C++/CLI but it did not work properly?
I need to know:1-What is wrong? 2-how can make it asynchronous event to not block the processing?
I need in this example to raise an event each time I reach 1000,here is my code
Count.h
typedef void pointertofunc(bool IsOverFlowOccured);
typedef pointertofunc *pointertofuncdelegate;
class count
{
public:
void startCounting(pointertofuncdelegate);
count();
~count();
};
Count.cpp
void count::startCounting(pointertofuncdelegate)
{
for (int i = 0; i < 100000; i++)
{
//printf("%d \n", i);
if (i == 1000)
{
pointertofuncdelegate(true);
printf("%d \n", i);
i = 0;
}
}
}
CLR(.h file)
public delegate void manageddelegate(bool isworkflowoccured);
ref class CounterRaiseAsynchronousEvent
{
public:
event manageddelegate^ managedEventHandler;
CounterRaiseAsynchronousEvent();
void initialize();
void raiseEvent(bool eoverFlow);
private:
count* wrapperObject;
};
CLR(.cpp file)
void CounterRaiseAsynchronousEvent::initialize()
{
//Create a new delegate and point it to the member function
manageddelegate^ prDel = gcnew manageddelegate(this, &CounterRaiseAsynchronousEvent::raiseEvent);
GCHandle gch = GCHandle::Alloc(prDel);
//Convert the delegate to a function pointer
IntPtr ip = Marshal::GetFunctionPointerForDelegate(prDel);
//... and cast it to the appropriate type
pointertofuncdelegate fp = static_cast<pointertofuncdelegate>(ip.ToPointer());
//set the native function pointer on the native class
wrapperObject->startCounting(fp);
}
void CounterRaiseAsynchronousEvent::raiseEvent(bool isOverflowOccurred)
{
//Do any thing
}
It's not usually a good idea for the event source to implement an asynchronous handling model -- that forces the client code to be multi-threaded and do synchronization, and causes complications when different components have chosen different models.
Instead, call events synchronously, and let the subscriber pend the callback and return immediately. This is compatible with any asynchronous model the client code prefers, such as native PostMessage or Control.BeginInvoke or TPL (async/await) or worker threadpool.
I have an ATL COM component which raises a few events which are handled by managed (C# and VB.NET) code. The component is currently used by a VS2005 VB.NET project (as an ActiveX control) and all of the events are raised and everything works.
However, in porting some of the code to C#, I noticed that all but one of the events are never raised. The only event which is raised passes no arguments back to the handler. All others do.
// this function is auto-generated
HRESULT Fire_SomeEvent(VARIANT_BOOL inOriginated, IUserType * inUserType)
{
CComVariant varResult;
T* pT = static_cast<T*>(this);
int nConnectionIndex;
CComVariant* pvars = new CComVariant[2];
int nConnections = m_vec.GetSize();
for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
{
pT->Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
pT->Unlock();
IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);
if (pDispatch != NULL)
{
VariantClear(&varResult);
pvars[1] = inOriginated;
pvars[0] = inUserType;
DISPPARAMS disp = { pvars, NULL, 2, 0 };
pDispatch->Invoke(0x7, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);
}
}
delete[] pvars;
return varResult.scode;
}
Invoke is returning 0x80070057 (E_INVALIDARG). Yet, in VB land, when used via the ActiveX wrapper, it works. Now, I'm no COM wizard by any means, and I just don't get it. I haven't been able to find anything applicable anywhere.
I thought that perhaps it had something to do with passing a UDT, but no; an almost identical version, with the IUserType replaced by a LONG also returns E_INVALIDARG. Again, the single event which passes no arguments works.
A short example of how it may be used by the managed code. Nothing crazy here.
class Program
{
private ComType _c;
static void Main(string[] args)
{
_c = new ComType();
_c.SomeEvent += _c_SomeEvent;
_c.DoSomethingWhichRaisesSomeEvent();
}
static void _c_SomeEvent(bool b, IUserType udt)
{
// never called
}
}
I would normally spend more time debugging before reaching out here, but I have to make a call here soon. I either have to fix this, or abandon this interface and use another (which is sub-optimal for my purpose). So, hoping some of you COM pros have run into this before.
Double-check your vt values in your dispparam variants; many IDispatch implementations are quite particular about having everything line up.
These days I'm trying to learn more things about threads in windows. I thought about making this practical application:
Let's say there are several threads started when a button "Start" is pressed. Assume these threads are intensive (they keep running / have always something to work on).
This app would also have a "Stop" button. When this button is pressed all the threads should close in a nice way: free resources and abandon work and return the state they were before the "Start" button was pressed.
Another request of the app is that the functions runned by the threads shouldn't contain any instruction checking if the "Stop" button was pressed. The function running in the thread shouldn't care about the stop button.
Language: C++
OS: Windows
Problems:
WrapperFunc(function, param)
{
// what to write here ?
// if i write this:
function(param);
// i cannot stop the function from executing
}
How should I construct the wrapper function so that I can stop the thread properly?
( without using TerminateThread or some other functions )
What if the programmer allocates some memory dynamically? How can I free it before closing
the thread?( note that when I press "Stop button" the thread is still processing data)
I though about overloading the new operator or just imposing the usage of a predefined
function to be used when allocating memory dynamically. This, however, means
that the programmer who uses this api is constrained and it's not what I want.
Thank you
Edit: Skeleton to describe the functionality I'd like to achieve.
struct wrapper_data
{
void* (*function)(LPVOID);
LPVOID *params;
};
/*
this function should make sure that the threads stop properly
( free memory allocated dynamically etc )
*/
void* WrapperFunc(LPVOID *arg)
{
wrapper_data *data = (wrapper_data*) arg;
// what to write here ?
// if i write this:
data->function(data->params);
// i cannot stop the function from executing
delete data;
}
// will have exactly the same arguments as CreateThread
MyCreateThread(..., function, params, ...)
{
// this should create a thread that runs the wrapper function
wrapper_data *data = new wrapper_data;
data->function = function;
data->params = params;
CreateThread(..., WrapperFunc, (LPVOID) wrapper_data, ...);
}
thread_function(LPVOID *data)
{
while(1)
{
//do stuff
}
}
// as you can see I want it to be completely invisible
// to the programmer who uses this
MyCreateThread(..., thread_function, (LPVOID) params,...);
One solution is to have some kind of signal that tells the threads to stop working. Often this can be a global boolean variable that is normally false but when set to true it tells the threads to stop. As for the cleaning up, do it when the threads main loop is done before returning from the thread.
I.e. something like this:
volatile bool gStopThreads = false; // Defaults to false, threads should not stop
void thread_function()
{
while (!gStopThreads)
{
// Do some stuff
}
// All processing done, clean up after my self here
}
As for the cleaning up bit, if you keep the data inside a struct or a class, you can forcibly kill them from outside the threads and just either delete the instances if you allocated them dynamically or let the system handle it if created e.g. on the stack or as global objects. Of course, all data your thread allocates (including files, sockets etc.) must be placed in this structure or class.
A way of keeping the stopping functionality in the wrapper, is to have the actual main loop in the wrapper, together with the check for the stop-signal. Then in the main loop just call a doStuff-like function that does the actual processing. However, if it contains operations that might take time, you end up with the first problem again.
See my answer to this similar question:
How do I guarantee fast shutdown of my win32 app?
Basically, you can use QueueUserAPC to queue a proc which throws an exception. The exception should bubble all the way up to a 'catch' in your thread proc.
As long as any libraries you're using are reasonably exception-aware and use RAII, this works remarkably well. I haven't successfully got this working with boost::threads however, as it's doesn't put suspended threads into an alertable wait state, so QueueUserAPC can't wake them.
If you don't want the "programmer" of the function that the thread will execute deal with the "stop" event, make the thread execute a function of "you" that deals with the "stop" event and when that event isn't signaled executes the "programmer" function...
In other words the "while(!event)" will be in a function that calls the "job" function.
Code Sample.
typedef void (*JobFunction)(LPVOID params); // The prototype of the function to execute inside the thread
struct structFunctionParams
{
int iCounter;
structFunctionParams()
{
iCounter = 0;
}
};
struct structJobParams
{
bool bStop;
JobFunction pFunction;
LPVOID pFunctionParams;
structJobParams()
{
bStop = false;
pFunction = NULL;
pFunctionParams = NULL;
}
};
DWORD WINAPI ThreadProcessJob(IN LPVOID pParams)
{
structJobParams* pJobParams = (structJobParams*)pParams;
while(!pJobParams->bStop)
{
// Execute the "programmer" function
pJobParams->pFunction(pJobParams->pFunctionParams);
}
return 0;
}
void ThreadFunction(LPVOID pParams)
{
// Do Something....
((structFunctionParams*)pParams)->iCounter ++;
}
int _tmain(int argc, _TCHAR* argv[])
{
structFunctionParams stFunctionParams;
structJobParams stJobParams;
stJobParams.pFunction = &ThreadFunction;
stJobParams.pFunctionParams = &stFunctionParams;
DWORD dwIdThread = 0;
HANDLE hThread = CreateThread(
NULL,
0,
ThreadProcessJob,
(LPVOID) &stJobParams, 0, &dwIdThread);
if(hThread)
{
// Give it 5 seconds to work
Sleep(5000);
stJobParams.bStop = true; // Signal to Stop
WaitForSingleObject(hThread, INFINITE); // Wait to finish
CloseHandle(hThread);
}
}