diff options
author | root <root> | 2011-01-11 01:40:25 +0000 |
---|---|---|
committer | root <root> | 2011-01-11 01:40:25 +0000 |
commit | 945c55f3ade6db3a07c9b980080b79776d5d368f (patch) | |
tree | 92850a94f8e502ce5ed5a141b9ae020fefd28c34 | |
parent | 57d47790ac7a4d83cd00f26007f47e47a2547900 (diff) |
*** empty log message ***
-rw-r--r-- | ev.pod | 343 |
1 files changed, 186 insertions, 157 deletions
@@ -508,7 +508,9 @@ events to filter out spurious ones, recreating the set when required. Last not least, it also refuses to work with some file descriptors which work perfectly fine with C<select> (files, many character devices...). -Epoll is truly the train wreck analog among event poll mechanisms. +Epoll is truly the train wreck analog among event poll mechanisms, +a frankenpoll, cobbled together in a hurry, no thought to design or +interaction with others. While stopping, setting and starting an I/O watcher in the same iteration will result in some caching, there is still a system call per such @@ -1616,26 +1618,19 @@ fd as you want (as long as you don't confuse yourself). Setting all file descriptors to non-blocking mode is also usually a good idea (but not required if you know what you are doing). -If you cannot use non-blocking mode, then force the use of a -known-to-be-good backend (at the time of this writing, this includes only -C<EVBACKEND_SELECT> and C<EVBACKEND_POLL>). The same applies to file -descriptors for which non-blocking operation makes no sense (such as -files) - libev doesn't guarantee any specific behaviour in that case. - Another thing you have to watch out for is that it is quite easy to -receive "spurious" readiness notifications, that is your callback might +receive "spurious" readiness notifications, that is, your callback might be called with C<EV_READ> but a subsequent C<read>(2) will actually block -because there is no data. Not only are some backends known to create a -lot of those (for example Solaris ports), it is very easy to get into -this situation even with a relatively standard program structure. Thus -it is best to always use non-blocking I/O: An extra C<read>(2) returning -C<EAGAIN> is far preferable to a program hanging until some data arrives. +because there is no data. It is very easy to get into this situation even +with a relatively standard program structure. Thus it is best to always +use non-blocking I/O: An extra C<read>(2) returning C<EAGAIN> is far +preferable to a program hanging until some data arrives. If you cannot run the fd in non-blocking mode (for example you should not play around with an Xlib connection), then you have to separately re-test whether a file descriptor is really ready with a known-to-be good -interface such as poll (fortunately in our Xlib example, Xlib already -does this on its own, so its quite safe to use). Some people additionally +interface such as poll (fortunately in the case of Xlib, it already does +this on its own, so its quite safe to use). Some people additionally use C<SIGALRM> and an interval timer, just to be sure you won't block indefinitely. @@ -1673,16 +1668,48 @@ There is no workaround possible except not registering events for potentially C<dup ()>'ed file descriptors, or to resort to C<EVBACKEND_SELECT> or C<EVBACKEND_POLL>. +=head3 The special problem of files + +Many people try to use C<select> (or libev) on file descriptors +representing files, and expect it to become ready when their program +doesn't block on disk accesses (which can take a long time on their own). + +However, this cannot ever work in the "expected" way - you get a readiness +notification as soon as the kernel knows whether and how much data is +there, and in the case of open files, that's always the case, so you +always get a readiness notification instantly, and your read (or possibly +write) will still block on the disk I/O. + +Another way to view it is that in the case of sockets, pipes, character +devices and so on, there is another party (the sender) that delivers data +on it's own, but in the case of files, there is no such thing: the disk +will not send data on it's own, simply because it doesn't know what you +wish to read - you would first have to request some data. + +Since files are typically not-so-well supported by advanced notification +mechanism, libev tries hard to emulate POSIX behaviour with respect +to files, even though you should not use it. The reason for this is +convenience: sometimes you want to watch STDIN or STDOUT, which is +usually a tty, often a pipe, but also sometimes files or special devices +(for example, C<epoll> on Linux works with F</dev/random> but not with +F</dev/urandom>), and even though the file might better be served with +asynchronous I/O instead of with non-blocking I/O, it is still useful when +it "just works" instead of freezing. + +So avoid file descriptors pointing to files when you know it (e.g. use +libeio), but use them when it is convenient, e.g. for STDIN/STDOUT, or +when you rarely read from a file instead of from a socket, and want to +reuse the same code path. + =head3 The special problem of fork Some backends (epoll, kqueue) do not support C<fork ()> at all or exhibit useless behaviour. Libev fully supports fork, but needs to be told about -it in the child. +it in the child if you want to continue to use it in the child. -To support fork in your programs, you either have to call -C<ev_default_fork ()> or C<ev_loop_fork ()> after a fork in the child, -enable C<EVFLAG_FORKCHECK>, or resort to C<EVBACKEND_SELECT> or -C<EVBACKEND_POLL>. +To support fork in your child processes, you have to call C<ev_loop_fork +()> after a fork in the child, enable C<EVFLAG_FORKCHECK>, or resort to +C<EVBACKEND_SELECT> or C<EVBACKEND_POLL>. =head3 The special problem of SIGPIPE @@ -3472,6 +3499,144 @@ To exit from any of these loops, just set the corresponding exit variable: // exit both exit_main_loop = exit_nested_loop = 1; +=item Thread locking example + +Here is a fictitious example of how to run an event loop in a different +thread than where callbacks are being invoked and watchers are +created/added/removed. + +For a real-world example, see the C<EV::Loop::Async> perl module, +which uses exactly this technique (which is suited for many high-level +languages). + +The example uses a pthread mutex to protect the loop data, a condition +variable to wait for callback invocations, an async watcher to notify the +event loop thread and an unspecified mechanism to wake up the main thread. + +First, you need to associate some data with the event loop: + + typedef struct { + mutex_t lock; /* global loop lock */ + ev_async async_w; + thread_t tid; + cond_t invoke_cv; + } userdata; + + void prepare_loop (EV_P) + { + // for simplicity, we use a static userdata struct. + static userdata u; + + ev_async_init (&u->async_w, async_cb); + ev_async_start (EV_A_ &u->async_w); + + pthread_mutex_init (&u->lock, 0); + pthread_cond_init (&u->invoke_cv, 0); + + // now associate this with the loop + ev_set_userdata (EV_A_ u); + ev_set_invoke_pending_cb (EV_A_ l_invoke); + ev_set_loop_release_cb (EV_A_ l_release, l_acquire); + + // then create the thread running ev_loop + pthread_create (&u->tid, 0, l_run, EV_A); + } + +The callback for the C<ev_async> watcher does nothing: the watcher is used +solely to wake up the event loop so it takes notice of any new watchers +that might have been added: + + static void + async_cb (EV_P_ ev_async *w, int revents) + { + // just used for the side effects + } + +The C<l_release> and C<l_acquire> callbacks simply unlock/lock the mutex +protecting the loop data, respectively. + + static void + l_release (EV_P) + { + userdata *u = ev_userdata (EV_A); + pthread_mutex_unlock (&u->lock); + } + + static void + l_acquire (EV_P) + { + userdata *u = ev_userdata (EV_A); + pthread_mutex_lock (&u->lock); + } + +The event loop thread first acquires the mutex, and then jumps straight +into C<ev_run>: + + void * + l_run (void *thr_arg) + { + struct ev_loop *loop = (struct ev_loop *)thr_arg; + + l_acquire (EV_A); + pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0); + ev_run (EV_A_ 0); + l_release (EV_A); + + return 0; + } + +Instead of invoking all pending watchers, the C<l_invoke> callback will +signal the main thread via some unspecified mechanism (signals? pipe +writes? C<Async::Interrupt>?) and then waits until all pending watchers +have been called (in a while loop because a) spurious wakeups are possible +and b) skipping inter-thread-communication when there are no pending +watchers is very beneficial): + + static void + l_invoke (EV_P) + { + userdata *u = ev_userdata (EV_A); + + while (ev_pending_count (EV_A)) + { + wake_up_other_thread_in_some_magic_or_not_so_magic_way (); + pthread_cond_wait (&u->invoke_cv, &u->lock); + } + } + +Now, whenever the main thread gets told to invoke pending watchers, it +will grab the lock, call C<ev_invoke_pending> and then signal the loop +thread to continue: + + static void + real_invoke_pending (EV_P) + { + userdata *u = ev_userdata (EV_A); + + pthread_mutex_lock (&u->lock); + ev_invoke_pending (EV_A); + pthread_cond_signal (&u->invoke_cv); + pthread_mutex_unlock (&u->lock); + } + +Whenever you want to start/stop a watcher or do other modifications to an +event loop, you will now have to lock: + + ev_timer timeout_watcher; + userdata *u = ev_userdata (EV_A); + + ev_timer_init (&timeout_watcher, timeout_cb, 5.5, 0.); + + pthread_mutex_lock (&u->lock); + ev_timer_start (EV_A_ &timeout_watcher); + ev_async_send (EV_A_ &u->async_w); + pthread_mutex_unlock (&u->lock); + +Note that sending the C<ev_async> watcher is required because otherwise +an event loop currently blocking in the kernel will have no knowledge +about the newly added timer. By waking up the loop it will pick up any new +watchers in the next event loop iteration. + =back @@ -4473,143 +4638,7 @@ watcher callback into the event loop interested in the signal. =back -=head4 THREAD LOCKING EXAMPLE - -Here is a fictitious example of how to run an event loop in a different -thread than where callbacks are being invoked and watchers are -created/added/removed. - -For a real-world example, see the C<EV::Loop::Async> perl module, -which uses exactly this technique (which is suited for many high-level -languages). - -The example uses a pthread mutex to protect the loop data, a condition -variable to wait for callback invocations, an async watcher to notify the -event loop thread and an unspecified mechanism to wake up the main thread. - -First, you need to associate some data with the event loop: - - typedef struct { - mutex_t lock; /* global loop lock */ - ev_async async_w; - thread_t tid; - cond_t invoke_cv; - } userdata; - - void prepare_loop (EV_P) - { - // for simplicity, we use a static userdata struct. - static userdata u; - - ev_async_init (&u->async_w, async_cb); - ev_async_start (EV_A_ &u->async_w); - - pthread_mutex_init (&u->lock, 0); - pthread_cond_init (&u->invoke_cv, 0); - - // now associate this with the loop - ev_set_userdata (EV_A_ u); - ev_set_invoke_pending_cb (EV_A_ l_invoke); - ev_set_loop_release_cb (EV_A_ l_release, l_acquire); - - // then create the thread running ev_loop - pthread_create (&u->tid, 0, l_run, EV_A); - } - -The callback for the C<ev_async> watcher does nothing: the watcher is used -solely to wake up the event loop so it takes notice of any new watchers -that might have been added: - - static void - async_cb (EV_P_ ev_async *w, int revents) - { - // just used for the side effects - } - -The C<l_release> and C<l_acquire> callbacks simply unlock/lock the mutex -protecting the loop data, respectively. - - static void - l_release (EV_P) - { - userdata *u = ev_userdata (EV_A); - pthread_mutex_unlock (&u->lock); - } - - static void - l_acquire (EV_P) - { - userdata *u = ev_userdata (EV_A); - pthread_mutex_lock (&u->lock); - } - -The event loop thread first acquires the mutex, and then jumps straight -into C<ev_run>: - - void * - l_run (void *thr_arg) - { - struct ev_loop *loop = (struct ev_loop *)thr_arg; - - l_acquire (EV_A); - pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0); - ev_run (EV_A_ 0); - l_release (EV_A); - - return 0; - } - -Instead of invoking all pending watchers, the C<l_invoke> callback will -signal the main thread via some unspecified mechanism (signals? pipe -writes? C<Async::Interrupt>?) and then waits until all pending watchers -have been called (in a while loop because a) spurious wakeups are possible -and b) skipping inter-thread-communication when there are no pending -watchers is very beneficial): - - static void - l_invoke (EV_P) - { - userdata *u = ev_userdata (EV_A); - - while (ev_pending_count (EV_A)) - { - wake_up_other_thread_in_some_magic_or_not_so_magic_way (); - pthread_cond_wait (&u->invoke_cv, &u->lock); - } - } - -Now, whenever the main thread gets told to invoke pending watchers, it -will grab the lock, call C<ev_invoke_pending> and then signal the loop -thread to continue: - - static void - real_invoke_pending (EV_P) - { - userdata *u = ev_userdata (EV_A); - - pthread_mutex_lock (&u->lock); - ev_invoke_pending (EV_A); - pthread_cond_signal (&u->invoke_cv); - pthread_mutex_unlock (&u->lock); - } - -Whenever you want to start/stop a watcher or do other modifications to an -event loop, you will now have to lock: - - ev_timer timeout_watcher; - userdata *u = ev_userdata (EV_A); - - ev_timer_init (&timeout_watcher, timeout_cb, 5.5, 0.); - - pthread_mutex_lock (&u->lock); - ev_timer_start (EV_A_ &timeout_watcher); - ev_async_send (EV_A_ &u->async_w); - pthread_mutex_unlock (&u->lock); - -Note that sending the C<ev_async> watcher is required because otherwise -an event loop currently blocking in the kernel will have no knowledge -about the newly added timer. By waking up the loop it will pick up any new -watchers in the next event loop iteration. +See also L<Thread locking example>. =head3 COROUTINES |