Alien-uv
view release on metacpan or search on metacpan
libuv/docs/src/guide/utilities.rst view on Meta::CPAN
* If the timer was non-repeating, the timer has already been stopped. Use
``uv_timer_start`` again.
* If the timer is repeating, the next timeout has already been scheduled, so
the old repeat interval will be used once more before the timer switches to
the new interval.
The utility function::
int uv_timer_again(uv_timer_t *)
applies **only to repeating timers** and is equivalent to stopping the timer
and then starting it with both initial ``timeout`` and ``repeat`` set to the
old ``repeat`` value. If the timer hasn't been started it fails (error code
``UV_EINVAL``) and returns -1.
An actual timer example is in the :ref:`reference count section
<reference-count>`.
.. _reference-count:
Event loop reference count
--------------------------
The event loop only runs as long as there are active handles. This system
works by having every handle increase the reference count of the event loop
when it is started and decreasing the reference count when stopped. It is also
possible to manually change the reference count of handles using::
void uv_ref(uv_handle_t*);
void uv_unref(uv_handle_t*);
These functions can be used to allow a loop to exit even when a watcher is
active or to use custom objects to keep the loop alive.
The latter can be used with interval timers. You might have a garbage collector
which runs every X seconds, or your network service might send a heartbeat to
others periodically, but you don't want to have to stop them along all clean
exit paths or error scenarios. Or you want the program to exit when all your
other watchers are done. In that case just unref the timer immediately after
creation so that if it is the only watcher running then ``uv_run`` will still
exit.
This is also used in node.js where some libuv methods are being bubbled up to
the JS API. A ``uv_handle_t`` (the superclass of all watchers) is created per
JS object and can be ref/unrefed.
.. rubric:: ref-timer/main.c
.. literalinclude:: ../../code/ref-timer/main.c
:linenos:
:lines: 5-8, 17-
:emphasize-lines: 9
We initialize the garbage collector timer, then immediately ``unref`` it.
Observe how after 9 seconds, when the fake job is done, the program
automatically exits, even though the garbage collector is still running.
Idler pattern
-------------
The callbacks of idle handles are invoked once per event loop. The idle
callback can be used to perform some very low priority activity. For example,
you could dispatch a summary of the daily application performance to the
developers for analysis during periods of idleness, or use the application's
CPU time to perform SETI calculations :) An idle watcher is also useful in
a GUI application. Say you are using an event loop for a file download. If the
TCP socket is still being established and no other events are present your
event loop will pause (**block**), which means your progress bar will freeze
and the user will face an unresponsive application. In such a case queue up and
idle watcher to keep the UI operational.
.. rubric:: idle-compute/main.c
.. literalinclude:: ../../code/idle-compute/main.c
:linenos:
:lines: 5-9, 34-
:emphasize-lines: 13
Here we initialize the idle watcher and queue it up along with the actual
events we are interested in. ``crunch_away`` will now be called repeatedly
until the user types something and presses Return. Then it will be interrupted
for a brief amount as the loop deals with the input data, after which it will
keep calling the idle callback again.
.. rubric:: idle-compute/main.c
.. literalinclude:: ../../code/idle-compute/main.c
:linenos:
:lines: 10-19
.. _baton:
Passing data to worker thread
-----------------------------
When using ``uv_queue_work`` you'll usually need to pass complex data through
to the worker thread. The solution is to use a ``struct`` and set
``uv_work_t.data`` to point to it. A slight variation is to have the
``uv_work_t`` itself as the first member of this struct (called a baton [#]_).
This allows cleaning up the work request and all the data in one free call.
.. code-block:: c
:linenos:
:emphasize-lines: 2
struct ftp_baton {
uv_work_t req;
char *host;
int port;
char *username;
char *password;
}
.. code-block:: c
:linenos:
:emphasize-lines: 2
ftp_baton *baton = (ftp_baton*) malloc(sizeof(ftp_baton));
baton->req.data = (void*) baton;
baton->host = strdup("my.webhost.com");
baton->port = 21;
// ...
libuv/docs/src/guide/utilities.rst view on Meta::CPAN
Here we create the baton and queue the task.
Now the task function can extract the data it needs:
.. code-block:: c
:linenos:
:emphasize-lines: 2, 12
void ftp_session(uv_work_t *req) {
ftp_baton *baton = (ftp_baton*) req->data;
fprintf(stderr, "Connecting to %s\n", baton->host);
}
void ftp_cleanup(uv_work_t *req) {
ftp_baton *baton = (ftp_baton*) req->data;
free(baton->host);
// ...
free(baton);
}
We then free the baton which also frees the watcher.
External I/O with polling
-------------------------
Usually third-party libraries will handle their own I/O, and keep track of
their sockets and other files internally. In this case it isn't possible to use
the standard stream I/O operations, but the library can still be integrated
into the libuv event loop. All that is required is that the library allow you
to access the underlying file descriptors and provide functions that process
tasks in small increments as decided by your application. Some libraries though
will not allow such access, providing only a standard blocking function which
will perform the entire I/O transaction and only then return. It is unwise to
use these in the event loop thread, use the :ref:`libuv-work-queue` instead. Of
course, this will also mean losing granular control on the library.
The ``uv_poll`` section of libuv simply watches file descriptors using the
operating system notification mechanism. In some sense, all the I/O operations
that libuv implements itself are also backed by ``uv_poll`` like code. Whenever
the OS notices a change of state in file descriptors being polled, libuv will
invoke the associated callback.
Here we will walk through a simple download manager that will use libcurl_ to
download files. Rather than give all control to libcurl, we'll instead be
using the libuv event loop, and use the non-blocking, async multi_ interface to
progress with the download whenever libuv notifies of I/O readiness.
.. _libcurl: http://curl.haxx.se/libcurl/
.. _multi: http://curl.haxx.se/libcurl/c/libcurl-multi.html
.. rubric:: uvwget/main.c - The setup
.. literalinclude:: ../../code/uvwget/main.c
:linenos:
:lines: 1-9,140-
:emphasize-lines: 7,21,24-25
The way each library is integrated with libuv will vary. In the case of
libcurl, we can register two callbacks. The socket callback ``handle_socket``
is invoked whenever the state of a socket changes and we have to start polling
it. ``start_timeout`` is called by libcurl to notify us of the next timeout
interval, after which we should drive libcurl forward regardless of I/O status.
This is so that libcurl can handle errors or do whatever else is required to
get the download moving.
Our downloader is to be invoked as::
$ ./uvwget [url1] [url2] ...
So we add each argument as an URL
.. rubric:: uvwget/main.c - Adding urls
.. literalinclude:: ../../code/uvwget/main.c
:linenos:
:lines: 39-56
:emphasize-lines: 13-14
We let libcurl directly write the data to a file, but much more is possible if
you so desire.
``start_timeout`` will be called immediately the first time by libcurl, so
things are set in motion. This simply starts a libuv `timer <Timers>`_ which
drives ``curl_multi_socket_action`` with ``CURL_SOCKET_TIMEOUT`` whenever it
times out. ``curl_multi_socket_action`` is what drives libcurl, and what we
call whenever sockets change state. But before we go into that, we need to poll
on sockets whenever ``handle_socket`` is called.
.. rubric:: uvwget/main.c - Setting up polling
.. literalinclude:: ../../code/uvwget/main.c
:linenos:
:lines: 102-140
:emphasize-lines: 9,11,15,21,24
We are interested in the socket fd ``s``, and the ``action``. For every socket
we create a ``uv_poll_t`` handle if it doesn't exist, and associate it with the
socket using ``curl_multi_assign``. This way ``socketp`` points to it whenever
the callback is invoked.
In the case that the download is done or fails, libcurl requests removal of the
poll. So we stop and free the poll handle.
Depending on what events libcurl wishes to watch for, we start polling with
``UV_READABLE`` or ``UV_WRITABLE``. Now libuv will invoke the poll callback
whenever the socket is ready for reading or writing. Calling ``uv_poll_start``
multiple times on the same handle is acceptable, it will just update the events
mask with the new value. ``curl_perform`` is the crux of this program.
.. rubric:: uvwget/main.c - Driving libcurl.
.. literalinclude:: ../../code/uvwget/main.c
:linenos:
:lines: 81-95
:emphasize-lines: 2,6-7,12
The first thing we do is to stop the timer, since there has been some progress
in the interval. Then depending on what event triggered the callback, we set
the correct flags. Then we call ``curl_multi_socket_action`` with the socket
that progressed and the flags informing about what events happened. At this
point libcurl does all of its internal tasks in small increments, and will
attempt to return as fast as possible, which is exactly what an evented program
( run in 1.197 second using v1.01-cache-2.11-cpan-796a6f069b2 )