--- libev/ev.pod 2008/09/23 09:11:14 1.184 +++ libev/ev.pod 2008/09/30 19:45:23 1.191 @@ -216,7 +216,7 @@ See the description of C watchers for more info. -=item ev_set_allocator (void *(*cb)(void *ptr, long size)) +=item ev_set_allocator (void *(*cb)(void *ptr, long size)) [NOT REENTRANT] Sets the allocation function to use (the prototype is similar - the semantics are identical to the C C89/SuS/POSIX function). It is @@ -252,7 +252,7 @@ ... ev_set_allocator (persistent_realloc); -=item ev_set_syserr_cb (void (*cb)(const char *msg)); +=item ev_set_syserr_cb (void (*cb)(const char *msg)); [NOT REENTRANT] Set the callback function to call on a retryable system call error (such as failed select, poll, epoll_wait). The message is a printable string @@ -1627,7 +1627,7 @@ =head3 Examples -Example: Try to exit cleanly on SIGINT and SIGTERM. +Example: Try to exit cleanly on SIGINT. static void sigint_cb (struct ev_loop *loop, struct ev_signal *w, int revents) @@ -1637,7 +1637,7 @@ struct ev_signal signal_watcher; ev_signal_init (&signal_watcher, sigint_cb, SIGINT); - ev_signal_start (loop, &sigint_cb); + ev_signal_start (loop, &signal_watcher); =head2 C - watch out for process status changes @@ -2244,6 +2244,14 @@ this is to have a separate variables for your embeddable loop, try to create it, and if that fails, use the normal loop for everything. +=head3 C and fork + +While the C watcher is running, forks in the embedding loop will +automatically be applied to the embedded loop as well, so no special +fork handling is required in that case. When the watcher is not running, +however, it is still the task of the libev user to call C +as applicable. + =head3 Watcher-Specific Functions and Data Members =over 4 @@ -2378,8 +2386,8 @@ =item queueing from a signal handler context To implement race-free queueing, you simply add to the queue in the signal -handler but you block the signal handler in the watcher callback. Here is an example that does that for -some fictitious SIGUSR1 handler: +handler but you block the signal handler in the watcher callback. Here is +an example that does that for some fictitious SIGUSR1 handler: static ev_async mysig; @@ -3243,6 +3251,8 @@ avoid the C as first argument in all cases, or to use method calls instead of plain function calls in C++. +=back + =head2 EXPORTED API SYMBOLS If you need to re-export the API (e.g. via a DLL) and you need a list of @@ -3298,17 +3308,19 @@ #include "ev_cpp.h" #include "ev.c" +=head1 INTERACTION WITH OTHER PROGRAMS OR LIBRARIES -=head1 THREADS AND COROUTINES +=head2 THREADS AND COROUTINES -=head2 THREADS +=head3 THREADS -Libev itself is thread-safe (unless the opposite is specifically -documented for a function), but it uses no locking itself. This means that -you can use as many loops as you want in parallel, as long as only one -thread ever calls into one libev function with the same loop parameter: -libev guarantees that different event loops share no data structures that -need locking. +All libev functions are reentrant and thread-safe unless explicitly +documented otherwise, but libev implements no locking itself. This means +that you can use as many loops as you want in parallel, as long as there +are no concurrent calls into any libev function with the same loop +parameter (C calls have an implicit default loop parameter, +of course): libev guarantees that different event loops share no data +structures that need any locking. Or to put it differently: calls with different loop parameters can be done concurrently from multiple threads, calls with the same loop parameter @@ -3318,11 +3330,12 @@ Specifically to support threads (and signal handlers), libev implements so-called C watchers, which allow some limited form of -concurrency on the same event loop. +concurrency on the same event loop, namely waking it up "from the +outside". If you want to know which design (one loop, locking, or multiple loops without or something else still) is best for your problem, then I cannot -help you. I can give some generic advice however: +help you, but here is some generic advice: =over 4 @@ -3356,90 +3369,84 @@ =back -=head2 COROUTINES +=head3 COROUTINES -Libev is much more accommodating to coroutines ("cooperative threads"): -libev fully supports nesting calls to it's functions from different +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 +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 coroutine switches. - +C, and other calls do not usually allow for coroutine switches as +they do not clal any callbacks. -=head1 COMPLEXITIES - -In this section the complexities of (many of) the algorithms used inside -libev will be explained. For complexity discussions about backends see the -documentation for C. - -All of the following are about amortised time: If an array needs to be -extended, libev needs to realloc and move the whole array, but this -happens asymptotically never with higher number of elements, so O(1) might -mean it might do a lengthy realloc operation in rare cases, but on average -it is much faster and asymptotically approaches constant time. - -=over 4 - -=item Starting and stopping timer/periodic watchers: O(log skipped_other_timers) - -This means that, when you have a watcher that triggers in one hour and -there are 100 watchers that would trigger before that then inserting will -have to skip roughly seven (C) of these watchers. - -=item Changing timer/periodic watchers (by autorepeat or calling again): O(log skipped_other_timers) +=head2 COMPILER WARNINGS -That means that changing a timer costs less than removing/adding them -as only the relative motion in the event queue has to be paid for. - -=item Starting io/check/prepare/idle/signal/child/fork/async watchers: O(1) - -These just add the watcher into an array or at the head of a list. +Depending on your compiler and compiler settings, you might get no or a +lot of warnings when compiling libev code. Some people are apparently +scared by this. -=item Stopping check/prepare/idle/fork/async watchers: O(1) +However, these are unavoidable for many reasons. For one, each compiler +has different warnings, and each user has different tastes regarding +warning options. "Warn-free" code therefore cannot be a goal except when +targeting a specific compiler and compiler-version. -=item Stopping an io/signal/child watcher: O(number_of_watchers_for_this_(fd/signal/pid % EV_PID_HASHSIZE)) +Another reason is that some compiler warnings require elaborate +workarounds, or other changes to the code that make it less clear and less +maintainable. -These watchers are stored in lists then need to be walked to find the -correct watcher to remove. The lists are usually short (you don't usually -have many watchers waiting for the same fd or signal). +And of course, some compiler warnings are just plain stupid, or simply +wrong (because they don't actually warn about the condition their message +seems to warn about). For example, certain older gcc versions had some +warnings that resulted an extreme number of false positives. These have +been fixed, but some people still insist on making code warn-free with +such buggy versions. -=item Finding the next timer in each loop iteration: O(1) +While libev is written to generate as few warnings as possible, +"warn-free" code is not a goal, and it is recommended not to build libev +with any compiler warnings enabled unless you are prepared to cope with +them (e.g. by ignoring them). Remember that warnings are just that: +warnings, not errors, or proof of bugs. -By virtue of using a binary or 4-heap, the next timer is always found at a -fixed position in the storage array. -=item Each change on a file descriptor per loop iteration: O(number_of_watchers_for_this_fd) +=head2 VALGRIND -A change means an I/O watcher gets started or stopped, which requires -libev to recalculate its status (and possibly tell the kernel, depending -on backend and whether C was used). +Valgrind has a special section here because it is a popular tool that is +highly useful. Unfortunately, valgrind reports are very hard to interpret. -=item Activating one watcher (putting it into the pending state): O(1) +If you think you found a bug (memory leak, uninitialised data access etc.) +in libev, then check twice: If valgrind reports something like: -=item Priority handling: O(number_of_priorities) + ==2274== definitely lost: 0 bytes in 0 blocks. + ==2274== possibly lost: 0 bytes in 0 blocks. + ==2274== still reachable: 256 bytes in 1 blocks. -Priorities are implemented by allocating some space for each -priority. When doing priority-based operations, libev usually has to -linearly search all the priorities, but starting/stopping and activating -watchers becomes O(1) with respect to priority handling. +Then there is no memory leak, just as memory accounted to global variables +is not a memleak - the memory is still being refernced, and didn't leak. -=item Sending an ev_async: O(1) +Similarly, under some circumstances, valgrind might report kernel bugs +as if it were a bug in libev (e.g. in realloc or in the poll backend, +although an acceptable workaround has been found here), or it might be +confused. -=item Processing ev_async_send: O(number_of_async_watchers) +Keep in mind that valgrind is a very good tool, but only a tool. Don't +make it into some kind of religion. -=item Processing signals: O(max_signal_number) +If you are unsure about something, feel free to contact the mailing list +with the full valgrind report and an explanation on why you think this +is a bug in libev (best check the archives, too :). However, don't be +annoyed when you get a brisk "this is no bug" answer and take the chance +of learning how to interpret valgrind properly. -Sending involves a system call I there were no other C -calls in the current loop iteration. Checking for async and signal events -involves iterating over all running async watchers or all signal numbers. +If you need, for some reason, empty reports from valgrind for your project +I suggest using suppression lists. -=back +=head1 PORTABILITY NOTES -=head1 WIN32 PLATFORM LIMITATIONS AND WORKAROUNDS +=head2 WIN32 PLATFORM LIMITATIONS AND WORKAROUNDS Win32 doesn't support any of the standards (e.g. POSIX) that libev requires, and its I/O model is fundamentally incompatible with the POSIX @@ -3536,11 +3543,10 @@ =back +=head2 PORTABILITY REQUIREMENTS -=head1 PORTABILITY REQUIREMENTS - -In addition to a working ISO-C implementation, libev relies on a few -additional extensions: +In addition to a working ISO-C implementation and of course the +backend-specific APIs, libev relies on a few additional extensions: =over 4 @@ -3575,11 +3581,11 @@ =item C must be large enough for common memory allocation sizes -To improve portability and simplify using libev, libev uses C -internally instead of C when allocating its data structures. On -non-POSIX systems (Microsoft...) this might be unexpectedly low, but -is still at least 31 bits everywhere, which is enough for hundreds of -millions of watchers. +To improve portability and simplify its API, libev uses C internally +instead of C when allocating its data structures. On non-POSIX +systems (Microsoft...) this might be unexpectedly low, but is still at +least 31 bits everywhere, which is enough for hundreds of millions of +watchers. =item C must hold a time value in seconds with enough accuracy @@ -3593,56 +3599,75 @@ If you know of other additional requirements drop me a note. -=head1 COMPILER WARNINGS +=head1 ALGORITHMIC COMPLEXITIES -Depending on your compiler and compiler settings, you might get no or a -lot of warnings when compiling libev code. Some people are apparently -scared by this. +In this section the complexities of (many of) the algorithms used inside +libev will be documented. For complexity discussions about backends see +the documentation for C. -However, these are unavoidable for many reasons. For one, each compiler -has different warnings, and each user has different tastes regarding -warning options. "Warn-free" code therefore cannot be a goal except when -targeting a specific compiler and compiler-version. +All of the following are about amortised time: If an array needs to be +extended, libev needs to realloc and move the whole array, but this +happens asymptotically rarer with higher number of elements, so O(1) might +mean that libev does a lengthy realloc operation in rare cases, but on +average it is much faster and asymptotically approaches constant time. -Another reason is that some compiler warnings require elaborate -workarounds, or other changes to the code that make it less clear and less -maintainable. +=over 4 -And of course, some compiler warnings are just plain stupid, or simply -wrong (because they don't actually warn about the condition their message -seems to warn about). +=item Starting and stopping timer/periodic watchers: O(log skipped_other_timers) -While libev is written to generate as few warnings as possible, -"warn-free" code is not a goal, and it is recommended not to build libev -with any compiler warnings enabled unless you are prepared to cope with -them (e.g. by ignoring them). Remember that warnings are just that: -warnings, not errors, or proof of bugs. +This means that, when you have a watcher that triggers in one hour and +there are 100 watchers that would trigger before that, then inserting will +have to skip roughly seven (C) of these watchers. +=item Changing timer/periodic watchers (by autorepeat or calling again): O(log skipped_other_timers) -=head1 VALGRIND +That means that changing a timer costs less than removing/adding them, +as only the relative motion in the event queue has to be paid for. -Valgrind has a special section here because it is a popular tool that is -highly useful, but valgrind reports are very hard to interpret. +=item Starting io/check/prepare/idle/signal/child/fork/async watchers: O(1) -If you think you found a bug (memory leak, uninitialised data access etc.) -in libev, then check twice: If valgrind reports something like: +These just add the watcher into an array or at the head of a list. - ==2274== definitely lost: 0 bytes in 0 blocks. - ==2274== possibly lost: 0 bytes in 0 blocks. - ==2274== still reachable: 256 bytes in 1 blocks. +=item Stopping check/prepare/idle/fork/async watchers: O(1) -Then there is no memory leak. Similarly, under some circumstances, -valgrind might report kernel bugs as if it were a bug in libev, or it -might be confused (it is a very good tool, but only a tool). +=item Stopping an io/signal/child watcher: O(number_of_watchers_for_this_(fd/signal/pid % EV_PID_HASHSIZE)) -If you are unsure about something, feel free to contact the mailing list -with the full valgrind report and an explanation on why you think this is -a bug in libev. However, don't be annoyed when you get a brisk "this is -no bug" answer and take the chance of learning how to interpret valgrind -properly. +These watchers are stored in lists, so they need to be walked to find the +correct watcher to remove. The lists are usually short (you don't usually +have many watchers waiting for the same fd or signal: one is typical, two +is rare). -If you need, for some reason, empty reports from valgrind for your project -I suggest using suppression lists. +=item Finding the next timer in each loop iteration: O(1) + +By virtue of using a binary or 4-heap, the next timer is always found at a +fixed position in the storage array. + +=item Each change on a file descriptor per loop iteration: O(number_of_watchers_for_this_fd) + +A change means an I/O watcher gets started or stopped, which requires +libev to recalculate its status (and possibly tell the kernel, depending +on backend and whether C was used). + +=item Activating one watcher (putting it into the pending state): O(1) + +=item Priority handling: O(number_of_priorities) + +Priorities are implemented by allocating some space for each +priority. When doing priority-based operations, libev usually has to +linearly search all the priorities, but starting/stopping and activating +watchers becomes O(1) with respect to priority handling. + +=item Sending an ev_async: O(1) + +=item Processing ev_async_send: O(number_of_async_watchers) + +=item Processing signals: O(max_signal_number) + +Sending involves a system call I there were no other C +calls in the current loop iteration. Checking for async and signal events +involves iterating over all running async watchers or all signal numbers. + +=back =head1 AUTHOR