| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 1fc8307 commit d0f1ff5
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -573,6 +573,8 @@ module.exports = { | |||
| 573 | 573 | emitBefore: emitBeforeScript, | |
| 574 | 574 | emitAfter: emitAfterScript, | |
| 575 | 575 | emitDestroy: emitDestroyScript, | |
| 576 | + pushAsyncContext, | ||
| 577 | + popAsyncContext, | ||
| 576 | 578 | registerDestroyHook, | |
| 577 | 579 | useDomainTrampoline, | |
| 578 | 580 | nativeHooks: { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -24,6 +24,12 @@ const { | |||
| 24 | 24 | triggerUncaughtException | |
| 25 | 25 | } = internalBinding('errors'); | |
| 26 | 26 | ||
| 27 | + const { | ||
| 28 | + pushAsyncContext, | ||
| 29 | + popAsyncContext, | ||
| 30 | + } = require('internal/async_hooks'); | ||
| 31 | + const async_hooks = require('async_hooks'); | ||
| 32 | + | ||
| 27 | 33 | // *Must* match Environment::TickInfo::Fields in src/env.h. | |
| 28 | 34 | const kHasRejectionToWarn = 1; | |
| 29 | 35 | ||
@@ -116,11 +122,28 @@ function resolveError(type, promise, reason) { | |||
| 116 | 122 | } | |
| 117 | 123 | ||
| 118 | 124 | function unhandledRejection(promise, reason) { | |
| 125 | + const asyncId = async_hooks.executionAsyncId(); | ||
| 126 | + const triggerAsyncId = async_hooks.triggerAsyncId(); | ||
| 127 | + const resource = promise; | ||
| 128 | + | ||
| 129 | + const emit = (reason, promise, promiseInfo) => { | ||
| 130 | + try { | ||
| 131 | + pushAsyncContext(asyncId, triggerAsyncId, resource); | ||
| 132 | + if (promiseInfo.domain) { | ||
| 133 | + return promiseInfo.domain.emit('error', reason); | ||
| 134 | + } | ||
| 135 | + return process.emit('unhandledRejection', reason, promise); | ||
| 136 | + } finally { | ||
| 137 | + popAsyncContext(asyncId); | ||
| 138 | + } | ||
| 139 | + }; | ||
| 140 | + | ||
| 119 | 141 | maybeUnhandledPromises.set(promise, { | |
| 120 | 142 | reason, | |
| 121 | 143 | uid: ++lastPromiseId, | |
| 122 | 144 | warned: false, | |
| 123 | - domain: process.domain | ||
| 145 | + domain: process.domain, | ||
| 146 | + emit | ||
| 124 | 147 | }); | |
| 125 | 148 | // This causes the promise to be referenced at least for one tick. | |
| 126 | 149 | ArrayPrototypePush(pendingUnhandledRejections, promise); | |
@@ -194,13 +217,8 @@ function processPromiseRejections() { | |||
| 194 | 217 | continue; | |
| 195 | 218 | } | |
| 196 | 219 | promiseInfo.warned = true; | |
| 197 | - const { reason, uid } = promiseInfo; | ||
| 198 | - function emit(reason, promise, promiseInfo) { | ||
| 199 | - if (promiseInfo.domain) { | ||
| 200 | - return promiseInfo.domain.emit('error', reason); | ||
| 201 | - } | ||
| 202 | - return process.emit('unhandledRejection', reason, promise); | ||
| 203 | - } | ||
| 220 | + const { reason, uid, emit } = promiseInfo; | ||
| 221 | + | ||
| 204 | 222 | switch (unhandledRejectionsMode) { | |
| 205 | 223 | case kStrictUnhandledRejections: { | |
| 206 | 224 | const err = reason instanceof Error ? | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,3 +1,4 @@ | |||
| 1 | + #include "async_wrap.h" | ||
| 1 | 2 | #include "env-inl.h" | |
| 2 | 3 | #include "node.h" | |
| 3 | 4 | #include "node_errors.h" | |
@@ -16,18 +17,54 @@ using v8::Context; | |||
| 16 | 17 | using v8::Function; | |
| 17 | 18 | using v8::FunctionCallbackInfo; | |
| 18 | 19 | using v8::Isolate; | |
| 20 | + using v8::Just; | ||
| 19 | 21 | using v8::kPromiseHandlerAddedAfterReject; | |
| 20 | 22 | using v8::kPromiseRejectAfterResolved; | |
| 21 | 23 | using v8::kPromiseRejectWithNoHandler; | |
| 22 | 24 | using v8::kPromiseResolveAfterResolved; | |
| 23 | 25 | using v8::Local; | |
| 26 | + using v8::Maybe; | ||
| 24 | 27 | using v8::Number; | |
| 25 | 28 | using v8::Object; | |
| 26 | 29 | using v8::Promise; | |
| 27 | 30 | using v8::PromiseRejectEvent; | |
| 28 | 31 | using v8::PromiseRejectMessage; | |
| 29 | 32 | using v8::Value; | |
| 30 | 33 | ||
| 34 | + static Maybe<double> GetAssignedPromiseAsyncId(Environment* env, | ||
| 35 | + Local<Promise> promise, | ||
| 36 | + Local<Value> id_symbol) { | ||
| 37 | + Local<Value> maybe_async_id; | ||
| 38 | + if (!promise->Get(env->context(), id_symbol).ToLocal(&maybe_async_id)) { | ||
| 39 | + return v8::Just(AsyncWrap::kInvalidAsyncId); | ||
| 40 | + } | ||
| 41 | + return maybe_async_id->IsNumber() | ||
| 42 | + ? maybe_async_id->NumberValue(env->context()) | ||
| 43 | + : v8::Just(AsyncWrap::kInvalidAsyncId); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + static Maybe<double> GetAssignedPromiseWrapAsyncId(Environment* env, | ||
| 47 | + Local<Promise> promise, | ||
| 48 | + Local<Value> id_symbol) { | ||
| 49 | + // This check is imperfect. If the internal field is set, it should | ||
| 50 | + // be an object. If it's not, we just ignore it. Ideally v8 would | ||
| 51 | + // have had GetInternalField returning a MaybeLocal but this works | ||
| 52 | + // for now. | ||
| 53 | + Local<Value> promiseWrap = promise->GetInternalField(0); | ||
| 54 | + if (promiseWrap->IsObject()) { | ||
| 55 | + Local<Value> maybe_async_id; | ||
| 56 | + if (!promiseWrap.As<Object>()->Get(env->context(), id_symbol) | ||
| 57 | + .ToLocal(&maybe_async_id)) { | ||
| 58 | + return v8::Just(AsyncWrap::kInvalidAsyncId); | ||
| 59 | + } | ||
| 60 | + return maybe_async_id->IsNumber() | ||
| 61 | + ? maybe_async_id->NumberValue(env->context()) | ||
| 62 | + : v8::Just(AsyncWrap::kInvalidAsyncId); | ||
| 63 | + } else { | ||
| 64 | + return v8::Just(AsyncWrap::kInvalidAsyncId); | ||
| 65 | + } | ||
| 66 | + } | ||
| 67 | + | ||
| 31 | 68 | void PromiseRejectCallback(PromiseRejectMessage message) { | |
| 32 | 69 | static std::atomic<uint64_t> unhandledRejections{0}; | |
| 33 | 70 | static std::atomic<uint64_t> rejectionsHandledAfter{0}; | |
@@ -76,12 +113,46 @@ void PromiseRejectCallback(PromiseRejectMessage message) { | |||
| 76 | 113 | ||
| 77 | 114 | Local<Value> args[] = { type, promise, value }; | |
| 78 | 115 | ||
| 79 | - // V8 does not expect this callback to have a scheduled exceptions once it | ||
| 80 | - // returns, so we print them out in a best effort to do something about it | ||
| 81 | - // without failing silently and without crashing the process. | ||
| 116 | + double async_id = AsyncWrap::kInvalidAsyncId; | ||
| 117 | + double trigger_async_id = AsyncWrap::kInvalidAsyncId; | ||
| 82 | 118 | TryCatchScope try_catch(env); | |
| 119 | + | ||
| 120 | + if (!GetAssignedPromiseAsyncId(env, promise, env->async_id_symbol()) | ||
| 121 | + .To(&async_id)) return; | ||
| 122 | + if (!GetAssignedPromiseAsyncId(env, promise, env->trigger_async_id_symbol()) | ||
| 123 | + .To(&trigger_async_id)) return; | ||
| 124 | + | ||
| 125 | + if (async_id == AsyncWrap::kInvalidAsyncId && | ||
| 126 | + trigger_async_id == AsyncWrap::kInvalidAsyncId) { | ||
| 127 | + // That means that promise might be a PromiseWrap, so we'll | ||
| 128 | + // check there as well. | ||
| 129 | + if (!GetAssignedPromiseWrapAsyncId(env, promise, env->async_id_symbol()) | ||
| 130 | + .To(&async_id)) return; | ||
| 131 | + if (!GetAssignedPromiseWrapAsyncId( | ||
| 132 | + env, promise, env->trigger_async_id_symbol()) | ||
| 133 | + .To(&trigger_async_id)) return; | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + if (async_id != AsyncWrap::kInvalidAsyncId && | ||
| 137 | + trigger_async_id != AsyncWrap::kInvalidAsyncId) { | ||
| 138 | + env->async_hooks()->push_async_context( | ||
| 139 | + async_id, trigger_async_id, promise); | ||
| 140 | + } | ||
| 141 | + | ||
| 83 | 142 | USE(callback->Call( | |
| 84 | 143 | env->context(), Undefined(isolate), arraysize(args), args)); | |
| 144 | + | ||
| 145 | + if (async_id != AsyncWrap::kInvalidAsyncId && | ||
| 146 | + trigger_async_id != AsyncWrap::kInvalidAsyncId && | ||
| 147 | + env->execution_async_id() == async_id) { | ||
| 148 | + // This condition might not be true if async_hooks was enabled during | ||
| 149 | + // the promise callback execution. | ||
| 150 | + env->async_hooks()->pop_async_context(async_id); | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + // V8 does not expect this callback to have a scheduled exceptions once it | ||
| 154 | + // returns, so we print them out in a best effort to do something about it | ||
| 155 | + // without failing silently and without crashing the process. | ||
| 85 | 156 | if (try_catch.HasCaught() && !try_catch.HasTerminated()) { | |
| 86 | 157 | fprintf(stderr, "Exception in PromiseRejectCallback:\n"); | |
| 87 | 158 | PrintCaughtException(isolate, env->context(), try_catch); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,27 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common'); | ||
| 4 | + | ||
| 5 | + const assert = require('assert'); | ||
| 6 | + const initHooks = require('./init-hooks'); | ||
| 7 | + const async_hooks = require('async_hooks'); | ||
| 8 | + | ||
| 9 | + if (!common.isMainThread) | ||
| 10 | + common.skip('Worker bootstrapping works differently -> different async IDs'); | ||
| 11 | + | ||
| 12 | + const promiseAsyncIds = []; | ||
| 13 | + const hooks = initHooks({ | ||
| 14 | + oninit(asyncId, type) { | ||
| 15 | + if (type === 'PROMISE') { | ||
| 16 | + promiseAsyncIds.push(asyncId); | ||
| 17 | + } | ||
| 18 | + } | ||
| 19 | + }); | ||
| 20 | + | ||
| 21 | + hooks.enable(); | ||
| 22 | + Promise.reject(); | ||
| 23 | + | ||
| 24 | + process.on('unhandledRejection', common.mustCall(() => { | ||
| 25 | + assert.strictEqual(promiseAsyncIds.length, 1); | ||
| 26 | + assert.strictEqual(async_hooks.executionAsyncId(), promiseAsyncIds[0]); | ||
| 27 | + })); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -102,6 +102,7 @@ const expectedModules = new Set([ | |||
| 102 | 102 | 'NativeModule internal/worker/io', | |
| 103 | 103 | 'NativeModule internal/worker/js_transferable', | |
| 104 | 104 | 'NativeModule internal/blob', | |
| 105 | + 'NativeModule async_hooks', | ||
| 105 | 106 | 'NativeModule path', | |
| 106 | 107 | 'NativeModule stream', | |
| 107 | 108 | 'NativeModule timers', | |
| Back | FazBrowse Home | New Git URL |
0 commit comments