Skip to content

Instantly share code, notes, and snippets.

@trevnorris
Last active June 23, 2019 04:23
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trevnorris/05531d8339f8e265bd49 to your computer and use it in GitHub Desktop.
Save trevnorris/05531d8339f8e265bd49 to your computer and use it in GitHub Desktop.

Notes:

  • Text in [[ ]] are the internal libuv function call.
  • Text in {{ }} are the Node functions that are affected.
  • Text in ( ) are notes about what is happening.
  • While the Windows event loop has minor variations, I don't believe any of those affect Node.

On process.nextTick():

process.nextTick() is poorly named. It doesn't actually wait until the "next tick" of the event loop. Instead it runs any callbacks passed directly after the synchronous execution of the current phase of the event loop (i.e. before the event loop can continue to the next phase).

This translates to the fact that the nextTickQueue (i.e. the mechanism that contains all callbacks passed to process.nextTick() during the execution of any callback) is processed at the end of every phase of the event loop.

This is poorly used in Node in cases like "close" events, where the event is emitted asynchronously but only by means of process.nextTick(). Instead of actually calling the close callback in the final phase of the event loop (uv__run_closing_handles()).

There has been discussion on correcting this, but my initial attempt broke several tests and I haven't taken the time to figure out why.


Brief overview of the current state of the event loop, and what runs at any given time.

              ┌───────────────────────┐
              │  Application Startup  │
              └───────────┬───────────┘
                          │
                          │ (bootstrap global environment)
                          │
              ┌───────────┴───────────┐
              │    [[ uv_run() ]]     │
              └───────────┬───────────┘
                          │
            ┌─────────────┴─────────────┐
       ╭────┤  [[ uv__run_timers()  ]]  │
       │    ┢━━━━━━━━━━━━━━━━━━━━━━━━━━━┪
       │    ┃    {{ setTimeout() }}     ┃
       │    ┃    {{ setInterval() }}    ┃
       │    ┗━━━━━━━━━━━━━┯━━━━━━━━━━━━━┛
       │                  │
       │    ┌─────────────┴─────────────┐
       │    │  [[ uv__run_pending() ]]  │
       │    ┢━━━━━━━━━━━━━━━━━━━━━━━━━━━┪
       │    ┃ All completed write reqs. ┃
       │    ┃                           ┃
       │    ┃ Error Reporting for:      ┃
       │    ┃ - TCP ECONNREFUSED        ┃
       │    ┃ - PIPE (all errors)       ┃
       │    ┗━━━━━━━━━━━━━┯━━━━━━━━━━━━━┛
       │                  │
       │    ┌─────────────┴─────────────┐
       │    │   [[ uv__run_idle() ]]    │
       │    ┢━━━━━━━━━━━━━━━━━━━━━━━━━━━┪
       │    ┃  {{ clearImmediate() }}   ┃
       │    ┗━━━━━━━━━━━━━┯━━━━━━━━━━━━━┛
       │                  │
       │    ┌─────────────┴─────────────┐
       │    │  [[ uv__run_prepare() ]]  │
       │    ┢━━━━━━━━━━━━━━━━━━━━━━━━━━━┪
       │    ┃    CPU Idle Profiler      ┃
       │    ┃ (Inform V8 profiler that  ┃
       │    ┃  Node is about to idle)   ┃
       │    ┗━━━━━━━━━━━━━┯━━━━━━━━━━━━━┛
       │                  │
       │    ┌─────────────┴─────────────┐
       │    │    [[ uv__io_poll() ]]    │
       │    ┢━━━━━━━━━━━━━━━━━━━━━━━━━━━┪
       │    ┃(Poll for incoming or      ┃
       │    ┃completed events signaled  ┃
       │    ┃back from the kernel)      ┃
       │    ┃                           ┃
       │    ┗━━━━━━━━━━━━━┯━━━━━━━━━━━━━┛
       │                  │
       │    ┌─────────────┴─────────────┐
       │    │   [[ uv__run_check() ]]   │
       │    ┢━━━━━━━━━━━━━━━━━━━━━━━━━━━┪
       │    ┃   {{ setImmediate() }}    ┃
       │    ┃                           ┃
       │    ┃    CPU Idle Profiler      ┃
       │    ┃ (Inform V8 profiler that  ┃
       │    ┃  Node is no longer idle)  ┃
       │    ┗━━━━━━━━━━━━━┯━━━━━━━━━━━━━┛
       │                  │
       │  ┌───────────────┴───────────────┐
       │  │[[ uv__run_closing_handles() ]]│
       │  ┢━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┪
       │  ┃(Currently not used by Node as ┃
       ╰──┨all close callbacks are instead┃
          ┃called prior to this via.      ┃
          ┃process.nextTick())            ┃
          ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
@iliakan
Copy link

iliakan commented Nov 16, 2014

Is this picture still up to date? What does clearImmediate() do in uv__run_idle()? Couldn't find the call in the sources.

Copy link

ghost commented Apr 21, 2016

@iliakan It's been awhile, but if you still care...prepare, check, and idle are all defined via macro. Take a look at uv/src/<win|unix>/loop-watcher.c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment