Skip to content

Commit 902b079

Browse files
Gabriel Schulhofrvagg
Gabriel Schulhof
authored andcommittedFeb 28, 2019
n-api: clean up thread-safe function
* Move class `TsFn` to name space `v8impl` and rename it to `ThreadSafeFunction` * Remove `NAPI_EXTERN` from API declarations, because it's only needed in the header file. Backport-PR-URL: #25002 PR-URL: #22259 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Kyle Farnung <kfarnung@microsoft.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
1 parent 09b88aa commit 902b079

File tree

1 file changed

+592
-587
lines changed

1 file changed

+592
-587
lines changed
 

‎src/node_api.cc

+592-587
Original file line numberDiff line numberDiff line change
@@ -856,180 +856,513 @@ napi_status ConcludeDeferred(napi_env env,
856856
return GET_RETURN_STATUS(env);
857857
}
858858

859-
} // end of namespace v8impl
860-
861-
// Intercepts the Node-V8 module registration callback. Converts parameters
862-
// to NAPI equivalents and then calls the registration callback specified
863-
// by the NAPI module.
864-
void napi_module_register_cb(v8::Local<v8::Object> exports,
865-
v8::Local<v8::Value> module,
866-
v8::Local<v8::Context> context,
867-
void* priv) {
868-
napi_module* mod = static_cast<napi_module*>(priv);
859+
class ThreadSafeFunction : public node::AsyncResource {
860+
public:
861+
ThreadSafeFunction(v8::Local<v8::Function> func,
862+
v8::Local<v8::Object> resource,
863+
v8::Local<v8::String> name,
864+
size_t thread_count_,
865+
void* context_,
866+
size_t max_queue_size_,
867+
napi_env env_,
868+
void* finalize_data_,
869+
napi_finalize finalize_cb_,
870+
napi_threadsafe_function_call_js call_js_cb_):
871+
AsyncResource(env_->isolate,
872+
resource,
873+
*v8::String::Utf8Value(env_->isolate, name)),
874+
thread_count(thread_count_),
875+
is_closing(false),
876+
context(context_),
877+
max_queue_size(max_queue_size_),
878+
env(env_),
879+
finalize_data(finalize_data_),
880+
finalize_cb(finalize_cb_),
881+
call_js_cb(call_js_cb_ == nullptr ? CallJs : call_js_cb_),
882+
handles_closing(false) {
883+
ref.Reset(env->isolate, func);
884+
node::AddEnvironmentCleanupHook(env->isolate, Cleanup, this);
885+
}
869886

870-
if (mod->nm_register_func == nullptr) {
871-
node::Environment::GetCurrent(context)->ThrowError(
872-
"Module has no declared entry point.");
873-
return;
887+
~ThreadSafeFunction() {
888+
node::RemoveEnvironmentCleanupHook(env->isolate, Cleanup, this);
889+
if (ref.IsEmpty())
890+
return;
891+
ref.ClearWeak();
892+
ref.Reset();
874893
}
875894

876-
// Create a new napi_env for this module or reference one if a pre-existing
877-
// one is found.
878-
napi_env env = v8impl::GetEnv(context);
895+
// These methods can be called from any thread.
879896

880-
napi_value _exports;
881-
NAPI_CALL_INTO_MODULE_THROW(env,
882-
_exports = mod->nm_register_func(env,
883-
v8impl::JsValueFromV8LocalValue(exports)));
897+
napi_status Push(void* data, napi_threadsafe_function_call_mode mode) {
898+
node::Mutex::ScopedLock lock(this->mutex);
884899

885-
// If register function returned a non-null exports object different from
886-
// the exports object we passed it, set that as the "exports" property of
887-
// the module.
888-
if (_exports != nullptr &&
889-
_exports != v8impl::JsValueFromV8LocalValue(exports)) {
890-
napi_value _module = v8impl::JsValueFromV8LocalValue(module);
891-
napi_set_named_property(env, _module, "exports", _exports);
900+
while (queue.size() >= max_queue_size &&
901+
max_queue_size > 0 &&
902+
!is_closing) {
903+
if (mode == napi_tsfn_nonblocking) {
904+
return napi_queue_full;
905+
}
906+
cond->Wait(lock);
907+
}
908+
909+
if (is_closing) {
910+
if (thread_count == 0) {
911+
return napi_invalid_arg;
912+
} else {
913+
thread_count--;
914+
return napi_closing;
915+
}
916+
} else {
917+
if (uv_async_send(&async) != 0) {
918+
return napi_generic_failure;
919+
}
920+
queue.push(data);
921+
return napi_ok;
922+
}
892923
}
893-
}
894924

895-
} // end of anonymous namespace
925+
napi_status Acquire() {
926+
node::Mutex::ScopedLock lock(this->mutex);
896927

897-
// Registers a NAPI module.
898-
void napi_module_register(napi_module* mod) {
899-
node::node_module* nm = new node::node_module {
900-
-1,
901-
mod->nm_flags,
902-
nullptr,
903-
mod->nm_filename,
904-
nullptr,
905-
napi_module_register_cb,
906-
mod->nm_modname,
907-
mod, // priv
908-
nullptr,
909-
};
910-
node::node_module_register(nm);
911-
}
928+
if (is_closing) {
929+
return napi_closing;
930+
}
912931

913-
napi_status napi_add_env_cleanup_hook(napi_env env,
914-
void (*fun)(void* arg),
915-
void* arg) {
916-
CHECK_ENV(env);
917-
CHECK_ARG(env, fun);
932+
thread_count++;
918933

919-
node::AddEnvironmentCleanupHook(env->isolate, fun, arg);
934+
return napi_ok;
935+
}
920936

921-
return napi_ok;
922-
}
937+
napi_status Release(napi_threadsafe_function_release_mode mode) {
938+
node::Mutex::ScopedLock lock(this->mutex);
923939

924-
napi_status napi_remove_env_cleanup_hook(napi_env env,
925-
void (*fun)(void* arg),
926-
void* arg) {
927-
CHECK_ENV(env);
928-
CHECK_ARG(env, fun);
940+
if (thread_count == 0) {
941+
return napi_invalid_arg;
942+
}
929943

930-
node::RemoveEnvironmentCleanupHook(env->isolate, fun, arg);
944+
thread_count--;
931945

932-
return napi_ok;
933-
}
946+
if (thread_count == 0 || mode == napi_tsfn_abort) {
947+
if (!is_closing) {
948+
is_closing = (mode == napi_tsfn_abort);
949+
if (is_closing && max_queue_size > 0) {
950+
cond->Signal(lock);
951+
}
952+
if (uv_async_send(&async) != 0) {
953+
return napi_generic_failure;
954+
}
955+
}
956+
}
934957

935-
// Warning: Keep in-sync with napi_status enum
936-
static
937-
const char* error_messages[] = {nullptr,
938-
"Invalid argument",
939-
"An object was expected",
940-
"A string was expected",
941-
"A string or symbol was expected",
942-
"A function was expected",
943-
"A number was expected",
944-
"A boolean was expected",
945-
"An array was expected",
946-
"Unknown failure",
947-
"An exception is pending",
948-
"The async work item was cancelled",
949-
"napi_escape_handle already called on scope",
950-
"Invalid handle scope usage",
951-
"Invalid callback scope usage",
952-
"Thread-safe function queue is full",
953-
"Thread-safe function handle is closing"
954-
};
958+
return napi_ok;
959+
}
955960

956-
static inline napi_status napi_clear_last_error(napi_env env) {
957-
env->last_error.error_code = napi_ok;
961+
void EmptyQueueAndDelete() {
962+
for (; !queue.empty() ; queue.pop()) {
963+
call_js_cb(nullptr, nullptr, context, queue.front());
964+
}
965+
delete this;
966+
}
958967

959-
// TODO(boingoing): Should this be a callback?
960-
env->last_error.engine_error_code = 0;
961-
env->last_error.engine_reserved = nullptr;
962-
return napi_ok;
963-
}
968+
// These methods must only be called from the loop thread.
964969

965-
static inline
966-
napi_status napi_set_last_error(napi_env env, napi_status error_code,
967-
uint32_t engine_error_code,
968-
void* engine_reserved) {
969-
env->last_error.error_code = error_code;
970-
env->last_error.engine_error_code = engine_error_code;
971-
env->last_error.engine_reserved = engine_reserved;
972-
return error_code;
973-
}
970+
napi_status Init() {
971+
ThreadSafeFunction* ts_fn = this;
974972

975-
napi_status napi_get_last_error_info(napi_env env,
976-
const napi_extended_error_info** result) {
977-
CHECK_ENV(env);
978-
CHECK_ARG(env, result);
973+
if (uv_async_init(env->loop, &async, AsyncCb) == 0) {
974+
if (max_queue_size > 0) {
975+
cond.reset(new node::ConditionVariable);
976+
}
977+
if ((max_queue_size == 0 || cond.get() != nullptr) &&
978+
uv_idle_init(env->loop, &idle) == 0) {
979+
return napi_ok;
980+
}
979981

980-
// you must update this assert to reference the last message
981-
// in the napi_status enum each time a new error message is added.
982-
// We don't have a napi_status_last as this would result in an ABI
983-
// change each time a message was added.
984-
static_assert(
985-
node::arraysize(error_messages) == napi_closing + 1,
986-
"Count of error messages must match count of error values");
987-
CHECK_LE(env->last_error.error_code, napi_callback_scope_mismatch);
982+
uv_close(reinterpret_cast<uv_handle_t*>(&async),
983+
[] (uv_handle_t* handle) -> void {
984+
ThreadSafeFunction* ts_fn =
985+
node::ContainerOf(&ThreadSafeFunction::async,
986+
reinterpret_cast<uv_async_t*>(handle));
987+
delete ts_fn;
988+
});
988989

989-
// Wait until someone requests the last error information to fetch the error
990-
// message string
991-
env->last_error.error_message =
992-
error_messages[env->last_error.error_code];
990+
// Prevent the thread-safe function from being deleted here, because
991+
// the callback above will delete it.
992+
ts_fn = nullptr;
993+
}
993994

994-
*result = &(env->last_error);
995-
return napi_ok;
996-
}
995+
delete ts_fn;
997996

998-
napi_status napi_fatal_exception(napi_env env, napi_value err) {
999-
NAPI_PREAMBLE(env);
1000-
CHECK_ARG(env, err);
997+
return napi_generic_failure;
998+
}
1001999

1002-
v8::Local<v8::Value> local_err = v8impl::V8LocalValueFromJsValue(err);
1003-
v8impl::trigger_fatal_exception(env, local_err);
1000+
napi_status Unref() {
1001+
uv_unref(reinterpret_cast<uv_handle_t*>(&async));
1002+
uv_unref(reinterpret_cast<uv_handle_t*>(&idle));
10041003

1005-
return napi_clear_last_error(env);
1006-
}
1004+
return napi_ok;
1005+
}
10071006

1008-
NAPI_NO_RETURN void napi_fatal_error(const char* location,
1009-
size_t location_len,
1010-
const char* message,
1011-
size_t message_len) {
1012-
std::string location_string;
1013-
std::string message_string;
1007+
napi_status Ref() {
1008+
uv_ref(reinterpret_cast<uv_handle_t*>(&async));
1009+
uv_ref(reinterpret_cast<uv_handle_t*>(&idle));
10141010

1015-
if (location_len != NAPI_AUTO_LENGTH) {
1016-
location_string.assign(
1017-
const_cast<char*>(location), location_len);
1018-
} else {
1019-
location_string.assign(
1020-
const_cast<char*>(location), strlen(location));
1011+
return napi_ok;
10211012
}
10221013

1023-
if (message_len != NAPI_AUTO_LENGTH) {
1024-
message_string.assign(
1025-
const_cast<char*>(message), message_len);
1026-
} else {
1027-
message_string.assign(
1028-
const_cast<char*>(message), strlen(message));
1029-
}
1014+
void DispatchOne() {
1015+
void* data = nullptr;
1016+
bool popped_value = false;
1017+
bool idle_stop_failed = false;
10301018

1031-
node::FatalError(location_string.c_str(), message_string.c_str());
1032-
}
1019+
{
1020+
node::Mutex::ScopedLock lock(this->mutex);
1021+
if (is_closing) {
1022+
CloseHandlesAndMaybeDelete();
1023+
} else {
1024+
size_t size = queue.size();
1025+
if (size > 0) {
1026+
data = queue.front();
1027+
queue.pop();
1028+
popped_value = true;
1029+
if (size == max_queue_size && max_queue_size > 0) {
1030+
cond->Signal(lock);
1031+
}
1032+
size--;
1033+
}
1034+
1035+
if (size == 0) {
1036+
if (thread_count == 0) {
1037+
is_closing = true;
1038+
if (max_queue_size > 0) {
1039+
cond->Signal(lock);
1040+
}
1041+
CloseHandlesAndMaybeDelete();
1042+
} else {
1043+
if (uv_idle_stop(&idle) != 0) {
1044+
idle_stop_failed = true;
1045+
}
1046+
}
1047+
}
1048+
}
1049+
}
1050+
1051+
if (popped_value || idle_stop_failed) {
1052+
v8::HandleScope scope(env->isolate);
1053+
CallbackScope cb_scope(this);
1054+
1055+
if (idle_stop_failed) {
1056+
CHECK(napi_throw_error(env,
1057+
"ERR_NAPI_TSFN_STOP_IDLE_LOOP",
1058+
"Failed to stop the idle loop") == napi_ok);
1059+
} else {
1060+
v8::Local<v8::Function> js_cb =
1061+
v8::Local<v8::Function>::New(env->isolate, ref);
1062+
call_js_cb(env,
1063+
v8impl::JsValueFromV8LocalValue(js_cb),
1064+
context,
1065+
data);
1066+
}
1067+
}
1068+
}
1069+
1070+
node::Environment* NodeEnv() {
1071+
// For some reason grabbing the Node.js environment requires a handle scope.
1072+
v8::HandleScope scope(env->isolate);
1073+
return node::Environment::GetCurrent(env->isolate);
1074+
}
1075+
1076+
void MaybeStartIdle() {
1077+
if (uv_idle_start(&idle, IdleCb) != 0) {
1078+
v8::HandleScope scope(env->isolate);
1079+
CallbackScope cb_scope(this);
1080+
CHECK(napi_throw_error(env,
1081+
"ERR_NAPI_TSFN_START_IDLE_LOOP",
1082+
"Failed to start the idle loop") == napi_ok);
1083+
}
1084+
}
1085+
1086+
void Finalize() {
1087+
v8::HandleScope scope(env->isolate);
1088+
if (finalize_cb) {
1089+
CallbackScope cb_scope(this);
1090+
finalize_cb(env, finalize_data, context);
1091+
}
1092+
EmptyQueueAndDelete();
1093+
}
1094+
1095+
inline void* Context() {
1096+
return context;
1097+
}
1098+
1099+
void CloseHandlesAndMaybeDelete(bool set_closing = false) {
1100+
if (set_closing) {
1101+
node::Mutex::ScopedLock lock(this->mutex);
1102+
is_closing = true;
1103+
if (max_queue_size > 0) {
1104+
cond->Signal(lock);
1105+
}
1106+
}
1107+
if (handles_closing) {
1108+
return;
1109+
}
1110+
handles_closing = true;
1111+
uv_close(
1112+
reinterpret_cast<uv_handle_t*>(&async),
1113+
[] (uv_handle_t* handle) -> void {
1114+
ThreadSafeFunction* ts_fn =
1115+
node::ContainerOf(&ThreadSafeFunction::async,
1116+
reinterpret_cast<uv_async_t*>(handle));
1117+
uv_close(
1118+
reinterpret_cast<uv_handle_t*>(&ts_fn->idle),
1119+
[] (uv_handle_t* handle) -> void {
1120+
ThreadSafeFunction* ts_fn =
1121+
node::ContainerOf(&ThreadSafeFunction::idle,
1122+
reinterpret_cast<uv_idle_t*>(handle));
1123+
ts_fn->Finalize();
1124+
});
1125+
});
1126+
}
1127+
1128+
// Default way of calling into JavaScript. Used when ThreadSafeFunction is
1129+
// constructed without a call_js_cb_.
1130+
static void CallJs(napi_env env, napi_value cb, void* context, void* data) {
1131+
if (!(env == nullptr || cb == nullptr)) {
1132+
napi_value recv;
1133+
napi_status status;
1134+
1135+
status = napi_get_undefined(env, &recv);
1136+
if (status != napi_ok) {
1137+
napi_throw_error(env, "ERR_NAPI_TSFN_GET_UNDEFINED",
1138+
"Failed to retrieve undefined value");
1139+
return;
1140+
}
1141+
1142+
status = napi_call_function(env, recv, cb, 0, nullptr, nullptr);
1143+
if (status != napi_ok && status != napi_pending_exception) {
1144+
napi_throw_error(env, "ERR_NAPI_TSFN_CALL_JS",
1145+
"Failed to call JS callback");
1146+
return;
1147+
}
1148+
}
1149+
}
1150+
1151+
static void IdleCb(uv_idle_t* idle) {
1152+
ThreadSafeFunction* ts_fn =
1153+
node::ContainerOf(&ThreadSafeFunction::idle, idle);
1154+
ts_fn->DispatchOne();
1155+
}
1156+
1157+
static void AsyncCb(uv_async_t* async) {
1158+
ThreadSafeFunction* ts_fn =
1159+
node::ContainerOf(&ThreadSafeFunction::async, async);
1160+
ts_fn->MaybeStartIdle();
1161+
}
1162+
1163+
static void Cleanup(void* data) {
1164+
reinterpret_cast<ThreadSafeFunction*>(data)
1165+
->CloseHandlesAndMaybeDelete(true);
1166+
}
1167+
1168+
private:
1169+
// These are variables protected by the mutex.
1170+
node::Mutex mutex;
1171+
std::unique_ptr<node::ConditionVariable> cond;
1172+
std::queue<void*> queue;
1173+
uv_async_t async;
1174+
uv_idle_t idle;
1175+
size_t thread_count;
1176+
bool is_closing;
1177+
1178+
// These are variables set once, upon creation, and then never again, which
1179+
// means we don't need the mutex to read them.
1180+
void* context;
1181+
size_t max_queue_size;
1182+
1183+
// These are variables accessed only from the loop thread.
1184+
v8::Persistent<v8::Function> ref;
1185+
napi_env env;
1186+
void* finalize_data;
1187+
napi_finalize finalize_cb;
1188+
napi_threadsafe_function_call_js call_js_cb;
1189+
bool handles_closing;
1190+
};
1191+
1192+
} // end of namespace v8impl
1193+
1194+
// Intercepts the Node-V8 module registration callback. Converts parameters
1195+
// to NAPI equivalents and then calls the registration callback specified
1196+
// by the NAPI module.
1197+
void napi_module_register_cb(v8::Local<v8::Object> exports,
1198+
v8::Local<v8::Value> module,
1199+
v8::Local<v8::Context> context,
1200+
void* priv) {
1201+
napi_module* mod = static_cast<napi_module*>(priv);
1202+
1203+
if (mod->nm_register_func == nullptr) {
1204+
node::Environment::GetCurrent(context)->ThrowError(
1205+
"Module has no declared entry point.");
1206+
return;
1207+
}
1208+
1209+
// Create a new napi_env for this module or reference one if a pre-existing
1210+
// one is found.
1211+
napi_env env = v8impl::GetEnv(context);
1212+
1213+
napi_value _exports;
1214+
NAPI_CALL_INTO_MODULE_THROW(env,
1215+
_exports = mod->nm_register_func(env,
1216+
v8impl::JsValueFromV8LocalValue(exports)));
1217+
1218+
// If register function returned a non-null exports object different from
1219+
// the exports object we passed it, set that as the "exports" property of
1220+
// the module.
1221+
if (_exports != nullptr &&
1222+
_exports != v8impl::JsValueFromV8LocalValue(exports)) {
1223+
napi_value _module = v8impl::JsValueFromV8LocalValue(module);
1224+
napi_set_named_property(env, _module, "exports", _exports);
1225+
}
1226+
}
1227+
1228+
} // end of anonymous namespace
1229+
1230+
// Registers a NAPI module.
1231+
void napi_module_register(napi_module* mod) {
1232+
node::node_module* nm = new node::node_module {
1233+
-1,
1234+
mod->nm_flags,
1235+
nullptr,
1236+
mod->nm_filename,
1237+
nullptr,
1238+
napi_module_register_cb,
1239+
mod->nm_modname,
1240+
mod, // priv
1241+
nullptr,
1242+
};
1243+
node::node_module_register(nm);
1244+
}
1245+
1246+
napi_status napi_add_env_cleanup_hook(napi_env env,
1247+
void (*fun)(void* arg),
1248+
void* arg) {
1249+
CHECK_ENV(env);
1250+
CHECK_ARG(env, fun);
1251+
1252+
node::AddEnvironmentCleanupHook(env->isolate, fun, arg);
1253+
1254+
return napi_ok;
1255+
}
1256+
1257+
napi_status napi_remove_env_cleanup_hook(napi_env env,
1258+
void (*fun)(void* arg),
1259+
void* arg) {
1260+
CHECK_ENV(env);
1261+
CHECK_ARG(env, fun);
1262+
1263+
node::RemoveEnvironmentCleanupHook(env->isolate, fun, arg);
1264+
1265+
return napi_ok;
1266+
}
1267+
1268+
// Warning: Keep in-sync with napi_status enum
1269+
static
1270+
const char* error_messages[] = {nullptr,
1271+
"Invalid argument",
1272+
"An object was expected",
1273+
"A string was expected",
1274+
"A string or symbol was expected",
1275+
"A function was expected",
1276+
"A number was expected",
1277+
"A boolean was expected",
1278+
"An array was expected",
1279+
"Unknown failure",
1280+
"An exception is pending",
1281+
"The async work item was cancelled",
1282+
"napi_escape_handle already called on scope",
1283+
"Invalid handle scope usage",
1284+
"Invalid callback scope usage",
1285+
"Thread-safe function queue is full",
1286+
"Thread-safe function handle is closing"
1287+
};
1288+
1289+
static inline napi_status napi_clear_last_error(napi_env env) {
1290+
env->last_error.error_code = napi_ok;
1291+
1292+
// TODO(boingoing): Should this be a callback?
1293+
env->last_error.engine_error_code = 0;
1294+
env->last_error.engine_reserved = nullptr;
1295+
return napi_ok;
1296+
}
1297+
1298+
static inline
1299+
napi_status napi_set_last_error(napi_env env, napi_status error_code,
1300+
uint32_t engine_error_code,
1301+
void* engine_reserved) {
1302+
env->last_error.error_code = error_code;
1303+
env->last_error.engine_error_code = engine_error_code;
1304+
env->last_error.engine_reserved = engine_reserved;
1305+
return error_code;
1306+
}
1307+
1308+
napi_status napi_get_last_error_info(napi_env env,
1309+
const napi_extended_error_info** result) {
1310+
CHECK_ENV(env);
1311+
CHECK_ARG(env, result);
1312+
1313+
// you must update this assert to reference the last message
1314+
// in the napi_status enum each time a new error message is added.
1315+
// We don't have a napi_status_last as this would result in an ABI
1316+
// change each time a message was added.
1317+
static_assert(
1318+
node::arraysize(error_messages) == napi_closing + 1,
1319+
"Count of error messages must match count of error values");
1320+
CHECK_LE(env->last_error.error_code, napi_callback_scope_mismatch);
1321+
1322+
// Wait until someone requests the last error information to fetch the error
1323+
// message string
1324+
env->last_error.error_message =
1325+
error_messages[env->last_error.error_code];
1326+
1327+
*result = &(env->last_error);
1328+
return napi_ok;
1329+
}
1330+
1331+
napi_status napi_fatal_exception(napi_env env, napi_value err) {
1332+
NAPI_PREAMBLE(env);
1333+
CHECK_ARG(env, err);
1334+
1335+
v8::Local<v8::Value> local_err = v8impl::V8LocalValueFromJsValue(err);
1336+
v8impl::trigger_fatal_exception(env, local_err);
1337+
1338+
return napi_clear_last_error(env);
1339+
}
1340+
1341+
NAPI_NO_RETURN void napi_fatal_error(const char* location,
1342+
size_t location_len,
1343+
const char* message,
1344+
size_t message_len) {
1345+
std::string location_string;
1346+
std::string message_string;
1347+
1348+
if (location_len != NAPI_AUTO_LENGTH) {
1349+
location_string.assign(
1350+
const_cast<char*>(location), location_len);
1351+
} else {
1352+
location_string.assign(
1353+
const_cast<char*>(location), strlen(location));
1354+
}
1355+
1356+
if (message_len != NAPI_AUTO_LENGTH) {
1357+
message_string.assign(
1358+
const_cast<char*>(message), message_len);
1359+
} else {
1360+
message_string.assign(
1361+
const_cast<char*>(message), strlen(message));
1362+
}
1363+
1364+
node::FatalError(location_string.c_str(), message_string.c_str());
1365+
}
10331366

10341367
napi_status napi_create_function(napi_env env,
10351368
const char* utf8name,
@@ -3473,456 +3806,126 @@ napi_status napi_create_async_work(napi_env env,
34733806
*result = reinterpret_cast<napi_async_work>(work);
34743807

34753808
return napi_clear_last_error(env);
3476-
}
3477-
3478-
napi_status napi_delete_async_work(napi_env env, napi_async_work work) {
3479-
CHECK_ENV(env);
3480-
CHECK_ARG(env, work);
3481-
3482-
uvimpl::Work::Delete(reinterpret_cast<uvimpl::Work*>(work));
3483-
3484-
return napi_clear_last_error(env);
3485-
}
3486-
3487-
napi_status napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) {
3488-
CHECK_ENV(env);
3489-
CHECK_ARG(env, loop);
3490-
*loop = env->loop;
3491-
return napi_clear_last_error(env);
3492-
}
3493-
3494-
napi_status napi_queue_async_work(napi_env env, napi_async_work work) {
3495-
CHECK_ENV(env);
3496-
CHECK_ARG(env, work);
3497-
3498-
napi_status status;
3499-
uv_loop_t* event_loop = nullptr;
3500-
status = napi_get_uv_event_loop(env, &event_loop);
3501-
if (status != napi_ok)
3502-
return napi_set_last_error(env, status);
3503-
3504-
uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
3505-
3506-
CALL_UV(env, uv_queue_work(event_loop,
3507-
w->Request(),
3508-
uvimpl::Work::ExecuteCallback,
3509-
uvimpl::Work::CompleteCallback));
3510-
3511-
return napi_clear_last_error(env);
3512-
}
3513-
3514-
napi_status napi_cancel_async_work(napi_env env, napi_async_work work) {
3515-
CHECK_ENV(env);
3516-
CHECK_ARG(env, work);
3517-
3518-
uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
3519-
3520-
CALL_UV(env, uv_cancel(reinterpret_cast<uv_req_t*>(w->Request())));
3521-
3522-
return napi_clear_last_error(env);
3523-
}
3524-
3525-
napi_status napi_create_promise(napi_env env,
3526-
napi_deferred* deferred,
3527-
napi_value* promise) {
3528-
NAPI_PREAMBLE(env);
3529-
CHECK_ARG(env, deferred);
3530-
CHECK_ARG(env, promise);
3531-
3532-
auto maybe = v8::Promise::Resolver::New(env->isolate->GetCurrentContext());
3533-
CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
3534-
3535-
auto v8_resolver = maybe.ToLocalChecked();
3536-
auto v8_deferred = new v8::Persistent<v8::Value>();
3537-
v8_deferred->Reset(env->isolate, v8_resolver);
3538-
3539-
*deferred = v8impl::JsDeferredFromV8Persistent(v8_deferred);
3540-
*promise = v8impl::JsValueFromV8LocalValue(v8_resolver->GetPromise());
3541-
return GET_RETURN_STATUS(env);
3542-
}
3543-
3544-
napi_status napi_resolve_deferred(napi_env env,
3545-
napi_deferred deferred,
3546-
napi_value resolution) {
3547-
return v8impl::ConcludeDeferred(env, deferred, resolution, true);
3548-
}
3549-
3550-
napi_status napi_reject_deferred(napi_env env,
3551-
napi_deferred deferred,
3552-
napi_value resolution) {
3553-
return v8impl::ConcludeDeferred(env, deferred, resolution, false);
3554-
}
3555-
3556-
napi_status napi_is_promise(napi_env env,
3557-
napi_value promise,
3558-
bool* is_promise) {
3559-
CHECK_ENV(env);
3560-
CHECK_ARG(env, promise);
3561-
CHECK_ARG(env, is_promise);
3562-
3563-
*is_promise = v8impl::V8LocalValueFromJsValue(promise)->IsPromise();
3564-
3565-
return napi_clear_last_error(env);
3566-
}
3567-
3568-
napi_status napi_run_script(napi_env env,
3569-
napi_value script,
3570-
napi_value* result) {
3571-
NAPI_PREAMBLE(env);
3572-
CHECK_ARG(env, script);
3573-
CHECK_ARG(env, result);
3574-
3575-
v8::Local<v8::Value> v8_script = v8impl::V8LocalValueFromJsValue(script);
3576-
3577-
if (!v8_script->IsString()) {
3578-
return napi_set_last_error(env, napi_string_expected);
3579-
}
3580-
3581-
v8::Local<v8::Context> context = env->isolate->GetCurrentContext();
3582-
3583-
auto maybe_script = v8::Script::Compile(context,
3584-
v8::Local<v8::String>::Cast(v8_script));
3585-
CHECK_MAYBE_EMPTY(env, maybe_script, napi_generic_failure);
3586-
3587-
auto script_result =
3588-
maybe_script.ToLocalChecked()->Run(context);
3589-
CHECK_MAYBE_EMPTY(env, script_result, napi_generic_failure);
3590-
3591-
*result = v8impl::JsValueFromV8LocalValue(script_result.ToLocalChecked());
3592-
return GET_RETURN_STATUS(env);
3593-
}
3594-
3595-
class TsFn: public node::AsyncResource {
3596-
public:
3597-
TsFn(v8::Local<v8::Function> func,
3598-
v8::Local<v8::Object> resource,
3599-
v8::Local<v8::String> name,
3600-
size_t thread_count_,
3601-
void* context_,
3602-
size_t max_queue_size_,
3603-
napi_env env_,
3604-
void* finalize_data_,
3605-
napi_finalize finalize_cb_,
3606-
napi_threadsafe_function_call_js call_js_cb_):
3607-
AsyncResource(env_->isolate,
3608-
resource,
3609-
*v8::String::Utf8Value(env_->isolate, name)),
3610-
thread_count(thread_count_),
3611-
is_closing(false),
3612-
context(context_),
3613-
max_queue_size(max_queue_size_),
3614-
env(env_),
3615-
finalize_data(finalize_data_),
3616-
finalize_cb(finalize_cb_),
3617-
call_js_cb(call_js_cb_ == nullptr ? CallJs : call_js_cb_),
3618-
handles_closing(false) {
3619-
ref.Reset(env->isolate, func);
3620-
node::AddEnvironmentCleanupHook(env->isolate, Cleanup, this);
3621-
}
3622-
3623-
~TsFn() {
3624-
node::RemoveEnvironmentCleanupHook(env->isolate, Cleanup, this);
3625-
if (ref.IsEmpty())
3626-
return;
3627-
ref.ClearWeak();
3628-
ref.Reset();
3629-
}
3630-
3631-
// These methods can be called from any thread.
3632-
3633-
napi_status Push(void* data, napi_threadsafe_function_call_mode mode) {
3634-
node::Mutex::ScopedLock lock(this->mutex);
3635-
3636-
while (queue.size() >= max_queue_size &&
3637-
max_queue_size > 0 &&
3638-
!is_closing) {
3639-
if (mode == napi_tsfn_nonblocking) {
3640-
return napi_queue_full;
3641-
}
3642-
cond->Wait(lock);
3643-
}
3644-
3645-
if (is_closing) {
3646-
if (thread_count == 0) {
3647-
return napi_invalid_arg;
3648-
} else {
3649-
thread_count--;
3650-
return napi_closing;
3651-
}
3652-
} else {
3653-
if (uv_async_send(&async) != 0) {
3654-
return napi_generic_failure;
3655-
}
3656-
queue.push(data);
3657-
return napi_ok;
3658-
}
3659-
}
3660-
3661-
napi_status Acquire() {
3662-
node::Mutex::ScopedLock lock(this->mutex);
3663-
3664-
if (is_closing) {
3665-
return napi_closing;
3666-
}
3667-
3668-
thread_count++;
3669-
3670-
return napi_ok;
3671-
}
3672-
3673-
napi_status Release(napi_threadsafe_function_release_mode mode) {
3674-
node::Mutex::ScopedLock lock(this->mutex);
3675-
3676-
if (thread_count == 0) {
3677-
return napi_invalid_arg;
3678-
}
3679-
3680-
thread_count--;
3681-
3682-
if (thread_count == 0 || mode == napi_tsfn_abort) {
3683-
if (!is_closing) {
3684-
is_closing = (mode == napi_tsfn_abort);
3685-
if (is_closing && max_queue_size > 0) {
3686-
cond->Signal(lock);
3687-
}
3688-
if (uv_async_send(&async) != 0) {
3689-
return napi_generic_failure;
3690-
}
3691-
}
3692-
}
3693-
3694-
return napi_ok;
3695-
}
3696-
3697-
void EmptyQueueAndDelete() {
3698-
for (; !queue.empty() ; queue.pop()) {
3699-
call_js_cb(nullptr, nullptr, context, queue.front());
3700-
}
3701-
delete this;
3702-
}
3703-
3704-
// These methods must only be called from the loop thread.
3705-
3706-
napi_status Init() {
3707-
TsFn* ts_fn = this;
3708-
3709-
if (uv_async_init(env->loop, &async, AsyncCb) == 0) {
3710-
if (max_queue_size > 0) {
3711-
cond.reset(new node::ConditionVariable);
3712-
}
3713-
if ((max_queue_size == 0 || cond.get() != nullptr) &&
3714-
uv_idle_init(env->loop, &idle) == 0) {
3715-
return napi_ok;
3716-
}
3809+
}
37173810

3718-
uv_close(reinterpret_cast<uv_handle_t*>(&async),
3719-
[] (uv_handle_t* handle) -> void {
3720-
TsFn* ts_fn =
3721-
node::ContainerOf(&TsFn::async,
3722-
reinterpret_cast<uv_async_t*>(handle));
3723-
delete ts_fn;
3724-
});
3811+
napi_status napi_delete_async_work(napi_env env, napi_async_work work) {
3812+
CHECK_ENV(env);
3813+
CHECK_ARG(env, work);
37253814

3726-
// Prevent the thread-safe function from being deleted here, because
3727-
// the callback above will delete it.
3728-
ts_fn = nullptr;
3729-
}
3815+
uvimpl::Work::Delete(reinterpret_cast<uvimpl::Work*>(work));
37303816

3731-
delete ts_fn;
3817+
return napi_clear_last_error(env);
3818+
}
37323819

3733-
return napi_generic_failure;
3734-
}
3820+
napi_status napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) {
3821+
CHECK_ENV(env);
3822+
CHECK_ARG(env, loop);
3823+
*loop = env->loop;
3824+
return napi_clear_last_error(env);
3825+
}
37353826

3736-
napi_status Unref() {
3737-
uv_unref(reinterpret_cast<uv_handle_t*>(&async));
3738-
uv_unref(reinterpret_cast<uv_handle_t*>(&idle));
3827+
napi_status napi_queue_async_work(napi_env env, napi_async_work work) {
3828+
CHECK_ENV(env);
3829+
CHECK_ARG(env, work);
37393830

3740-
return napi_ok;
3741-
}
3831+
napi_status status;
3832+
uv_loop_t* event_loop = nullptr;
3833+
status = napi_get_uv_event_loop(env, &event_loop);
3834+
if (status != napi_ok)
3835+
return napi_set_last_error(env, status);
37423836

3743-
napi_status Ref() {
3744-
uv_ref(reinterpret_cast<uv_handle_t*>(&async));
3745-
uv_ref(reinterpret_cast<uv_handle_t*>(&idle));
3837+
uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
37463838

3747-
return napi_ok;
3748-
}
3839+
CALL_UV(env, uv_queue_work(event_loop,
3840+
w->Request(),
3841+
uvimpl::Work::ExecuteCallback,
3842+
uvimpl::Work::CompleteCallback));
37493843

3750-
void DispatchOne() {
3751-
void* data = nullptr;
3752-
bool popped_value = false;
3753-
bool idle_stop_failed = false;
3844+
return napi_clear_last_error(env);
3845+
}
37543846

3755-
{
3756-
node::Mutex::ScopedLock lock(this->mutex);
3757-
if (is_closing) {
3758-
CloseHandlesAndMaybeDelete();
3759-
} else {
3760-
size_t size = queue.size();
3761-
if (size > 0) {
3762-
data = queue.front();
3763-
queue.pop();
3764-
popped_value = true;
3765-
if (size == max_queue_size && max_queue_size > 0) {
3766-
cond->Signal(lock);
3767-
}
3768-
size--;
3769-
}
3847+
napi_status napi_cancel_async_work(napi_env env, napi_async_work work) {
3848+
CHECK_ENV(env);
3849+
CHECK_ARG(env, work);
37703850

3771-
if (size == 0) {
3772-
if (thread_count == 0) {
3773-
is_closing = true;
3774-
if (max_queue_size > 0) {
3775-
cond->Signal(lock);
3776-
}
3777-
CloseHandlesAndMaybeDelete();
3778-
} else {
3779-
if (uv_idle_stop(&idle) != 0) {
3780-
idle_stop_failed = true;
3781-
}
3782-
}
3783-
}
3784-
}
3785-
}
3851+
uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work);
37863852

3787-
if (popped_value || idle_stop_failed) {
3788-
v8::HandleScope scope(env->isolate);
3789-
CallbackScope cb_scope(this);
3853+
CALL_UV(env, uv_cancel(reinterpret_cast<uv_req_t*>(w->Request())));
37903854

3791-
if (idle_stop_failed) {
3792-
CHECK(napi_throw_error(env,
3793-
"ERR_NAPI_TSFN_STOP_IDLE_LOOP",
3794-
"Failed to stop the idle loop") == napi_ok);
3795-
} else {
3796-
v8::Local<v8::Function> js_cb =
3797-
v8::Local<v8::Function>::New(env->isolate, ref);
3798-
call_js_cb(env,
3799-
v8impl::JsValueFromV8LocalValue(js_cb),
3800-
context,
3801-
data);
3802-
}
3803-
}
3804-
}
3855+
return napi_clear_last_error(env);
3856+
}
38053857

3806-
node::Environment* NodeEnv() {
3807-
// For some reason grabbing the Node.js environment requires a handle scope.
3808-
v8::HandleScope scope(env->isolate);
3809-
return node::Environment::GetCurrent(env->isolate);
3810-
}
3858+
napi_status napi_create_promise(napi_env env,
3859+
napi_deferred* deferred,
3860+
napi_value* promise) {
3861+
NAPI_PREAMBLE(env);
3862+
CHECK_ARG(env, deferred);
3863+
CHECK_ARG(env, promise);
38113864

3812-
void MaybeStartIdle() {
3813-
if (uv_idle_start(&idle, IdleCb) != 0) {
3814-
v8::HandleScope scope(env->isolate);
3815-
CallbackScope cb_scope(this);
3816-
CHECK(napi_throw_error(env,
3817-
"ERR_NAPI_TSFN_START_IDLE_LOOP",
3818-
"Failed to start the idle loop") == napi_ok);
3819-
}
3820-
}
3865+
auto maybe = v8::Promise::Resolver::New(env->isolate->GetCurrentContext());
3866+
CHECK_MAYBE_EMPTY(env, maybe, napi_generic_failure);
38213867

3822-
void Finalize() {
3823-
v8::HandleScope scope(env->isolate);
3824-
if (finalize_cb) {
3825-
CallbackScope cb_scope(this);
3826-
finalize_cb(env, finalize_data, context);
3827-
}
3828-
EmptyQueueAndDelete();
3829-
}
3868+
auto v8_resolver = maybe.ToLocalChecked();
3869+
auto v8_deferred = new v8::Persistent<v8::Value>();
3870+
v8_deferred->Reset(env->isolate, v8_resolver);
38303871

3831-
inline void* Context() {
3832-
return context;
3833-
}
3872+
*deferred = v8impl::JsDeferredFromV8Persistent(v8_deferred);
3873+
*promise = v8impl::JsValueFromV8LocalValue(v8_resolver->GetPromise());
3874+
return GET_RETURN_STATUS(env);
3875+
}
38343876

3835-
void CloseHandlesAndMaybeDelete(bool set_closing = false) {
3836-
if (set_closing) {
3837-
node::Mutex::ScopedLock lock(this->mutex);
3838-
is_closing = true;
3839-
if (max_queue_size > 0) {
3840-
cond->Signal(lock);
3841-
}
3842-
}
3843-
if (handles_closing) {
3844-
return;
3845-
}
3846-
handles_closing = true;
3847-
uv_close(
3848-
reinterpret_cast<uv_handle_t*>(&async),
3849-
[] (uv_handle_t* handle) -> void {
3850-
TsFn* ts_fn = node::ContainerOf(&TsFn::async,
3851-
reinterpret_cast<uv_async_t*>(handle));
3852-
uv_close(
3853-
reinterpret_cast<uv_handle_t*>(&ts_fn->idle),
3854-
[] (uv_handle_t* handle) -> void {
3855-
TsFn* ts_fn = node::ContainerOf(&TsFn::idle,
3856-
reinterpret_cast<uv_idle_t*>(handle));
3857-
ts_fn->Finalize();
3858-
});
3859-
});
3860-
}
3877+
napi_status napi_resolve_deferred(napi_env env,
3878+
napi_deferred deferred,
3879+
napi_value resolution) {
3880+
return v8impl::ConcludeDeferred(env, deferred, resolution, true);
3881+
}
38613882

3862-
// Default way of calling into JavaScript. Used when TsFn is constructed
3863-
// without a call_js_cb_.
3864-
static void CallJs(napi_env env, napi_value cb, void* context, void* data) {
3865-
if (!(env == nullptr || cb == nullptr)) {
3866-
napi_value recv;
3867-
napi_status status;
3883+
napi_status napi_reject_deferred(napi_env env,
3884+
napi_deferred deferred,
3885+
napi_value resolution) {
3886+
return v8impl::ConcludeDeferred(env, deferred, resolution, false);
3887+
}
38683888

3869-
status = napi_get_undefined(env, &recv);
3870-
if (status != napi_ok) {
3871-
napi_throw_error(env, "ERR_NAPI_TSFN_GET_UNDEFINED",
3872-
"Failed to retrieve undefined value");
3873-
return;
3874-
}
3889+
napi_status napi_is_promise(napi_env env,
3890+
napi_value promise,
3891+
bool* is_promise) {
3892+
CHECK_ENV(env);
3893+
CHECK_ARG(env, promise);
3894+
CHECK_ARG(env, is_promise);
38753895

3876-
status = napi_call_function(env, recv, cb, 0, nullptr, nullptr);
3877-
if (status != napi_ok && status != napi_pending_exception) {
3878-
napi_throw_error(env, "ERR_NAPI_TSFN_CALL_JS",
3879-
"Failed to call JS callback");
3880-
return;
3881-
}
3882-
}
3883-
}
3896+
*is_promise = v8impl::V8LocalValueFromJsValue(promise)->IsPromise();
38843897

3885-
static void IdleCb(uv_idle_t* idle) {
3886-
TsFn* ts_fn =
3887-
node::ContainerOf(&TsFn::idle, idle);
3888-
ts_fn->DispatchOne();
3889-
}
3898+
return napi_clear_last_error(env);
3899+
}
38903900

3891-
static void AsyncCb(uv_async_t* async) {
3892-
TsFn* ts_fn =
3893-
node::ContainerOf(&TsFn::async, async);
3894-
ts_fn->MaybeStartIdle();
3895-
}
3901+
napi_status napi_run_script(napi_env env,
3902+
napi_value script,
3903+
napi_value* result) {
3904+
NAPI_PREAMBLE(env);
3905+
CHECK_ARG(env, script);
3906+
CHECK_ARG(env, result);
38963907

3897-
static void Cleanup(void* data) {
3898-
reinterpret_cast<TsFn*>(data)->CloseHandlesAndMaybeDelete(true);
3908+
v8::Local<v8::Value> v8_script = v8impl::V8LocalValueFromJsValue(script);
3909+
3910+
if (!v8_script->IsString()) {
3911+
return napi_set_last_error(env, napi_string_expected);
38993912
}
39003913

3901-
private:
3902-
// These are variables protected by the mutex.
3903-
node::Mutex mutex;
3904-
std::unique_ptr<node::ConditionVariable> cond;
3905-
std::queue<void*> queue;
3906-
uv_async_t async;
3907-
uv_idle_t idle;
3908-
size_t thread_count;
3909-
bool is_closing;
3914+
v8::Local<v8::Context> context = env->isolate->GetCurrentContext();
39103915

3911-
// These are variables set once, upon creation, and then never again, which
3912-
// means we don't need the mutex to read them.
3913-
void* context;
3914-
size_t max_queue_size;
3916+
auto maybe_script = v8::Script::Compile(context,
3917+
v8::Local<v8::String>::Cast(v8_script));
3918+
CHECK_MAYBE_EMPTY(env, maybe_script, napi_generic_failure);
39153919

3916-
// These are variables accessed only from the loop thread.
3917-
v8::Persistent<v8::Function> ref;
3918-
napi_env env;
3919-
void* finalize_data;
3920-
napi_finalize finalize_cb;
3921-
napi_threadsafe_function_call_js call_js_cb;
3922-
bool handles_closing;
3923-
};
3920+
auto script_result =
3921+
maybe_script.ToLocalChecked()->Run(context);
3922+
CHECK_MAYBE_EMPTY(env, script_result, napi_generic_failure);
3923+
3924+
*result = v8impl::JsValueFromV8LocalValue(script_result.ToLocalChecked());
3925+
return GET_RETURN_STATUS(env);
3926+
}
39243927

3925-
NAPI_EXTERN napi_status
3928+
napi_status
39263929
napi_create_threadsafe_function(napi_env env,
39273930
napi_value func,
39283931
napi_value async_resource,
@@ -3957,16 +3960,17 @@ napi_create_threadsafe_function(napi_env env,
39573960
v8::Local<v8::String> v8_name;
39583961
CHECK_TO_STRING(env, v8_context, v8_name, async_resource_name);
39593962

3960-
TsFn* ts_fn = new TsFn(v8_func,
3961-
v8_resource,
3962-
v8_name,
3963-
initial_thread_count,
3964-
context,
3965-
max_queue_size,
3966-
env,
3967-
thread_finalize_data,
3968-
thread_finalize_cb,
3969-
call_js_cb);
3963+
v8impl::ThreadSafeFunction* ts_fn =
3964+
new v8impl::ThreadSafeFunction(v8_func,
3965+
v8_resource,
3966+
v8_name,
3967+
initial_thread_count,
3968+
context,
3969+
max_queue_size,
3970+
env,
3971+
thread_finalize_data,
3972+
thread_finalize_cb,
3973+
call_js_cb);
39703974

39713975
if (ts_fn == nullptr) {
39723976
status = napi_generic_failure;
@@ -3981,45 +3985,46 @@ napi_create_threadsafe_function(napi_env env,
39813985
return napi_set_last_error(env, status);
39823986
}
39833987

3984-
NAPI_EXTERN napi_status
3988+
napi_status
39853989
napi_get_threadsafe_function_context(napi_threadsafe_function func,
39863990
void** result) {
39873991
CHECK(func != nullptr);
39883992
CHECK(result != nullptr);
39893993

3990-
*result = reinterpret_cast<TsFn*>(func)->Context();
3994+
*result = reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Context();
39913995
return napi_ok;
39923996
}
39933997

3994-
NAPI_EXTERN napi_status
3998+
napi_status
39953999
napi_call_threadsafe_function(napi_threadsafe_function func,
39964000
void* data,
39974001
napi_threadsafe_function_call_mode is_blocking) {
39984002
CHECK(func != nullptr);
3999-
return reinterpret_cast<TsFn*>(func)->Push(data, is_blocking);
4003+
return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Push(data,
4004+
is_blocking);
40004005
}
40014006

4002-
NAPI_EXTERN napi_status
4007+
napi_status
40034008
napi_acquire_threadsafe_function(napi_threadsafe_function func) {
40044009
CHECK(func != nullptr);
4005-
return reinterpret_cast<TsFn*>(func)->Acquire();
4010+
return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Acquire();
40064011
}
40074012

4008-
NAPI_EXTERN napi_status
4013+
napi_status
40094014
napi_release_threadsafe_function(napi_threadsafe_function func,
40104015
napi_threadsafe_function_release_mode mode) {
40114016
CHECK(func != nullptr);
4012-
return reinterpret_cast<TsFn*>(func)->Release(mode);
4017+
return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Release(mode);
40134018
}
40144019

4015-
NAPI_EXTERN napi_status
4020+
napi_status
40164021
napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
40174022
CHECK(func != nullptr);
4018-
return reinterpret_cast<TsFn*>(func)->Unref();
4023+
return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Unref();
40194024
}
40204025

4021-
NAPI_EXTERN napi_status
4026+
napi_status
40224027
napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) {
40234028
CHECK(func != nullptr);
4024-
return reinterpret_cast<TsFn*>(func)->Ref();
4029+
return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Ref();
40254030
}

0 commit comments

Comments
 (0)
Please sign in to comment.