From 6025c0f8922661a3d0e6c5d06c2c0dd4cada7059 Mon Sep 17 00:00:00 2001 From: 404-html <45104241+404-html@users.noreply.github.com> Date: Tue, 30 Apr 2019 22:59:32 +0200 Subject: [PATCH] Internet cleaning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Powered by [Internet Cleaner®️](https://github.com/404-html/internet-cleaner) --- ChatScript/SRC/evserver/ev.pod | 10888 +++++++++++++++---------------- 1 file changed, 5444 insertions(+), 5444 deletions(-) diff --git a/ChatScript/SRC/evserver/ev.pod b/ChatScript/SRC/evserver/ev.pod index 87d146e..ef7a15b 100644 --- a/ChatScript/SRC/evserver/ev.pod +++ b/ChatScript/SRC/evserver/ev.pod @@ -1,5444 +1,5444 @@ -=head1 NAME - -libev - a high performance full-featured event loop written in C - -=head1 SYNOPSIS - - #include - -=head2 EXAMPLE PROGRAM - - // a single header file is required - #include - - #include // for puts - - // every watcher type has its own typedef'd struct - // with the name ev_TYPE - ev_io stdin_watcher; - ev_timer timeout_watcher; - - // all watcher callbacks have a similar signature - // this callback is called when data is readable on stdin - static void - stdin_cb (EV_P_ ev_io *w, int revents) - { - puts ("stdin ready"); - // for one-shot events, one must manually stop the watcher - // with its corresponding stop function. - ev_io_stop (EV_A_ w); - - // this causes all nested ev_run's to stop iterating - ev_break (EV_A_ EVBREAK_ALL); - } - - // another callback, this time for a time-out - static void - timeout_cb (EV_P_ ev_timer *w, int revents) - { - puts ("timeout"); - // this causes the innermost ev_run to stop iterating - ev_break (EV_A_ EVBREAK_ONE); - } - - int - main (void) - { - // use the default event loop unless you have special needs - struct ev_loop *loop = EV_DEFAULT; - - // initialise an io watcher, then start it - // this one will watch for stdin to become readable - ev_io_init (&stdin_watcher, stdin_cb, /*STDIN_FILENO*/ 0, EV_READ); - ev_io_start (loop, &stdin_watcher); - - // initialise a timer watcher, then start it - // simple non-repeating 5.5 second timeout - ev_timer_init (&timeout_watcher, timeout_cb, 5.5, 0.); - ev_timer_start (loop, &timeout_watcher); - - // now wait for events to arrive - ev_run (loop, 0); - - // break was called, so exit - return 0; - } - -=head1 ABOUT THIS DOCUMENT - -This document documents the libev software package. - -The newest version of this document is also available as an html-formatted -web page you might find easier to navigate when reading it for the first -time: L. - -While this document tries to be as complete as possible in documenting -libev, its usage and the rationale behind its design, it is not a tutorial -on event-based programming, nor will it introduce event-based programming -with libev. - -Familiarity with event based programming techniques in general is assumed -throughout this document. - -=head1 WHAT TO READ WHEN IN A HURRY - -This manual tries to be very detailed, but unfortunately, this also makes -it very long. If you just want to know the basics of libev, I suggest -reading L, then the L above and -look up the missing functions in L and the C and -C sections in L. - -=head1 ABOUT LIBEV - -Libev is an event loop: you register interest in certain events (such as a -file descriptor being readable or a timeout occurring), and it will manage -these event sources and provide your program with events. - -To do this, it must take more or less complete control over your process -(or thread) by executing the I handler, and will then -communicate events via a callback mechanism. - -You register interest in certain events by registering so-called I, which are relatively small C structures you initialise with the -details of the event, and then hand it over to libev by I the -watcher. - -=head2 FEATURES - -Libev supports C (files, many character devices...). - -Epoll is truly the train wreck among event poll mechanisms, a frankenpoll, -cobbled together in a hurry, no thought to design or interaction with -others. Oh, the pain, will it ever stop... - -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 -incident (because the same I could point to a different -I now), so its best to avoid that. Also, C'ed -file descriptors might not work very well if you register events for both -file descriptors. - -Best performance from this backend is achieved by not unregistering all -watchers for a file descriptor until it has been closed, if possible, -i.e. keep at least one watcher active per fd at all times. Stopping and -starting a watcher (without re-setting it) also usually doesn't cause -extra overhead. A fork can both result in spurious notifications as well -as in libev having to destroy and recreate the epoll object, which can -take considerable time and thus should be avoided. - -All this means that, in practice, C can be as fast or -faster than epoll for maybe up to a hundred file descriptors, depending on -the usage. So sad. - -While nominally embeddable in other event loops, this feature is broken in -all kernel versions tested so far. - -This backend maps C and C in the same way as -C. - -=item C (value 8, most BSD clones) - -Kqueue deserves special mention, as at the time of this writing, it -was broken on all BSDs except NetBSD (usually it doesn't work reliably -with anything but sockets and pipes, except on Darwin, where of course -it's completely useless). Unlike epoll, however, whose brokenness -is by design, these kqueue bugs can (and eventually will) be fixed -without API changes to existing programs. For this reason it's not being -"auto-detected" unless you explicitly specify it in the flags (i.e. using -C) or libev was compiled on a known-to-be-good (-enough) -system like NetBSD. - -You still can embed kqueue into a normal poll or select backend and use it -only for sockets (after having made sure that sockets work with kqueue on -the target platform). See C watchers for more info. - -It scales in the same way as the epoll backend, but the interface to the -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 - -This backend usually performs well under most conditions. - -While nominally embeddable in other event loops, this doesn't work -everywhere, so you might need to test for this. And since it is broken -almost everywhere, you should only use it when you have a lot of sockets -(for which it usually works), by embedding it into another event loop -(e.g. C or C (but C is of course -also broken on OS X)) and, did I mention it, using it only for sockets. - -This backend maps C into an C kevent with -C, and C into an C kevent with -C. - -=item C (value 16, Solaris 8) - -This is not implemented yet (and might never be, unless you send me an -implementation). According to reports, C only supports sockets -and is not embeddable, which would limit the usefulness of this backend -immensely. - -=item C (value 32, Solaris 10) - -This uses the Solaris 10 event port mechanism. As with everything on Solaris, -it's really slow, but it still scales very well (O(active_fds)). - -While this backend scales well, it requires one system call per active -file descriptor per loop iteration. For small and medium numbers of file -descriptors a "slow" C or C backend -might perform better. - -On the positive side, this backend actually performed fully to -specification in all tests and is fully embeddable, which is a rare feat -among the OS-specific backends (I vastly prefer correctness over speed -hacks). - -On the negative side, the interface is I - so bizarre that -even sun itself gets it wrong in their code examples: The event polling -function sometimes returns events to the caller even though an error -occurred, but with no indication whether it has done so or not (yes, it's -even documented that way) - deadly for edge-triggered interfaces where you -absolutely have to know whether an event occurred or not because you have -to re-arm the watcher. - -Fortunately libev seems to be able to work around these idiocies. - -This backend maps C and C in the same way as -C. - -=item C - -Try all backends (even potentially broken ones that wouldn't be tried -with C). Since this is a mask, you can do stuff such as -C. - -It is definitely not recommended to use this flag, use whatever -C returns, or simply do not specify a backend -at all. - -=item C - -Not a backend at all, but a mask to select all backend bits from a -C value, in case you want to mask out any backends from a flags -value (e.g. when modifying the C environment variable). - -=back - -If one or more of the backend flags are or'ed into the flags value, -then only these backends will be tried (in the reverse order as listed -here). If none are specified, all backends in C will be tried. - -Example: Try to create a event loop that uses epoll and nothing else. - - struct ev_loop *epoller = ev_loop_new (EVBACKEND_EPOLL | EVFLAG_NOENV); - if (!epoller) - fatal ("no epoll found here, maybe it hides under your chair"); - -Example: Use whatever libev has to offer, but make sure that kqueue is -used if available. - - struct ev_loop *loop = ev_loop_new (ev_recommended_backends () | EVBACKEND_KQUEUE); - -=item ev_loop_destroy (loop) - -Destroys an event loop object (frees all memory and kernel state -etc.). None of the active event watchers will be stopped in the normal -sense, so e.g. C might still return true. It is your -responsibility to either stop all watchers cleanly yourself I -calling this function, or cope with the fact afterwards (which is usually -the easiest thing, you can just ignore the watchers and/or C them -for example). - -Note that certain global state, such as signal state (and installed signal -handlers), will not be freed by this function, and related watchers (such -as signal and child watchers) would need to be stopped manually. - -This function is normally used on loop objects allocated by -C, but it can also be used on the default loop returned by -C, in which case it is not thread-safe. - -Note that it is not advisable to call this function on the default loop -except in the rare occasion where you really need to free its resources. -If you need dynamically allocated loops it is better to use C -and C. - -=item ev_loop_fork (loop) - -This function sets a flag that causes subsequent C iterations to -reinitialise the kernel state for backends that have one. Despite the -name, you can call it anytime, but it makes most sense after forking, in -the child process. You I call it (or use C) in the -child before resuming or calling C. - -Again, you I to call it on I loop that you want to re-use after -a fork, I. This is -because some kernel interfaces *cough* I *cough* do funny things -during fork. - -On the other hand, you only need to call this function in the child -process if and only if you want to use the event loop in the child. If -you just fork+exec or create a new loop in the child, you don't have to -call it at all (in fact, C is so badly broken that it makes a -difference, but libev will usually detect this case on its own and do a -costly reset of the backend). - -The function itself is quite fast and it's usually not a problem to call -it just in case after a fork. - -Example: Automate calling C on the default loop when -using pthreads. - - static void - post_fork_child (void) - { - ev_loop_fork (EV_DEFAULT); - } - - ... - pthread_atfork (0, 0, post_fork_child); - -=item int ev_is_default_loop (loop) - -Returns true when the given loop is, in fact, the default loop, and false -otherwise. - -=item unsigned int ev_iteration (loop) - -Returns the current iteration count for the event loop, which is identical -to the number of times libev did poll for new events. It starts at C<0> -and happily wraps around with enough iterations. - -This value can sometimes be useful as a generation counter of sorts (it -"ticks" the number of loop iterations), as it roughly corresponds with -C and C calls - and is incremented between the -prepare and check phases. - -=item unsigned int ev_depth (loop) - -Returns the number of times C was entered minus the number of -times C was exited normally, in other words, the recursion depth. - -Outside C, this number is zero. In a callback, this number is -C<1>, unless C was invoked recursively (or from another thread), -in which case it is higher. - -Leaving C abnormally (setjmp/longjmp, cancelling the thread, -throwing an exception etc.), doesn't count as "exit" - consider this -as a hint to avoid such ungentleman-like behaviour unless it's really -convenient, in which case it is fully supported. - -=item unsigned int ev_backend (loop) - -Returns one of the C flags indicating the event backend in -use. - -=item ev_tstamp ev_now (loop) - -Returns the current "event loop time", which is the time the event loop -received events and started processing them. This timestamp does not -change as long as callbacks are being processed, and this is also the base -time used for relative timers. You can treat it as the timestamp of the -event occurring (or more correctly, libev finding out about it). - -=item ev_now_update (loop) - -Establishes the current time by querying the kernel, updating the time -returned by C in the progress. This is a costly operation and -is usually done automatically within C. - -This function is rarely useful, but when some event callback runs for a -very long time without entering the event loop, updating libev's idea of -the current time is a good idea. - -See also L in the C section. - -=item ev_suspend (loop) - -=item ev_resume (loop) - -These two functions suspend and resume an event loop, for use when the -loop is not used for a while and timeouts should not be processed. - -A typical use case would be an interactive program such as a game: When -the user presses C<^Z> to suspend the game and resumes it an hour later it -would be best to handle timeouts as if no time had actually passed while -the program was suspended. This can be achieved by calling C -in your C handler, sending yourself a C and calling -C directly afterwards to resume timer processing. - -Effectively, all C watchers will be delayed by the time spend -between C and C, and all C watchers -will be rescheduled (that is, they will lose any events that would have -occurred while suspended). - -After calling C you B call I function on the -given loop other than C, and you B call C -without a previous call to C. - -Calling C/C has the side effect of updating the -event loop time (see C). - -=item 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 -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. - -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 -that automatically loops as long as it has to and no longer by virtue -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++ -exception and so on. This does not decrement the C value, nor -will it clear any outstanding C breaks. - -A flags value of C will look for new events, will handle -those events and any already outstanding ones, but will not wait and -block your process in case there are no events and will return after one -iteration of the loop. This is sometimes useful to poll and handle new -events while doing lengthy calculations, to keep the program responsive. - -A flags value of C will look for new events (waiting if -necessary) and will handle those and any already outstanding ones. It -will block your process until at least one new event arrives (which could -be an event internal to libev itself, so there is no guarantee that a -user-registered callback will be called), and will return after one -iteration of the loop. - -This is useful if you are waiting for some external event in conjunction -with something not expressible using other libev watchers (i.e. "roll your -own C"). However, a pair of C/C watchers is -usually a better approach for this kind of thing. - -Here are the gory details of what C does (this is for your -understanding, not a guarantee that things will work exactly like this in -future versions): - - - Increment loop depth. - - Reset the ev_break status. - - Before the first iteration, call any pending watchers. - LOOP: - - If EVFLAG_FORKCHECK was used, check for a fork. - - If a fork was detected (by any means), queue and call all fork watchers. - - Queue and call all prepare watchers. - - If ev_break was called, goto FINISH. - - If we have been forked, detach and recreate the kernel state - as to not disturb the other process. - - Update the kernel state with all outstanding changes. - - Update the "event loop time" (ev_now ()). - - Calculate for how long to sleep or block, if at all - (active idle watchers, EVRUN_NOWAIT or not having - any active watchers at all will result in not sleeping). - - Sleep if the I/O and timer collect interval say so. - - Increment loop iteration counter. - - Block the process, waiting for any events. - - Queue all outstanding I/O (fd) events. - - Update the "event loop time" (ev_now ()), and do time jump adjustments. - - Queue all expired timers. - - Queue all expired periodics. - - Queue all idle watchers with priority higher than that of pending events. - - Queue all check watchers. - - Call all queued watchers in reverse order (i.e. check watchers first). - Signals and child watchers are implemented as I/O watchers, and will - be handled here by queueing them when their watcher gets executed. - - If ev_break has been called, or EVRUN_ONCE or EVRUN_NOWAIT - were used, or there are no active watchers, goto FINISH, otherwise - continue with step LOOP. - FINISH: - - Reset the ev_break status iff it was EVBREAK_ONE. - - Decrement the loop depth. - - Return. - -Example: Queue some jobs and then loop until no events are outstanding -anymore. - - ... queue jobs here, make sure they register event watchers as long - ... as they still have work to do (even an idle watcher will do..) - ev_run (my_loop, 0); - ... jobs done or somebody called break. yeah! - -=item ev_break (loop, how) - -Can be used to make a call to C return early (but only after it -has processed all outstanding events). The C argument must be either -C, which will make the innermost C call return, or -C, which will make all nested C calls return. - -This "break state" will be cleared on the next call to C. - -It is safe to call C from outside any C calls, too, in -which case it will have no effect. - -=item ev_ref (loop) - -=item ev_unref (loop) - -Ref/unref can be used to add or remove a reference count on the event -loop: Every watcher keeps one reference, and as long as the reference -count is nonzero, C will not return on its own. - -This is useful when you have a watcher that you never intend to -unregister, but that nevertheless should not keep C from -returning. In such a case, call C after starting, and C -before stopping it. - -As an example, libev itself uses this for its internal signal pipe: It -is not visible to the libev user and should not keep C from -exiting if no event watchers registered by it are active. It is also an -excellent way to do this for generic recurring timers or from within -third-party libraries. Just remember to I and I (but only if the watcher wasn't active before, or was active -before, respectively. Note also that libev might stop watchers itself -(e.g. non-repeating timers) in which case you have to C -in the callback). - -Example: Create a signal watcher, but keep it from keeping C -running when nothing else is active. - - ev_signal exitsig; - ev_signal_init (&exitsig, sig_cb, SIGINT); - ev_signal_start (loop, &exitsig); - ev_unref (loop); - -Example: For some weird reason, unregister the above signal handler again. - - ev_ref (loop); - ev_signal_stop (loop, &exitsig); - -=item ev_set_io_collect_interval (loop, ev_tstamp interval) - -=item ev_set_timeout_collect_interval (loop, ev_tstamp interval) - -These advanced functions influence the time that libev will spend waiting -for events. Both time intervals are by default C<0>, meaning that libev -will try to invoke timer/periodic callbacks and I/O callbacks with minimum -latency. - -Setting these to a higher value (the C I be >= C<0>) -allows libev to delay invocation of I/O and timer/periodic callbacks -to increase efficiency of loop iterations (or to increase power-saving -opportunities). - -The idea is that sometimes your program runs just fast enough to handle -one (or very few) event(s) per loop iteration. While this makes the -program responsive, it also wastes a lot of CPU time to poll for new -events, especially with backends like 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 if you want to continue to use it in the child. - -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 - -While not really specific to libev, it is easy to forget about C: -when writing to a pipe whose other end has been closed, your program gets -sent a SIGPIPE, which, by default, aborts your program. For most programs -this is sensible behaviour, for daemons, this is usually undesirable. - -So when you encounter spurious, unexplained daemon exits, make sure you -ignore SIGPIPE (and maybe make sure you log the exit status of your daemon -somewhere, as that would have given you a big clue). - -=head3 The special problem of accept()ing when you can't - -Many implementations of the POSIX C function (for example, -found in post-2004 Linux) have the peculiar behaviour of not removing a -connection from the pending queue in all error cases. - -For example, larger servers often run out of file descriptors (because -of resource limits), causing C to fail with C but not -rejecting the connection, leading to libev signalling readiness on -the next iteration again (the connection still exists after all), and -typically causing the program to loop at 100% CPU usage. - -Unfortunately, the set of errors that cause this issue differs between -operating systems, there is usually little the app can do to remedy the -situation, and no known thread-safe method of removing the connection to -cope with overload is known (to me). - -One of the easiest ways to handle this situation is to just ignore it -- when the program encounters an overload, it will just loop until the -situation is over. While this is a form of busy waiting, no OS offers an -event-based way to handle this situation, so it's the best one can do. - -A better way to handle the situation is to log any errors other than -C and C, making sure not to flood the log with such -messages, and continue as usual, which at least gives the user an idea of -what could be wrong ("raise the ulimit!"). For extra points one could stop -the C watcher on the listening fd "for a while", which reduces CPU -usage. - -If your program is single-threaded, then you could also keep a dummy file -descriptor for overload situations (e.g. by opening F), and -when you run into C or C, close it, run C, -close that fd, and create a new dummy fd. This will gracefully refuse -clients under typical overload conditions. - -The last way to handle it is to simply log the error and C, as -is often done with C failures, but this results in an easy -opportunity for a DoS attack. - -=head3 Watcher-Specific Functions - -=over 4 - -=item ev_io_init (ev_io *, callback, int fd, int events) - -=item ev_io_set (ev_io *, int fd, int events) - -Configures an C watcher. The C is the file descriptor to -receive events for and C is either C, C or -C, to express the desire to receive the given events. - -=item int fd [read-only] - -The file descriptor being watched. - -=item int events [read-only] - -The events being watched. - -=back - -=head3 Examples - -Example: Call C when STDIN_FILENO has become, well -readable, but only once. Since it is likely line-buffered, you could -attempt to read a whole line in the callback. - - static void - stdin_readable_cb (struct ev_loop *loop, ev_io *w, int revents) - { - ev_io_stop (loop, w); - .. read from stdin here (or from w->fd) and handle any I/O errors - } - - ... - struct ev_loop *loop = ev_default_init (0); - ev_io stdin_readable; - ev_io_init (&stdin_readable, stdin_readable_cb, STDIN_FILENO, EV_READ); - ev_io_start (loop, &stdin_readable); - ev_run (loop, 0); - - -=head2 C - relative and optionally repeating timeouts - -Timer watchers are simple relative timers that generate an event after a -given time, and optionally repeating in regular intervals after that. - -The timers are based on real time, that is, if you register an event that -times out after an hour and you reset your system clock to January last -year, it will still time out after (roughly) one hour. "Roughly" because -detecting time jumps is hard, and some inaccuracies are unavoidable (the -monotonic clock option helps a lot here). - -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, 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 - -Many real-world problems involve some kind of timeout, usually for error -recovery. A typical example is an HTTP request - if the other side hangs, -you want to raise some error after a while. - -What follows are some ways to handle this problem, from obvious and -inefficient to smart and efficient. - -In the following, a 60 second activity timeout is assumed - a timeout that -gets reset to 60 seconds each time there is activity (e.g. each time some -data or other life sign was received). - -=over 4 - -=item 1. Use a timer and stop, reinitialise and start it on activity. - -This is the most obvious, but not the most simple way: In the beginning, -start the watcher: - - ev_timer_init (timer, callback, 60., 0.); - ev_timer_start (loop, timer); - -Then, each time there is some activity, C it, initialise it -and start it again: - - ev_timer_stop (loop, timer); - ev_timer_set (timer, 60., 0.); - ev_timer_start (loop, timer); - -This is relatively simple to implement, but means that each time there is -some activity, libev will first have to remove the timer from its internal -data structure and then add it again. Libev tries to be fast, but it's -still not a constant-time operation. - -=item 2. Use a timer and re-start it with C inactivity. - -This is the easiest way, and involves using C instead of -C. - -To implement this, configure an C with a C value -of C<60> and then call C at start and each time you -successfully read or write some data. If you go into an idle state where -you do not expect data to travel on the socket, you can C -the timer, and C will automatically restart it if need be. - -That means you can ignore both the C function and the -C argument to C, and only ever use the C -member and C. - -At start: - - ev_init (timer, callback); - timer->repeat = 60.; - ev_timer_again (loop, timer); - -Each time there is some activity: - - ev_timer_again (loop, timer); - -It is even possible to change the time-out on the fly, regardless of -whether the watcher is active or not: - - timer->repeat = 30.; - ev_timer_again (loop, timer); - -This is slightly more efficient then stopping/starting the timer each time -you want to modify its timeout value, as libev does not have to completely -remove and re-insert the timer from/into its internal data structure. - -It is, however, even simpler than the "obvious" way to do it. - -=item 3. Let the timer time out, but then re-arm it as required. - -This method is more tricky, but usually most efficient: Most timeouts are -relatively long compared to the intervals between other activity - in -our example, within 60 seconds, there are usually many I/O events with -associated activity resets. - -In this case, it would be more efficient to leave the C alone, -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) - { - // calculate when the timeout would happen - ev_tstamp after = last_activity - ev_now (EV_A) + timeout; - - // if negative, it means we the timeout already occured - if (after < 0.) - { - // timeout occurred, take action - } - else - { - // 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 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 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); - -When there is some activity, simply store the current time in -C, no libev calls at all: - - 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. - -=item 4. Wee, just use a double-linked list for your timeouts. - -If there is not one request, but many thousands (millions...), all -employing some kind of timeout with the same timeout value, then one can -do even better: - -When starting the timeout, calculate the timeout value and put the timeout -at the I of the list. - -Then use an C to fire when the timeout at the I of -the list is expected to fire (for example, using the technique #3). - -When there is some activity, remove the timer from the list, recalculate -the timeout, append it to the end of the list again, and make sure to -update the C if it was taken from the beginning of the list. - -This way, one can manage an unlimited number of timeouts in O(1) time for -starting, stopping and updating the timers, at the expense of a major -complication, and having to use a constant timeout. The constant timeout -ensures that the list stays sorted. - -=back - -So which method the best? - -Method #2 is a simple no-brain-required solution that is adequate in most -situations. Method #3 requires a bit more thinking, but handles many cases -better, and isn't very complicated either. In most case, choosing either -one is fine, with #3 being better in typical situations. - -Method #1 is almost always a bad idea, and buys you nothing. Method #4 is -rather complicated, but extremely efficient, something that really pays -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 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. - -The relative timeouts are calculated relative to the C -time. This is usually the right thing as this timestamp refers to the time -of the event triggering whatever timeout you are modifying/starting. If -you suspect event processing to be delayed and you I to base the -timeout on the current time, use something like this to adjust for this: - - ev_timer_set (&timer, after + ev_now () - ev_time (), 0.); - -If the event loop is suspended for a long time, you can also force an -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