Skip to content

Commit

Permalink
Merge pull request #331 from cloudflare/jsnell/asynccontext-additiona…
Browse files Browse the repository at this point in the history
…l-tweaks
  • Loading branch information
jasnell authored Feb 19, 2023
2 parents e497feb + dbf9bc6 commit e5fa877
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 1 deletion.
12 changes: 12 additions & 0 deletions src/workerd/api/node/async-hooks.c++
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ v8::Local<v8::Value> AsyncLocalStorage::getStore(jsg::Lock& js) {
return v8::Undefined(js.v8Isolate);
}

v8::Local<v8::Function> AsyncLocalStorage::bind(jsg::Lock& js, v8::Local<v8::Function> fn) {
KJ_IF_MAYBE(frame, jsg::AsyncContextFrame::current(js)) {
return frame->wrap(js, fn);
} else {
return jsg::AsyncContextFrame::wrapRoot(js, fn);
}
}

v8::Local<v8::Function> AsyncLocalStorage::snapshot(jsg::Lock& js) {
return jsg::AsyncContextFrame::wrapSnapshot(js);
}

namespace {
kj::Maybe<jsg::Ref<jsg::AsyncContextFrame>> tryGetFrameRef(jsg::Lock& js) {
return jsg::AsyncContextFrame::current(js).map(
Expand Down
17 changes: 17 additions & 0 deletions src/workerd/api/node/async-hooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ class AsyncLocalStorage final: public jsg::Object {

v8::Local<v8::Value> getStore(jsg::Lock& js);

static v8::Local<v8::Function> bind(jsg::Lock& js, v8::Local<v8::Function> fn);
// Binds the given function to the current async context frame such that
// whenever the function is called, the bound frame is entered.

static v8::Local<v8::Function> snapshot(jsg::Lock& js);
// Returns a function bound to the current async context frame that calls
// the function passed to it as the only argument within that frame.
// Equivalent to AsyncLocalStorage.bind((cb, ...args) => cb(...args)).

inline void enterWith(jsg::Lock&, v8::Local<v8::Value>) {
KJ_UNIMPLEMENTED("asyncLocalStorage.enterWith() is not implemented");
}
Expand All @@ -60,6 +69,8 @@ class AsyncLocalStorage final: public jsg::Object {
JSG_METHOD(getStore);
JSG_METHOD(enterWith);
JSG_METHOD(disable);
JSG_STATIC_METHOD(bind);
JSG_STATIC_METHOD(snapshot);

if (flags.getNodeJsCompat()) {
JSG_TS_OVERRIDE(AsyncLocalStorage<T> {
Expand All @@ -68,6 +79,8 @@ class AsyncLocalStorage final: public jsg::Object {
exit<R, TArgs extends any[]>(callback: (...args: TArgs) => R, ...args: TArgs): R;
disable(): void;
enterWith(store: T): void;
static bind<Func extends (...args: any[]) => any>(fn: Func): Func;
static snapshot<R, TArgs extends any[]>() : ((...args: TArgs) => R, ...args: TArgs) => R;
});
} else {
JSG_TS_OVERRIDE(type AsyncLocalStorage = never);
Expand All @@ -80,6 +93,10 @@ class AsyncLocalStorage final: public jsg::Object {


class AsyncResource final: public jsg::Object {
// Note: The AsyncResource class is provided for Node.js backwards compatibility.
// The class can be replaced entirely for async context tracking using the
// AsyncLocalStorage.bind() and AsyncLocalStorage.snapshot() APIs.
//
// The AsyncResource class is an object that user code can use to define its own
// async resources for the purpose of storage context propagation. For instance,
// let's imagine that we have an EventTarget and we want to register two event listeners
Expand Down
23 changes: 22 additions & 1 deletion src/workerd/jsg/async-context.c++
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,28 @@ v8::Local<v8::Function> AsyncContextFrame::wrap(
return wrap(js, fn.getHandle(js), thisArg);
}

v8::Local<v8::Function> AsyncContextFrame::wrapSnapshot(Lock& js) {
auto isolate = js.v8Isolate;
auto context = isolate->GetCurrentContext();

return js.wrapReturningFunction(context, JSG_VISITABLE_LAMBDA(
(frame = AsyncContextFrame::currentRef(js)),
(frame),
(Lock& js, const v8::FunctionCallbackInfo<v8::Value>& args) {
auto context = js.v8Isolate->GetCurrentContext();
JSG_REQUIRE(args[0]->IsFunction(), TypeError, "The first argument must be a function");
auto fn = args[0].As<v8::Function>();
kj::Vector<v8::Local<v8::Value>> argv(args.Length() - 1);
for (int n = 1; n < args.Length(); n++) {
argv.add(args[n]);
}

AsyncContextFrame::Scope scope(js, frame);
return check(fn->Call(context, context->Global(), argv.size(), argv.begin()));
}
));
}

v8::Local<v8::Function> AsyncContextFrame::wrap(
Lock& js,
v8::Local<v8::Function> fn,
Expand All @@ -93,7 +115,6 @@ v8::Local<v8::Function> AsyncContextFrame::wrap(
}

AsyncContextFrame::Scope scope(js, *frame.get());
v8::Local<v8::Value> result;
return check(function->Call(context, thisArg.getHandle(js), args.Length(), argv.begin()));
}));
}
Expand Down
5 changes: 5 additions & 0 deletions src/workerd/jsg/async-context.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ class AsyncContextFrame final: public Wrappable {
// Wraps the given JavaScript function such that whenever the wrapper function is called,
// the root AsyncContextFrame will be entered.

static v8::Local<v8::Function> wrapSnapshot(Lock& js);
// Returns a function that captures the current frame and calls the function passed
// in as an argument within that captured context. Equivalent to wrapping a function
// with the signature (cb, ...args) => cb(...args).

v8::Local<v8::Function> wrap(
Lock& js, V8Ref<v8::Function>& fn,
kj::Maybe<v8::Local<v8::Value>> thisArg = nullptr);
Expand Down

0 comments on commit e5fa877

Please sign in to comment.