11'use strict' ; 
22
33const  async_wrap  =  process . binding ( 'async_wrap' ) ; 
4- /* Both these arrays are used to communicate between JS and C++ with as little 
5-  * overhead as possible. 
4+ /* async_hook_fields is a Uint32Array wrapping the uint32_t array of 
5+  * Environment::AsyncHooks::fields_[]. Each index tracks the number of active 
6+  * hooks for each type. 
67 * 
7-  * async_hook_fields is a Uint32Array() that communicates the number of each 
8-  * type of active hooks of each type and wraps the uin32_t array of 
9-  * node::Environment::AsyncHooks::fields_. 
10-  * 
11-  * async_uid_fields is a Float64Array() that contains the async/trigger ids for 
12-  * several operations. These fields are as follows: 
13-  *   kCurrentAsyncId: The async id of the current execution stack. 
14-  *   kCurrentTriggerId: The trigger id of the current execution stack. 
15-  *   kAsyncUidCntr: Counter that tracks the unique ids given to new resources. 
16-  *   kInitTriggerId: Written to just before creating a new resource, so the 
17-  *    constructor knows what other resource is responsible for its init(). 
18-  *    Used this way so the trigger id doesn't need to be passed to every 
19-  *    resource's constructor. 
8+  * async_uid_fields is a Float64Array wrapping the double array of 
9+  * Environment::AsyncHooks::uid_fields_[]. Each index contains the ids for the 
10+  * various asynchronous states of the application. These are: 
11+  *  kCurrentAsyncId: The async_id assigned to the resource responsible for the 
12+  *    current execution stack. 
13+  *  kCurrentTriggerId: The trigger_async_id of the resource responsible for the 
14+  *    current execution stack. 
15+  *  kAsyncUidCntr: Incremental counter tracking the next assigned async_id. 
16+  *  kInitTriggerId: Written immediately before a resource's constructor that 
17+  *    sets the value of the init()'s triggerAsyncId. The order of retrieving 
18+  *    the triggerAsyncId value is passing directly to the constructor -> value 
19+  *   set in kInitTriggerId -> executionAsyncId of the current resource. 
2020 */ 
2121const  {  async_hook_fields,  async_uid_fields }  =  async_wrap ; 
22- // Used to change the state of the async id stack. 
22+ // Store the pair executionAsyncId and triggerAsyncId in a std::stack on 
23+ // Environment::AsyncHooks::ids_stack_ tracks the resource responsible for the 
24+ // current execution stack. This is unwound as each resource exits. In the case 
25+ // of a fatal exception this stack is emptied after calling each hook's after() 
26+ // callback. 
2327const  {  pushAsyncIds,  popAsyncIds }  =  async_wrap ; 
24- // Array of all AsyncHooks that will be iterated whenever an async event fires. 
25- // Using var instead of (preferably const) in order to assign 
26- // tmp_active_hooks_array if a hook is enabled/disabled during hook execution. 
27- var  active_hooks_array  =  [ ] ; 
28- // Use a counter to track whether a hook callback is currently being processed. 
29- // Used to make sure active_hooks_array isn't altered in mid execution if 
30- // another hook is added or removed. A counter is used to track nested calls. 
31- var  processing_hook  =  0 ; 
32- // Use to temporarily store and updated active_hooks_array if the user enables 
33- // or disables a hook while hooks are being processed. 
34- var  tmp_active_hooks_array  =  null ; 
35- // Keep track of the field counts held in tmp_active_hooks_array. 
36- var  tmp_async_hook_fields  =  null ; 
28+ // For performance reasons, only track Proimses when a hook is enabled. 
29+ const  {  enablePromiseHook,  disablePromiseHook }  =  async_wrap ; 
30+ // Properties in active_hooks are used to keep track of the set of hooks being 
31+ // executed in case another hook is enabled/disabled. The new set of hooks is 
32+ // then restored once the active set of hooks is finished executing. 
33+ const  active_hooks  =  { 
34+   // Array of all AsyncHooks that will be iterated whenever an async event 
35+   // fires. Using var instead of (preferably const) in order to assign 
36+   // active_hooks.tmp_array if a hook is enabled/disabled during hook 
37+   // execution. 
38+   array : [ ] , 
39+   // Use a counter to track nested calls of async hook callbacks and make sure 
40+   // the active_hooks.array isn't altered mid execution. 
41+   call_depth : 0 , 
42+   // Use to temporarily store and updated active_hooks.array if the user 
43+   // enables or disables a hook while hooks are being processed. If a hook is 
44+   // enabled() or disabled() during hook execution then the current set of 
45+   // active hooks is duplicated and set equal to active_hooks.tmp_array. Any 
46+   // subsequent changes are on the duplicated array. When all hooks have 
47+   // completed executing active_hooks.tmp_array is assigned to 
48+   // active_hooks.array. 
49+   tmp_array : null , 
50+   // Keep track of the field counts held in active_hooks.tmp_array. Because the 
51+   // async_hook_fields can't be reassigned, store each uint32 in an array that 
52+   // is written back to async_hook_fields when active_hooks.array is restored. 
53+   tmp_fields : null 
54+ } ; 
55+ 
3756
3857// Each constant tracks how many callbacks there are for any given step of 
3958// async execution. These are tracked so if the user didn't include callbacks 
@@ -42,6 +61,9 @@ const { kInit, kBefore, kAfter, kDestroy, kTotals, kCurrentAsyncId,
4261        kCurrentTriggerId,  kAsyncUidCntr, 
4362        kInitTriggerId }  =  async_wrap . constants ; 
4463
64+ // Symbols used to store the respective ids on both AsyncResource instances and 
65+ // internal resources. They will also be assigned to arbitrary objects passed 
66+ // in by the user that take place of internally constructed objects. 
4567const  {  async_id_symbol,  trigger_id_symbol }  =  async_wrap ; 
4668
4769// Used in AsyncHook and AsyncResource. 
@@ -53,6 +75,9 @@ const emitBeforeNative = emitHookFactory(before_symbol, 'emitBeforeNative');
5375const  emitAfterNative  =  emitHookFactory ( after_symbol ,  'emitAfterNative' ) ; 
5476const  emitDestroyNative  =  emitHookFactory ( destroy_symbol ,  'emitDestroyNative' ) ; 
5577
78+ // TODO(refack): move to node-config.cc 
79+ const  abort_regex  =  / ^ - - a b o r t [ _ - ] o n [ _ - ] u n c a u g h t [ _ - ] e x c e p t i o n $ / ; 
80+ 
5681// Setup the callbacks that node::AsyncWrap will call when there are hooks to 
5782// process. They use the same functions as the JS embedder API. These callbacks 
5883// are setup immediately to prevent async_wrap.setupHooks() from being hijacked 
@@ -71,7 +96,7 @@ function fatalError(e) {
7196    Error . captureStackTrace ( o ,  fatalError ) ; 
7297    process . _rawDebug ( o . stack ) ; 
7398  } 
74-   if  ( process . execArgv . some ( ( e )  =>  / ^ - - a b o r t [ _ - ] o n [ _ - ] u n c a u g h t [ _ - ] e x c e p t i o n $ / . test ( e ) ) )  { 
99+   if  ( process . execArgv . some ( ( e )  =>  abort_regex . test ( e ) ) )  { 
75100    process . abort ( ) ; 
76101  } 
77102  process . exit ( 1 ) ; 
@@ -121,7 +146,7 @@ class AsyncHook {
121146    hooks_array . push ( this ) ; 
122147
123148    if  ( prev_kTotals  ===  0  &&  hook_fields [ kTotals ]  >  0 ) 
124-       async_wrap . enablePromiseHook ( ) ; 
149+       enablePromiseHook ( ) ; 
125150
126151    return  this ; 
127152  } 
@@ -143,49 +168,49 @@ class AsyncHook {
143168    hooks_array . splice ( index ,  1 ) ; 
144169
145170    if  ( prev_kTotals  >  0  &&  hook_fields [ kTotals ]  ===  0 ) 
146-       async_wrap . disablePromiseHook ( ) ; 
171+       disablePromiseHook ( ) ; 
147172
148173    return  this ; 
149174  } 
150175} 
151176
152177
153178function  getHookArrays ( )  { 
154-   if  ( processing_hook  ===  0 ) 
155-     return  [ active_hooks_array ,  async_hook_fields ] ; 
179+   if  ( active_hooks . call_depth  ===  0 ) 
180+     return  [ active_hooks . array ,  async_hook_fields ] ; 
156181  // If this hook is being enabled while in the middle of processing the array 
157182  // of currently active hooks then duplicate the current set of active hooks 
158183  // and store this there. This shouldn't fire until the next time hooks are 
159184  // processed. 
160-   if  ( tmp_active_hooks_array  ===  null ) 
185+   if  ( active_hooks . tmp_array  ===  null ) 
161186    storeActiveHooks ( ) ; 
162-   return  [ tmp_active_hooks_array ,   tmp_async_hook_fields ] ; 
187+   return  [ active_hooks . tmp_array ,   active_hooks . tmp_fields ] ; 
163188} 
164189
165190
166191function  storeActiveHooks ( )  { 
167-   tmp_active_hooks_array  =  active_hooks_array . slice ( ) ; 
192+   active_hooks . tmp_array  =  active_hooks . array . slice ( ) ; 
168193  // Don't want to make the assumption that kInit to kDestroy are indexes 0 to 
169194  // 4. So do this the long way. 
170-   tmp_async_hook_fields  =  [ ] ; 
171-   tmp_async_hook_fields [ kInit ]  =  async_hook_fields [ kInit ] ; 
172-   tmp_async_hook_fields [ kBefore ]  =  async_hook_fields [ kBefore ] ; 
173-   tmp_async_hook_fields [ kAfter ]  =  async_hook_fields [ kAfter ] ; 
174-   tmp_async_hook_fields [ kDestroy ]  =  async_hook_fields [ kDestroy ] ; 
195+   active_hooks . tmp_fields  =  [ ] ; 
196+   active_hooks . tmp_fields [ kInit ]  =  async_hook_fields [ kInit ] ; 
197+   active_hooks . tmp_fields [ kBefore ]  =  async_hook_fields [ kBefore ] ; 
198+   active_hooks . tmp_fields [ kAfter ]  =  async_hook_fields [ kAfter ] ; 
199+   active_hooks . tmp_fields [ kDestroy ]  =  async_hook_fields [ kDestroy ] ; 
175200} 
176201
177202
178203// Then restore the correct hooks array in case any hooks were added/removed 
179204// during hook callback execution. 
180- function  restoreTmpHooks ( )  { 
181-   active_hooks_array  =  tmp_active_hooks_array ; 
182-   async_hook_fields [ kInit ]  =  tmp_async_hook_fields [ kInit ] ; 
183-   async_hook_fields [ kBefore ]  =  tmp_async_hook_fields [ kBefore ] ; 
184-   async_hook_fields [ kAfter ]  =  tmp_async_hook_fields [ kAfter ] ; 
185-   async_hook_fields [ kDestroy ]  =  tmp_async_hook_fields [ kDestroy ] ; 
186- 
187-   tmp_active_hooks_array  =  null ; 
188-   tmp_async_hook_fields  =  null ; 
205+ function  restoreActiveHooks ( )  { 
206+   active_hooks . array  =  active_hooks . tmp_array ; 
207+   async_hook_fields [ kInit ]  =  active_hooks . tmp_fields [ kInit ] ; 
208+   async_hook_fields [ kBefore ]  =  active_hooks . tmp_fields [ kBefore ] ; 
209+   async_hook_fields [ kAfter ]  =  active_hooks . tmp_fields [ kAfter ] ; 
210+   async_hook_fields [ kDestroy ]  =  active_hooks . tmp_fields [ kDestroy ] ; 
211+ 
212+   active_hooks . tmp_array  =  null ; 
213+   active_hooks . tmp_fields  =  null ; 
189214} 
190215
191216
@@ -322,25 +347,30 @@ function emitHookFactory(symbol, name) {
322347  // before this is called. 
323348  // eslint-disable-next-line func-style 
324349  const  fn  =  function ( asyncId )  { 
325-     processing_hook  +=  1 ; 
350+     active_hooks . call_depth  +=  1 ; 
326351    // Use a single try/catch for all hook to avoid setting up one per 
327352    // iteration. 
328353    try  { 
329-       for  ( var  i  =  0 ;  i  <  active_hooks_array . length ;  i ++ )  { 
330-         if  ( typeof  active_hooks_array [ i ] [ symbol ]  ===  'function' )  { 
331-           active_hooks_array [ i ] [ symbol ] ( asyncId ) ; 
354+       for  ( var  i  =  0 ;  i  <  active_hooks . array . length ;  i ++ )  { 
355+         if  ( typeof  active_hooks . array [ i ] [ symbol ]  ===  'function' )  { 
356+           active_hooks . array [ i ] [ symbol ] ( asyncId ) ; 
332357        } 
333358      } 
334359    }  catch  ( e )  { 
335360      fatalError ( e ) ; 
336361    }  finally  { 
337-       processing_hook  -=  1 ; 
362+       active_hooks . call_depth  -=  1 ; 
338363    } 
339364
340-     if  ( processing_hook  ===  0  &&  tmp_active_hooks_array  !==  null )  { 
341-       restoreTmpHooks ( ) ; 
365+     // Hooks can only be restored if there have been no recursive hook calls. 
366+     // Also the active hooks do not need to be restored if enable()/disable() 
367+     // weren't called during hook execution, in which case 
368+     // active_hooks.tmp_array will be null. 
369+     if  ( active_hooks . call_depth  ===  0  &&  active_hooks . tmp_array  !==  null )  { 
370+       restoreActiveHooks ( ) ; 
342371    } 
343372  } ; 
373+ 
344374  // Set the name property of the anonymous function as it looks good in the 
345375  // stack trace. 
346376  Object . defineProperty ( fn ,  'name' ,  { 
@@ -367,9 +397,6 @@ function emitBeforeScript(asyncId, triggerAsyncId) {
367397} 
368398
369399
370- // TODO(trevnorris): Calling emitBefore/emitAfter from native can't adjust the 
371- // kIdStackIndex. But what happens if the user doesn't have both before and 
372- // after callbacks. 
373400function  emitAfterScript ( asyncId )  { 
374401  if  ( async_hook_fields [ kAfter ]  >  0 ) 
375402    emitAfterNative ( asyncId ) ; 
@@ -387,26 +414,16 @@ function emitDestroyScript(asyncId) {
387414} 
388415
389416
390- // Emit callbacks for native calls. Since some state can be setup directly from 
391- // C++ there's no need to perform all the work here. 
392- 
393- // This should only be called if hooks_array has kInit > 0. There are no global 
394- // values to setup. Though hooks_array will be cloned if C++ needed to call 
395- // init(). 
396- // TODO(trevnorris): Perhaps have MakeCallback call a single JS function that 
397- // does the before/callback/after calls to remove two additional calls to JS. 
398- 
399- // Force the application to shutdown if one of the callbacks throws. This may 
400- // change in the future depending on whether it can be determined if there's a 
401- // slim chance of the application remaining stable after handling one of these 
402- // exceptions. 
417+ // Used by C++ to call all init() callbacks. Because some state can be setup 
418+ // from C++ there's no need to perform all the same operations as in 
419+ // emitInitScript. 
403420function  emitInitNative ( asyncId ,  type ,  triggerAsyncId ,  resource )  { 
404-   processing_hook  +=  1 ; 
421+   active_hooks . call_depth  +=  1 ; 
405422  // Use a single try/catch for all hook to avoid setting up one per iteration. 
406423  try  { 
407-     for  ( var  i  =  0 ;  i  <  active_hooks_array . length ;  i ++ )  { 
408-       if  ( typeof  active_hooks_array [ i ] [ init_symbol ]  ===  'function' )  { 
409-         active_hooks_array [ i ] [ init_symbol ] ( 
424+     for  ( var  i  =  0 ;  i  <  active_hooks . array . length ;  i ++ )  { 
425+       if  ( typeof  active_hooks . array [ i ] [ init_symbol ]  ===  'function' )  { 
426+         active_hooks . array [ i ] [ init_symbol ] ( 
410427          asyncId ,  type ,  triggerAsyncId , 
411428          resource 
412429        ) ; 
@@ -415,18 +432,15 @@ function emitInitNative(asyncId, type, triggerAsyncId, resource) {
415432  }  catch  ( e )  { 
416433    fatalError ( e ) ; 
417434  }  finally  { 
418-     processing_hook  -=  1 ; 
435+     active_hooks . call_depth  -=  1 ; 
419436  } 
420437
421-   // * `tmp_active_hooks_array` is null if no hooks were added/removed while 
422-   //   the hooks were running. In that case no restoration is needed. 
423-   // * In the case where another hook was added/removed while the hooks were 
424-   //   running and a handle was created causing the `init` hooks to fire again, 
425-   //   then `restoreTmpHooks` should not be called for the nested `hooks`. 
426-   //   Otherwise `active_hooks_array` can change during execution of the 
427-   //   `hooks`. 
428-   if  ( processing_hook  ===  0  &&  tmp_active_hooks_array  !==  null )  { 
429-     restoreTmpHooks ( ) ; 
438+   // Hooks can only be restored if there have been no recursive hook calls. 
439+   // Also the active hooks do not need to be restored if enable()/disable() 
440+   // weren't called during hook execution, in which case active_hooks.tmp_array 
441+   // will be null. 
442+   if  ( active_hooks . call_depth  ===  0  &&  active_hooks . tmp_array  !==  null )  { 
443+     restoreActiveHooks ( ) ; 
430444  } 
431445} 
432446
0 commit comments