Skip to content

Commit cb291d5

Browse files
trevnorrisMyles Borins
authored and
Myles Borins
committed
src: add AsyncCallbackScope
Add a scope that will allow MakeCallback to know whether or not it's currently running. This will prevent nextTickQueue and the MicrotaskQueue from being processed recursively. It is also required to wrap the bootloading stage since it doesn't run within a MakeCallback. Ref: nodejs/node-v0.x-archive#9245 Ref: #7048 PR-URL: #4507 Reviewed-By: Fedor Indutny <[email protected]>
1 parent 2eb097f commit cb291d5

7 files changed

+45
-17
lines changed

src/async-wrap.cc

+3-5
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
184184
Local<Object> domain;
185185
bool has_domain = false;
186186

187+
Environment::AsyncCallbackScope callback_scope(env());
188+
187189
if (env()->using_domains()) {
188190
Local<Value> domain_v = context->Get(env()->domain_string());
189191
has_domain = domain_v->IsObject();
@@ -236,7 +238,7 @@ Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
236238

237239
Environment::TickInfo* tick_info = env()->tick_info();
238240

239-
if (tick_info->in_tick()) {
241+
if (callback_scope.in_makecallback()) {
240242
return ret;
241243
}
242244

@@ -249,12 +251,8 @@ Local<Value> AsyncWrap::MakeCallback(const Local<Function> cb,
249251
return ret;
250252
}
251253

252-
tick_info->set_in_tick(true);
253-
254254
env()->tick_callback_function()->Call(process, 0, nullptr);
255255

256-
tick_info->set_in_tick(false);
257-
258256
if (try_catch.HasCaught()) {
259257
tick_info->set_last_threw(true);
260258
return Undefined(env()->isolate());

src/env-inl.h

+15
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,20 @@ inline void Environment::AsyncHooks::set_enable_callbacks(uint32_t flag) {
8888
fields_[kEnableCallbacks] = flag;
8989
}
9090

91+
inline Environment::AsyncCallbackScope::AsyncCallbackScope(Environment* env)
92+
: env_(env) {
93+
env_->makecallback_cntr_++;
94+
}
95+
96+
inline Environment::AsyncCallbackScope::~AsyncCallbackScope() {
97+
env_->makecallback_cntr_--;
98+
CHECK_GE(env_->makecallback_cntr_, 0);
99+
}
100+
101+
inline bool Environment::AsyncCallbackScope::in_makecallback() {
102+
return env_->makecallback_cntr_ > 1;
103+
}
104+
91105
inline Environment::DomainFlag::DomainFlag() {
92106
for (int i = 0; i < kFieldsCount; ++i) fields_[i] = 0;
93107
}
@@ -210,6 +224,7 @@ inline Environment::Environment(v8::Local<v8::Context> context,
210224
using_domains_(false),
211225
printed_error_(false),
212226
trace_sync_io_(false),
227+
makecallback_cntr_(0),
213228
async_wrap_uid_(0),
214229
debugger_agent_(this),
215230
http_parser_buffer_(nullptr),

src/env.cc

+2-6
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ void Environment::PrintSyncTrace() const {
5757
}
5858

5959

60-
bool Environment::KickNextTick() {
60+
bool Environment::KickNextTick(Environment::AsyncCallbackScope* scope) {
6161
TickInfo* info = tick_info();
6262

63-
if (info->in_tick()) {
63+
if (scope->in_makecallback()) {
6464
return true;
6565
}
6666

@@ -73,15 +73,11 @@ bool Environment::KickNextTick() {
7373
return true;
7474
}
7575

76-
info->set_in_tick(true);
77-
7876
// process nextTicks after call
7977
TryCatch try_catch;
8078
try_catch.SetVerbose(true);
8179
tick_callback_function()->Call(process_object(), 0, nullptr);
8280

83-
info->set_in_tick(false);
84-
8581
if (try_catch.HasCaught()) {
8682
info->set_last_threw(true);
8783
return false;

src/env.h

+15-1
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,19 @@ class Environment {
294294
DISALLOW_COPY_AND_ASSIGN(AsyncHooks);
295295
};
296296

297+
class AsyncCallbackScope {
298+
public:
299+
explicit AsyncCallbackScope(Environment* env);
300+
~AsyncCallbackScope();
301+
302+
inline bool in_makecallback();
303+
304+
private:
305+
Environment* env_;
306+
307+
DISALLOW_COPY_AND_ASSIGN(AsyncCallbackScope);
308+
};
309+
297310
class DomainFlag {
298311
public:
299312
inline uint32_t* fields();
@@ -446,7 +459,7 @@ class Environment {
446459

447460
inline int64_t get_async_wrap_uid();
448461

449-
bool KickNextTick();
462+
bool KickNextTick(AsyncCallbackScope* scope);
450463

451464
inline uint32_t* heap_statistics_buffer() const;
452465
inline void set_heap_statistics_buffer(uint32_t* pointer);
@@ -541,6 +554,7 @@ class Environment {
541554
bool using_domains_;
542555
bool printed_error_;
543556
bool trace_sync_io_;
557+
size_t makecallback_cntr_;
544558
int64_t async_wrap_uid_;
545559
debugger::Agent debugger_agent_;
546560

src/node.cc

+7-2
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,8 @@ Local<Value> MakeCallback(Environment* env,
11321132
bool ran_init_callback = false;
11331133
bool has_domain = false;
11341134

1135+
Environment::AsyncCallbackScope callback_scope(env);
1136+
11351137
// TODO(trevnorris): Adding "_asyncQueue" to the "this" in the init callback
11361138
// is a horrible way to detect usage. Rethink how detection should happen.
11371139
if (recv->IsObject()) {
@@ -1192,7 +1194,7 @@ Local<Value> MakeCallback(Environment* env,
11921194
}
11931195
}
11941196

1195-
if (!env->KickNextTick())
1197+
if (!env->KickNextTick(&callback_scope))
11961198
return Undefined(env->isolate());
11971199

11981200
return ret;
@@ -4100,7 +4102,10 @@ static void StartNodeInstance(void* arg) {
41004102
if (instance_data->use_debug_agent())
41014103
StartDebug(env, debug_wait_connect);
41024104

4103-
LoadEnvironment(env);
4105+
{
4106+
Environment::AsyncCallbackScope callback_scope(env);
4107+
LoadEnvironment(env);
4108+
}
41044109

41054110
env->set_trace_sync_io(trace_sync_io);
41064111

src/node_http_parser.cc

+3-1
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,8 @@ class Parser : public BaseObject {
579579
if (!cb->IsFunction())
580580
return;
581581

582+
Environment::AsyncCallbackScope callback_scope(parser->env());
583+
582584
// Hooks for GetCurrentBuffer
583585
parser->current_buffer_len_ = nread;
584586
parser->current_buffer_data_ = buf->base;
@@ -588,7 +590,7 @@ class Parser : public BaseObject {
588590
parser->current_buffer_len_ = 0;
589591
parser->current_buffer_data_ = nullptr;
590592

591-
parser->env()->KickNextTick();
593+
parser->env()->KickNextTick(&callback_scope);
592594
}
593595

594596

src/node_internals.h

-2
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ v8::Local<v8::Value> MakeCallback(Environment* env,
6969
int argc = 0,
7070
v8::Local<v8::Value>* argv = nullptr);
7171

72-
bool KickNextTick();
73-
7472
// Convert a struct sockaddr to a { address: '1.2.3.4', port: 1234 } JS object.
7573
// Sets address and port properties on the info object and returns it.
7674
// If |info| is omitted, a new object is returned.

0 commit comments

Comments
 (0)