Mercurial
comparison third_party/libuv/docs/src/guide/basics.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 Basics of libuv | |
| 2 =============== | |
| 3 | |
| 4 libuv enforces an **asynchronous**, **event-driven** style of programming. Its | |
| 5 core job is to provide an event loop and callback based notifications of I/O | |
| 6 and other activities. libuv offers core utilities like timers, non-blocking | |
| 7 networking support, asynchronous file system access, child processes and more. | |
| 8 | |
| 9 Event loops | |
| 10 ----------- | |
| 11 | |
| 12 In event-driven programming, an application expresses interest in certain events | |
| 13 and respond to them when they occur. The responsibility of gathering events | |
| 14 from the operating system or monitoring other sources of events is handled by | |
| 15 libuv, and the user can register callbacks to be invoked when an event occurs. | |
| 16 The event-loop usually keeps running *forever*. In pseudocode: | |
| 17 | |
| 18 .. code-block:: python | |
| 19 | |
| 20 while there are still events to process: | |
| 21 e = get the next event | |
| 22 if there is a callback associated with e: | |
| 23 call the callback | |
| 24 | |
| 25 Some examples of events are: | |
| 26 | |
| 27 * File is ready for writing | |
| 28 * A socket has data ready to be read | |
| 29 * A timer has timed out | |
| 30 | |
| 31 This event loop is encapsulated by ``uv_run()`` -- the end-all function when using | |
| 32 libuv. | |
| 33 | |
| 34 The most common activity of systems programs is to deal with input and output, | |
| 35 rather than a lot of number-crunching. The problem with using conventional | |
| 36 input/output functions (``read``, ``fprintf``, etc.) is that they are | |
| 37 **blocking**. The actual write to a hard disk or reading from a network, takes | |
| 38 a disproportionately long time compared to the speed of the processor. The | |
| 39 functions don't return until the task is done, so that your program is doing | |
| 40 nothing. For programs which require high performance this is a major roadblock | |
| 41 as other activities and other I/O operations are kept waiting. | |
| 42 | |
| 43 One of the standard solutions is to use threads. Each blocking I/O operation is | |
| 44 started in a separate thread (or in a thread pool). When the blocking function | |
| 45 gets invoked in the thread, the operating system can schedule another thread to run, | |
| 46 which actually needs the CPU. | |
| 47 | |
| 48 The approach followed by libuv uses another style, which is the **asynchronous, | |
| 49 non-blocking** style. Most modern operating systems provide event notification | |
| 50 subsystems. For example, a normal ``read`` call on a socket would block until | |
| 51 the sender actually sent something. Instead, the application can request the | |
| 52 operating system to watch the socket and put an event notification in the | |
| 53 queue. The application can inspect the events at its convenience (perhaps doing | |
| 54 some number crunching before to use the processor to the maximum) and grab the | |
| 55 data. It is **asynchronous** because the application expressed interest at one | |
| 56 point, then used the data at another point (in time and space). It is | |
| 57 **non-blocking** because the application process was free to do other tasks. | |
| 58 This fits in well with libuv's event-loop approach, since the operating system | |
| 59 events can be treated as just another libuv event. The non-blocking ensures | |
| 60 that other events can continue to be handled as fast as they come in [#]_. | |
| 61 | |
| 62 .. NOTE:: | |
| 63 | |
| 64 How the I/O is run in the background is not of our concern, but due to the | |
| 65 way our computer hardware works, with the thread as the basic unit of the | |
| 66 processor, libuv and OSes will usually run background/worker threads and/or | |
| 67 polling to perform tasks in a non-blocking manner. | |
| 68 | |
| 69 Bert Belder, one of the libuv core developers has a small video explaining the | |
| 70 architecture of libuv and its background. If you have no prior experience with | |
| 71 either libuv or libev, it is a quick, useful watch. | |
| 72 | |
| 73 libuv's event loop is explained in more detail in the `documentation | |
| 74 <https://docs.libuv.org/en/v1.x/design.html#the-i-o-loop>`_. | |
| 75 | |
| 76 .. raw:: html | |
| 77 | |
| 78 <iframe width="560" height="315" | |
| 79 src="https://www.youtube-nocookie.com/embed/nGn60vDSxQ4" frameborder="0" | |
| 80 allowfullscreen></iframe> | |
| 81 | |
| 82 Hello World | |
| 83 ----------- | |
| 84 | |
| 85 With the basics out of the way, let's write our first libuv program. It does | |
| 86 nothing, except start a loop which will exit immediately. | |
| 87 | |
| 88 .. rubric:: helloworld/main.c | |
| 89 .. literalinclude:: ../../code/helloworld/main.c | |
| 90 :language: c | |
| 91 :linenos: | |
| 92 | |
| 93 This program quits immediately because it has no events to process. A libuv | |
| 94 event loop has to be told to watch out for events using the various API | |
| 95 functions. | |
| 96 | |
| 97 Starting with libuv v1.0, users should allocate the memory for the loops before | |
| 98 initializing it with ``uv_loop_init(uv_loop_t *)``. This allows you to plug in | |
| 99 custom memory management. Remember to de-initialize the loop using | |
| 100 ``uv_loop_close(uv_loop_t *)`` and then delete the storage. The examples never | |
| 101 close loops since the program quits after the loop ends and the system will | |
| 102 reclaim memory. Production grade projects, especially long running systems | |
| 103 programs, should take care to release correctly. | |
| 104 | |
| 105 Default loop | |
| 106 ++++++++++++ | |
| 107 | |
| 108 A default loop is provided by libuv and can be accessed using | |
| 109 ``uv_default_loop()``. You should use this loop if you only want a single | |
| 110 loop. | |
| 111 | |
| 112 .. rubric:: default-loop/main.c | |
| 113 .. literalinclude:: ../../code/default-loop/main.c | |
| 114 :language: c | |
| 115 :linenos: | |
| 116 | |
| 117 .. note:: | |
| 118 | |
| 119 node.js uses the default loop as its main loop. If you are writing bindings | |
| 120 you should be aware of this. | |
| 121 | |
| 122 .. _libuv-error-handling: | |
| 123 | |
| 124 Error handling | |
| 125 -------------- | |
| 126 | |
| 127 Initialization functions or synchronous functions which may fail return a negative number on error. Async functions that may fail will pass a status parameter to their callbacks. The error messages are defined as ``UV_E*`` `constants`_. | |
| 128 | |
| 129 .. _constants: https://docs.libuv.org/en/v1.x/errors.html#error-constants | |
| 130 | |
| 131 You can use the ``uv_strerror(int)`` and ``uv_err_name(int)`` functions | |
| 132 to get a ``const char *`` describing the error or the error name respectively. | |
| 133 | |
| 134 I/O read callbacks (such as for files and sockets) are passed a parameter ``nread``. If ``nread`` is less than 0, there was an error (UV_EOF is the end of file error, which you may want to handle differently). | |
| 135 | |
| 136 Handles and Requests | |
| 137 -------------------- | |
| 138 | |
| 139 libuv works by the user expressing interest in particular events. This is | |
| 140 usually done by creating a **handle** to an I/O device, timer or process. | |
| 141 Handles are opaque structs named as ``uv_TYPE_t`` where type signifies what the | |
| 142 handle is used for. | |
| 143 | |
| 144 .. rubric:: libuv watchers | |
| 145 .. code-block:: c | |
| 146 | |
| 147 /* Handle types. */ | |
| 148 typedef struct uv_loop_s uv_loop_t; | |
| 149 typedef struct uv_handle_s uv_handle_t; | |
| 150 typedef struct uv_dir_s uv_dir_t; | |
| 151 typedef struct uv_stream_s uv_stream_t; | |
| 152 typedef struct uv_tcp_s uv_tcp_t; | |
| 153 typedef struct uv_udp_s uv_udp_t; | |
| 154 typedef struct uv_pipe_s uv_pipe_t; | |
| 155 typedef struct uv_tty_s uv_tty_t; | |
| 156 typedef struct uv_poll_s uv_poll_t; | |
| 157 typedef struct uv_timer_s uv_timer_t; | |
| 158 typedef struct uv_prepare_s uv_prepare_t; | |
| 159 typedef struct uv_check_s uv_check_t; | |
| 160 typedef struct uv_idle_s uv_idle_t; | |
| 161 typedef struct uv_async_s uv_async_t; | |
| 162 typedef struct uv_process_s uv_process_t; | |
| 163 typedef struct uv_fs_event_s uv_fs_event_t; | |
| 164 typedef struct uv_fs_poll_s uv_fs_poll_t; | |
| 165 typedef struct uv_signal_s uv_signal_t; | |
| 166 | |
| 167 /* Request types. */ | |
| 168 typedef struct uv_req_s uv_req_t; | |
| 169 typedef struct uv_getaddrinfo_s uv_getaddrinfo_t; | |
| 170 typedef struct uv_getnameinfo_s uv_getnameinfo_t; | |
| 171 typedef struct uv_shutdown_s uv_shutdown_t; | |
| 172 typedef struct uv_write_s uv_write_t; | |
| 173 typedef struct uv_connect_s uv_connect_t; | |
| 174 typedef struct uv_udp_send_s uv_udp_send_t; | |
| 175 typedef struct uv_fs_s uv_fs_t; | |
| 176 typedef struct uv_work_s uv_work_t; | |
| 177 typedef struct uv_random_s uv_random_t; | |
| 178 | |
| 179 /* None of the above. */ | |
| 180 typedef struct uv_env_item_s uv_env_item_t; | |
| 181 typedef struct uv_cpu_info_s uv_cpu_info_t; | |
| 182 typedef struct uv_interface_address_s uv_interface_address_t; | |
| 183 typedef struct uv_dirent_s uv_dirent_t; | |
| 184 typedef struct uv_passwd_s uv_passwd_t; | |
| 185 typedef struct uv_utsname_s uv_utsname_t; | |
| 186 typedef struct uv_statfs_s uv_statfs_t; | |
| 187 | |
| 188 | |
| 189 Handles represent long-lived objects. Async operations on such handles are | |
| 190 identified using **requests**. A request is short-lived (usually used across | |
| 191 only one callback) and usually indicates one I/O operation on a handle. | |
| 192 Requests are used to preserve context between the initiation and the callback | |
| 193 of individual actions. For example, an UDP socket is represented by | |
| 194 a ``uv_udp_t``, while individual writes to the socket use a ``uv_udp_send_t`` | |
| 195 structure that is passed to the callback after the write is done. | |
| 196 | |
| 197 Handles are setup by a corresponding:: | |
| 198 | |
| 199 uv_TYPE_init(uv_loop_t *, uv_TYPE_t *) | |
| 200 | |
| 201 function. | |
| 202 | |
| 203 Callbacks are functions which are called by libuv whenever an event the watcher | |
| 204 is interested in has taken place. Application specific logic will usually be | |
| 205 implemented in the callback. For example, an IO watcher's callback will receive | |
| 206 the data read from a file, a timer callback will be triggered on timeout and so | |
| 207 on. | |
| 208 | |
| 209 Idling | |
| 210 ++++++ | |
| 211 | |
| 212 Here is an example of using an idle handle. The callback is called once on | |
| 213 every turn of the event loop. A use case for idle handles is discussed in | |
| 214 :doc:`utilities`. Let us use an idle watcher to look at the watcher life cycle | |
| 215 and see how ``uv_run()`` will now block because a watcher is present. The idle | |
| 216 watcher is stopped when the count is reached and ``uv_run()`` exits since no | |
| 217 event watchers are active. | |
| 218 | |
| 219 .. rubric:: idle-basic/main.c | |
| 220 .. literalinclude:: ../../code/idle-basic/main.c | |
| 221 :language: c | |
| 222 :emphasize-lines: 6,10,14-17 | |
| 223 | |
| 224 Storing context | |
| 225 +++++++++++++++ | |
| 226 | |
| 227 In callback based programming style you'll often want to pass some 'context' -- | |
| 228 application specific information -- between the call site and the callback. All | |
| 229 handles and requests have a ``void* data`` member which you can set to the | |
| 230 context and cast back in the callback. This is a common pattern used throughout | |
| 231 the C library ecosystem. In addition ``uv_loop_t`` also has a similar data | |
| 232 member. | |
| 233 | |
| 234 ---- | |
| 235 | |
| 236 .. [#] Depending on the capacity of the hardware of course. |