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 :language: c 91 :linenos: 92 :lines: 5-8, 17- 93 :emphasize-lines: 9 94 95We initialize the garbage collector timer, then immediately ``unref`` it. 96Observe how after 9 seconds, when the fake job is done, the program 97automatically exits, even though the garbage collector is still running. 98 99Idler pattern 100------------- 101 102The callbacks of idle handles are invoked once per event loop. The idle 103callback can be used to perform some very low priority activity. For example, 104you could dispatch a summary of the daily application performance to the 105developers for analysis during periods of idleness, or use the application's 106CPU time to perform SETI calculations :) An idle watcher is also useful in 107a GUI application. Say you are using an event loop for a file download. If the 108TCP socket is still being established and no other events are present your 109event loop will pause (**block**), which means your progress bar will freeze 110and the user will face an unresponsive application. In such a case queue up and 111idle 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 120Here we initialize the idle watcher and queue it up along with the actual 121events we are interested in. ``crunch_away`` will now be called repeatedly 122until the user types something and presses Return. Then it will be interrupted 123for a brief amount as the loop deals with the input data, after which it will 124keep 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 134Passing data to worker thread 135----------------------------- 136 137When using ``uv_queue_work`` you'll usually need to pass complex data through 138to 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 [#]_). 141This 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 167Here we create the baton and queue the task. 168 169Now 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 189We then free the baton which also frees the watcher. 190 191External I/O with polling 192------------------------- 193 194Usually third-party libraries will handle their own I/O, and keep track of 195their sockets and other files internally. In this case it isn't possible to use 196the standard stream I/O operations, but the library can still be integrated 197into the libuv event loop. All that is required is that the library allow you 198to access the underlying file descriptors and provide functions that process 199tasks in small increments as decided by your application. Some libraries though 200will not allow such access, providing only a standard blocking function which 201will perform the entire I/O transaction and only then return. It is unwise to 202use these in the event loop thread, use the :ref:`threadpool` instead. Of 203course, this will also mean losing granular control on the library. 204 205The ``uv_poll`` section of libuv simply watches file descriptors using the 206operating system notification mechanism. In some sense, all the I/O operations 207that libuv implements itself are also backed by ``uv_poll`` like code. Whenever 208the OS notices a change of state in file descriptors being polled, libuv will 209invoke the associated callback. 210 211Here we will walk through a simple download manager that will use libcurl_ to 212download files. Rather than give all control to libcurl, we'll instead be 213using the libuv event loop, and use the non-blocking, async multi_ interface to 214progress 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,140- 224 :emphasize-lines: 7,21,24-25 225 226The way each library is integrated with libuv will vary. In the case of 227libcurl, we can register two callbacks. The socket callback ``handle_socket`` 228is invoked whenever the state of a socket changes and we have to start polling 229it. ``start_timeout`` is called by libcurl to notify us of the next timeout 230interval, after which we should drive libcurl forward regardless of I/O status. 231This is so that libcurl can handle errors or do whatever else is required to 232get the download moving. 233 234Our downloader is to be invoked as:: 235 236 $ ./uvwget [url1] [url2] ... 237 238So we add each argument as an 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 247We let libcurl directly write the data to a file, but much more is possible if 248you so desire. 249 250``start_timeout`` will be called immediately the first time by libcurl, so 251things are set in motion. This simply starts a libuv `timer <#timers>`_ which 252drives ``curl_multi_socket_action`` with ``CURL_SOCKET_TIMEOUT`` whenever it 253times out. ``curl_multi_socket_action`` is what drives libcurl, and what we 254call whenever sockets change state. But before we go into that, we need to poll 255on 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 264We are interested in the socket fd ``s``, and the ``action``. For every socket 265we create a ``uv_poll_t`` handle if it doesn't exist, and associate it with the 266socket using ``curl_multi_assign``. This way ``socketp`` points to it whenever 267the callback is invoked. 268 269In the case that the download is done or fails, libcurl requests removal of the 270poll. So we stop and free the poll handle. 271 272Depending 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 274whenever the socket is ready for reading or writing. Calling ``uv_poll_start`` 275multiple times on the same handle is acceptable, it will just update the events 276mask 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 285The first thing we do is to stop the timer, since there has been some progress 286in the interval. Then depending on what event triggered the callback, we set 287the correct flags. Then we call ``curl_multi_socket_action`` with the socket 288that progressed and the flags informing about what events happened. At this 289point libcurl does all of its internal tasks in small increments, and will 290attempt to return as fast as possible, which is exactly what an evented program 291wants in its main thread. libcurl keeps queueing messages into its own queue 292about transfer progress. In our case we are only interested in transfers that 293are completed. So we extract these messages, and clean up handles whose 294transfers 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 303Check & Prepare watchers 304------------------------ 305 306TODO 307 308Loading libraries 309----------------- 310 311libuv provides a cross platform API to dynamically load `shared libraries`_. 312This can be used to implement your own plugin/extension/module system and is 313used by node.js to implement ``require()`` support for bindings. The usage is 314quite simple as long as your library exports the right symbols. Be careful with 315sanity and security checks when loading third party code, otherwise your 316program will behave unpredictably. This example implements a very simple 317plugin system which does nothing except print the name of the plugin. 318 319Let 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 326You can similarly add more functions that plugin authors can use to do useful 327things 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 334Our interface defines that all plugins should have an ``initialize`` function 335which will be called by the application. This plugin is compiled as a shared 336library 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 347This 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`` 360to get the error message. 361 362``uv_dlsym`` stores a pointer to the symbol in the second argument in the third 363argument. ``init_plugin_function`` is a function pointer to the sort of 364function we are looking for in the application's plugins. 365 366.. _shared libraries: https://en.wikipedia.org/wiki/Shared_library#Shared_libraries 367 368TTY 369--- 370 371Text terminals have supported basic formatting for a long time, with a `pretty 372standardised`_ command set. This formatting is often used by programs to 373improve the readability of terminal output. For example ``grep --colour``. 374libuv provides the ``uv_tty_t`` abstraction (a stream) and related functions to 375implement the ANSI escape codes across all platforms. By this I mean that libuv 376converts ANSI codes to the Windows equivalent, and provides functions to get 377terminal information. 378 379.. _pretty standardised: https://en.wikipedia.org/wiki/ANSI_escape_sequences 380 381The first thing to do is to initialize a ``uv_tty_t`` with the file descriptor 382it 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 386The ``unused`` parameter is now auto-detected and ignored. It previously needed 387to be set to use ``uv_read_start()`` on the stream. 388 389It is then best to use ``uv_tty_set_mode`` to set the mode to *normal* 390which enables most TTY formatting, flow-control and other settings. Other_ modes 391are also available. 392 393.. _Other: http://docs.libuv.org/en/v1.x/tty.html#c.uv_tty_mode_t 394 395Remember to call ``uv_tty_reset_mode`` when your program exits to restore the 396state of the terminal. Just good manners. Another set of good manners is to be 397aware of redirection. If the user redirects the output of your command to 398a 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 403Here 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 411The final TTY helper is ``uv_tty_get_winsize()`` which is used to get the 412width and height of the terminal and returns ``0`` on success. Here is a small 413program which does some animation using the function and character position 414escape 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 422The escape codes are: 423 424====== ======================= 425Code Meaning 426====== ======================= 427*2* J Clear part of the screen, 2 is entire screen 428H 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 431m Obeys string of display settings, in this case green background (40+2), white text (30+7) 432====== ======================= 433 434As you can see this is very useful to produce nicely formatted output, or even 435console based arcade games if that tickles your fancy. For fancier control you 436can 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 451