--- libev/ev.pod 2011/11/29 15:10:05 1.385 +++ libev/ev.pod 2012/04/02 23:14:41 1.399 @@ -569,9 +569,9 @@ kernel is more efficient (which says nothing about its actual speed, of course). While stopping, setting and starting an I/O watcher does never cause an extra system call as with C, it still adds up to -two event changes per incident. Support for C is very bad (but -sane, unlike epoll) and it drops fds silently in similarly hard-to-detect -cases +two event changes per incident. Support for C is very bad (you +might have to leak fd's on fork, but it's more sane than epoll) and it +drops fds silently in similarly hard-to-detect cases This backend usually performs well under most conditions. @@ -794,18 +794,22 @@ Calling C/C has the side effect of updating the event loop time (see C). -=item ev_run (loop, int flags) +=item bool ev_run (loop, int flags) Finally, this is it, the event handler. This function usually is called after you have initialised all your watchers and you want to start handling events. It will ask the operating system for any new events, call -the watcher callbacks, an then repeat the whole process indefinitely: This +the watcher callbacks, and then repeat the whole process indefinitely: This is why event loops are called I. If the flags argument is specified as C<0>, it will keep handling events until either no event watchers are active anymore or C was called. +The return value is false if there are no more active watchers (which +usually means "all jobs done" or "deadlock"), and true in all other cases +(which usually means " you should call C again"). + Please note that an explicit C is usually better than relying on all watchers to be stopped when deciding when a program has finished (especially in interactive programs), but having a program @@ -813,8 +817,8 @@ of relying on its watchers stopping correctly, that is truly a thing of beauty. -This function is also I exception-safe - you can break out of -a C call by calling C in a callback, throwing a C++ +This function is I exception-safe - you can break out of a +C call by calling C in a callback, throwing a C++ exception and so on. This does not decrement the C value, nor will it clear any outstanding C breaks. @@ -1862,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 occured + 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 cocured. 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: - - ev_init (timer, callback); - last_activity = ev_now (loop); - callback (loop, timer, EV_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); -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 agaion 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 @@ -1960,7 +1978,7 @@ 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 a STOP signal for a few hours for example. +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. @@ -2096,15 +2114,24 @@ =item ev_timer_again (loop, ev_timer *) -This will act as if the timer timed out and restarts it again if it is -repeating. The exact semantics are: +This will act as if the timer timed out, and restarts it again if it is +repeating. It basically works like calling C, updating the +timeout to the C value and calling C. -If the timer is pending, its pending status is cleared. +The exact semantics are as in the following rules, all of which will be +applied to the watcher: + +=over 4 -If the timer is started but non-repeating, stop it (as if it timed out). +=item If the timer is pending, the pending status is always cleared. -If the timer is repeating, either start it if necessary (with the -C value), or reset the running timer to the C value. +=item If the timer is started but non-repeating, stop it (as if it timed +out, without invoking it). + +=item If the timer is repeating, make the C value the new timeout +and start the timer, if necessary. + +=back This sounds a bit complicated, see L, above, for a usage example. @@ -3475,7 +3502,7 @@ =item ev_feed_fd_event (loop, int fd, int revents) Feed an event on the given fd, as if a file descriptor backend detected -the given events it. +the given events. =item ev_feed_signal_event (loop, int signum) @@ -3559,6 +3586,46 @@ (((char *)w) - offsetof (struct my_biggy, t2)); } +=head2 AVOIDING FINISHING BEFORE RETURNING + +Often you have structures like this in event-based programs: + + callback () + { + free (request); + } + + request = start_new_request (..., callback); + +The intent is to start some "lengthy" operation. The C could be +used to cancel the operation, or do other things with it. + +It's not uncommon to have code paths in C that +immediately invoke the callback, for example, to report errors. Or you add +some caching layer that finds that it can skip the lengthy aspects of the +operation and simply invoke the callback with the result. + +The problem here is that this will happen I C +has returned, so C is not set. + +Even if you pass the request by some safer means to the callback, you +might want to do something to the request after starting it, such as +canceling it, which probably isn't working so well when the callback has +already been invoked. + +A common way around all these issues is to make sure that +C I returns before the callback is invoked. If +C immediately knows the result, it can artificially +delay invoking the callback by e.g. using a C or C watcher +for example, or more sneakily, by reusing an existing (stopped) watcher +and pushing it into the pending queue: + + ev_set_cb (watcher, callback); + ev_feed_event (EV_A_ watcher, 0); + +This way, C can safely return before the callback is +invoked, while not delaying callback invocation too much. + =head2 MODEL/NESTED EVENT LOOP INVOCATIONS AND EXIT CONDITIONS Often (especially in GUI toolkits) there are places where you have @@ -3581,7 +3648,7 @@ while (!exit_main_loop) ev_run (EV_DEFAULT_ EVRUN_ONCE); - // in a model watcher + // in a modal watcher int exit_nested_loop = 0; while (!exit_nested_loop) @@ -3771,7 +3838,7 @@ That basically suspends the coroutine inside C and continues the libev coroutine, which, when appropriate, switches back to -this or any other coroutine. I am sure if you sue this your own :) +this or any other coroutine. You can do similar tricks if you have, say, threads with an event queue - instead of storing a coroutine, you store the queue object and instead of @@ -3856,6 +3923,10 @@ you need support for other types of functors please contact the author (preferably after implementing it). +For all this to work, your C++ compiler either has to use the same calling +conventions as your C compiler (for static member functions), or you have +to embed libev and compile libev itself as C++. + Here is a list of things available in the C namespace: =over 4 @@ -3874,7 +3945,7 @@ For each C watcher in F there is a corresponding class of the same name in the C namespace, with the exception of C which is called C to avoid clashes with the C macro -defines by many implementations. +defined by many implementations. All of those classes have these methods: @@ -4443,6 +4514,19 @@ be detected at runtime. If undefined, it will be enabled if the headers indicate GNU/Linux + Glibc 2.4 or newer, otherwise disabled. +=item EV_NO_SMP + +If defined to be C<1>, libev will assume that memory is always coherent +between threads, that is, threads can be used, but threads never run on +different cpus (or different cpu cores). This reduces dependencies +and makes libev faster. + +=item EV_NO_THREADS + +If defined to be C<1>, libev will assume that it will never be called +from different threads, which is a stronger assumption than C, +above. This reduces dependencies and makes libev faster. + =item EV_ATOMIC_T Libev requires an integer type (suitable for storing C<0> or C<1>) whose @@ -4600,6 +4684,20 @@ your program might be left out as well - a binary starting a timer and an I/O watcher then might come out at only 5Kb. +=item EV_API_STATIC + +If this symbol is defined (by default it is not), then all identifiers +will have static linkage. This means that libev will not export any +identifiers, and you cannot link against libev anymore. This can be useful +when you embed libev, only want to use libev functions in a single file, +and do not want its identifiers to be visible. + +To use this, define C and include F in the file that +wants to use libev. + +This option only works when libev is compiled with a C compiler, as C++ +doesn't support the required declaration syntax. + =item EV_AVOID_STDIO If this is set to C<1> at compiletime, then libev will avoid using stdio