Skip to content
This repository has been archived by the owner on Nov 20, 2022. It is now read-only.

Commit

Permalink
Update basics and idle timers section for v1
Browse files Browse the repository at this point in the history
  • Loading branch information
nikhilm committed Feb 1, 2015
1 parent a54a382 commit 95c48c7
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 61 deletions.
6 changes: 5 additions & 1 deletion code/helloworld/main.c
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>

int main() {
uv_loop_t *loop = uv_loop_new();
uv_loop_t *loop = malloc(sizeof(uv_loop_t));
uv_loop_init(loop);

printf("Now quitting.\n");
uv_run(loop, UV_RUN_DEFAULT);

uv_loop_close(loop);
free(loop);
return 0;
}
1 change: 1 addition & 0 deletions code/idle-basic/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ int main() {
printf("Idling...\n");
uv_run(uv_default_loop(), UV_RUN_DEFAULT);

uv_loop_close(uv_default_loop());
return 0;
}
80 changes: 34 additions & 46 deletions source/basics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ Bert Belder, one of the libuv core developers has a small video explaining the
architecture of libuv and its background. If you have no prior experience with
either libuv or libev, it is a quick, useful watch.

libuv's event loop is explained in more detail in the `documentation
<http://docs.libuv.org/en/v1.x/design.html#the-i-o-loop>`_.

.. raw:: html

<iframe width="560" height="315"
Expand All @@ -90,6 +93,14 @@ This program quits immediately because it has no events to process. A libuv
event loop has to be told to watch out for events using the various API
functions.

Starting with libuv v1.0, users should allocate the memory for the loops before
initializing it with ``uv_loop_init(uv_loop_t *)``. This allows you to plug in
custom memory management. Remember to de-initialize the loop using
``uv_loop_close(uv_loop_t *)`` and then delete the storage. The examples never
close loops since the program quits after the loop ends and the system will
reclaim memory. Production grade projects, especially long running systems
programs, should take care to release correctly.

Default loop
++++++++++++

Expand All @@ -105,65 +116,41 @@ loop.
Error handling
--------------

libuv functions which may fail return ``-1`` on error. The error code itself is
set on the event loop as ``last_err``. Use ``uv_last_error(loop)`` to get
a ``uv_err_t`` which has a ``code`` member with the error code. ``code`` is an
enumeration of ``UV_*`` as defined here:
Initialization functions or synchronous functions which may fail return a negative number on error. Async functions that may fail will pass a status parameter to their callbacks. The error messages are defined as ``UV_E*`` `constants`_.

.. rubric:: libuv error codes
.. literalinclude:: ../libuv/include/uv.h
:lines: 66-140
.. _constants: http://docs.libuv.org/en/v1.x/errors.html#error-constants

You can use the ``uv_strerror(uv_err_t)`` and ``uv_err_name(uv_err_t)`` functions
You can use the ``uv_strerror(int)`` and ``uv_err_name(int)`` functions
to get a ``const char *`` describing the error or the error name respectively.

Some async callbacks have a ``status`` argument as the last argument. Use this instead
of the return value. ``status`` is 0 for no error.

I/O read callbacks (such as for files and sockets) are passed a parameter ``nread``. If ``nread`` is less than 0, there was an error (UV_EOF is the end of file error, which you may want to handle differently).

Watchers
--------
Handles and Requests
--------------------

Watchers are how users of libuv express interest in particular events. Watchers
are opaque structs named as ``uv_TYPE_t`` where type signifies what the watcher
is used for. A full list of watchers supported by libuv is:
libuv works by the user expressing interest in particular events. This is
usually done by creating a **handle** to an I/O device, timer or process.
Handles are opaque structs named as ``uv_TYPE_t`` where type signifies what the
handle is used for.

.. rubric:: libuv watchers
.. literalinclude:: ../libuv/include/uv.h
:lines: 197-230


Handles are for actions that usually support a ``open/start, act, close/stop`` sort of
cycle, like dealing with streams or timers or polling. Properties on the handle
can be read/written to affect the action.

Handles represent long-lived objects. Async operations on such handles are
identified using **requests**. A request is short-lived (usually used across
only one callback) and usually indicates one I/O operation on a handle.
Requests are used to preserve context between the initiation and the callback
of individual actions. For example, an UDP socket is represented by
a ``uv_udp_t``, while individual writes to the socket use a ``uv_udp_send_t``
structure.

Finally the last 3 structs aren't related to the event loop mechanism, but
store system information.
structure that is passed to the callback after the write is done.

Watchers are setup by a corresponding::
Handles are setup by a corresponding::

uv_TYPE_init(uv_TYPE_t*)
uv_TYPE_init(uv_loop_t *, uv_TYPE_t *)

function.

.. note::

Some watcher initialization functions require the loop as a first argument.

A watcher is set to actually listen for events by invoking::

uv_TYPE_start(uv_TYPE_t*, callback)

and stopped by calling the corresponding::

uv_TYPE_stop(uv_TYPE_t*)

Callbacks are functions which are called by libuv whenever an event the watcher
is interested in has taken place. Application specific logic will usually be
implemented in the callback. For example, an IO watcher's callback will receive
Expand All @@ -173,12 +160,12 @@ on.
Idling
++++++

Here is an example of using a watcher. An idle watcher's callback is repeatedly
called. There are some deeper semantics, discussed in :doc:`utilities`, but
we'll ignore them for now. Let's just use an idle watcher to look at the
watcher life cycle and see how ``uv_run()`` will now block because a watcher is
present. The idle watcher is stopped when the count is reached and ``uv_run()``
exits since no event watchers are active.
Here is an example of using an idle handle. The callback is called once on
every turn of the event loop. A use case for idle handles is discussed in
:doc:`utilities`. Let us use an idle watcher to look at the watcher life cycle
and see how ``uv_run()`` will now block because a watcher is present. The idle
watcher is stopped when the count is reached and ``uv_run()`` exits since no
event watchers are active.

.. rubric:: idle-basic/main.c
.. literalinclude:: ../code/idle-basic/main.c
Expand All @@ -191,7 +178,8 @@ In callback based programming style you'll often want to pass some 'context' --
application specific information -- between the call site and the callback. All
handles and requests have a ``void* data`` member which you can set to the
context and cast back in the callback. This is a common pattern used throughout
the C library ecosystem.
the C library ecosystem. In addition ``uv_loop_t`` also has a similar data
member.

----

Expand Down
27 changes: 13 additions & 14 deletions source/utilities.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,20 +95,19 @@ 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.

Idle watcher pattern
--------------------

The callbacks of idle watchers are only invoked when the event loop has no
other pending events. In such a situation they are invoked once every iteration
of the 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 think the application crashed. In such a case
queue up and idle watcher to keep the UI operational.
Idler pattern
-------------

The callbacks of idle watchers 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 think the application crashed. 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
Expand Down

0 comments on commit 95c48c7

Please sign in to comment.