Mercurial
comparison third_party/libuv/docs/src/design.rst @ 160:948de3f54cea
[ThirdParty] Added libuv
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Wed, 14 Jan 2026 19:39:52 -0800 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 159:05cf9467a1c3 | 160:948de3f54cea |
|---|---|
| 1 | |
| 2 .. _design: | |
| 3 | |
| 4 Design overview | |
| 5 =============== | |
| 6 | |
| 7 libuv is cross-platform support library which was originally written for `Node.js`_. It's designed | |
| 8 around the event-driven asynchronous I/O model. | |
| 9 | |
| 10 .. _Node.js: https://nodejs.org | |
| 11 | |
| 12 The 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; | |
| 14 cross-platform file I/O and threading functionality is also provided, amongst other things. | |
| 15 | |
| 16 Here is a diagram illustrating the different parts that compose libuv and what subsystem they | |
| 17 relate to: | |
| 18 | |
| 19 .. image:: static/architecture.png | |
| 20 :scale: 75% | |
| 21 :align: center | |
| 22 | |
| 23 | |
| 24 Handles and requests | |
| 25 ^^^^^^^^^^^^^^^^^^^^ | |
| 26 | |
| 27 libuv provides users with 2 abstractions to work with, in combination with the event loop: | |
| 28 handles and requests. | |
| 29 | |
| 30 Handles 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 | |
| 35 Requests represent (typically) short-lived operations. These operations can be performed over a | |
| 36 handle: write requests are used to write data on a handle; or standalone: getaddrinfo requests | |
| 37 don't need a handle they run directly on the loop. | |
| 38 | |
| 39 | |
| 40 The I/O loop | |
| 41 ^^^^^^^^^^^^ | |
| 42 | |
| 43 The I/O (or event) loop is the central part of libuv. It establishes the content for all I/O | |
| 44 operations, and it's meant to be tied to a single thread. One can run multiple event loops | |
| 45 as long as each runs in a different thread. The libuv event loop (or any other API involving | |
| 46 the loop or handles, for that matter) **is not thread-safe** except where stated otherwise. | |
| 47 | |
| 48 The event loop follows the rather usual single threaded asynchronous I/O approach: all (network) | |
| 49 I/O is performed on non-blocking sockets which are polled using the best mechanism available | |
| 50 on the given platform: epoll on Linux, kqueue on OSX and other BSDs, event ports on SunOS and IOCP | |
| 51 on Windows. As part of a loop iteration the loop will block waiting for I/O activity on sockets | |
| 52 which 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 | |
| 55 In order to better understand how the event loop operates, the following diagram illustrates all | |
| 56 stages 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 initially set. | |
| 64 | |
| 65 #. Due timers are run if the loop was run with ``UV_RUN_DEFAULT``. All active timers scheduled | |
| 66 for a time before the loop's concept of *now* get their callbacks called. | |
| 67 | |
| 68 #. If the loop is *alive* an iteration is started, otherwise the loop will exit immediately. So, | |
| 69 when is a loop considered to be *alive*? If a loop has active and ref'd handles, active | |
| 70 requests or closing handles it's considered to be *alive*. | |
| 71 | |
| 72 #. Pending callbacks are called. All I/O callbacks are called right after polling for I/O, for the | |
| 73 most part. There are cases, however, in which calling such a callback is deferred for the next | |
| 74 loop iteration. If the previous iteration deferred any I/O callback it will be run at this point. | |
| 75 | |
| 76 #. Idle handle callbacks are called. Despite the unfortunate name, idle handles are run on every | |
| 77 loop iteration, if they are active. | |
| 78 | |
| 79 #. Prepare handle callbacks are called. Prepare handles get their callbacks called right before | |
| 80 the loop will block for I/O. | |
| 81 | |
| 82 #. Poll timeout is calculated. Before blocking for I/O the loop calculates for how long it should | |
| 83 block. These are the rules when calculating the timeout: | |
| 84 | |
| 85 * If the loop was run with the ``UV_RUN_NOWAIT`` flag, the timeout is 0. | |
| 86 * If the loop is going to be stopped (:c:func:`uv_stop` was called), the timeout is 0. | |
| 87 * If there are no active handles or requests, the timeout is 0. | |
| 88 * If there are any idle handles active, the timeout is 0. | |
| 89 * If there are any handles pending to be closed, the timeout is 0. | |
| 90 * If none of the above cases matches, the timeout of the closest timer is taken, or | |
| 91 if there are no active timers, infinity. | |
| 92 | |
| 93 #. The loop blocks for I/O. At this point the loop will block for I/O for the duration calculated | |
| 94 in the previous step. All I/O related handles that were monitoring a given file descriptor | |
| 95 for a read or write operation get their callbacks called at this point. | |
| 96 | |
| 97 #. Check handle callbacks are called. Check handles get their callbacks called right after the | |
| 98 loop has blocked for I/O. Check handles are essentially the counterpart of prepare handles. | |
| 99 | |
| 100 #. Close callbacks are called. If a handle was closed by calling :c:func:`uv_close` it will | |
| 101 get the close callback called. | |
| 102 | |
| 103 #. The loop concept of 'now' is updated. | |
| 104 | |
| 105 #. Due timers are run. Note that 'now' is not updated again until the next loop iteration. | |
| 106 So if a timer became due while other timers were being processed, it won't be run until | |
| 107 the following event loop iteration. | |
| 108 | |
| 109 #. Iteration ends. If the loop was run with ``UV_RUN_NOWAIT`` or ``UV_RUN_ONCE`` modes the | |
| 110 iteration ends and :c:func:`uv_run` will return. If the loop was run with ``UV_RUN_DEFAULT`` | |
| 111 it will continue from the start if it's still *alive*, otherwise it will also end. | |
| 112 | |
| 113 | |
| 114 .. important:: | |
| 115 libuv uses a thread pool to make asynchronous file I/O operations possible, but | |
| 116 network I/O is **always** performed in a single thread, each loop's thread. | |
| 117 | |
| 118 .. note:: | |
| 119 While the polling mechanism is different, libuv makes the execution model consistent | |
| 120 across Unix systems and Windows. | |
| 121 | |
| 122 | |
| 123 File I/O | |
| 124 ^^^^^^^^ | |
| 125 | |
| 126 Unlike network I/O, there are no platform-specific file I/O primitives libuv could rely on, | |
| 127 so the current approach is to run blocking file I/O operations in a thread pool. | |
| 128 | |
| 129 For a thorough explanation of the cross-platform file I/O landscape, check out | |
| 130 `this post <https://blog.libtorrent.org/2012/10/asynchronous-disk-io/>`_. | |
| 131 | |
| 132 libuv currently uses a global thread pool on which all loops can queue work. 3 types of | |
| 133 operations are currently run on this pool: | |
| 134 | |
| 135 * File system operations | |
| 136 * DNS functions (getaddrinfo and getnameinfo) | |
| 137 * User specified code via :c:func:`uv_queue_work` | |
| 138 | |
| 139 .. warning:: | |
| 140 See the :c:ref:`threadpool` section for more details, but keep in mind the thread pool size | |
| 141 is quite limited. |