--- libev/ev.pod 2009/07/08 04:29:31 1.249 +++ libev/ev.pod 2009/07/14 19:11:31 1.255 @@ -858,6 +858,66 @@ ev_set_timeout_collect_interval (EV_DEFAULT_UC_ 0.1); ev_set_io_collect_interval (EV_DEFAULT_UC_ 0.01); +=item ev_invoke_pending (loop) + +This call will simply invoke all pending watchers while resetting their +pending state. Normally, C does this automatically when required, +but when overriding the invoke callback this call comes handy. + +=item ev_set_invoke_pending_cb (loop, void (*invoke_pending_cb)(EV_P)) + +This overrides the invoke pending functionality of the loop: Instead of +invoking all pending watchers when there are any, C will call +this callback instead. This is useful, for example, when you want to +invoke the actual watchers inside another context (another thread etc.). + +If you want to reset the callback, use C as new +callback. + +=item ev_set_loop_release_cb (loop, void (*release)(EV_P), void (*acquire)(EV_P)) + +Sometimes you want to share the same loop between multiple threads. This +can be done relatively simply by putting mutex_lock/unlock calls around +each call to a libev function. + +However, C can run an indefinite time, so it is not feasible to +wait for it to return. One way around this is to wake up the loop via +C and C, another way is to set these I +and I callbacks on the loop. + +When set, then C will be called just before the thread is +suspended waiting for new events, and C is called just +afterwards. + +Ideally, C will just call your mutex_unlock function, and +C will just call the mutex_lock function again. + +While event loop modifications are allowed between invocations of +C and C (that's their only purpose after all), no +modifications done will affect the event loop, i.e. adding watchers will +have no effect on the set of file descriptors being watched, or the time +waited. USe an C watcher to wake up C when you want it +to take note of any changes you made. + +In theory, threads executing C will be async-cancel safe between +invocations of C and C. + +See also the locking example in the C section later in this +document. + +=item ev_set_userdata (loop, void *data) + +=item ev_userdata (loop) + +Set and retrieve a single C associated with a loop. When +C has never been called, then C returns +C<0.> + +These two functions can be used to associate arbitrary data with a loop, +and are intended solely for the C, C and +C callbacks described above, but of course can be (ab-)used for +any other purpose as well. + =item ev_loop_verify (loop) This function only does something when C support has been @@ -3673,9 +3733,19 @@ =item EV_MINIMAL If you need to shave off some kilobytes of code at the expense of some -speed, define this symbol to C<1>. Currently this is used to override some -inlining decisions, saves roughly 30% code size on amd64. It also selects a -much smaller 2-heap for timer management over the default 4-heap. +speed (but with the full API), define this symbol to C<1>. Currently this +is used to override some inlining decisions, saves roughly 30% code size +on amd64. It also selects a much smaller 2-heap for timer management over +the default 4-heap. + +You can save even more by disabling watcher types you do not need +and setting C == C. Also, disabling C +(C<-DNDEBUG>) will usually reduce code size a lot. + +Defining C to C<2> will additionally reduce the core API to +provide a bare-bones event library. See C for details on what parts +of the API are still available, and do not complain if this subset changes +over time. =item EV_PID_HASHSIZE @@ -3871,14 +3941,148 @@ =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 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 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 and C 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: + + 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_loop (EV_A_ 0); + l_release (EV_A); + + return 0; + } + +Instead of invoking all pending watchers, the C callback will +signal the main thread via some unspecified mechanism (signals? pipe +writes? C?) and then waits until all pending watchers +have been called: + + static void + l_invoke (EV_P) + { + userdata *u = ev_userdata (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 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 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. + =head3 COROUTINES Libev is very accommodating to coroutines ("cooperative threads"): libev fully supports nesting calls to its functions from different coroutines (e.g. you can call C on the same loop from two -different coroutines, and switch freely between both coroutines running the -loop, as long as you don't confuse yourself). The only exception is that -you must not do this from C reschedule callbacks. +different coroutines, and switch freely between both coroutines running +the loop, as long as you don't confuse yourself). The only exception is +that you must not do this from C reschedule callbacks. Care has been taken to ensure that libev does not keep local state inside C, and other calls do not usually allow for coroutine switches as