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

Commit

Permalink
Update utilities
Browse files Browse the repository at this point in the history
  • Loading branch information
nikhilm committed Feb 8, 2015
1 parent e3d2b4a commit d210484
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 45 deletions.
2 changes: 1 addition & 1 deletion code/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ all: plugin/plugin proc-streams/test cgi/tick multi-echo-server/worker uvwget/uv
for dir in $(examples); do cd $$dir; echo "--- `pwd`"; gcc $(CFLAGS) -o $$dir main.c $(UV_LIB) $(LIBS); cd ..; done

plugin/plugin: plugin/*.c
gcc $(CFLAGS) $(PLUGIN_EXE_FLAGS) -o plugin/plugin plugin/main.c plugin/plugin.c $(UV_LIB) $(LIBS)
gcc $(CFLAGS) $(PLUGIN_EXE_FLAGS) -o plugin/plugin plugin/main.c $(UV_LIB) $(LIBS)
gcc -g -Wall -c -fPIC -o plugin/hello.o plugin/hello.c
gcc $(SHARED_LIB_FLAGS) plugin/hello.o

Expand Down
6 changes: 3 additions & 3 deletions code/idle-compute/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ void on_type(uv_fs_t *req) {
printf("Typed %s\n", buffer);

uv_buf_t buf = uv_buf_init(buffer, 1024);
uv_fs_read(loop, &stdin_watcher, 1, &buf, 1, 0, on_type);
uv_fs_read(loop, &stdin_watcher, 0, &buf, 1, -1, on_type);
uv_idle_start(&idler, crunch_away);
}
else if (stdin_watcher.result < 0) {
fprintf(stderr, "error opening file: %s\n", uv_err_name(req->result));
fprintf(stderr, "error opening file: %s\n", uv_strerror(req->result));
}
}

Expand All @@ -37,7 +37,7 @@ int main() {
uv_idle_init(loop, &idler);

uv_buf_t buf = uv_buf_init(buffer, 1024);
uv_fs_read(loop, &stdin_watcher, 1, &buf, 1, 0, on_type);
uv_fs_read(loop, &stdin_watcher, 0, &buf, 1, -1, on_type);
uv_idle_start(&idler, crunch_away);
return uv_run(loop, UV_RUN_DEFAULT);
}
4 changes: 4 additions & 0 deletions code/plugin/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

typedef void (*init_plugin_function)();

void mfp_register(const char *name) {
fprintf(stderr, "Registered plugin \"%s\"\n", name);
}

int main(int argc, char **argv) {
if (argc == 1) {
fprintf(stderr, "Usage: %s [plugin1] [plugin2] ...\n", argv[0]);
Expand Down
5 changes: 0 additions & 5 deletions code/plugin/plugin.c

This file was deleted.

1 change: 1 addition & 0 deletions code/plugin/plugin.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef UVBOOK_PLUGIN_SYSTEM
#define UVBOOK_PLUGIN_SYSTEM

// Plugin authors should use this to register their plugins with mfp.
void mfp_register(const char *name);

#endif
2 changes: 1 addition & 1 deletion code/tty/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ int main() {
loop = uv_default_loop();

uv_tty_init(loop, &tty, 1, 0);
uv_tty_set_mode(&tty, 0);
uv_tty_set_mode(&tty, UV_TTY_MODE_NORMAL);

if (uv_guess_handle(1) == UV_TTY) {
uv_write_t req;
Expand Down
12 changes: 7 additions & 5 deletions code/uvwget/main.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>
Expand All @@ -13,14 +14,14 @@ typedef struct curl_context_s {
} curl_context_t;

curl_context_t *create_curl_context(curl_socket_t sockfd) {
int r;
curl_context_t *context;

context = (curl_context_t*) malloc(sizeof *context);

context->sockfd = sockfd;

r = uv_poll_init_socket(loop, &context->poll_handle, sockfd);
int r = uv_poll_init_socket(loop, &context->poll_handle, sockfd);
assert(r == 0);
context->poll_handle.data = context;

return context;
Expand Down Expand Up @@ -81,8 +82,9 @@ void curl_perform(uv_poll_t *req, int status, int events) {
uv_timer_stop(&timeout);
int running_handles;
int flags = 0;
if (events & UV_READABLE) flags |= CURL_CSELECT_IN;
if (events & UV_WRITABLE) flags |= CURL_CSELECT_OUT;
if (status < 0) flags = CURL_CSELECT_ERR;
if (!status && events & UV_READABLE) flags |= CURL_CSELECT_IN;
if (!status && events & UV_WRITABLE) flags |= CURL_CSELECT_OUT;

curl_context_t *context;

Expand Down Expand Up @@ -112,8 +114,8 @@ int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, void *so
}
else {
curl_context = create_curl_context(s);
curl_multi_assign(curl_handle, s, (void *) curl_context);
}
curl_multi_assign(curl_handle, s, (void *) curl_context);
}

switch (action) {
Expand Down
68 changes: 38 additions & 30 deletions source/utilities.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ An actual timer example is in the :ref:`reference count section
Event loop reference count
--------------------------

The event loop only runs as long as there are active watchers. This system
works by having every watcher increase the reference count of the event loop
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::

Expand All @@ -73,15 +73,15 @@ possible to manually change the reference count of handles using::
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 former can be used with interval timers. You might have a garbage collector
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.

The later is used in node.js where some libuv methods are being bubbled up to
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.

Expand All @@ -98,22 +98,22 @@ automatically exits, even though the garbage collector is still running.
Idler pattern
-------------

The callbacks of idle watchers are invoked once per event loop. The idle
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 think the application crashed. In such a case queue up and
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, 32-
:emphasize-lines: 38
: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
Expand Down Expand Up @@ -197,7 +197,7 @@ 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.
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
Expand All @@ -216,7 +216,7 @@ progress with the download whenever libuv notifies of I/O readiness.
.. rubric:: uvwget/main.c - The setup
.. literalinclude:: ../code/uvwget/main.c
:linenos:
:lines: 1-9,135-
:lines: 1-9,140-
:emphasize-lines: 7,21,24-25

The way each library is integrated with libuv will vary. In the case of
Expand All @@ -236,8 +236,8 @@ So we add each argument as an URL
.. rubric:: uvwget/main.c - Adding urls
.. literalinclude:: ../code/uvwget/main.c
:linenos:
:lines: 10-27
:emphasize-lines: 14
: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.
Expand All @@ -252,8 +252,8 @@ on sockets whenever ``handle_socket`` is called.
.. rubric:: uvwget/main.c - Setting up polling
.. literalinclude:: ../code/uvwget/main.c
:linenos:
:lines: 102-133
:emphasize-lines: 8,10,15,18,22
: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
Expand All @@ -269,15 +269,15 @@ 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 - Setting up polling
.. rubric:: uvwget/main.c - Driving libcurl.
.. literalinclude:: ../code/uvwget/main.c
:linenos:
:lines: 57-89
:emphasize-lines: 2,5-6,12,18,20
:lines: 81-95
:emphasize-lines: 2,6-7,12,18,20

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 inform
libcurl of the same. Then we call ``curl_multi_socket_action`` with the socket
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
Expand All @@ -286,6 +286,12 @@ about transfer progress. In our case we are only interested in transfers that
are completed. So we extract these messages, and clean up handles whose
transfers are done.

.. rubric:: uvwget/main.c - Reading transfer status.
.. literalinclude:: ../code/uvwget/main.c
:linenos:
:lines: 58-79
:emphasize-lines: 6,9-10,13-14

Check & Prepare watchers
------------------------

Expand All @@ -308,10 +314,6 @@ Let us first look at the interface provided to plugin authors.
.. literalinclude:: ../code/plugin/plugin.h
:linenos:

.. rubric:: plugin/plugin.c
.. literalinclude:: ../code/plugin/plugin.c
:linenos:

You can similarly add more functions that plugin authors can use to do useful
things in your application [#]_. A sample plugin using this API is:

Expand All @@ -327,6 +329,11 @@ library and can be loaded by running our application::
Loading libhello.dylib
Registered plugin "Hello World!"

.. NOTE::

The shared library filename will be different depending on platforms. On
Linux it is ``libhello.so``.

This is done by using ``uv_dlopen`` to first load the shared library
``libhello.dylib``. Then we get access to the ``initialize`` function using
``uv_dlsym`` and invoke it.
Expand All @@ -335,7 +342,7 @@ This is done by using ``uv_dlopen`` to first load the shared library
.. literalinclude:: ../code/plugin/main.c
:linenos:
:lines: 7-
:emphasize-lines: 14, 20, 25
:emphasize-lines: 15, 18, 24

``uv_dlopen`` expects a path to the shared library and sets the opaque
``uv_lib_t`` pointer. It returns 0 on success, -1 on error. Use ``uv_dlerror``
Expand Down Expand Up @@ -365,12 +372,13 @@ it reads/writes from. This is achieved with::

int uv_tty_init(uv_loop_t*, uv_tty_t*, uv_file fd, int readable)

If ``readable`` is false, ``uv_write`` calls to this stream will be
**blocking**.
Set ``readable`` to true if you plan to use ``uv_read_start()`` on the stream.

It is then best to use ``uv_tty_set_mode`` to set the mode to *normal*
which enables most TTY formatting, flow-control and other settings. Other_ modes
are also available.

It is then best to use ``uv_tty_set_mode`` to set the mode to *normal* (0)
which enables most TTY formatting, flow-control and other settings. *raw* mode
(1) is also supported.
.. _Other: http://docs.libuv.org/en/v1.x/tty.html#c.uv_tty_mode_t

Remember to call ``uv_tty_reset_mode`` when your program exits to restore the
state of the terminal. Just good manners. Another set of good manners is to be
Expand Down Expand Up @@ -417,9 +425,9 @@ can try `ncurses`_.

----

.. [#] mfp is My Fancy Plugin
.. [#] I was first introduced to the term baton in this context, in Konstantin
Käfer's excellent slides on writing node.js bindings --
http://kkaefer.github.com/node-cpp-modules/#baton
.. [#] mfp is My Fancy Plugin
.. _libev man page: http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#COMMON_OR_USEFUL_IDIOMS_OR_BOTH

0 comments on commit d210484

Please sign in to comment.