diff --git a/code/Makefile b/code/Makefile index 98ff918..0ac729a 100644 --- a/code/Makefile +++ b/code/Makefile @@ -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 diff --git a/code/idle-compute/main.c b/code/idle-compute/main.c index f151435..ff44b69 100644 --- a/code/idle-compute/main.c +++ b/code/idle-compute/main.c @@ -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)); } } @@ -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); } diff --git a/code/plugin/main.c b/code/plugin/main.c index 89e77b3..06e581e 100644 --- a/code/plugin/main.c +++ b/code/plugin/main.c @@ -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]); diff --git a/code/plugin/plugin.c b/code/plugin/plugin.c deleted file mode 100644 index 31829a9..0000000 --- a/code/plugin/plugin.c +++ /dev/null @@ -1,5 +0,0 @@ -#include - -void mfp_register(const char *name) { - fprintf(stderr, "Registered plugin \"%s\"\n", name); -} diff --git a/code/plugin/plugin.h b/code/plugin/plugin.h index 0c10f37..21f194e 100644 --- a/code/plugin/plugin.h +++ b/code/plugin/plugin.h @@ -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 diff --git a/code/tty/main.c b/code/tty/main.c index 76e2b55..03b26fb 100644 --- a/code/tty/main.c +++ b/code/tty/main.c @@ -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; diff --git a/code/uvwget/main.c b/code/uvwget/main.c index 4c3fb25..4018624 100644 --- a/code/uvwget/main.c +++ b/code/uvwget/main.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -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; @@ -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; @@ -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) { diff --git a/source/utilities.rst b/source/utilities.rst index 2150dae..9da738d 100644 --- a/source/utilities.rst +++ b/source/utilities.rst @@ -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:: @@ -73,7 +73,7 @@ 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 @@ -81,7 +81,7 @@ 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. @@ -98,7 +98,7 @@ 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 @@ -106,14 +106,14 @@ 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 @@ -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 @@ -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 @@ -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. @@ -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 @@ -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 @@ -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 ------------------------ @@ -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: @@ -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. @@ -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`` @@ -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 @@ -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