A DEEP DIVE INTO LIBUV
Saúl Ibarra Corretgé
NodeConf EU 2016
A DEEP DIVE INTO LIBUV
DIA DHAOIBH A ÉIRE!
▸ @saghul
▸ libuv core janitor
▸ Nodejs collaborator
▸ Open Source enthusiast
LIBUV
A DEEP DIVE INTO LIBUV
LIBUV
▸ Cross-platform async I/O and more
▸ Small (relatively) C library: ~30K LOC (without tests)
▸ Extensive test suite and CI coverage
▸ Designed for C programs that miss the joy of JavaScript
callback hell
▸ Used by Node and many other projects:

https://github.com/libuv/libuv/wiki/Projects-that-use-libuv
A DEEP DIVE INTO LIBUV
LIBUV: FEATURES
▸ Event loop
▸ Timers
▸ TCP / UDP sockets
▸ Named pipes
▸ Filesystem operations
▸ Signal handling
▸ Child processes
▸ ANSI escaped TTY
▸ Threading utilities
▸ Coolest logo ever
A DEEP DIVE INTO LIBUV
LIBUV: ARCHITECTURE
▸ The event loop: uv_loop_t
▸ Handles: uv_handle_t
▸ Requests: uv_req_t
▸ Other utilities
A DEEP DIVE INTO LIBUV
LIBUV: ARCHITECTURE
OPERATING SYSTEM
NETWORK I/O FILE I/O OTHER STUFF
OS INDEPENDENT
STUFF
A DEEP DIVE INTO LIBUV
LIBUV ARCHITECTURE: NETWORK I/O
OPERATING SYSTEM
UV_TCP_T UV_PIPE_T UV_TTY_T
UV_UDP_T UV_POLL_TUV_STREAM_T
UV__IO_T (UNIX ONLY)
A DEEP DIVE INTO LIBUV
LIBUV ARCHITECTURE: FILE I/O
UV_FS_T UV_GETADDRINFO_T UV_GETNAMEINFO_T
UV__WORK_T
THREAD POOL
UV_WORK_T
A DEEP DIVE INTO LIBUV
LIBUV: A WORD ON THREADS
▸ We only use threads for file i/o and getaddrinfo
▸ http://blog.libtorrent.org/2012/10/asynchronous-disk-io/
▸ Default thread pool size is 4

(runtime env var: UV_THREADPOOL_SIZE)
▸ NOT FOR NETWORK I/O
▸ NOT FOR NETWORK I/O
▸ NOT FOR NETWORK I/O
THE LIBUV EVENT LOOP IS
SINGLE THREADED. THE THREAD
POOL IS ONLY USED FOR FILE I/O.
The libuv police
A DEEP DIVE INTO LIBUV
A DEEP DIVE INTO LIBUV
LIBUV ARCHITECTURE: OTHER
▸ OS independent
▸ uv_timer_t
▸ uv_idle_t, uv_prepare_t, uv_check_t

aka “loop watchers”
▸ The thread pool
▸ OS dependent
▸ uv_signal_t
▸ uv_process_t
▸ Threading and miscellaneous utilities
A DEEP DIVE INTO LIBUV
LIBUV: THE EVENT LOOP
int uv_run(uv_loop_t* loop, uv_run_mode mode) {
int timeout, r, ran_pending;
r = uv__loop_alive(loop);
if (!r)
uv__update_time(loop);
while (r != 0 && loop->stop_flag == 0) {
uv__update_time(loop);
uv__run_timers(loop);
ran_pending = uv__run_pending(loop);
uv__run_idle(loop);
uv__run_prepare(loop);
timeout = 0;
if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
timeout = uv_backend_timeout(loop);
uv__io_poll(loop, timeout);
uv__run_check(loop);
uv__run_closing_handles(loop);
if (mode == UV_RUN_ONCE) {
uv__update_time(loop);
uv__run_timers(loop);
}
r = uv__loop_alive(loop);
if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
break;
}
loop->stop_flag = 0;
return r;
}
static int uv__loop_alive(const uv_loop_t* loop) {
return uv__has_active_handles(loop) ||
uv__has_active_reqs(loop) ||
loop->closing_handles != NULL;
}
int uv_backend_timeout(const uv_loop_t* loop) {
if (loop->stop_flag != 0)
return 0;
if (!uv_loop_alive(loop))
return 0;
if (!QUEUE_EMPTY(&loop->idle_handles))
return 0;
if (!QUEUE_EMPTY(&loop->pending_queue))
return 0;
return uv__next_timeout(loop);
}
void uv__run_timers(uv_loop_t* loop) {
struct heap_node* heap_node;
uv_timer_t* handle;
for (;;) {
heap_node = heap_min((struct heap*) &loop->timer_heap);
if (heap_node == NULL)
break;
handle = container_of(heap_node, uv_timer_t, heap_node);
if (handle->timeout > loop->time)
break;
uv_timer_stop(handle);
uv_timer_again(handle);
handle->timer_cb(handle);
}
}
LIBUV IN NODE
A DEEP DIVE INTO LIBUV
LIBUV: THE EVENT LOOP
REMEMBER?
A DEEP DIVE INTO LIBUV
NODE EVENT LOOP
COALESCED, 1 NODE
TIMER != 1 LIBUV
TIMER
RUN
ON A CHECK + IDLE
HANDLE
NEXT TICK CALLBACKS RUN FROM
NODE::MAKECALLBACK
A DEEP DIVE INTO LIBUV
ONION ARCHITECTURE (TM)
net.Socket
TCPWrap
uv_tcp_t
Socket._handle
TCPWrap.handle_
fd / HANDLE
STATE OF

THE UNICORN
A DEEP DIVE INTO LIBUV
STATE OF THE UNICORN: V1.X
▸ 1.10 release imminent
▸ Enterprise Ready (R)
▸ Release champion: @cjihrig
▸ 12 new APIs since 1.0.0
▸ ABI remained stable!
▸ 7 maintainers (~ 5 active)
A DEEP DIVE INTO LIBUV
STATE OF THE UNICORN: MASTER
▸ ~ 2kloc less!
▸ Supported platforms cleanup
▸ A number of pending PRs (sorry!)
A DEEP DIVE INTO LIBUV
STATE OF THE UNICORN: THE PLAN FOR V2
▸ Improve documentation
▸ Improve thread pool
▸ uv_timeout_t and uv_read_t requests
▸ libuv-extras: companion library
▸ uv_link_t, uv_ssl_t
▸ uv_serial_t ?
RESOURCES
libuv.org
docs.libuv.org
#libuv on IRC
libuv Google Group
#libuv on StackOverflow
WHEN THERE IS
SOMETHING STRANGE

IN YOUR
EVENT LOOP
A DEEP DIVE INTO LIBUV
WHO YOU GONNA CALL? @LIBUV/COLLABORATORS
▸ @piscisaureus
▸ @bnoordhuis
▸ @indutny
▸ @saghul
▸ @cjihrig
▸ @iWuzHere
▸ @santigimeno
QUESTIONS?
@saghul
bettercallsaghul.com

A deep dive into libuv

  • 1.
    A DEEP DIVEINTO LIBUV Saúl Ibarra Corretgé NodeConf EU 2016
  • 2.
    A DEEP DIVEINTO LIBUV DIA DHAOIBH A ÉIRE! ▸ @saghul ▸ libuv core janitor ▸ Nodejs collaborator ▸ Open Source enthusiast
  • 3.
  • 4.
    A DEEP DIVEINTO LIBUV LIBUV ▸ Cross-platform async I/O and more ▸ Small (relatively) C library: ~30K LOC (without tests) ▸ Extensive test suite and CI coverage ▸ Designed for C programs that miss the joy of JavaScript callback hell ▸ Used by Node and many other projects:
 https://github.com/libuv/libuv/wiki/Projects-that-use-libuv
  • 5.
    A DEEP DIVEINTO LIBUV LIBUV: FEATURES ▸ Event loop ▸ Timers ▸ TCP / UDP sockets ▸ Named pipes ▸ Filesystem operations ▸ Signal handling ▸ Child processes ▸ ANSI escaped TTY ▸ Threading utilities ▸ Coolest logo ever
  • 6.
    A DEEP DIVEINTO LIBUV LIBUV: ARCHITECTURE ▸ The event loop: uv_loop_t ▸ Handles: uv_handle_t ▸ Requests: uv_req_t ▸ Other utilities
  • 7.
    A DEEP DIVEINTO LIBUV LIBUV: ARCHITECTURE OPERATING SYSTEM NETWORK I/O FILE I/O OTHER STUFF OS INDEPENDENT STUFF
  • 8.
    A DEEP DIVEINTO LIBUV LIBUV ARCHITECTURE: NETWORK I/O OPERATING SYSTEM UV_TCP_T UV_PIPE_T UV_TTY_T UV_UDP_T UV_POLL_TUV_STREAM_T UV__IO_T (UNIX ONLY)
  • 9.
    A DEEP DIVEINTO LIBUV LIBUV ARCHITECTURE: FILE I/O UV_FS_T UV_GETADDRINFO_T UV_GETNAMEINFO_T UV__WORK_T THREAD POOL UV_WORK_T
  • 10.
    A DEEP DIVEINTO LIBUV LIBUV: A WORD ON THREADS ▸ We only use threads for file i/o and getaddrinfo ▸ http://blog.libtorrent.org/2012/10/asynchronous-disk-io/ ▸ Default thread pool size is 4
 (runtime env var: UV_THREADPOOL_SIZE) ▸ NOT FOR NETWORK I/O ▸ NOT FOR NETWORK I/O ▸ NOT FOR NETWORK I/O
  • 11.
    THE LIBUV EVENTLOOP IS SINGLE THREADED. THE THREAD POOL IS ONLY USED FOR FILE I/O. The libuv police A DEEP DIVE INTO LIBUV
  • 12.
    A DEEP DIVEINTO LIBUV LIBUV ARCHITECTURE: OTHER ▸ OS independent ▸ uv_timer_t ▸ uv_idle_t, uv_prepare_t, uv_check_t
 aka “loop watchers” ▸ The thread pool ▸ OS dependent ▸ uv_signal_t ▸ uv_process_t ▸ Threading and miscellaneous utilities
  • 13.
    A DEEP DIVEINTO LIBUV LIBUV: THE EVENT LOOP
  • 14.
    int uv_run(uv_loop_t* loop,uv_run_mode mode) { int timeout, r, ran_pending; r = uv__loop_alive(loop); if (!r) uv__update_time(loop); while (r != 0 && loop->stop_flag == 0) { uv__update_time(loop); uv__run_timers(loop); ran_pending = uv__run_pending(loop); uv__run_idle(loop); uv__run_prepare(loop); timeout = 0; if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT) timeout = uv_backend_timeout(loop); uv__io_poll(loop, timeout); uv__run_check(loop); uv__run_closing_handles(loop); if (mode == UV_RUN_ONCE) { uv__update_time(loop); uv__run_timers(loop); } r = uv__loop_alive(loop); if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT) break; } loop->stop_flag = 0; return r; }
  • 15.
    static int uv__loop_alive(constuv_loop_t* loop) { return uv__has_active_handles(loop) || uv__has_active_reqs(loop) || loop->closing_handles != NULL; }
  • 16.
    int uv_backend_timeout(const uv_loop_t*loop) { if (loop->stop_flag != 0) return 0; if (!uv_loop_alive(loop)) return 0; if (!QUEUE_EMPTY(&loop->idle_handles)) return 0; if (!QUEUE_EMPTY(&loop->pending_queue)) return 0; return uv__next_timeout(loop); }
  • 17.
    void uv__run_timers(uv_loop_t* loop){ struct heap_node* heap_node; uv_timer_t* handle; for (;;) { heap_node = heap_min((struct heap*) &loop->timer_heap); if (heap_node == NULL) break; handle = container_of(heap_node, uv_timer_t, heap_node); if (handle->timeout > loop->time) break; uv_timer_stop(handle); uv_timer_again(handle); handle->timer_cb(handle); } }
  • 18.
  • 19.
    A DEEP DIVEINTO LIBUV LIBUV: THE EVENT LOOP REMEMBER?
  • 20.
    A DEEP DIVEINTO LIBUV NODE EVENT LOOP COALESCED, 1 NODE TIMER != 1 LIBUV TIMER RUN ON A CHECK + IDLE HANDLE NEXT TICK CALLBACKS RUN FROM NODE::MAKECALLBACK
  • 21.
    A DEEP DIVEINTO LIBUV ONION ARCHITECTURE (TM) net.Socket TCPWrap uv_tcp_t Socket._handle TCPWrap.handle_ fd / HANDLE
  • 22.
  • 23.
    A DEEP DIVEINTO LIBUV STATE OF THE UNICORN: V1.X ▸ 1.10 release imminent ▸ Enterprise Ready (R) ▸ Release champion: @cjihrig ▸ 12 new APIs since 1.0.0 ▸ ABI remained stable! ▸ 7 maintainers (~ 5 active)
  • 24.
    A DEEP DIVEINTO LIBUV STATE OF THE UNICORN: MASTER ▸ ~ 2kloc less! ▸ Supported platforms cleanup ▸ A number of pending PRs (sorry!)
  • 25.
    A DEEP DIVEINTO LIBUV STATE OF THE UNICORN: THE PLAN FOR V2 ▸ Improve documentation ▸ Improve thread pool ▸ uv_timeout_t and uv_read_t requests ▸ libuv-extras: companion library ▸ uv_link_t, uv_ssl_t ▸ uv_serial_t ?
  • 26.
    RESOURCES libuv.org docs.libuv.org #libuv on IRC libuvGoogle Group #libuv on StackOverflow
  • 27.
    WHEN THERE IS SOMETHINGSTRANGE
 IN YOUR EVENT LOOP
  • 28.
    A DEEP DIVEINTO LIBUV WHO YOU GONNA CALL? @LIBUV/COLLABORATORS ▸ @piscisaureus ▸ @bnoordhuis ▸ @indutny ▸ @saghul ▸ @cjihrig ▸ @iWuzHere ▸ @santigimeno
  • 29.