Mercurial
comparison third_party/libuv/docs/src/guide/utilities.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 Utilities | |
| 2 ========= | |
| 3 | |
| 4 This chapter catalogues tools and techniques which are useful for common tasks. | |
| 5 The `libev man page`_ already covers some patterns which can be adopted to | |
| 6 libuv through simple API changes. It also covers parts of the libuv API that | |
| 7 don't require entire chapters dedicated to them. | |
| 8 | |
| 9 Timers | |
| 10 ------ | |
| 11 | |
| 12 Timers invoke the callback after a certain time has elapsed since the timer was | |
| 13 started. libuv timers can also be set to invoke at regular intervals instead of | |
| 14 just once. | |
| 15 | |
| 16 Simple use is to init a watcher and start it with a ``timeout``, and optional ``repeat``. | |
| 17 Timers can be stopped at any time. | |
| 18 | |
| 19 .. code-block:: c | |
| 20 | |
| 21 uv_timer_t timer_req; | |
| 22 | |
| 23 uv_timer_init(loop, &timer_req); | |
| 24 uv_timer_start(&timer_req, callback, 5000, 2000); | |
| 25 | |
| 26 will start a repeating timer, which first starts 5 seconds (the ``timeout``) after the execution | |
| 27 of ``uv_timer_start``, then repeats every 2 seconds (the ``repeat``). Use: | |
| 28 | |
| 29 .. code-block:: c | |
| 30 | |
| 31 uv_timer_stop(&timer_req); | |
| 32 | |
| 33 to stop the timer. This can be used safely from within the callback as well. | |
| 34 | |
| 35 The repeat interval can be modified at any time with:: | |
| 36 | |
| 37 uv_timer_set_repeat(uv_timer_t *timer, int64_t repeat); | |
| 38 | |
| 39 which will take effect **when possible**. If this function is called from | |
| 40 a timer callback, it means: | |
| 41 | |
| 42 * If the timer was non-repeating, the timer has already been stopped. Use | |
| 43 ``uv_timer_start`` again. | |
| 44 * If the timer is repeating, the next timeout has already been scheduled, so | |
| 45 the old repeat interval will be used once more before the timer switches to | |
| 46 the new interval. | |
| 47 | |
| 48 The utility function:: | |
| 49 | |
| 50 int uv_timer_again(uv_timer_t *) | |
| 51 | |
| 52 applies **only to repeating timers** and is equivalent to stopping the timer | |
| 53 and then starting it with both initial ``timeout`` and ``repeat`` set to the | |
| 54 old ``repeat`` value. If the timer hasn't been started it fails (error code | |
| 55 ``UV_EINVAL``) and returns -1. | |
| 56 | |
| 57 An actual timer example is in the :ref:`reference count section | |
| 58 <reference-count>`. | |
| 59 | |
| 60 .. _reference-count: | |
| 61 | |
| 62 Event loop reference count | |
| 63 -------------------------- | |
| 64 | |
| 65 The event loop only runs as long as there are active handles. This system | |
| 66 works by having every handle increase the reference count of the event loop | |
| 67 when it is started and decreasing the reference count when stopped. It is also | |
| 68 possible to manually change the reference count of handles using:: | |
| 69 | |
| 70 void uv_ref(uv_handle_t*); | |
| 71 void uv_unref(uv_handle_t*); | |
| 72 | |
| 73 These functions can be used to allow a loop to exit even when a watcher is | |
| 74 active or to use custom objects to keep the loop alive. | |
| 75 | |
| 76 The latter can be used with interval timers. You might have a garbage collector | |
| 77 which runs every X seconds, or your network service might send a heartbeat to | |
| 78 others periodically, but you don't want to have to stop them along all clean | |
| 79 exit paths or error scenarios. Or you want the program to exit when all your | |
| 80 other watchers are done. In that case just unref the timer immediately after | |
| 81 creation so that if it is the only watcher running then ``uv_run`` will still | |
| 82 exit. | |
| 83 | |
| 84 This is also used in node.js where some libuv methods are being bubbled up to | |
| 85 the JS API. A ``uv_handle_t`` (the superclass of all watchers) is created per | |
| 86 JS object and can be ref/unrefed. | |
| 87 | |
| 88 .. rubric:: ref-timer/main.c | |
| 89 .. literalinclude:: ../../code/ref-timer/main.c | |
| 90 :language: c | |
| 91 :linenos: | |
| 92 :lines: 5-8, 17- | |
| 93 :emphasize-lines: 9 | |
| 94 | |
| 95 We initialize the garbage collector timer, then immediately ``unref`` it. | |
| 96 Observe how after 9 seconds, when the fake job is done, the program | |
| 97 automatically exits, even though the garbage collector is still running. | |
| 98 | |
| 99 Idler pattern | |
| 100 ------------- | |
| 101 | |
| 102 The callbacks of idle handles are invoked once per event loop. The idle | |
| 103 callback can be used to perform some very low priority activity. For example, | |
| 104 you could dispatch a summary of the daily application performance to the | |
| 105 developers for analysis during periods of idleness, or use the application's | |
| 106 CPU time to perform SETI calculations :) An idle watcher is also useful in | |
| 107 a GUI application. Say you are using an event loop for a file download. If the | |
| 108 TCP socket is still being established and no other events are present your | |
| 109 event loop will pause (**block**), which means your progress bar will freeze | |
| 110 and the user will face an unresponsive application. In such a case queue up and | |
| 111 idle watcher to keep the UI operational. | |
| 112 | |
| 113 .. rubric:: idle-compute/main.c | |
| 114 .. literalinclude:: ../../code/idle-compute/main.c | |
| 115 :language: c | |
| 116 :linenos: | |
| 117 :lines: 5-9, 34- | |
| 118 :emphasize-lines: 13 | |
| 119 | |
| 120 Here we initialize the idle watcher and queue it up along with the actual | |
| 121 events we are interested in. ``crunch_away`` will now be called repeatedly | |
| 122 until the user types something and presses Return. Then it will be interrupted | |
| 123 for a brief amount as the loop deals with the input data, after which it will | |
| 124 keep calling the idle callback again. | |
| 125 | |
| 126 .. rubric:: idle-compute/main.c | |
| 127 .. literalinclude:: ../../code/idle-compute/main.c | |
| 128 :language: c | |
| 129 :linenos: | |
| 130 :lines: 10-19 | |
| 131 | |
| 132 .. _baton: | |
| 133 | |
| 134 Passing data to worker thread | |
| 135 ----------------------------- | |
| 136 | |
| 137 When using ``uv_queue_work`` you'll usually need to pass complex data through | |
| 138 to the worker thread. The solution is to use a ``struct`` and set | |
| 139 ``uv_work_t.data`` to point to it. A slight variation is to have the | |
| 140 ``uv_work_t`` itself as the first member of this struct (called a baton [#]_). | |
| 141 This allows cleaning up the work request and all the data in one free call. | |
| 142 | |
| 143 .. code-block:: c | |
| 144 :linenos: | |
| 145 :emphasize-lines: 2 | |
| 146 | |
| 147 struct ftp_baton { | |
| 148 uv_work_t req; | |
| 149 char *host; | |
| 150 int port; | |
| 151 char *username; | |
| 152 char *password; | |
| 153 } | |
| 154 | |
| 155 .. code-block:: c | |
| 156 :linenos: | |
| 157 :emphasize-lines: 2 | |
| 158 | |
| 159 ftp_baton *baton = (ftp_baton*) malloc(sizeof(ftp_baton)); | |
| 160 baton->req.data = (void*) baton; | |
| 161 baton->host = strdup("my.webhost.com"); | |
| 162 baton->port = 21; | |
| 163 // ... | |
| 164 | |
| 165 uv_queue_work(loop, &baton->req, ftp_session, ftp_cleanup); | |
| 166 | |
| 167 Here we create the baton and queue the task. | |
| 168 | |
| 169 Now the task function can extract the data it needs: | |
| 170 | |
| 171 .. code-block:: c | |
| 172 :linenos: | |
| 173 :emphasize-lines: 2, 12 | |
| 174 | |
| 175 void ftp_session(uv_work_t *req) { | |
| 176 ftp_baton *baton = (ftp_baton*) req->data; | |
| 177 | |
| 178 fprintf(stderr, "Connecting to %s\n", baton->host); | |
| 179 } | |
| 180 | |
| 181 void ftp_cleanup(uv_work_t *req) { | |
| 182 ftp_baton *baton = (ftp_baton*) req->data; | |
| 183 | |
| 184 free(baton->host); | |
| 185 // ... | |
| 186 free(baton); | |
| 187 } | |
| 188 | |
| 189 We then free the baton which also frees the watcher. | |
| 190 | |
| 191 External I/O with polling | |
| 192 ------------------------- | |
| 193 | |
| 194 Usually third-party libraries will handle their own I/O, and keep track of | |
| 195 their sockets and other files internally. In this case it isn't possible to use | |
| 196 the standard stream I/O operations, but the library can still be integrated | |
| 197 into the libuv event loop. All that is required is that the library allow you | |
| 198 to access the underlying file descriptors and provide functions that process | |
| 199 tasks in small increments as decided by your application. Some libraries though | |
| 200 will not allow such access, providing only a standard blocking function which | |
| 201 will perform the entire I/O transaction and only then return. It is unwise to | |
| 202 use these in the event loop thread, use the :ref:`threadpool` instead. Of | |
| 203 course, this will also mean losing granular control on the library. | |
| 204 | |
| 205 The ``uv_poll`` section of libuv simply watches file descriptors using the | |
| 206 operating system notification mechanism. In some sense, all the I/O operations | |
| 207 that libuv implements itself are also backed by ``uv_poll`` like code. Whenever | |
| 208 the OS notices a change of state in file descriptors being polled, libuv will | |
| 209 invoke the associated callback. | |
| 210 | |
| 211 Here we will walk through a simple download manager that will use libcurl_ to | |
| 212 download files. Rather than give all control to libcurl, we'll instead be | |
| 213 using the libuv event loop, and use the non-blocking, async multi_ interface to | |
| 214 progress with the download whenever libuv notifies of I/O readiness. | |
| 215 | |
| 216 .. _libcurl: https://curl.haxx.se/libcurl/ | |
| 217 .. _multi: https://curl.haxx.se/libcurl/c/libcurl-multi.html | |
| 218 | |
| 219 .. rubric:: uvwget/main.c - The setup | |
| 220 .. literalinclude:: ../../code/uvwget/main.c | |
| 221 :language: c | |
| 222 :linenos: | |
| 223 :lines: 1-9,142- | |
| 224 :emphasize-lines: 7,21,24-25 | |
| 225 | |
| 226 The way each library is integrated with libuv will vary. In the case of | |
| 227 libcurl, we can register two callbacks. The socket callback ``handle_socket`` | |
| 228 is invoked whenever the state of a socket changes and we have to start polling | |
| 229 it. ``start_timeout`` is called by libcurl to notify us of the next timeout | |
| 230 interval, after which we should drive libcurl forward regardless of I/O status. | |
| 231 This is so that libcurl can handle errors or do whatever else is required to | |
| 232 get the download moving. | |
| 233 | |
| 234 Our downloader is to be invoked as:: | |
| 235 | |
| 236 $ ./uvwget [url1] [url2] ... | |
| 237 | |
| 238 So we add each argument as a URL | |
| 239 | |
| 240 .. rubric:: uvwget/main.c - Adding urls | |
| 241 .. literalinclude:: ../../code/uvwget/main.c | |
| 242 :language: c | |
| 243 :linenos: | |
| 244 :lines: 39-56 | |
| 245 :emphasize-lines: 13-14 | |
| 246 | |
| 247 We let libcurl directly write the data to a file, but much more is possible if | |
| 248 you so desire. | |
| 249 | |
| 250 ``start_timeout`` will be called immediately the first time by libcurl, so | |
| 251 things are set in motion. This simply starts a libuv `timer <#timers>`_ which | |
| 252 drives ``curl_multi_socket_action`` with ``CURL_SOCKET_TIMEOUT`` whenever it | |
| 253 times out. ``curl_multi_socket_action`` is what drives libcurl, and what we | |
| 254 call whenever sockets change state. But before we go into that, we need to poll | |
| 255 on sockets whenever ``handle_socket`` is called. | |
| 256 | |
| 257 .. rubric:: uvwget/main.c - Setting up polling | |
| 258 .. literalinclude:: ../../code/uvwget/main.c | |
| 259 :language: c | |
| 260 :linenos: | |
| 261 :lines: 102-140 | |
| 262 :emphasize-lines: 9,11,15,21,24 | |
| 263 | |
| 264 We are interested in the socket fd ``s``, and the ``action``. For every socket | |
| 265 we create a ``uv_poll_t`` handle if it doesn't exist, and associate it with the | |
| 266 socket using ``curl_multi_assign``. This way ``socketp`` points to it whenever | |
| 267 the callback is invoked. | |
| 268 | |
| 269 In the case that the download is done or fails, libcurl requests removal of the | |
| 270 poll. So we stop and free the poll handle. | |
| 271 | |
| 272 Depending on what events libcurl wishes to watch for, we start polling with | |
| 273 ``UV_READABLE`` or ``UV_WRITABLE``. Now libuv will invoke the poll callback | |
| 274 whenever the socket is ready for reading or writing. Calling ``uv_poll_start`` | |
| 275 multiple times on the same handle is acceptable, it will just update the events | |
| 276 mask with the new value. ``curl_perform`` is the crux of this program. | |
| 277 | |
| 278 .. rubric:: uvwget/main.c - Driving libcurl. | |
| 279 .. literalinclude:: ../../code/uvwget/main.c | |
| 280 :language: c | |
| 281 :linenos: | |
| 282 :lines: 81-95 | |
| 283 :emphasize-lines: 2,6-7,12 | |
| 284 | |
| 285 The first thing we do is to stop the timer, since there has been some progress | |
| 286 in the interval. Then depending on what event triggered the callback, we set | |
| 287 the correct flags. Then we call ``curl_multi_socket_action`` with the socket | |
| 288 that progressed and the flags informing about what events happened. At this | |
| 289 point libcurl does all of its internal tasks in small increments, and will | |
| 290 attempt to return as fast as possible, which is exactly what an evented program | |
| 291 wants in its main thread. libcurl keeps queueing messages into its own queue | |
| 292 about transfer progress. In our case we are only interested in transfers that | |
| 293 are completed. So we extract these messages, and clean up handles whose | |
| 294 transfers are done. | |
| 295 | |
| 296 .. rubric:: uvwget/main.c - Reading transfer status. | |
| 297 .. literalinclude:: ../../code/uvwget/main.c | |
| 298 :language: c | |
| 299 :linenos: | |
| 300 :lines: 58-79 | |
| 301 :emphasize-lines: 6,9-10,13-14 | |
| 302 | |
| 303 Check & Prepare watchers | |
| 304 ------------------------ | |
| 305 | |
| 306 TODO | |
| 307 | |
| 308 Loading libraries | |
| 309 ----------------- | |
| 310 | |
| 311 libuv provides a cross platform API to dynamically load `shared libraries`_. | |
| 312 This can be used to implement your own plugin/extension/module system and is | |
| 313 used by node.js to implement ``require()`` support for bindings. The usage is | |
| 314 quite simple as long as your library exports the right symbols. Be careful with | |
| 315 sanity and security checks when loading third party code, otherwise your | |
| 316 program will behave unpredictably. This example implements a very simple | |
| 317 plugin system which does nothing except print the name of the plugin. | |
| 318 | |
| 319 Let us first look at the interface provided to plugin authors. | |
| 320 | |
| 321 .. rubric:: plugin/plugin.h | |
| 322 .. literalinclude:: ../../code/plugin/plugin.h | |
| 323 :language: c | |
| 324 :linenos: | |
| 325 | |
| 326 You can similarly add more functions that plugin authors can use to do useful | |
| 327 things in your application [#]_. A sample plugin using this API is: | |
| 328 | |
| 329 .. rubric:: plugin/hello.c | |
| 330 .. literalinclude:: ../../code/plugin/hello.c | |
| 331 :language: c | |
| 332 :linenos: | |
| 333 | |
| 334 Our interface defines that all plugins should have an ``initialize`` function | |
| 335 which will be called by the application. This plugin is compiled as a shared | |
| 336 library and can be loaded by running our application:: | |
| 337 | |
| 338 $ ./plugin libhello.dylib | |
| 339 Loading libhello.dylib | |
| 340 Registered plugin "Hello World!" | |
| 341 | |
| 342 .. NOTE:: | |
| 343 | |
| 344 The shared library filename will be different depending on platforms. On | |
| 345 Linux it is ``libhello.so``. | |
| 346 | |
| 347 This is done by using ``uv_dlopen`` to first load the shared library | |
| 348 ``libhello.dylib``. Then we get access to the ``initialize`` function using | |
| 349 ``uv_dlsym`` and invoke it. | |
| 350 | |
| 351 .. rubric:: plugin/main.c | |
| 352 .. literalinclude:: ../../code/plugin/main.c | |
| 353 :language: c | |
| 354 :linenos: | |
| 355 :lines: 7- | |
| 356 :emphasize-lines: 15, 18, 24 | |
| 357 | |
| 358 ``uv_dlopen`` expects a path to the shared library and sets the opaque | |
| 359 ``uv_lib_t`` pointer. It returns 0 on success, -1 on error. Use ``uv_dlerror`` | |
| 360 to get the error message. | |
| 361 | |
| 362 ``uv_dlsym`` stores a pointer to the symbol in the second argument in the third | |
| 363 argument. ``init_plugin_function`` is a function pointer to the sort of | |
| 364 function we are looking for in the application's plugins. | |
| 365 | |
| 366 .. _shared libraries: https://en.wikipedia.org/wiki/Shared_library | |
| 367 | |
| 368 TTY | |
| 369 --- | |
| 370 | |
| 371 Text terminals have supported basic formatting for a long time, with a `pretty | |
| 372 standardised`_ command set. This formatting is often used by programs to | |
| 373 improve the readability of terminal output. For example ``grep --colour``. | |
| 374 libuv provides the ``uv_tty_t`` abstraction (a stream) and related functions to | |
| 375 implement the ANSI escape codes across all platforms. By this I mean that libuv | |
| 376 converts ANSI codes to the Windows equivalent, and provides functions to get | |
| 377 terminal information. | |
| 378 | |
| 379 .. _pretty standardised: https://en.wikipedia.org/wiki/ANSI_escape_sequences | |
| 380 | |
| 381 The first thing to do is to initialize a ``uv_tty_t`` with the file descriptor | |
| 382 it reads/writes from. This is achieved with:: | |
| 383 | |
| 384 int uv_tty_init(uv_loop_t*, uv_tty_t*, uv_file fd, int unused) | |
| 385 | |
| 386 The ``unused`` parameter is now auto-detected and ignored. It previously needed | |
| 387 to be set to use ``uv_read_start()`` on the stream. | |
| 388 | |
| 389 It is then best to use ``uv_tty_set_mode`` to set the mode to *normal* | |
| 390 which enables most TTY formatting, flow-control and other settings. Other_ modes | |
| 391 are also available. | |
| 392 | |
| 393 .. _Other: http://docs.libuv.org/en/v1.x/tty.html#c.uv_tty_mode_t | |
| 394 | |
| 395 Remember to call ``uv_tty_reset_mode`` when your program exits to restore the | |
| 396 state of the terminal. Just good manners. Another set of good manners is to be | |
| 397 aware of redirection. If the user redirects the output of your command to | |
| 398 a file, control sequences should not be written as they impede readability and | |
| 399 ``grep``. To check if the file descriptor is indeed a TTY, call | |
| 400 ``uv_guess_handle`` with the file descriptor and compare the return value with | |
| 401 ``UV_TTY``. | |
| 402 | |
| 403 Here is a simple example which prints white text on a red background: | |
| 404 | |
| 405 .. rubric:: tty/main.c | |
| 406 .. literalinclude:: ../../code/tty/main.c | |
| 407 :language: c | |
| 408 :linenos: | |
| 409 :emphasize-lines: 11-12,14,17,27 | |
| 410 | |
| 411 The final TTY helper is ``uv_tty_get_winsize()`` which is used to get the | |
| 412 width and height of the terminal and returns ``0`` on success. Here is a small | |
| 413 program which does some animation using the function and character position | |
| 414 escape codes. | |
| 415 | |
| 416 .. rubric:: tty-gravity/main.c | |
| 417 .. literalinclude:: ../../code/tty-gravity/main.c | |
| 418 :language: c | |
| 419 :linenos: | |
| 420 :emphasize-lines: 19,25,38 | |
| 421 | |
| 422 The escape codes are: | |
| 423 | |
| 424 ====== ======================= | |
| 425 Code Meaning | |
| 426 ====== ======================= | |
| 427 *2* J Clear part of the screen, 2 is entire screen | |
| 428 H Moves cursor to certain position, default top-left | |
| 429 *n* B Moves cursor down by n lines | |
| 430 *n* C Moves cursor right by n columns | |
| 431 m Obeys string of display settings, in this case green background (40+2), white text (30+7) | |
| 432 ====== ======================= | |
| 433 | |
| 434 As you can see this is very useful to produce nicely formatted output, or even | |
| 435 console based arcade games if that tickles your fancy. For fancier control you | |
| 436 can try `ncurses`_. | |
| 437 | |
| 438 .. _ncurses: https://www.gnu.org/software/ncurses/ncurses.html | |
| 439 | |
| 440 .. versionchanged:: 1.23.1: the `readable` parameter is now unused and ignored. | |
| 441 The appropriate value will now be auto-detected from the kernel. | |
| 442 | |
| 443 ---- | |
| 444 | |
| 445 .. [#] I was first introduced to the term baton in this context, in Konstantin | |
| 446 Käfer's excellent slides on writing node.js bindings -- | |
| 447 https://kkaefer.com/node-cpp-modules/#baton | |
| 448 .. [#] mfp is My Fancy Plugin | |
| 449 | |
| 450 .. _libev man page: http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#COMMON_OR_USEFUL_IDIOMS_OR_BOTH |