-
Notifications
You must be signed in to change notification settings - Fork 30.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
src,napi: add helper for addons to get the event loop #17109
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4419,6 +4419,11 @@ void RunAtExit(Environment* env) { | |
} | ||
|
||
|
||
uv_loop_t* GetCurrentEventLoop(v8::Isolate* isolate) { | ||
return Environment::GetCurrent(isolate)->event_loop(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Leaks a handle into the caller's scope. The overload that takes a That should be documented because it's the kind of thing that add-ons will run into. You could robustify (def a word) this function by doing: uv_loop_t* GetCurrentEventLoop(v8::Isolate* isolate) {
HandleScope handle_scope(isolate);
auto context = isolate->GetCurrentContext();
if (context.IsEmpty()) return nullptr;
return Environment::GetCurrent(context);
} That can still crash with a non-node context, though. Maybe we should file a CL with V8 to expose There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, thanks for catching those. Updated with your suggestion! |
||
} | ||
|
||
|
||
static uv_key_t thread_local_env; | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,7 @@ | |
#include "node_api.h" | ||
#include "node_internals.h" | ||
|
||
#define NAPI_VERSION 1 | ||
#define NAPI_VERSION 2 | ||
|
||
static | ||
napi_status napi_set_last_error(napi_env env, napi_status error_code, | ||
|
@@ -3401,15 +3401,22 @@ napi_status napi_delete_async_work(napi_env env, napi_async_work work) { | |
return napi_clear_last_error(env); | ||
} | ||
|
||
napi_status napi_get_uv_event_loop(napi_env env, uv_loop_t** loop) { | ||
CHECK_ENV(env); | ||
CHECK_ARG(env, loop); | ||
*loop = node::GetCurrentEventLoop(env->isolate); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you make that change, consider adding a sanity check here that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’ve moved the |
||
return napi_clear_last_error(env); | ||
} | ||
|
||
napi_status napi_queue_async_work(napi_env env, napi_async_work work) { | ||
CHECK_ENV(env); | ||
CHECK_ARG(env, work); | ||
|
||
// Consider: Encapsulate the uv_loop_t into an opaque pointer parameter. | ||
// Currently the environment event loop is the same as the UV default loop. | ||
// Someday (if node ever supports multiple isolates), it may be better to get | ||
// the loop from node::Environment::GetCurrent(env->isolate)->event_loop(); | ||
uv_loop_t* event_loop = uv_default_loop(); | ||
napi_status status; | ||
uv_loop_t* event_loop = nullptr; | ||
status = napi_get_uv_event_loop(env, &event_loop); | ||
if (status != napi_ok) | ||
return napi_set_last_error(env, status); | ||
|
||
uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"targets": [ | ||
{ | ||
"target_name": "test_uv_loop", | ||
"sources": [ "test_uv_loop.cc" ] | ||
} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
'use strict'; | ||
const common = require('../../common'); | ||
const { SetImmediate } = require(`./build/${common.buildType}/test_uv_loop`); | ||
|
||
SetImmediate(common.mustCall()); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
#include <node_api.h> | ||
#include <uv.h> | ||
#include <utility> | ||
#include <memory> | ||
#include <assert.h> | ||
#include "../common.h" | ||
|
||
template<typename T> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Space before |
||
void* SetImmediate(napi_env env, T&& cb) { | ||
T* ptr = new T(std::move(cb)); | ||
uv_loop_t* loop = nullptr; | ||
uv_check_t* check = new uv_check_t; | ||
check->data = static_cast<void*>(ptr); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cast-to-void is not necessary. |
||
NAPI_ASSERT(env, | ||
napi_get_uv_event_loop(env, &loop) == napi_ok, | ||
"can get event loop"); | ||
uv_check_init(loop, check); | ||
uv_check_start(check, [](uv_check_t* check) { | ||
std::unique_ptr<T> ptr {static_cast<T*>(check->data)}; | ||
T cb = std::move(*ptr); | ||
uv_check_stop(check); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not that it hurts but you don't have to stop the handle if you're closing it anyway. |
||
uv_close(reinterpret_cast<uv_handle_t*>(check), [](uv_handle_t* handle) { | ||
delete reinterpret_cast<uv_check_t*>(handle); | ||
}); | ||
|
||
assert(cb() != nullptr); | ||
}); | ||
return nullptr; | ||
} | ||
|
||
static char dummy; | ||
|
||
napi_value SetImmediateBinding(napi_env env, napi_callback_info info) { | ||
size_t argc = 1; | ||
napi_value argv[1]; | ||
napi_value _this; | ||
void* data; | ||
NAPI_CALL(env, | ||
napi_get_cb_info(env, info, &argc, argv, &_this, &data)); | ||
NAPI_ASSERT(env, argc >= 1, "Not enough arguments, expected 1."); | ||
|
||
napi_valuetype t; | ||
NAPI_CALL(env, napi_typeof(env, argv[0], &t)); | ||
NAPI_ASSERT(env, t == napi_function, | ||
"Wrong first argument, function expected."); | ||
|
||
napi_ref cbref; | ||
NAPI_CALL(env, | ||
napi_create_reference(env, argv[0], 1, &cbref)); | ||
|
||
SetImmediate(env, [=]() -> char* { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just curious, doesn't the compiler infer the return type? Or is this extra protection against someone changing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It gets confused because |
||
napi_value undefined; | ||
napi_value callback; | ||
napi_handle_scope scope; | ||
NAPI_CALL(env, napi_open_handle_scope(env, &scope)); | ||
NAPI_CALL(env, napi_get_undefined(env, &undefined)); | ||
NAPI_CALL(env, napi_get_reference_value(env, cbref, &callback)); | ||
NAPI_CALL(env, napi_delete_reference(env, cbref)); | ||
NAPI_CALL(env, | ||
napi_call_function(env, undefined, callback, 0, nullptr, nullptr)); | ||
NAPI_CALL(env, napi_close_handle_scope(env, scope)); | ||
return &dummy; | ||
}); | ||
|
||
return nullptr; | ||
} | ||
|
||
napi_value Init(napi_env env, napi_value exports) { | ||
napi_property_descriptor properties[] = { | ||
DECLARE_NAPI_PROPERTY("SetImmediate", SetImmediateBinding) | ||
}; | ||
|
||
NAPI_CALL(env, napi_define_properties( | ||
env, exports, sizeof(properties) / sizeof(*properties), properties)); | ||
|
||
return exports; | ||
} | ||
|
||
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be
napi_get_uv_event_loop
I think.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cjihrig Thanks, fixed!