Skip to content

Instantly share code, notes, and snippets.

@RomiTT
Last active July 18, 2019 04:02
Show Gist options
  • Save RomiTT/f56f8d30a756c022170faefd42b845c4 to your computer and use it in GitHub Desktop.
Save RomiTT/f56f8d30a756c022170faefd42b845c4 to your computer and use it in GitHub Desktop.
For thread-safe function call in native node module.
Napi::Value FGM::test(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
auto cb = info[0].As<Napi::Function>();
threadSafeCallback = ThreadSafeFunction::Create(cb);
std::thread t([threadSafeCallback]() => {
// do something
// call thread-safe callback
threadSafeCallback->Call(_callbackStarted, [](napi_env env) {
// make a callback argument (Object, String, Array, Number ...)
auto obj = Napi::Object::New(env);
obj.Set("name", "Romi");
obj.Set("age", 30);
// return the argument.
return obj;
});
});
t.deatch();
return env.Undefined();
}
test((arg) => {
console.log("name: ", arg.name);
console.log("age: ", arg.age);
});
#include "ThreadSafeFunction.h"
#include <assert.h>
class JsArgument {
std::shared_ptr<ThreadSafeFunction> _owner;
ThreadSafeFunction::GetValueFunction _fGetValue;
public:
JsArgument(std::shared_ptr<ThreadSafeFunction> owner, ThreadSafeFunction::GetValueFunction fGetValue)
: _owner(owner)
, _fGetValue(std::move(fGetValue)) {}
Napi::Value GetArgument(napi_env env) {
return _fGetValue(env);
}
void Destory() { delete this; };
};
ThreadSafeFunction::ThreadSafeFunction(const Napi::Function& callback) {
_env = callback.Env();
_callback = static_cast<napi_value>(callback);
_callbackRef = Napi::Persistent(callback);
napi_value work_name;
assert(napi_create_string_utf8(_env, "N-API Thread-safe Call from ThreadSafeFunction class", NAPI_AUTO_LENGTH, &work_name) == napi_ok);
assert(napi_create_threadsafe_function(_env, _callback, NULL, work_name, 0, 1, NULL, NULL, NULL, CallJs, &_func) == napi_ok);
}
ThreadSafeFunction::~ThreadSafeFunction() {
Release();
}
void ThreadSafeFunction::Acquire() {
assert(napi_acquire_threadsafe_function(_func) == napi_ok);
}
void ThreadSafeFunction::Release() {
assert(napi_release_threadsafe_function(_func, napi_tsfn_release) == napi_ok);
}
void ThreadSafeFunction::Call(std::shared_ptr<ThreadSafeFunction> owner, GetValueFunction f) {
assert(napi_call_threadsafe_function(_func, new JsArgument{owner, f}, napi_tsfn_blocking) == napi_ok);
}
std::shared_ptr<ThreadSafeFunction> ThreadSafeFunction::Create(const Napi::Function& callback) {
return std::shared_ptr<ThreadSafeFunction>(new ThreadSafeFunction(callback));
}
void ThreadSafeFunction::CallJs(napi_env env, napi_value js_cb, void* context, void* data) {
(void)context;
JsArgument* jsArg = (JsArgument*)data;
if (env != NULL) {
Napi::Value argVal;
napi_value arg = (napi_value)jsArg->GetArgument(Napi::Env(env));
napi_value undefined;
// Retrieve the JavaScript `undefined` value so we can use it as the `this`
// value of the JavaScript function call.
assert(napi_get_undefined(env, &undefined) == napi_ok);
// Call the JavaScript function and pass it the prime that the secondary
// thread found.
assert(napi_call_function(env,
undefined,
js_cb,
1,
(arg == NULL) ? NULL : &arg,
NULL) == napi_ok);
}
jsArg->Destory();
}
#pragma once
#ifndef __THREAD_SAFE_FUNCTION_H__
#define __THREAD_SAFE_FUNCTION_H__
#include <napi.h>
#include <functional>
#include <memory>
class ThreadSafeFunction {
public:
typedef std::function<Napi::Value(napi_env)> GetValueFunction;
private:
napi_threadsafe_function _func;
napi_env _env;
napi_value _callback;
Napi::FunctionReference _callbackRef;
private:
ThreadSafeFunction(const Napi::Function& callback);
public:
~ThreadSafeFunction();
void Acquire();
void Release();
void Call(std::shared_ptr<ThreadSafeFunction> owner, GetValueFunction f);
static std::shared_ptr<ThreadSafeFunction> Create(const Napi::Function& callback);
private:
static void CallJs(napi_env env, napi_value js_cb, void* context, void* data);
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment