You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The program below causes a crash in node (both mac OS w/ node 5.6 & Win Server 2012 R2 with node 5.9).
'use strict';varasyncWrap=process.binding('async_wrap');functionsetupHooks(){asyncWrap.disable();asyncWrap.setupHooks(functioninit(){asyncWrap.disable();console.log('hello from init');asyncWrap.enable();},functionbefore(){},functionafter(){},functiondestroy(){});asyncWrap.enable();}functiondoSomethingAsync(){// comment out line below to prevent crashconsole.log('setting timeout');setTimeout(functiont1(){console.log('timeout fired');},1000);}// uncomment line below to prevent crash//doSomethingAsync();setupHooks();doSomethingAsync();
The stack of the above call (on windows) is the following:
node.exe!v8::internal::LoadIC::Load(v8::internal::Handlev8::internal::Object object, v8::internal::Handlev8::internal::Name name) Line 680 C++
node.exe!v8::internal::__RT_impl_Runtime_LoadIC_Miss(v8::internal::Arguments args, v8::internal::Isolate * isolate) Line 2389 C++
node.exe!v8::internal::Runtime_LoadIC_Miss(int args_length, v8::internal::Object * * args_object, v8::internal::Isolate * isolate) Line 2371 C++
000001bcc860b61f() Unknown
000002f100000004() Unknown
-- many similar frames omitted--
Any help/suggestions appreciated. It's odd that the crash is prevented by calling doSomethingAsync() at least once before calling setupHooks() and doSomethingAsync() a second time. Also, crash is prevented by removing the first console.log statement in doSomethingAsync().
Update: I debugged through this a bit more, and here's what seems to be happening.
This console.log statement in above code triggers a NativeModule.load('console') call:
functiondoSomethingAsync(){// comment out line below to prevent crashconsole.log('setting timeout');
...
NativeModule.load implementation first caches the console module, and then compiles it. (Node.js, lines 905-906)
During compilation, console module (line 100) will new up the default Console instance. This triggers a call to new tty.WriteStream(). which calls net.Socket.call(...) (tty.js, line 45). This last call triggers the call to the registered async init callback.
The async-init callback also has a dep on console. I believe (but haven't verified) that it is picking up the partially compiled console module, and failing because certain types are undefined.
Net of the above seems to be a cyclic dependency that occurs when loading some module X triggers an async operation and the async-init handler has a dep on that module X.
Are there specific things that can be done to fix this?
You can use Asyncwrap more node#5756 if you want to see the error message instead of just node::AsyncWrap::AsyncWrap init hook threw.
Yes the first console.log() will cause other handle objects to be created. But I'm not sure why that would call the init hook when asyncWrap is disabled.
If it is just a matter of debugging, you can use a sync version of console.log() such as process._rawDebug('message') or fs.writeSync(1, 'message'). Even if asyncWrap is enabled that won't create a recursive loop.
•Yes the first console.log() will cause other handle objects to be created. But I'm not sure why that would call the init hook when asyncWrap is disabled.
Thanks Andreas. There's effectively a cyclic dependency that occurs when loading a module triggers an AsyncWrap construction (before the module's compilation is completed), the AsyncWrap construction calls the async-wrap init() handler, and the init() handler, having a dep on the (partially compiled) module, attempts to use it. This causes exceptions (probably non-deterministic depending on which parts of the module have been compiled), and code calling the init() handler errors out with a fatal error.
The question I have is what is the right fix in the runtime? E.g., can this situation of using a partially-compiled module be detected and "failed fast" with an appropriate error message? Or, is there a more general work-around?
(I understand that I can use process._rawDebug and this goes away (or a require('console') before my handlers are hooked in), but these don't address the root cause.)
Though excluding the conditional does cause the application to error out because the console module hasn't been loaded yet. Are you suggesting we preemtively load the console module so this doesn't happen?
Are you suggesting we preemtively load the console module so this doesn't happen?
I wasn't suggesting that, but that may not be a bad idea, given that console is widely used for debugging and can easily trigger this behavior.
That said, the root cause is more general. Namely, anytime the load of a module X triggers an async operation, and the async init() handler, also tries to load module X, then async init() handler will pick up the partially compiled module. I haven't tried it, but you should be able to repro this with a something other than console.
My question here is "what is the right fix"? e.g.,
is there some way to hoist explicit & implicit loads out of an init handler? Or is that way too complicated?
Is there some way to detect the attempted usage of partially compiled module and fail fast with an appropriate error message?
is there some way to run the handlers in a different "context" such that they don't see a partially compiled module?
Activity
mike-kaufman commentedon Mar 21, 2016
Update: I debugged through this a bit more, and here's what seems to be happening.
console.logstatement in above code triggers aNativeModule.load('console')call:new tty.WriteStream().which callsnet.Socket.call(...)(tty.js, line 45). This last call triggers the call to the registered async init callback.Net of the above seems to be a cyclic dependency that occurs when loading some module X triggers an async operation and the async-init handler has a dep on that module X.
Are there specific things that can be done to fix this?
Thanks,
Mike
AndreasMadsen commentedon Mar 22, 2016
node::AsyncWrap::AsyncWrap init hook threw.console.log()will cause other handle objects to be created. But I'm not sure why that would call theinithook when asyncWrap is disabled.console.log()such asprocess._rawDebug('message')orfs.writeSync(1, 'message'). Even if asyncWrap is enabled that won't create a recursive loop.mike-kaufman commentedon Mar 22, 2016
Thanks Andreas. There's effectively a cyclic dependency that occurs when loading a module triggers an AsyncWrap construction (before the module's compilation is completed), the AsyncWrap construction calls the async-wrap init() handler, and the init() handler, having a dep on the (partially compiled) module, attempts to use it. This causes exceptions (probably non-deterministic depending on which parts of the module have been compiled), and code calling the init() handler errors out with a fatal error.
The question I have is what is the right fix in the runtime? E.g., can this situation of using a partially-compiled module be detected and "failed fast" with an appropriate error message? Or, is there a more general work-around?
(I understand that I can use process._rawDebug and this goes away (or a require('console') before my handlers are hooked in), but these don't address the root cause.)
AndreasMadsen commentedon Mar 22, 2016
/cc @trevnorris
trevnorris commentedon Mar 23, 2016
To make sure we're on the same page, using the nodejs/node#5756 patch the following script works fine for me:
Though excluding the conditional does cause the application to error out because the
consolemodule hasn't been loaded yet. Are you suggesting we preemtively load theconsolemodule so this doesn't happen?mike-kaufman commentedon Mar 23, 2016
I wasn't suggesting that, but that may not be a bad idea, given that console is widely used for debugging and can easily trigger this behavior.
That said, the root cause is more general. Namely, anytime the load of a module X triggers an async operation, and the async init() handler, also tries to load module X, then async init() handler will pick up the partially compiled module. I haven't tried it, but you should be able to repro this with a something other than console.
My question here is "what is the right fix"? e.g.,
joshgav commentedon Sep 21, 2016
closing old AsyncWrap issues, please start a new thread if appropriate