Skip to content

Commit 9b16e15

Browse files
vdeturckheimMylesBorins
authored andcommitted
domain: re-implement domain over async_hook
Domain core module has been re-implemented over async_hook. PR-URL: #16222 Reviewed-By: Andreas Madsen <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
1 parent 60423f5 commit 9b16e15

File tree

9 files changed

+49
-228
lines changed

9 files changed

+49
-228
lines changed

lib/domain.js

+44-16
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
const util = require('util');
3030
const EventEmitter = require('events');
31-
const { inherits } = util;
31+
const { createHook } = require('async_hooks');
3232

3333
// communicate with events module, but don't require that
3434
// module to have to load this one, since this module has
@@ -48,13 +48,54 @@ Object.defineProperty(process, 'domain', {
4848
}
4949
});
5050

51+
const pairing = new Map();
52+
const asyncHook = createHook({
53+
init(asyncId, type, triggerAsyncId, resource) {
54+
if (process.domain !== null && process.domain !== undefined) {
55+
// if this operation is created while in a domain, let's mark it
56+
pairing.set(asyncId, process.domain);
57+
resource.domain = process.domain;
58+
if (resource.promise !== undefined &&
59+
resource.promise instanceof Promise) {
60+
// resource.promise instanceof Promise make sure that the
61+
// promise comes from the same context
62+
// see https://github.com/nodejs/node/issues/15673
63+
resource.promise.domain = process.domain;
64+
}
65+
}
66+
},
67+
before(asyncId) {
68+
const current = pairing.get(asyncId);
69+
if (current !== undefined) { // enter domain for this cb
70+
current.enter();
71+
}
72+
},
73+
after(asyncId) {
74+
const current = pairing.get(asyncId);
75+
if (current !== undefined) { // exit domain for this cb
76+
current.exit();
77+
}
78+
},
79+
destroy(asyncId) {
80+
pairing.delete(asyncId); // cleaning up
81+
}
82+
});
83+
5184
// It's possible to enter one domain while already inside
5285
// another one. The stack is each entered domain.
5386
const stack = [];
5487
exports._stack = stack;
88+
process._setupDomainUse(stack);
89+
90+
class Domain extends EventEmitter {
5591

56-
// let the process know we're using domains
57-
const _domain_flag = process._setupDomainUse(_domain, stack);
92+
constructor() {
93+
super();
94+
95+
this.members = [];
96+
asyncHook.enable();
97+
}
98+
}
5899

59100
exports.Domain = Domain;
60101

@@ -64,19 +105,8 @@ exports.create = exports.createDomain = function() {
64105

65106
// the active domain is always the one that we're currently in.
66107
exports.active = null;
67-
68-
69-
inherits(Domain, EventEmitter);
70-
71-
function Domain() {
72-
EventEmitter.call(this);
73-
74-
this.members = [];
75-
}
76-
77108
Domain.prototype.members = undefined;
78109

79-
80110
// Called by process._fatalException in case an error was thrown.
81111
Domain.prototype._errorHandler = function _errorHandler(er) {
82112
var caught = false;
@@ -155,7 +185,6 @@ Domain.prototype.enter = function() {
155185
// to push it onto the stack so that we can pop it later.
156186
exports.active = process.domain = this;
157187
stack.push(this);
158-
_domain_flag[0] = stack.length;
159188
};
160189

161190

@@ -166,7 +195,6 @@ Domain.prototype.exit = function() {
166195

167196
// exit all domains until this one.
168197
stack.splice(index);
169-
_domain_flag[0] = stack.length;
170198

171199
exports.active = stack[stack.length - 1];
172200
process.domain = exports.active;

lib/internal/process/next_tick.js

-41
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ function setupNextTick() {
7373
process.nextTick = nextTick;
7474
// Needs to be accessible from beyond this scope.
7575
process._tickCallback = _tickCallback;
76-
process._tickDomainCallback = _tickDomainCallback;
7776

7877
// Set the nextTick() function for internal usage.
7978
exports.nextTick = internalNextTick;
@@ -190,46 +189,6 @@ function setupNextTick() {
190189
} while (tickInfo[kLength] !== 0);
191190
}
192191

193-
function _tickDomainCallback() {
194-
do {
195-
while (tickInfo[kIndex] < tickInfo[kLength]) {
196-
++tickInfo[kIndex];
197-
const tock = nextTickQueue.shift();
198-
const callback = tock.callback;
199-
const domain = tock.domain;
200-
const args = tock.args;
201-
if (domain)
202-
domain.enter();
203-
204-
// CHECK(Number.isSafeInteger(tock[async_id_symbol]))
205-
// CHECK(tock[async_id_symbol] > 0)
206-
// CHECK(Number.isSafeInteger(tock[trigger_async_id_symbol]))
207-
// CHECK(tock[trigger_async_id_symbol] > 0)
208-
209-
emitBefore(tock[async_id_symbol], tock[trigger_async_id_symbol]);
210-
// TODO(trevnorris): See comment in _tickCallback() as to why this
211-
// isn't a good solution.
212-
if (async_hook_fields[kDestroy] > 0)
213-
emitDestroy(tock[async_id_symbol]);
214-
215-
// Using separate callback execution functions allows direct
216-
// callback invocation with small numbers of arguments to avoid the
217-
// performance hit associated with using `fn.apply()`
218-
_combinedTickCallback(args, callback);
219-
220-
emitAfter(tock[async_id_symbol]);
221-
222-
if (kMaxCallbacksPerLoop < tickInfo[kIndex])
223-
tickDone();
224-
if (domain)
225-
domain.exit();
226-
}
227-
tickDone();
228-
_runMicrotasks();
229-
emitPendingUnhandledRejections();
230-
} while (tickInfo[kLength] !== 0);
231-
}
232-
233192
class TickObject {
234193
constructor(callback, args, asyncId, triggerAsyncId) {
235194
this.callback = callback;

src/cares_wrap.cc

-6
Original file line numberDiff line numberDiff line change
@@ -596,12 +596,6 @@ class QueryWrap : public AsyncWrap {
596596
QueryWrap(ChannelWrap* channel, Local<Object> req_wrap_obj)
597597
: AsyncWrap(channel->env(), req_wrap_obj, AsyncWrap::PROVIDER_QUERYWRAP),
598598
channel_(channel) {
599-
if (env()->in_domain()) {
600-
req_wrap_obj->Set(env()->domain_string(),
601-
env()->domain_array()->Get(env()->context(), 0)
602-
.ToLocalChecked());
603-
}
604-
605599
Wrap(req_wrap_obj, this);
606600

607601
// Make sure the channel object stays alive during the query lifetime.

src/env-inl.h

-26
Original file line numberDiff line numberDiff line change
@@ -200,22 +200,6 @@ inline bool Environment::AsyncCallbackScope::in_makecallback() const {
200200
return env_->makecallback_cntr_ > 1;
201201
}
202202

203-
inline Environment::DomainFlag::DomainFlag() {
204-
for (int i = 0; i < kFieldsCount; ++i) fields_[i] = 0;
205-
}
206-
207-
inline uint32_t* Environment::DomainFlag::fields() {
208-
return fields_;
209-
}
210-
211-
inline int Environment::DomainFlag::fields_count() const {
212-
return kFieldsCount;
213-
}
214-
215-
inline uint32_t Environment::DomainFlag::count() const {
216-
return fields_[kCount];
217-
}
218-
219203
inline Environment::TickInfo::TickInfo() {
220204
for (int i = 0; i < kFieldsCount; ++i)
221205
fields_[i] = 0;
@@ -347,12 +331,6 @@ inline v8::Isolate* Environment::isolate() const {
347331
return isolate_;
348332
}
349333

350-
inline bool Environment::in_domain() const {
351-
// The const_cast is okay, it doesn't violate conceptual const-ness.
352-
return using_domains() &&
353-
const_cast<Environment*>(this)->domain_flag()->count() > 0;
354-
}
355-
356334
inline Environment* Environment::from_immediate_check_handle(
357335
uv_check_t* handle) {
358336
return ContainerOf(&Environment::immediate_check_handle_, handle);
@@ -393,10 +371,6 @@ inline Environment::AsyncHooks* Environment::async_hooks() {
393371
return &async_hooks_;
394372
}
395373

396-
inline Environment::DomainFlag* Environment::domain_flag() {
397-
return &domain_flag_;
398-
}
399-
400374
inline Environment::TickInfo* Environment::tick_info() {
401375
return &tick_info_;
402376
}

src/env.h

-24
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,6 @@ class ModuleWrap;
309309
V(internal_binding_cache_object, v8::Object) \
310310
V(buffer_prototype_object, v8::Object) \
311311
V(context, v8::Context) \
312-
V(domain_array, v8::Array) \
313312
V(domains_stack_array, v8::Array) \
314313
V(http2ping_constructor_template, v8::ObjectTemplate) \
315314
V(http2stream_constructor_template, v8::ObjectTemplate) \
@@ -474,26 +473,6 @@ class Environment {
474473
DISALLOW_COPY_AND_ASSIGN(AsyncCallbackScope);
475474
};
476475

477-
class DomainFlag {
478-
public:
479-
inline uint32_t* fields();
480-
inline int fields_count() const;
481-
inline uint32_t count() const;
482-
483-
private:
484-
friend class Environment; // So we can call the constructor.
485-
inline DomainFlag();
486-
487-
enum Fields {
488-
kCount,
489-
kFieldsCount
490-
};
491-
492-
uint32_t fields_[kFieldsCount];
493-
494-
DISALLOW_COPY_AND_ASSIGN(DomainFlag);
495-
};
496-
497476
class TickInfo {
498477
public:
499478
inline uint32_t* fields();
@@ -562,7 +541,6 @@ class Environment {
562541

563542
inline v8::Isolate* isolate() const;
564543
inline uv_loop_t* event_loop() const;
565-
inline bool in_domain() const;
566544
inline uint32_t watched_providers() const;
567545

568546
static inline Environment* from_immediate_check_handle(uv_check_t* handle);
@@ -579,7 +557,6 @@ class Environment {
579557
inline void FinishHandleCleanup(uv_handle_t* handle);
580558

581559
inline AsyncHooks* async_hooks();
582-
inline DomainFlag* domain_flag();
583560
inline TickInfo* tick_info();
584561
inline uint64_t timer_base() const;
585562

@@ -709,7 +686,6 @@ class Environment {
709686
uv_check_t idle_check_handle_;
710687

711688
AsyncHooks async_hooks_;
712-
DomainFlag domain_flag_;
713689
TickInfo tick_info_;
714690
const uint64_t timer_base_;
715691
bool using_domains_;

src/node.cc

+5-60
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ using v8::Number;
150150
using v8::Object;
151151
using v8::ObjectTemplate;
152152
using v8::Promise;
153-
using v8::PromiseHookType;
154153
using v8::PromiseRejectMessage;
155154
using v8::PropertyCallbackInfo;
156155
using v8::ScriptOrigin;
@@ -1178,7 +1177,6 @@ bool ShouldAbortOnUncaughtException(Isolate* isolate) {
11781177
return isEmittingTopLevelDomainError || !DomainsStackHasErrorHandler(env);
11791178
}
11801179

1181-
11821180
Local<Value> GetDomainProperty(Environment* env, Local<Object> object) {
11831181
Local<Value> domain_v =
11841182
object->GetPrivate(env->context(), env->domain_private_symbol())
@@ -1219,36 +1217,6 @@ void DomainExit(Environment* env, v8::Local<v8::Object> object) {
12191217
}
12201218
}
12211219

1222-
1223-
void DomainPromiseHook(PromiseHookType type,
1224-
Local<Promise> promise,
1225-
Local<Value> parent,
1226-
void* arg) {
1227-
Environment* env = static_cast<Environment*>(arg);
1228-
Local<Context> context = env->context();
1229-
1230-
if (type == PromiseHookType::kInit && env->in_domain()) {
1231-
Local<Value> domain_obj =
1232-
env->domain_array()->Get(context, 0).ToLocalChecked();
1233-
if (promise->CreationContext() == context) {
1234-
promise->Set(context, env->domain_string(), domain_obj).FromJust();
1235-
} else {
1236-
// Do not expose object from another context publicly in promises created
1237-
// in non-main contexts.
1238-
promise->SetPrivate(context, env->domain_private_symbol(), domain_obj)
1239-
.FromJust();
1240-
}
1241-
return;
1242-
}
1243-
1244-
if (type == PromiseHookType::kBefore) {
1245-
DomainEnter(env, promise);
1246-
} else if (type == PromiseHookType::kAfter) {
1247-
DomainExit(env, promise);
1248-
}
1249-
}
1250-
1251-
12521220
void SetupDomainUse(const FunctionCallbackInfo<Value>& args) {
12531221
Environment* env = Environment::GetCurrent(args);
12541222

@@ -1259,38 +1227,13 @@ void SetupDomainUse(const FunctionCallbackInfo<Value>& args) {
12591227
HandleScope scope(env->isolate());
12601228
Local<Object> process_object = env->process_object();
12611229

1262-
Local<String> tick_callback_function_key = env->tick_domain_cb_string();
1263-
Local<Function> tick_callback_function =
1264-
process_object->Get(tick_callback_function_key).As<Function>();
1265-
1266-
if (!tick_callback_function->IsFunction()) {
1267-
fprintf(stderr, "process._tickDomainCallback assigned to non-function\n");
1268-
ABORT();
1269-
}
1270-
1271-
process_object->Set(env->tick_callback_string(), tick_callback_function);
1272-
env->set_tick_callback_function(tick_callback_function);
1273-
12741230
CHECK(args[0]->IsArray());
1275-
env->set_domain_array(args[0].As<Array>());
1276-
1277-
CHECK(args[1]->IsArray());
1278-
env->set_domains_stack_array(args[1].As<Array>());
1231+
env->set_domains_stack_array(args[0].As<Array>());
12791232

12801233
// Do a little housekeeping.
12811234
env->process_object()->Delete(
12821235
env->context(),
12831236
FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupDomainUse")).FromJust();
1284-
1285-
uint32_t* const fields = env->domain_flag()->fields();
1286-
uint32_t const fields_count = env->domain_flag()->fields_count();
1287-
1288-
Local<ArrayBuffer> array_buffer =
1289-
ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count);
1290-
1291-
env->AddPromiseHook(DomainPromiseHook, static_cast<void*>(env));
1292-
1293-
args.GetReturnValue().Set(Uint32Array::New(array_buffer, 0, fields_count));
12941237
}
12951238

12961239

@@ -1414,7 +1357,8 @@ InternalCallbackScope::InternalCallbackScope(Environment* env,
14141357
// If you hit this assertion, you forgot to enter the v8::Context first.
14151358
CHECK_EQ(Environment::GetCurrent(env->isolate()), env);
14161359

1417-
if (env->using_domains() && !object_.IsEmpty()) {
1360+
if (asyncContext.async_id == 0 && env->using_domains() &&
1361+
!object_.IsEmpty()) {
14181362
DomainEnter(env, object_);
14191363
}
14201364

@@ -1447,7 +1391,8 @@ void InternalCallbackScope::Close() {
14471391
AsyncWrap::EmitAfter(env_, async_context_.async_id);
14481392
}
14491393

1450-
if (env_->using_domains() && !object_.IsEmpty()) {
1394+
if (async_context_.async_id == 0 && env_->using_domains() &&
1395+
!object_.IsEmpty()) {
14511396
DomainExit(env_, object_);
14521397
}
14531398

0 commit comments

Comments
 (0)