-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
lib: rewrite AsyncLocalStorage without async_hooks
- Loading branch information
Stephen Belanger
committed
Jun 23, 2023
1 parent
198affc
commit b35d35c
Showing
8 changed files
with
357 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
#include "async_context_frame.h" // NOLINT(build/include_inline) | ||
#include "env-inl.h" | ||
#include "node_errors.h" | ||
#include "node_external_reference.h" | ||
#include "tracing/traced_value.h" | ||
#include "util-inl.h" | ||
|
||
#include "debug_utils-inl.h" | ||
|
||
#include "v8.h" | ||
|
||
#include <iostream> | ||
|
||
using v8::ArrayBufferView; | ||
using v8::Context; | ||
using v8::Function; | ||
using v8::FunctionCallbackInfo; | ||
using v8::FunctionTemplate; | ||
using v8::Isolate; | ||
using v8::Local; | ||
using v8::MaybeLocal; | ||
using v8::Object; | ||
using v8::ObjectTemplate; | ||
using v8::Value; | ||
|
||
namespace node { | ||
|
||
// | ||
// Constructor | ||
// | ||
AsyncContextFrame::AsyncContextFrame(Environment* env, | ||
Local<Object> obj, | ||
Local<Object> current, | ||
Local<Value> key, | ||
Local<Value> value) | ||
: BaseObject(env, obj), | ||
parent_(env->isolate(), current), | ||
key_(env->isolate(), key), | ||
value_(env->isolate(), value) {} | ||
|
||
Local<Value> AsyncContextFrame::current(Local<Context> context) { | ||
return context->GetContinuationPreservedEmbedderData(); | ||
} | ||
|
||
Local<Value> AsyncContextFrame::exchange(Local<Context> context, | ||
Local<Value> value) { | ||
auto prior = current(context); | ||
context->SetContinuationPreservedEmbedderData(value); | ||
return prior; | ||
} | ||
|
||
MaybeLocal<Value> AsyncContextFrame::run(Local<Context> context, | ||
Local<Function> fn, | ||
int argc, | ||
Local<Value>* args) { | ||
auto prior = AsyncContextFrame::exchange(context, this->object()); | ||
|
||
auto ret = fn->Call(context, v8::Undefined(context->GetIsolate()), argc, args); | ||
|
||
AsyncContextFrame::exchange(context, prior); | ||
|
||
return ret; | ||
} | ||
|
||
Local<Value> AsyncContextFrame::get(Local<Context> context, Local<Value> key) { | ||
Environment* env = Environment::GetCurrent(context); | ||
Isolate* isolate = context->GetIsolate(); | ||
|
||
if (key_ == key) { | ||
return value_.Get(isolate); | ||
} | ||
|
||
auto parent = parent_.Get(isolate); | ||
if (parent.IsEmpty()) { | ||
return v8::Undefined(isolate); | ||
} | ||
|
||
if (!AsyncContextFrame::HasInstance(env, parent)) { | ||
return v8::Undefined(isolate); | ||
} | ||
|
||
return Unwrap<AsyncContextFrame>(parent)->get(context, key); | ||
} | ||
|
||
// | ||
// JS Static Methods | ||
// | ||
void AsyncContextFrame::Exchange(const FunctionCallbackInfo<Value>& info) { | ||
Isolate* isolate = info.GetIsolate(); | ||
Local<Context> context = isolate->GetCurrentContext(); | ||
info.GetReturnValue().Set(AsyncContextFrame::exchange(context, info[0])); | ||
} | ||
|
||
void AsyncContextFrame::Current(const FunctionCallbackInfo<Value>& info) { | ||
Isolate* isolate = info.GetIsolate(); | ||
Local<Context> context = isolate->GetCurrentContext(); | ||
info.GetReturnValue().Set(AsyncContextFrame::current(context)); | ||
} | ||
|
||
void AsyncContextFrame::RunStatic(const FunctionCallbackInfo<Value>& args) { | ||
CHECK(args.Length() >= 3); | ||
CHECK(args[2]->IsFunction()); | ||
|
||
Isolate* isolate = args.GetIsolate(); | ||
Environment* env = Environment::GetCurrent(isolate); | ||
Local<Context> context = isolate->GetCurrentContext(); | ||
|
||
// Extract args | ||
auto key = args[0]; | ||
auto value = args[1]; | ||
auto fn = args[2].As<Function>(); | ||
SlicedArguments call_args(args, 3); | ||
|
||
// Create new frame continuing from current frame | ||
auto current = AsyncContextFrame::current(context).As<Object>(); | ||
if (current.IsEmpty()) current = Local<Object>(); | ||
auto acf = AsyncContextFrame::Create(env, current, key, value); | ||
|
||
// Run given function within the frame | ||
Local<Value> ret; | ||
if (acf->run(context, fn, call_args.length(), call_args.out()).ToLocal(&ret)) { | ||
args.GetReturnValue().Set(ret); | ||
} | ||
} | ||
|
||
void AsyncContextFrame::Run(const FunctionCallbackInfo<Value>& args) { | ||
Local<Context> context = args.GetIsolate()->GetCurrentContext(); | ||
|
||
auto fn = args[0].As<Function>(); | ||
SlicedArguments call_args(args, 1); | ||
|
||
AsyncContextFrame* acf = Unwrap<AsyncContextFrame>(args.This()); | ||
|
||
Local<Value> ret; | ||
if (acf->run(context, fn, call_args.length(), call_args.out()) | ||
.ToLocal(&ret)) { | ||
args.GetReturnValue().Set(ret); | ||
} | ||
} | ||
|
||
void AsyncContextFrame::Get(const FunctionCallbackInfo<Value>& args) { | ||
Local<Context> context = args.GetIsolate()->GetCurrentContext(); | ||
AsyncContextFrame* acf = Unwrap<AsyncContextFrame>(args.This()); | ||
args.GetReturnValue().Set(acf->get(context, args[0])); | ||
} | ||
|
||
// | ||
// Class construction infra | ||
// | ||
Local<FunctionTemplate> AsyncContextFrame::GetConstructorTemplate( | ||
Environment* env) { | ||
return GetConstructorTemplate(env->isolate_data()); | ||
} | ||
|
||
Local<FunctionTemplate> AsyncContextFrame::GetConstructorTemplate( | ||
IsolateData* isolate_data) { | ||
Local<FunctionTemplate> tmpl = isolate_data->async_context_frame_ctor_template(); | ||
if (tmpl.IsEmpty()) { | ||
Isolate* isolate = isolate_data->isolate(); | ||
Environment* env = Environment::GetCurrent(isolate); | ||
tmpl = BaseObject::MakeLazilyInitializedJSTemplate(env); | ||
tmpl->SetClassName( | ||
FIXED_ONE_BYTE_STRING(isolate, "AsyncContextFrame")); | ||
SetProtoMethodNoSideEffect(isolate, tmpl, "get", Get); | ||
SetProtoMethod(isolate, tmpl, "run", Run); | ||
SetMethod(isolate, tmpl, "run", RunStatic); | ||
SetMethod(isolate, tmpl, "exchange", Exchange); | ||
SetMethod(isolate, tmpl, "current", Current); | ||
isolate_data->set_async_context_frame_ctor_template(tmpl); | ||
} | ||
return tmpl; | ||
} | ||
|
||
bool AsyncContextFrame::HasInstance(Environment* env, | ||
v8::Local<v8::Value> object) { | ||
return GetConstructorTemplate(env->isolate_data())->HasInstance(object); | ||
} | ||
|
||
BaseObjectPtr<AsyncContextFrame> AsyncContextFrame::Create( | ||
Environment* env, | ||
Local<Object> current, | ||
Local<Value> key, | ||
Local<Value> value) { | ||
Local<Object> obj; | ||
|
||
if (UNLIKELY(!GetConstructorTemplate(env) | ||
->InstanceTemplate() | ||
->NewInstance(env->context()) | ||
.ToLocal(&obj))) { | ||
return BaseObjectPtr<AsyncContextFrame>(); | ||
} | ||
|
||
return MakeBaseObject<AsyncContextFrame>(env, obj, current, key, value); | ||
} | ||
|
||
void AsyncContextFrame::RegisterExternalReferences( | ||
ExternalReferenceRegistry* registry) { | ||
registry->Register(Get); | ||
registry->Register(RunStatic); | ||
registry->Register(Run); | ||
registry->Register(Current); | ||
registry->Register(Exchange); | ||
} | ||
|
||
void AsyncContextFrame::CreatePerContextProperties(Local<Object> target, | ||
Local<Value> unused, | ||
Local<Context> context, | ||
void* priv) { | ||
Environment* env = Environment::GetCurrent(context); | ||
|
||
auto t = AsyncContextFrame::GetConstructorTemplate(env); | ||
SetConstructorFunction(context, target, "AsyncContextFrame", t); | ||
} | ||
|
||
void AsyncContextFrame::MemoryInfo(MemoryTracker* tracker) const { | ||
tracker->TrackField("parent", parent_); | ||
tracker->TrackField("key", key_); | ||
tracker->TrackField("value", value_); | ||
} | ||
|
||
} // namespace node | ||
|
||
NODE_BINDING_CONTEXT_AWARE_INTERNAL(async_context_frame, | ||
node::AsyncContextFrame::CreatePerContextProperties) | ||
NODE_BINDING_EXTERNAL_REFERENCE(async_context_frame, | ||
node::AsyncContextFrame::RegisterExternalReferences) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
#ifndef SRC_ASYNC_CONTEXT_FRAME_H_ | ||
#define SRC_ASYNC_CONTEXT_FRAME_H_ | ||
|
||
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS | ||
|
||
#include "base_object.h" | ||
#include "v8.h" | ||
|
||
#include <cstdint> | ||
|
||
namespace node { | ||
|
||
class ExternalReferenceRegistry; | ||
|
||
class AsyncContextFrame final : public BaseObject { | ||
public: | ||
AsyncContextFrame(Environment* env, | ||
v8::Local<v8::Object> object, | ||
v8::Local<v8::Object> current, | ||
v8::Local<v8::Value> key, | ||
v8::Local<v8::Value> value); | ||
|
||
AsyncContextFrame() = delete; | ||
|
||
static v8::Local<v8::Value> current(v8::Local<v8::Context> context); | ||
static v8::Local<v8::Value> exchange(v8::Local<v8::Context> context, | ||
v8::Local<v8::Value> value); | ||
v8::MaybeLocal<v8::Value> run(v8::Local<v8::Context> context, | ||
v8::Local<v8::Function> fn, | ||
int argc, | ||
v8::Local<v8::Value>* args); | ||
v8::Local<v8::Value> get(v8::Local<v8::Context> context, v8::Local<v8::Value> key); | ||
|
||
static void Get(const v8::FunctionCallbackInfo<v8::Value>& args); | ||
static void RunStatic(const v8::FunctionCallbackInfo<v8::Value>& args); | ||
static void Run(const v8::FunctionCallbackInfo<v8::Value>& args); | ||
static void Current(const v8::FunctionCallbackInfo<v8::Value>& args); | ||
static void Exchange(const v8::FunctionCallbackInfo<v8::Value>& args); | ||
|
||
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate( | ||
IsolateData* isolate_data); | ||
inline static v8::Local<v8::FunctionTemplate> GetConstructorTemplate( | ||
Environment* env); | ||
static bool HasInstance(Environment* env, v8::Local<v8::Value> value); | ||
static BaseObjectPtr<AsyncContextFrame> Create(Environment* env, | ||
v8::Local<v8::Object> current, | ||
v8::Local<v8::Value> key, | ||
v8::Local<v8::Value> value); | ||
|
||
static void RegisterExternalReferences(ExternalReferenceRegistry* registry); | ||
static void CreatePerContextProperties(v8::Local<v8::Object> target, | ||
v8::Local<v8::Value> unused, | ||
v8::Local<v8::Context> context, | ||
void* priv); | ||
|
||
// If this needs memory info, swap the next two lines | ||
void MemoryInfo(MemoryTracker* tracker) const override; | ||
SET_MEMORY_INFO_NAME(AsyncContextFrame) | ||
SET_SELF_SIZE(AsyncContextFrame) | ||
|
||
private: | ||
v8::Global<v8::Object> parent_; | ||
v8::Global<v8::Value> key_; | ||
v8::Global<v8::Value> value_; | ||
}; | ||
|
||
} // namespace node | ||
|
||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS | ||
|
||
#endif // SRC_ASYNC_CONTEXT_FRAME_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.