--- libev/ev.pod 2011/01/10 14:30:15 1.352 +++ libev/ev.pod 2012/04/18 06:56:53 1.403 @@ -60,7 +60,7 @@ // now wait for events to arrive ev_run (loop, 0); - // unloop was called, so exit + // break was called, so exit return 0; } @@ -176,13 +176,19 @@ Returns the current time as libev would use it. Please note that the C function is usually faster and also often returns the timestamp you actually want to know. Also interesting is the combination of -C and C. +C and C. =item ev_sleep (ev_tstamp interval) -Sleep for the given interval: The current thread will be blocked until -either it is interrupted or the given time interval has passed. Basically -this is a sub-second-resolution C. +Sleep for the given interval: The current thread will be blocked +until either it is interrupted or the given time interval has +passed (approximately - it might return a bit earlier even if not +interrupted). Returns immediately if C<< interval <= 0 >>. + +Basically this is a sub-second-resolution C. + +The range of the C is limited - libev only guarantees to work +with sleep times of up to one day (C<< interval <= 86400 >>). =item int ev_version_major () @@ -243,7 +249,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) throw ()) Sets the allocation function to use (the prototype is similar - the semantics are identical to the C C89/SuS/POSIX function). It is @@ -279,7 +285,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) throw ()) 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 @@ -437,13 +443,16 @@ =item C When this flag is specified, then libev will avoid to modify the signal -mask. Specifically, this means you ahve to make sure signals are unblocked +mask. Specifically, this means you have to make sure signals are unblocked when you want to receive them. This behaviour is useful when you want to do your own signal handling, or want to handle signals only in specific threads and want to avoid libev unblocking the signals. +It's also required by POSIX in a threaded program, as libev calls +C, whose behaviour is officially unspecified. + This flag's behaviour will become the default in future versions of libev. =item C (value 1, portable select backend) @@ -482,10 +491,10 @@ Use the linux-specific epoll(7) interface (for both pre- and post-2.6.9 kernels). -For few fds, this backend is a bit little slower than poll and select, -but it scales phenomenally better. While poll and select usually scale -like O(total_fds) where n is the total number of fds (or the highest fd), -epoll scales either O(1) or O(active_fds). +For few fds, this backend is a bit little slower than poll and select, but +it scales phenomenally better. While poll and select usually scale like +O(total_fds) where total_fds is the total number of fds (or the highest +fd), epoll scales either O(1) or O(active_fds). The epoll mechanism deserves honorable mention as the most misdesigned of the more advanced event mechanisms: mere annoyances include silently @@ -498,17 +507,22 @@ set, which can take considerable time (one syscall per file descriptor) and is of course hard to detect. -Epoll is also notoriously buggy - embedding epoll fds I work, but -of course I, and epoll just loves to report events for totally -I file descriptors (even already closed ones, so one cannot -even remove them from the set) than registered in the set (especially -on SMP systems). Libev tries to counter these spurious notifications by -employing an additional generation counter and comparing that against the -events to filter out spurious ones, recreating the set when required. Last +Epoll is also notoriously buggy - embedding epoll fds I work, +but of course I, and epoll just loves to report events for +totally I file descriptors (even already closed ones, so +one cannot even remove them from the set) than registered in the set +(especially on SMP systems). Libev tries to counter these spurious +notifications by employing an additional generation counter and comparing +that against the events to filter out spurious ones, recreating the set +when required. Epoll also erroneously rounds down timeouts, but gives you +no way to know when and by how much, so sometimes you have to busy-wait +because epoll returns immediately despite a nonzero timeout. And last not least, it also refuses to work with some file descriptors which work perfectly fine with C (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 its own, but in the case of files, there is no such thing: the disk +will not send data on its 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 on Linux works with F but not with +F), 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 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 or C after a fork in the child, -enable C, or resort to C or -C. +To support fork in your child processes, you have to call C after a fork in the child, enable C, or resort to +C or C. =head3 The special problem of SIGPIPE @@ -1790,10 +1777,11 @@ The callback is guaranteed to be invoked only I its timeout has passed (not I, so on systems with very low-resolution clocks this -might introduce a small delay). If multiple timers become ready during the -same loop iteration then the ones with earlier time-out values are invoked -before ones of the same priority with later time-out values (but this is -no longer true when a callback calls C recursively). +might introduce a small delay, see "the special problem of being too +early", below). If multiple timers become ready during the same loop +iteration then the ones with earlier time-out values are invoked before +ones of the same priority with later time-out values (but this is no +longer true when a callback calls C recursively). =head3 Be smart about timeouts @@ -1878,63 +1866,77 @@ but remember the time of last activity, and check for a real timeout only within the callback: + ev_tstamp timeout = 60.; ev_tstamp last_activity; // time of last activity + ev_timer timer; static void callback (EV_P_ ev_timer *w, int revents) { - ev_tstamp now = ev_now (EV_A); - ev_tstamp timeout = last_activity + 60.; + // calculate when the timeout would happen + ev_tstamp after = last_activity - ev_now (EV_A) + timeout; - // if last_activity + 60. is older than now, we did time out - if (timeout < now) + // if negative, it means we the timeout already occurred + if (after < 0.) { // timeout occurred, take action } else { - // callback was invoked, but there was some activity, re-arm - // the watcher to fire in last_activity + 60, which is - // guaranteed to be in the future, so "again" is positive: - w->repeat = timeout - now; - ev_timer_again (EV_A_ w); + // callback was invoked, but there was some recent + // activity. simply restart the timer to time out + // after "after" seconds, which is the earliest time + // the timeout can occur. + ev_timer_set (w, after, 0.); + ev_timer_start (EV_A_ w); } } -To summarise the callback: first calculate the real timeout (defined -as "60 seconds after the last activity"), then check if that time has -been reached, which means something I, in fact, time out. Otherwise -the callback was invoked too early (C is in the future), so -re-schedule the timer to fire at that future time, to see if maybe we have -a timeout then. - -Note how C is used, taking advantage of the -C optimisation when the timer is already running. +To summarise the callback: first calculate in how many seconds the +timeout will occur (by calculating the absolute time when it would occur, +C, and subtracting the current time, C from that). + +If this value is negative, then we are already past the timeout, i.e. we +timed out, and need to do whatever is needed in this case. + +Otherwise, we now the earliest time at which the timeout would trigger, +and simply start the timer with this timeout value. + +In other words, each time the callback is invoked it will check whether +the timeout occurred. If not, it will simply reschedule itself to check +again at the earliest time it could time out. Rinse. Repeat. This scheme causes more callback invocations (about one every 60 seconds minus half the average time between activity), but virtually no calls to libev to change the timeout. -To start the timer, simply initialise the watcher and set C -to the current time (meaning we just have some activity :), then call the -callback, which will "do the right thing" and start the timer: +To start the machinery, simply initialise the watcher and set +C to the current time (meaning there was some activity just +now), then call the callback, which will "do the right thing" and start +the timer: + + last_activity = ev_now (EV_A); + ev_init (&timer, callback); + callback (EV_A_ &timer, 0); - ev_init (timer, callback); - last_activity = ev_now (loop); - callback (loop, timer, EV_TIMER); - -And when there is some activity, simply store the current time in +When there is some activity, simply store the current time in C, no libev calls at all: - last_activity = ev_now (loop); + if (activity detected) + last_activity = ev_now (EV_A); + +When your timeout value changes, then the timeout can be changed by simply +providing a new value, stopping the timer and calling the callback, which +will again do the right thing (for example, time out immediately :). + + timeout = new_value; + ev_timer_stop (EV_A_ &timer); + callback (EV_A_ &timer, 0); This technique is slightly more complex, but in most cases where the time-out is unlikely to be triggered, much more efficient. -Changing the timeout is trivial as well (if it isn't hard-coded in the -callback :) - just change the timeout and invoke the callback, which will -fix things for you. - =item 4. Wee, just use a double-linked list for your timeouts. If there is not one request, but many thousands (millions...), all @@ -1970,10 +1972,47 @@ off after the first million or so of active timers, i.e. it's usually overkill :) +=head3 The special problem of being too early + +If you ask a timer to call your callback after three seconds, then +you expect it to be invoked after three seconds - but of course, this +cannot be guaranteed to infinite precision. Less obviously, it cannot be +guaranteed to any precision by libev - imagine somebody suspending the +process with a STOP signal for a few hours for example. + +So, libev tries to invoke your callback as soon as possible I the +delay has occurred, but cannot guarantee this. + +A less obvious failure mode is calling your callback too early: many event +loops compare timestamps with a "elapsed delay >= requested delay", but +this can cause your callback to be invoked much earlier than you would +expect. + +To see why, imagine a system with a clock that only offers full second +resolution (think windows if you can't come up with a broken enough OS +yourself). If you schedule a one-second timer at the time 500.9, then the +event loop will schedule your timeout to elapse at a system time of 500 +(500.9 truncated to the resolution) + 1, or 501. + +If an event library looks at the timeout 0.1s later, it will see "501 >= +501" and invoke the callback 0.1s after it was started, even though a +one-second delay was requested - this is being "too early", despite best +intentions. + +This is the reason why libev will never invoke the callback if the elapsed +delay equals the requested delay, but only when the elapsed delay is +larger than the requested delay. In the example above, libev would only invoke +the callback at system time 502, or 1.1s after the timer was started. + +So, while libev cannot guarantee that your callback will be invoked +exactly when requested, it I and I guarantee that the requested +delay has actually elapsed, or in other words, it always errs on the "too +late" side of things. + =head3 The special problem of time updates -Establishing the current time is a costly operation (it usually takes at -least two system calls): EV therefore updates its idea of the current +Establishing the current time is a costly operation (it usually takes +at least one system call): EV therefore updates its idea of the current time only before and after C collects new events, which causes a growing difference between C and C when handling lots of events in one iteration. @@ -1990,6 +2029,39 @@ update of the time returned by C by calling C. +=head3 The special problem of unsynchronised clocks + +Modern systems have a variety of clocks - libev itself uses the normal +"wall clock" clock and, if available, the monotonic clock (to avoid time +jumps). + +Neither of these clocks is synchronised with each other or any other clock +on the system, so C might return a considerably different time +than C or C