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