LIBUV, NODEJS
AND EVERYTHING
IN BETWEEN
Saúl Ibarra Corretgé
@saghul
LIBUV, NODE AND EVERYTHING IN BETWEEN
HIYA!
▸ @saghul
▸ Bilbao — Amsterdam — Liverpool
▸ libuv core janitor
▸ Nodejs collaborator
▸ Recently obsessed with 3D printing
▸ AMA!
ASYNC I/O 101
LIBUV, NODE AND EVERYTHING IN BETWEEN
ASYNC I/O 101
from __future__ import print_function
import socket
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
server.bind(('127.0.0.1', 1234))
server.listen(10)
print("Server listening on: {}".format(server.getsockname()))
client, addr = server.accept()
print("Client connected: {}".format(addr))
while True:
data = client.recv(4096)
if not data:
print("Client has disconnected")
break
client.send(data.upper())
server.close()
LIBUV, NODE AND EVERYTHING IN BETWEEN
CRAP, IT BLOCKS!
▸ Only one client at a time!
▸ Scaling up
▸ Threads
▸ I/O multiplexing
▸ http://www.kegel.com/c10k.html
▸ http://www.slideshare.net/saghul/how-do-eventloops-
work-in-python
LIBUV, NODE AND EVERYTHING IN BETWEEN
THREADS?
▸ Overhead: stack size, scheduling
▸ Synchronisation
▸ It’s still a good solution in some cases
LIBUV, NODE AND EVERYTHING IN BETWEEN
I/O MULTIPLEXING TO THE RESCUE
▸ Single thread
▸ Examine and wait for i/o in multiple sockets at once
▸ When a socket is ready the operation won’t block
▸ Good for i/o bound tasks, bad for CPU intensive tasks
LIBUV, NODE AND EVERYTHING IN BETWEEN
I/O MULTIPLEXING 101
1. Put the fd in non-blocking mode
2. Add the fd to the i/o multiplexor
3. Wait for some time
4. Perform read / writes on the fd, it won’t block!
5. Profit?
LIBUV, NODE AND EVERYTHING IN BETWEEN
I/O MULTIPLEXING APIS
▸ Different APIs: select, poll, epoll, kqueue, …
▸ Unix shares a common model: readiness
▸ Windows uses a completion model
▸ IOCP is The Good Stuff
LIBUV
LIBUV, NODE AND EVERYTHING IN BETWEEN
LIBUV
▸ Small (relatively) C library: ~30K LOC (without tests)
▸ Extensive test suite
▸ 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
LIBUV, NODE AND EVERYTHING IN BETWEEN
LIBUV: FEATURES
▸ Event loop backed by epoll,
kqueue, IOCP, event ports,
etc.
▸ Timers
▸ Async TCP / UDP sockets
▸ Async named pipes
▸ Async filesystem operations
▸ Async signal handling
▸ Child processes
▸ ANSI escaped TTY
▸ Threading utilities
▸ Coolest logo ever
LIBUV, NODE AND EVERYTHING IN BETWEEN
LIBUV: ARCHITECTURE
LIBUV, NODE AND EVERYTHING IN BETWEEN
LIBUV: THE EVENT LOOP
LIBUV, NODE AND EVERYTHING IN BETWEEN
LIBUV: FILESYSTEM APIS
▸ Follow the Unix style
▸ Executed in a thread pool
▸ http://blog.libtorrent.org/2012/10/asynchronous-disk-io/
LIBUV, NODE AND EVERYTHING IN BETWEEN
LIBUV: A WORD ON THREADS
▸ We only use threads for file i/o and getaddrinfo
▸ 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
▸ 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
LIBUV, NODE AND EVERYTHING IN BETWEEN
A CHAT APP,

OF COURSE
LIBUV, NODE AND EVERYTHING IN BETWEEN
A LIBUV CHAT APPLICATION
▸ Simple TCP server application
▸ A twist on libuv-chat by Ben Noordhuis in 2011
▸ Multiple users, single “chat room”
▸ Pokemon, because why not?
▸ https://github.com/saghul/libuv-chat
THE MAKEFILE
all: build
deps/libuv:
git clone --depth 1 https://github.com/libuv/libuv deps/libuv
build/gyp:
git clone --depth 1 https://chromium.googlesource.com/external/gyp build/gyp
out/Makefile: deps/libuv build/gyp
build/gyp/gyp -Duv_library=static_library --depth=$(PWD) --generator-output=$(PWD)/out -Goutput_dir=$(PWD)/out -f make build.gyp
build: out/Makefile
$(MAKE) -C out
clean:
rm -rf out
distclean:
rm -rf build deps out
.PHONY: clean distclean
THE GYP BUILD FILE
# Copyright (c) 2016, Saúl Ibarra Corretgé <saghul@gmail.com>
# Copyright (c) 2012, Ben Noordhuis <info@bnoordhuis.nl>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# gyp -Duv_library=static_library --depth=$PWD --generator-output=$PWD/out -Goutput_dir=$PWD/out -f make build.gyp
{
'targets': [
{
'target_name': 'chat-server',
'type': 'executable',
'dependencies': ['deps/libuv/uv.gyp:libuv'],
'sources': ['src/main.c', 'src/queue.h', 'src/pokemon_names.h'],
}
]
}
INITIALISE AND RUN SERVER
struct user
{
QUEUE queue;
uv_tcp_t handle;
char id[32];
};
static QUEUE users;
int main(void)
{
QUEUE_INIT(&users);
srand(1234);
int r;
uv_tcp_t server_handle;
uv_tcp_init(uv_default_loop(), &server_handle);
struct sockaddr_in addr;
uv_ip4_addr(SERVER_ADDR, SERVER_PORT, &addr);
r = uv_tcp_bind(&server_handle, (const struct sockaddr*) &addr, 0);
if (r < 0)
fatal("uv_tcp_bind", r);
const int backlog = 128;
r = uv_listen((uv_stream_t*) &server_handle, backlog, on_connection);
if (r < 0)
fatal("uv_listen", r);
printf("Listening at %s:%dn", SERVER_ADDR, SERVER_PORT);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
return 0;
}
HANDLE INCOMING CONNECTIONS
static void on_connection(uv_stream_t* server_handle, int status)
{
assert(status == 0);
int r;
// hurray, a new user!
struct user *user = xmalloc(sizeof(*user));
uv_tcp_init(uv_default_loop(), &user->handle);
r = uv_accept(server_handle, (uv_stream_t*) &user->handle);
if (r < 0)
fatal("uv_accept", r);
// add him to the list of users
QUEUE_INSERT_TAIL(&users, &user->queue);
make_user_id(user);
// now tell everyone, including yourself (to know your name!)
broadcast(NULL, "* A wild %s appeared from %sn", user->id, addr_and_port(user));
// start accepting messages from the user
uv_read_start((uv_stream_t*) &user->handle, on_alloc, on_read);
}
READ INCOMING DATA
static void on_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
{
buf->base = xmalloc(suggested_size);
buf->len = suggested_size;
}
static void on_read(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf)
{
struct user *user = QUEUE_DATA(handle, struct user, handle);
if (nread == UV_EOF) {
// user disconnected
QUEUE_REMOVE(&user->queue);
uv_close((uv_handle_t*) &user->handle, on_close);
broadcast(NULL, "* %s fled!n", user->id);
} else if (nread > 0) {
// broadcast message
broadcast(user, "%s said: %.*s", user->id, (int) nread, buf->base);
} else {
fprintf(stderr, "on_read: %sn", uv_strerror(nread));
}
free(buf->base);
}
BROADCAST DATA
static void broadcast(const struct user* sender, const char *fmt, ...)
{
QUEUE *q;
char msg[512];
va_list ap;
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
QUEUE_FOREACH(q, &users) {
struct user *user = QUEUE_DATA(q, struct user, queue);
if (user != sender) {
unicast(user, msg);
}
}
}
static void unicast(struct user *user, const char *msg)
{
size_t len = strlen(msg);
uv_write_t *req = xmalloc(sizeof(*req) + len);
void *addr = req + 1;
memcpy(addr, msg, len);
uv_buf_t buf = uv_buf_init(addr, len);
uv_write(req, (uv_stream_t*) &user->handle, &buf, 1, on_write);
}
static void on_write(uv_write_t *req, int status)
{
free(req);
}
MISCELLANEOUS FUNCTIONS
static void on_close(uv_handle_t* handle)
{
struct user *user = QUEUE_DATA(handle, struct user, handle);
free(user);
}
static void make_user_id(struct user *user)
{
snprintf(user->id, sizeof(user->id), "%s", pokemon_names[rand() % ARRAY_SIZE(pokemon_names)]);
}
NODE
LIBUV, NODE AND EVERYTHING IN BETWEEN
NODEJS
▸ Server side JavaScript
▸ V8 JavaScript engine
▸ Web inspired async model
▸ Auxiliary libraries: libuv, OpenSSL, c-ares, http_parser, …
LIBUV, NODE AND EVERYTHING IN BETWEEN
PLATFORM LAYER REQUIREMENTS
▸ ASYNC ALL THE THINGS
▸ Completion based (callbacks)
▸ Consistent API for asynchronous network i/o
▸ Filesystem operations
▸ Basic name resolution (getaddrinfo / getnameinfo)
LIBUV, NODE AND EVERYTHING IN BETWEEN
ARCHITECTURE IN NODE 0.4
BINDINGS / WRAPS
JS STANDARD LIBRARY
USER APPLICATIONS
V8 OPENSSL LIBEV LIBEIO …
LIBUV, NODE AND EVERYTHING IN BETWEEN
ARCHITECTURE IN NODE 0.6
BINDINGS / WRAPS
JS STANDARD LIBRARY
USER APPLICATIONS
V8 OPENSSL LIBUV …
LIBEV LIBEIO
LIBUV, NODE AND EVERYTHING IN BETWEEN
ARCHITECTURE IN NODE 0.10 AND ONWARDS
BINDINGS / WRAPS
JS STANDARD LIBRARY
USER APPLICATIONS
V8 OPENSSL LIBUV …
LIBUV IN NODE
LIBUV, NODE AND EVERYTHING IN BETWEEN
LIBUV: THE EVENT LOOP
REMEMBER?
LIBUV, NODE AND EVERYTHING IN BETWEEN
NODE EVENT LOOP
COALESCED, 1 NODE
TIMER != 1 LIBUV
TIMER
RUN
ON A CHECK + IDLE
HANDLE
NEXT TICK CALLBACKS RUN FROM
NODE::MAKECALLBACK
LIBUV, NODE AND EVERYTHING IN BETWEEN
ONION ARCHITECTURE (TM)
net.Socket
TCPWrap
uv_tcp_t
Socket._handle
TCPWrap.handle_
fd / HANDLE
QUESTIONS?
libuv.org
docs.libuv.org
#libuv on IRC
libuv Google Group
#libuv on StackOverflow
QUESTIONS?
@saghul
bettercallsaghul.com

libuv, NodeJS and everything in between

  • 1.
    LIBUV, NODEJS AND EVERYTHING INBETWEEN Saúl Ibarra Corretgé @saghul
  • 2.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN HIYA! ▸ @saghul ▸ Bilbao — Amsterdam — Liverpool ▸ libuv core janitor ▸ Nodejs collaborator ▸ Recently obsessed with 3D printing ▸ AMA!
  • 3.
  • 4.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN ASYNC I/O 101 from __future__ import print_function import socket server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) server.bind(('127.0.0.1', 1234)) server.listen(10) print("Server listening on: {}".format(server.getsockname())) client, addr = server.accept() print("Client connected: {}".format(addr)) while True: data = client.recv(4096) if not data: print("Client has disconnected") break client.send(data.upper()) server.close()
  • 5.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN CRAP, IT BLOCKS! ▸ Only one client at a time! ▸ Scaling up ▸ Threads ▸ I/O multiplexing ▸ http://www.kegel.com/c10k.html ▸ http://www.slideshare.net/saghul/how-do-eventloops- work-in-python
  • 6.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN THREADS? ▸ Overhead: stack size, scheduling ▸ Synchronisation ▸ It’s still a good solution in some cases
  • 7.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN I/O MULTIPLEXING TO THE RESCUE ▸ Single thread ▸ Examine and wait for i/o in multiple sockets at once ▸ When a socket is ready the operation won’t block ▸ Good for i/o bound tasks, bad for CPU intensive tasks
  • 8.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN I/O MULTIPLEXING 101 1. Put the fd in non-blocking mode 2. Add the fd to the i/o multiplexor 3. Wait for some time 4. Perform read / writes on the fd, it won’t block! 5. Profit?
  • 9.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN I/O MULTIPLEXING APIS ▸ Different APIs: select, poll, epoll, kqueue, … ▸ Unix shares a common model: readiness ▸ Windows uses a completion model ▸ IOCP is The Good Stuff
  • 10.
  • 11.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN LIBUV ▸ Small (relatively) C library: ~30K LOC (without tests) ▸ Extensive test suite ▸ 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
  • 12.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN LIBUV: FEATURES ▸ Event loop backed by epoll, kqueue, IOCP, event ports, etc. ▸ Timers ▸ Async TCP / UDP sockets ▸ Async named pipes ▸ Async filesystem operations ▸ Async signal handling ▸ Child processes ▸ ANSI escaped TTY ▸ Threading utilities ▸ Coolest logo ever
  • 13.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN LIBUV: ARCHITECTURE
  • 14.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN LIBUV: THE EVENT LOOP
  • 15.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN LIBUV: FILESYSTEM APIS ▸ Follow the Unix style ▸ Executed in a thread pool ▸ http://blog.libtorrent.org/2012/10/asynchronous-disk-io/
  • 16.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN LIBUV: A WORD ON THREADS ▸ We only use threads for file i/o and getaddrinfo ▸ 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 ▸ NOT FOR NETWORK I/O
  • 17.
    THE LIBUV EVENTLOOP IS SINGLE THREADED. THE THREAD POOL IS ONLY USED FOR FILE I/O. The libuv police LIBUV, NODE AND EVERYTHING IN BETWEEN
  • 18.
  • 19.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN A LIBUV CHAT APPLICATION ▸ Simple TCP server application ▸ A twist on libuv-chat by Ben Noordhuis in 2011 ▸ Multiple users, single “chat room” ▸ Pokemon, because why not? ▸ https://github.com/saghul/libuv-chat
  • 20.
    THE MAKEFILE all: build deps/libuv: gitclone --depth 1 https://github.com/libuv/libuv deps/libuv build/gyp: git clone --depth 1 https://chromium.googlesource.com/external/gyp build/gyp out/Makefile: deps/libuv build/gyp build/gyp/gyp -Duv_library=static_library --depth=$(PWD) --generator-output=$(PWD)/out -Goutput_dir=$(PWD)/out -f make build.gyp build: out/Makefile $(MAKE) -C out clean: rm -rf out distclean: rm -rf build deps out .PHONY: clean distclean
  • 21.
    THE GYP BUILDFILE # Copyright (c) 2016, Saúl Ibarra Corretgé <saghul@gmail.com> # Copyright (c) 2012, Ben Noordhuis <info@bnoordhuis.nl> # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # gyp -Duv_library=static_library --depth=$PWD --generator-output=$PWD/out -Goutput_dir=$PWD/out -f make build.gyp { 'targets': [ { 'target_name': 'chat-server', 'type': 'executable', 'dependencies': ['deps/libuv/uv.gyp:libuv'], 'sources': ['src/main.c', 'src/queue.h', 'src/pokemon_names.h'], } ] }
  • 22.
    INITIALISE AND RUNSERVER struct user { QUEUE queue; uv_tcp_t handle; char id[32]; }; static QUEUE users; int main(void) { QUEUE_INIT(&users); srand(1234); int r; uv_tcp_t server_handle; uv_tcp_init(uv_default_loop(), &server_handle); struct sockaddr_in addr; uv_ip4_addr(SERVER_ADDR, SERVER_PORT, &addr); r = uv_tcp_bind(&server_handle, (const struct sockaddr*) &addr, 0); if (r < 0) fatal("uv_tcp_bind", r); const int backlog = 128; r = uv_listen((uv_stream_t*) &server_handle, backlog, on_connection); if (r < 0) fatal("uv_listen", r); printf("Listening at %s:%dn", SERVER_ADDR, SERVER_PORT); uv_run(uv_default_loop(), UV_RUN_DEFAULT); return 0; }
  • 23.
    HANDLE INCOMING CONNECTIONS staticvoid on_connection(uv_stream_t* server_handle, int status) { assert(status == 0); int r; // hurray, a new user! struct user *user = xmalloc(sizeof(*user)); uv_tcp_init(uv_default_loop(), &user->handle); r = uv_accept(server_handle, (uv_stream_t*) &user->handle); if (r < 0) fatal("uv_accept", r); // add him to the list of users QUEUE_INSERT_TAIL(&users, &user->queue); make_user_id(user); // now tell everyone, including yourself (to know your name!) broadcast(NULL, "* A wild %s appeared from %sn", user->id, addr_and_port(user)); // start accepting messages from the user uv_read_start((uv_stream_t*) &user->handle, on_alloc, on_read); }
  • 24.
    READ INCOMING DATA staticvoid on_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { buf->base = xmalloc(suggested_size); buf->len = suggested_size; } static void on_read(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { struct user *user = QUEUE_DATA(handle, struct user, handle); if (nread == UV_EOF) { // user disconnected QUEUE_REMOVE(&user->queue); uv_close((uv_handle_t*) &user->handle, on_close); broadcast(NULL, "* %s fled!n", user->id); } else if (nread > 0) { // broadcast message broadcast(user, "%s said: %.*s", user->id, (int) nread, buf->base); } else { fprintf(stderr, "on_read: %sn", uv_strerror(nread)); } free(buf->base); }
  • 25.
    BROADCAST DATA static voidbroadcast(const struct user* sender, const char *fmt, ...) { QUEUE *q; char msg[512]; va_list ap; va_start(ap, fmt); vsnprintf(msg, sizeof(msg), fmt, ap); va_end(ap); QUEUE_FOREACH(q, &users) { struct user *user = QUEUE_DATA(q, struct user, queue); if (user != sender) { unicast(user, msg); } } } static void unicast(struct user *user, const char *msg) { size_t len = strlen(msg); uv_write_t *req = xmalloc(sizeof(*req) + len); void *addr = req + 1; memcpy(addr, msg, len); uv_buf_t buf = uv_buf_init(addr, len); uv_write(req, (uv_stream_t*) &user->handle, &buf, 1, on_write); } static void on_write(uv_write_t *req, int status) { free(req); }
  • 26.
    MISCELLANEOUS FUNCTIONS static voidon_close(uv_handle_t* handle) { struct user *user = QUEUE_DATA(handle, struct user, handle); free(user); } static void make_user_id(struct user *user) { snprintf(user->id, sizeof(user->id), "%s", pokemon_names[rand() % ARRAY_SIZE(pokemon_names)]); }
  • 27.
  • 28.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN NODEJS ▸ Server side JavaScript ▸ V8 JavaScript engine ▸ Web inspired async model ▸ Auxiliary libraries: libuv, OpenSSL, c-ares, http_parser, …
  • 29.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN PLATFORM LAYER REQUIREMENTS ▸ ASYNC ALL THE THINGS ▸ Completion based (callbacks) ▸ Consistent API for asynchronous network i/o ▸ Filesystem operations ▸ Basic name resolution (getaddrinfo / getnameinfo)
  • 30.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN ARCHITECTURE IN NODE 0.4 BINDINGS / WRAPS JS STANDARD LIBRARY USER APPLICATIONS V8 OPENSSL LIBEV LIBEIO …
  • 31.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN ARCHITECTURE IN NODE 0.6 BINDINGS / WRAPS JS STANDARD LIBRARY USER APPLICATIONS V8 OPENSSL LIBUV … LIBEV LIBEIO
  • 32.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN ARCHITECTURE IN NODE 0.10 AND ONWARDS BINDINGS / WRAPS JS STANDARD LIBRARY USER APPLICATIONS V8 OPENSSL LIBUV …
  • 33.
  • 34.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN LIBUV: THE EVENT LOOP REMEMBER?
  • 35.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN NODE EVENT LOOP COALESCED, 1 NODE TIMER != 1 LIBUV TIMER RUN ON A CHECK + IDLE HANDLE NEXT TICK CALLBACKS RUN FROM NODE::MAKECALLBACK
  • 36.
    LIBUV, NODE ANDEVERYTHING IN BETWEEN ONION ARCHITECTURE (TM) net.Socket TCPWrap uv_tcp_t Socket._handle TCPWrap.handle_ fd / HANDLE
  • 37.
    QUESTIONS? libuv.org docs.libuv.org #libuv on IRC libuvGoogle Group #libuv on StackOverflow
  • 38.