• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1
2.. _design:
3
4Design overview
5===============
6
7libuv is cross-platform support library which was originally written for `Node.js`_. It's designed
8around the event-driven asynchronous I/O model.
9
10.. _Node.js: https://nodejs.org
11
12The library provides much more than a simple abstraction over different I/O polling mechanisms:
13'handles' and 'streams' provide a high level abstraction for sockets and other entities;
14cross-platform file I/O and threading functionality is also provided, amongst other things.
15
16Here is a diagram illustrating the different parts that compose libuv and what subsystem they
17relate to:
18
19.. image:: static/architecture.png
20    :scale: 75%
21    :align: center
22
23
24Handles and requests
25^^^^^^^^^^^^^^^^^^^^
26
27libuv provides users with 2 abstractions to work with, in combination with the event loop:
28handles and requests.
29
30Handles represent long-lived objects capable of performing certain operations while active. Some examples:
31
32- A prepare handle gets its callback called once every loop iteration when active.
33- A TCP server handle that gets its connection callback called every time there is a new connection.
34
35Requests represent (typically) short-lived operations. These operations can be performed over a
36handle: write requests are used to write data on a handle; or standalone: getaddrinfo requests
37don't need a handle they run directly on the loop.
38
39
40The I/O loop
41^^^^^^^^^^^^
42
43The I/O (or event) loop is the central part of libuv. It establishes the content for all I/O
44operations, and it's meant to be tied to a single thread. One can run multiple event loops
45as long as each runs in a different thread. The libuv event loop (or any other API involving
46the loop or handles, for that matter) **is not thread-safe** except where stated otherwise.
47
48The event loop follows the rather usual single threaded asynchronous I/O approach: all (network)
49I/O is performed on non-blocking sockets which are polled using the best mechanism available
50on the given platform: epoll on Linux, kqueue on OSX and other BSDs, event ports on SunOS and IOCP
51on Windows. As part of a loop iteration the loop will block waiting for I/O activity on sockets
52which have been added to the poller and callbacks will be fired indicating socket conditions
53(readable, writable hangup) so handles can read, write or perform the desired I/O operation.
54
55In order to better understand how the event loop operates, the following diagram illustrates all
56stages of a loop iteration:
57
58.. image:: static/loop_iteration.png
59    :scale: 75%
60    :align: center
61
62
63#. The loop concept of 'now' is updated. The event loop caches the current time at the start of
64   the event loop tick in order to reduce the number of time-related system calls.
65
66#. If the loop is *alive*  an iteration is started, otherwise the loop will exit immediately. So,
67   when is a loop considered to be *alive*? If a loop has active and ref'd handles, active
68   requests or closing handles it's considered to be *alive*.
69
70#. Due timers are run. All active timers scheduled for a time before the loop's concept of *now*
71   get their callbacks called.
72
73#. Pending callbacks are called. All I/O callbacks are called right after polling for I/O, for the
74   most part. There are cases, however, in which calling such a callback is deferred for the next
75   loop iteration. If the previous iteration deferred any I/O callback it will be run at this point.
76
77#. Idle handle callbacks are called. Despite the unfortunate name, idle handles are run on every
78   loop iteration, if they are active.
79
80#. Prepare handle callbacks are called. Prepare handles get their callbacks called right before
81   the loop will block for I/O.
82
83#. Poll timeout is calculated. Before blocking for I/O the loop calculates for how long it should
84   block. These are the rules when calculating the timeout:
85
86        * If the loop was run with the ``UV_RUN_NOWAIT`` flag, the timeout is 0.
87        * If the loop is going to be stopped (:c:func:`uv_stop` was called), the timeout is 0.
88        * If there are no active handles or requests, the timeout is 0.
89        * If there are any idle handles active, the timeout is 0.
90        * If there are any handles pending to be closed, the timeout is 0.
91        * If none of the above cases matches, the timeout of the closest timer is taken, or
92          if there are no active timers, infinity.
93
94#. The loop blocks for I/O. At this point the loop will block for I/O for the duration calculated
95   in the previous step. All I/O related handles that were monitoring a given file descriptor
96   for a read or write operation get their callbacks called at this point.
97
98#. Check handle callbacks are called. Check handles get their callbacks called right after the
99   loop has blocked for I/O. Check handles are essentially the counterpart of prepare handles.
100
101#. Close callbacks are called. If a handle was closed by calling :c:func:`uv_close` it will
102   get the close callback called.
103
104#. Special case in case the loop was run with ``UV_RUN_ONCE``, as it implies forward progress.
105   It's possible that no I/O callbacks were fired after blocking for I/O, but some time has passed
106   so there might be timers which are due, those timers get their callbacks called.
107
108#. Iteration ends. If the loop was run with ``UV_RUN_NOWAIT`` or ``UV_RUN_ONCE`` modes the
109   iteration ends and :c:func:`uv_run` will return. If the loop was run with ``UV_RUN_DEFAULT``
110   it will continue from the start if it's still *alive*, otherwise it will also end.
111
112
113.. important::
114    libuv uses a thread pool to make asynchronous file I/O operations possible, but
115    network I/O is **always** performed in a single thread, each loop's thread.
116
117.. note::
118    While the polling mechanism is different, libuv makes the execution model consistent
119    across Unix systems and Windows.
120
121
122File I/O
123^^^^^^^^
124
125Unlike network I/O, there are no platform-specific file I/O primitives libuv could rely on,
126so the current approach is to run blocking file I/O operations in a thread pool.
127
128For a thorough explanation of the cross-platform file I/O landscape, check out
129`this post <https://blog.libtorrent.org/2012/10/asynchronous-disk-io/>`_.
130
131libuv currently uses a global thread pool on which all loops can queue work. 3 types of
132operations are currently run on this pool:
133
134    * File system operations
135    * DNS functions (getaddrinfo and getnameinfo)
136    * User specified code via :c:func:`uv_queue_work`
137
138.. warning::
139    See the :c:ref:`threadpool` section for more details, but keep in mind the thread pool size
140    is quite limited.
141