Skip to content

Instantly share code, notes, and snippets.

@thlorenz
Last active December 16, 2019 20:09
Show Gist options
  • Save thlorenz/8dd5d3a50ee14457afce to your computer and use it in GitHub Desktop.
Save thlorenz/8dd5d3a50ee14457afce to your computer and use it in GitHub Desktop.
uv timers

Table of Contents generated with DocToc

Timers

  • are handles and thus inherit uv_handle_t

starting/stopping timers

omitted error handling for brevity

uv_timer_t timer;
int repeat = 0;
int timeout = 100; // in ms

// initialize and start
r = uv_timer_init(uv_default_loop(), &timer);
// repeat is 0 to fire once or greater to fire multiple times
r = uv_timer_start(timer, timer_cb, timeout, repeat);

// run event loop
uv_run(uv_default_loop(), UV_RUN_DEFAULT);

// no need to explicitly stop timer if they run to completion (including all repeats)

If a timer is stopped before the event loop is run, it'll never fire.

uv_timer_init

  • initializes timer handle with the provided loop via uv__handle_init
  • sets timer_cb and repeat to default values

uv_timer_start

  • checks for given cb otherwise bails
  • if handle is active, stops it first via uv_timer_stop
  • adjusts given timeout
  • sets timer_cb, timeout and repeat
  • sets start_id and increases loop->timer_counter
  • adds handle to the heap
  • starts handle via uv__handle_start

NOTE: repeat is a flag (on/off) NOT the number of repeats

uv_timer_stop

  • returns if handle isn't active
  • removes handle from heap
  • stops handle via uv__handle_stop

uv_timer_again

  • only for repeating timers does the following
  • stops timer
  • starts timer via uv_timer_start

uv__run_timers

  • timers are run in this function when it is called by uv_run right after uv__update_time
    • if mode == UV_RUN_ONCE, they are executed twice and then the loop is exited
    • if mode == UV_RUN_NOWAIT, they are executed once and then the loop is exited
    • if mode == UV_RUN_DEFAULT, thery are executed indefinitely until a loop stop is signaled
    • read this comment for more info
  • for each timer on the heap
    • if timer expired does nothing
    • otherwise it stops the timer, runs uv_timer_again and invokes the timer_cb

uv__next_timeout

  • returns -1 if no timers are found
  • return 0 if next timer expired
  • otherwise returns time until next timer expires

Handles

uv__handle_init

  • assigns loop and type to given handle
  • references handle
  • adds handle to the end of the queue
#define uv__handle_init(loop_, h, type_)                                      \
  do {                                                                        \
    (h)->loop = (loop_);                                                      \
    (h)->type = (type_);                                                      \
    (h)->flags = UV__HANDLE_REF;  /* Ref the loop when active. */             \
    QUEUE_INSERT_TAIL(&(loop_)->handle_queue, &(h)->handle_queue);            \
    uv__handle_platform_init(h);                                              \
  }                                                                           \
  while (0)

uv__handle_start

  • assumes handle isn't closing
  • breaks if handle already active
  • activates handle
  • if handle is referenced adds handle to active handles
#define uv__handle_start(h)                                                   \
  do {                                                                        \
    assert(((h)->flags & UV__HANDLE_CLOSING) == 0);                           \
    if (((h)->flags & UV__HANDLE_ACTIVE) != 0) break;                         \
    (h)->flags |= UV__HANDLE_ACTIVE;                                          \
    if (((h)->flags & UV__HANDLE_REF) != 0) uv__active_handle_add(h);         \
  }                                                                           \
  while (0)

uv__handle_stop

  • assumes handle isn't closing
  • breaks if handle isn't active
  • deactivates handle
  • if handle is referenced removes handle from active handles
#define uv__handle_stop(h)                                                    \
  do {                                                                        \
    assert(((h)->flags & UV__HANDLE_CLOSING) == 0);                           \
    if (((h)->flags & UV__HANDLE_ACTIVE) == 0) break;                         \
    (h)->flags &= ~UV__HANDLE_ACTIVE;                                         \
    if (((h)->flags & UV__HANDLE_REF) != 0) uv__active_handle_rm(h);          \
  }                                                                           \
  while (0)

uv__handle_ref

  • breaks if handle is referenced
  • references handle
  • breaks if handle is closing
  • if handle is active adds it to active handles
#define uv__handle_ref(h)                                                     \
  do {                                                                        \
    if (((h)->flags & UV__HANDLE_REF) != 0) break;                            \
    (h)->flags |= UV__HANDLE_REF;                                             \
    if (((h)->flags & UV__HANDLE_CLOSING) != 0) break;                        \
    if (((h)->flags & UV__HANDLE_ACTIVE) != 0) uv__active_handle_add(h);      \
  }                                                                           \
  while (0)

uv__handle_unref

  • breaks if handle is not referenced
  • dereferences handle
  • breaks if handle is closing
  • if handle is active removes it from active handles
#define uv__handle_unref(h)                                                   \
  do {                                                                        \
    if (((h)->flags & UV__HANDLE_REF) == 0) break;                            \
    (h)->flags &= ~UV__HANDLE_REF;                                            \
    if (((h)->flags & UV__HANDLE_CLOSING) != 0) break;                        \
    if (((h)->flags & UV__HANDLE_ACTIVE) != 0) uv__active_handle_rm(h);       \
  }                                                                           \
  while (0)

uv_run modes

docs

  • UV_RUN_DEFAULT: Runs the event loop until there are no more active and referenced handles or requests. Always returns zero.
  • UV_RUN_ONCE: Poll for i/o once. Note that this function blocks if there are no pending callbacks. Returns zero when done (no active handles or requests left), or non-zero if more callbacks are expected (meaning you should run the event loop again sometime in the future).
  • UV_RUN_NOWAIT: Poll for i/o once but don’t block if there are no pending callbacks. Returns zero if done (no active handles or requests left), or non-zero if more callbacks are expected (meaning you should run the event loop again sometime in the future).

uv_run in UV_RUN_DEFAULT mode

  • checks if loop is alive and if not uv__update_time
  • runs the following until loop->stop_flag == 0
      1. update time, run timers
      1. run pending and assign result to ran_pending flag which will be set unless the QUEUE was empty
      1. idle and prepare loop
      1. get timeout via uv_backend_timeout
      1. polls io for given timeout
      1. finishes closing for all loop->closing_handles

uv_run in UV_RUN_ONCE mode

  • checks if loop is alive and if not uv__update_time
  • runs the following once
    • 1 - 3 as above
    • get timeout via uv_backend_timeout if pending reqs or handles, otherwise leave timeout as 0
    • 5 -6 as above
    • update time, run timers one last time

uv_run in UV_RUN_NOWAIT mode

  • checks if loop is alive and if not uv__update_time
  • runs the following once
    • 1 - 3 as above
    • 5 -6 as above

uv_backend_timeout

  • returns 0 if either of the following are true
    • the loop stop flag is set
    • no more handles or requests are active
    • the QUEUE of idle_handles is NOT empty
    • the loop has closing_handles
    • uv__next_timeout returns 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment