2. LIBUV, NODE AND EVERYTHING IN BETWEEN
HIYA!
▸ @saghul
▸ Bilbao — Amsterdam — Liverpool
▸ libuv core janitor
▸ Nodejs collaborator
▸ Recently obsessed with 3D printing
▸ AMA!
4. 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()
5. 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
6. LIBUV, NODE AND EVERYTHING IN BETWEEN
THREADS?
▸ Overhead: stack size, scheduling
▸ Synchronisation
▸ It’s still a good solution in some cases
7. 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
8. 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?
9. 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
11. 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
12. 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
15. 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/
16. 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
17. 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
19. 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
20. 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
21. 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'],
}
]
}
22. 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;
}
23. 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);
}
28. 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, …
29. 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)
30. LIBUV, NODE AND EVERYTHING IN BETWEEN
ARCHITECTURE IN NODE 0.4
BINDINGS / WRAPS
JS STANDARD LIBRARY
USER APPLICATIONS
V8 OPENSSL LIBEV LIBEIO …
31. LIBUV, NODE AND EVERYTHING IN BETWEEN
ARCHITECTURE IN NODE 0.6
BINDINGS / WRAPS
JS STANDARD LIBRARY
USER APPLICATIONS
V8 OPENSSL LIBUV …
LIBEV LIBEIO
32. LIBUV, NODE AND EVERYTHING IN BETWEEN
ARCHITECTURE IN NODE 0.10 AND ONWARDS
BINDINGS / WRAPS
JS STANDARD LIBRARY
USER APPLICATIONS
V8 OPENSSL LIBUV …
34. LIBUV, NODE AND EVERYTHING IN BETWEEN
LIBUV: THE EVENT LOOP
REMEMBER?
35. 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
36. LIBUV, NODE AND EVERYTHING IN BETWEEN
ONION ARCHITECTURE (TM)
net.Socket
TCPWrap
uv_tcp_t
Socket._handle
TCPWrap.handle_
fd / HANDLE