The event loop from
the inside out
Sam Roberts <rsam@ca.ibm.com>, IBM
github: @sam-github, twitter: @octetcloud
We	should	be	able	to	answer	these	questions:
• What	is	the	event	loop?	(Hint:	its	not	an	EventEmitter)
• Is	node	single- or	multi-threaded?	(Bonus	question:	When?)
• Why	is	Node.js said	to	"scale	well"?
A	primer	in	Unix	system	programming
Warning:	pseudo	"C"	code	lies	ahead!
Network	connections	use	"sockets",	named	after	the	system	call	used:
int s	=	socket();
Socket	descriptors	are	often	referred	to	as	"file	descriptors",	confusingly,	since	file	descriptors	are	not	necessarily	references	to	
the	file	system.	Sorry.
File	descriptors	are	O/S	"object	orientation",	they	point	to	objects	in	the	kernel	with	a	virtual	"interface"	
(read/write/close/poll/etc.).
Scale	problem:	thread-per-connection
int server	=	socket();
bind(server,	80)
listen(server)
while(int connection	=	accept(server))	{
pthread_create(echo,	connection)
}
void	echo(int connection)	{
char	buf[4096];
while(int size	=	read(connection,	buffer,	sizeof buf))	{
write(connection,	buffer,	size);
}
}
Scale	solution:	epoll - setup
int server	=	...	//	like	before
int eventfd =	epoll_create1(0);
struct epoll_event ev =	{	.events	=	EPOLLIN,	.data.fd =	server	};
epoll_ctl(epollfd,	EPOLL_CTL_ADD,	server,	&ev);
…
epoll is	used	on	Linux,	kqueue on	BSDs	is	similar,	Windows	has	quite	a	different	API,	but	with	the	same	end	result.
Scale	solution:	epoll - loop
…
struct epoll_event events[10];
//	This	is the	"event	loop",	each	loop	could	be	called	a	"tick”	of	the	loop,	but	then	it	would	be	confused
//	with	process.nextTick().
while((int max	=	epoll_wait(eventfd,	events,	10)))	{
…
Scale	solution:	epoll – handle	server	socket
…
while((int max	=	epoll_wait(eventfd,	events,	10)))	{
for(n	=	0;	n	< max;	n++)	{
if	(events[n].data.fd.fd ==	server)	{
//	Server	socket	has	connection!
int connection	=	accept(server);
ev.events =	EPOLLIN;	ev.data.fd =	connection;
epoll_ctl(eventfd,	EPOLL_CTL_ADD,	connection,	&ev);
}	else	…
Scale	solution:	epoll – handle	connection	socket
…
while((int max	=	epoll_wait(eventfd,	events,	10)))	{
for(n	=	0;	n	< max;	n++)	{
…
}	else	{
//	Connection	socket	has	data!
char	buf[4096];
int size	=	read(connection,	buffer,	sizeof buf);
write(connection,	buffer,	size);
}
}}
What	is	the	node	event	loop?
A	semi-infinite	loop,	polling	and	blocking	on	the	O/S	until	some	in	a	set	of	file	descriptors	are	ready.
When	does	node	exit?
It	exits	when	it	no	longer	has	any	events	to	wait	for,	at	which	point	the	loop	must	complete.
Note:	.unref() marks	handles	that	are	being	waited	on	in	the	loop	as	"not	counting"	towards	keeping	the	event	loop	alive.
Can	we	poll	for	all	Node.js events?
Yes	and	no.	There	are	basically	three	classes	of	things:
1. Pollable file	descriptors:	can	be	directly	waited	on
2. Time:	next	timeout	can	be	directly	waited	on
3. Everything	else:	must	happen	off	loop,	and	signal	back	to	the	loop	when	done
Pollable:	sockets	(net/dgram/http/tls/https/child_process pipes/stdin,out,err)
Classic,	well	supported.
Pollable:	time	(timeouts	and	intervals)
poll(...,	int timeout)
kqueue(...,	struct timespec*	timeout)
epoll_wait(...,	int timeout,	...)
timeout resolution	is	milliseconds,	timespec is	nanoseconds,	but	both	are	rounded	up to	system	clock	granularity.
Only	one	timeout	at	a	time	can	be	waited	on,	but	Node.js keeps	all	timeouts	sorted,	and	sets	the	timeout	value	to	the	next	one.
Not	pollable:	file	system
Everything	in	fs.* uses	the	uv thread	pool	(unless	they	are	sync).
The	blocking	call	is	made	by	a	thread,	and	when	it	completes,	readiness	is	signaled	back	to	the	event	loop	using	either	an	eventfd
or	a	self-pipe.
Aside:	self-pipe
A	pipe,	where	one	end	is	written	to	by	a	thread	or	signal	handler,	and	the	other	end	is	polled	in	the	loop.	Traditional	way	to	
"wake	up"	a	polling	loop	when	the	event	to	wait	for	is	not	directly	representable	as	a	file	descriptor.
Sometimes	pollable:	dns
• dns.lookup() calls	getaddrinfo(),	a	function	in	the	system	resolver	library	that	makes	blocking	socket	calls	and	cannot	be	
integrated		into	a	polling	loop.	Like	fs.*,	it’s	called	in	the	thread	pool.
• dns.<everything	else> calls	functions	in	c-ares,	a	non-blocking	DNS	resolver,	and	integrates	with	the	loop,	not the	thread	pool.
Docs	bend	over	backwards	to	explain	how	these	two	differ,	but	now	that	you	know	that	blocking	library	calls	must	be	shunted	off	
to	the	thread	pool,	whereas	DNS	queries	use	TCP/UDP	sockets	and	can	integrate	into	the	event	loop,	the	distinction	should	be	
clear.
Important	notes	about	the	UV	thread	pool
It	is	shared	by:
• fs,
• dns,
• http.get/request() (if	called	with	a	name,	dns.lookup() is	used),	and
• any	C++	addons that	use	it.
Default	number	of	threads	is	4,	significantly	parallel	users	of	the	above	should	increase	the	size.
Hints:
• Resolve	DNS	names	yourself,	directly,	using	the	direct	APIs	to	avoid		dns.lookup(),	and	stay	out	of	the	thread	pool.
• Increase	the	thread	pool	size	with	UV_THREADPOOL_SIZE.
Pollable:	signals
The	ultimate	async...	uses	the	self-pipe	pattern	to	write	the	signal	number	to	the	loop.
Note	that	listening	for	signals	doesn't	"ref"	the	event	loop,	which	is	consistent	with	signal	usage	as	a	"probably	won't	happen"	
IPC	mechanism.
Pollable:	child	processes
• Unix	signals	child	process	termination	with	SIGCHLD
• Pipes	between	the	parent	and	child	are	pollable.
Sometimes	pollable:	C++	addons
Addons should	use	the	UV thread	pool	or	integrate	with	the	loop,	but	can	do	anything,	including	making	loop-blocking	system	
calls	(perhaps	unintentionally).
Hints:
• Review	addon code
• Track	loop	metrics
Sometimes	pollable:	C++	addons
Addons should	use	the	UV thread	pool	or	integrate	with	the	loop,	but	can	do	anything,	including	making	loop-blocking	system	
calls	(perhaps	unintentionally).
Hints:
• Review	addon code
• Track	loop	metrics
You	should	now	be	able	to	describe:
• What	is	the	event	loop
• When	is	node	multi-threaded
• Why	it	"scales	well"
This	talk,	including	versions	of	the	pseudo	"C"	code	here	to	compile	and	play	with:
• https://goo.gl/N7oLVK or		https://gist.github.com/sam-github/71d0ebf53a63ab7ca35a1eccc8536e47
Bert	Belder's talk	about	the	Node.js event	loop	from	a	higher	level,	the"outside in":
• https://goo.gl/EIPclI or		https://www.youtube.com/watch?v=PNa9OMajw9w&list=PLfMzBWSH11xaxRcsreXF-
jB16geIJ8Foc&index=35

Node's Event Loop From the Inside Out - Sam Roberts, IBM