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