summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorroot <root>2009-07-17 14:43:38 +0000
committerroot <root>2009-07-17 14:43:38 +0000
commit0bbb0b25b366255e5bcab67187ab88b59a3974e7 (patch)
treea4abd8c17ae23bc48ffa29b94cd0752205edf263
parenta52b95eb63ad4307adb0e4e86d85b1f8a583a74f (diff)
-rw-r--r--Changes4
-rw-r--r--Symbols.ev7
-rw-r--r--ev.3448
3 files changed, 377 insertions, 82 deletions
diff --git a/Changes b/Changes
index d4899b1..af902a6 100644
--- a/Changes
+++ b/Changes
@@ -1,8 +1,6 @@
Revision history for libev, a high-performance and full-featured event loop.
-TODO: ev_walk
-TODO: signal handling per loop
-
+3.7 Fri Jul 17 16:36:32 CEST 2009
- ev_unloop and ev_loop wrongly used a global variable to exit loops,
instead of using a per-loop variable (bug caught by accident...).
- the ev_set_io_collect_interval interpretation has changed.
diff --git a/Symbols.ev b/Symbols.ev
index 121c550..cd59804 100644
--- a/Symbols.ev
+++ b/Symbols.ev
@@ -23,6 +23,7 @@ ev_fork_stop
ev_idle_start
ev_idle_stop
ev_invoke
+ev_invoke_pending
ev_io_start
ev_io_stop
ev_loop
@@ -35,6 +36,7 @@ ev_loop_verify
ev_now
ev_now_update
ev_once
+ev_pending_count
ev_periodic_again
ev_periodic_start
ev_periodic_stop
@@ -44,9 +46,12 @@ ev_recommended_backends
ev_ref
ev_resume
ev_set_allocator
+ev_set_invoke_pending_cb
ev_set_io_collect_interval
+ev_set_loop_release_cb
ev_set_syserr_cb
ev_set_timeout_collect_interval
+ev_set_userdata
ev_signal_start
ev_signal_stop
ev_sleep
@@ -57,9 +62,11 @@ ev_supported_backends
ev_suspend
ev_time
ev_timer_again
+ev_timer_remaining
ev_timer_start
ev_timer_stop
ev_unloop
ev_unref
+ev_userdata
ev_version_major
ev_version_minor
diff --git a/ev.3 b/ev.3
index e87b7f7..ea52301 100644
--- a/ev.3
+++ b/ev.3
@@ -1,15 +1,7 @@
-.\" Automatically generated by Pod::Man 2.16 (Pod::Simple 3.05)
+.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.07)
.\"
.\" Standard preamble:
.\" ========================================================================
-.de Sh \" Subsection heading
-.br
-.if t .Sp
-.ne 5
-.PP
-\fB\\$1\fR
-.PP
-..
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
@@ -53,7 +45,7 @@
.el .ds Aq '
.\"
.\" If the F register is turned on, we'll generate index entries on stderr for
-.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index
+.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.ie \nF \{\
@@ -132,7 +124,7 @@
.\" ========================================================================
.\"
.IX Title "LIBEV 3"
-.TH LIBEV 3 "2009-04-25" "libev-3.6" "libev - high performance full featured event loop"
+.TH LIBEV 3 "2009-07-15" "libev-3.7" "libev - high performance full featured event loop"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
@@ -144,7 +136,7 @@ libev \- a high performance full\-featured event loop written in C
.Vb 1
\& #include <ev.h>
.Ve
-.Sh "\s-1EXAMPLE\s0 \s-1PROGRAM\s0"
+.SS "\s-1EXAMPLE\s0 \s-1PROGRAM\s0"
.IX Subsection "EXAMPLE PROGRAM"
.Vb 2
\& // a single header file is required
@@ -232,7 +224,7 @@ You register interest in certain events by registering so-called \fIevent
watchers\fR, which are relatively small C structures you initialise with the
details of the event, and then hand it over to libev by \fIstarting\fR the
watcher.
-.Sh "\s-1FEATURES\s0"
+.SS "\s-1FEATURES\s0"
.IX Subsection "FEATURES"
Libev supports \f(CW\*(C`select\*(C'\fR, \f(CW\*(C`poll\*(C'\fR, the Linux-specific \f(CW\*(C`epoll\*(C'\fR, the
BSD-specific \f(CW\*(C`kqueue\*(C'\fR and the Solaris-specific event port mechanisms
@@ -246,9 +238,9 @@ file watchers (\f(CW\*(C`ev_stat\*(C'\fR) and even limited support for fork even
(\f(CW\*(C`ev_fork\*(C'\fR).
.PP
It also is quite fast (see this
-benchmark comparing it to libevent
+<benchmark> comparing it to libevent
for example).
-.Sh "\s-1CONVENTIONS\s0"
+.SS "\s-1CONVENTIONS\s0"
.IX Subsection "CONVENTIONS"
Libev is very configurable. In this manual the default (and most common)
configuration will be described, which supports multiple event loops. For
@@ -257,7 +249,7 @@ more info about various configuration options please have a look at
for multiple event loops, then all functions taking an initial argument of
name \f(CW\*(C`loop\*(C'\fR (which is always of type \f(CW\*(C`ev_loop *\*(C'\fR) will not have
this argument.
-.Sh "\s-1TIME\s0 \s-1REPRESENTATION\s0"
+.SS "\s-1TIME\s0 \s-1REPRESENTATION\s0"
.IX Subsection "TIME REPRESENTATION"
Libev represents time as a single floating point number, representing
the (fractional) number of seconds since the (\s-1POSIX\s0) epoch (somewhere
@@ -751,6 +743,17 @@ happily wraps around with enough iterations.
This value can sometimes be useful as a generation counter of sorts (it
\&\*(L"ticks\*(R" the number of loop iterations), as it roughly corresponds with
\&\f(CW\*(C`ev_prepare\*(C'\fR and \f(CW\*(C`ev_check\*(C'\fR calls.
+.IP "unsigned int ev_loop_depth (loop)" 4
+.IX Item "unsigned int ev_loop_depth (loop)"
+Returns the number of times \f(CW\*(C`ev_loop\*(C'\fR was entered minus the number of
+times \f(CW\*(C`ev_loop\*(C'\fR was exited, in other words, the recursion depth.
+.Sp
+Outside \f(CW\*(C`ev_loop\*(C'\fR, this number is zero. In a callback, this number is
+\&\f(CW1\fR, unless \f(CW\*(C`ev_loop\*(C'\fR was invoked recursively (or from another thread),
+in which case it is higher.
+.Sp
+Leaving \f(CW\*(C`ev_loop\*(C'\fR abnormally (setjmp/longjmp, cancelling the thread
+etc.), doesn't count as exit.
.IP "unsigned int ev_backend (loop)" 4
.IX Item "unsigned int ev_backend (loop)"
Returns one of the \f(CW\*(C`EVBACKEND_*\*(C'\fR flags indicating the event backend in
@@ -948,7 +951,9 @@ By setting a higher \fIio collect interval\fR you allow libev to spend more
time collecting I/O events, so you can handle more events per iteration,
at the cost of increasing latency. Timeouts (both \f(CW\*(C`ev_periodic\*(C'\fR and
\&\f(CW\*(C`ev_timer\*(C'\fR) will be not affected. Setting this to a non-null value will
-introduce an additional \f(CW\*(C`ev_sleep ()\*(C'\fR call into most loop iterations.
+introduce an additional \f(CW\*(C`ev_sleep ()\*(C'\fR call into most loop iterations. The
+sleep time ensures that libev will not poll for I/O events more often then
+once per this interval, on average.
.Sp
Likewise, by setting a higher \fItimeout collect interval\fR you allow libev
to spend more time collecting timeouts, at the expense of increased
@@ -960,7 +965,11 @@ Many (busy) programs can usually benefit by setting the I/O collect
interval to a value near \f(CW0.1\fR or so, which is often enough for
interactive servers (of course not for games), likewise for timeouts. It
usually doesn't make much sense to set it to a lower value than \f(CW0.01\fR,
-as this approaches the timing granularity of most systems.
+as this approaches the timing granularity of most systems. Note that if
+you do transactions with the outside world and you can't increase the
+parallelity, then this setting will limit your transaction rate (if you
+need to poll once per transaction and the I/O collect interval is 0.01,
+then you can't do more than 100 transations per second).
.Sp
Setting the \fItimeout collect interval\fR can improve the opportunity for
saving power, as the program will \*(L"bundle\*(R" timer callback invocations that
@@ -968,6 +977,76 @@ are \*(L"near\*(R" in time together, by delaying some, thus reducing the number
times the process sleeps and wakes up again. Another useful technique to
reduce iterations/wake\-ups is to use \f(CW\*(C`ev_periodic\*(C'\fR watchers and make sure
they fire on, say, one-second boundaries only.
+.Sp
+Example: we only need 0.1s timeout granularity, and we wish not to poll
+more often than 100 times per second:
+.Sp
+.Vb 2
+\& ev_set_timeout_collect_interval (EV_DEFAULT_UC_ 0.1);
+\& ev_set_io_collect_interval (EV_DEFAULT_UC_ 0.01);
+.Ve
+.IP "ev_invoke_pending (loop)" 4
+.IX Item "ev_invoke_pending (loop)"
+This call will simply invoke all pending watchers while resetting their
+pending state. Normally, \f(CW\*(C`ev_loop\*(C'\fR does this automatically when required,
+but when overriding the invoke callback this call comes handy.
+.IP "int ev_pending_count (loop)" 4
+.IX Item "int ev_pending_count (loop)"
+Returns the number of pending watchers \- zero indicates that no watchers
+are pending.
+.IP "ev_set_invoke_pending_cb (loop, void (*invoke_pending_cb)(\s-1EV_P\s0))" 4
+.IX 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, \f(CW\*(C`ev_loop\*(C'\fR will call
+this callback instead. This is useful, for example, when you want to
+invoke the actual watchers inside another context (another thread etc.).
+.Sp
+If you want to reset the callback, use \f(CW\*(C`ev_invoke_pending\*(C'\fR as new
+callback.
+.IP "ev_set_loop_release_cb (loop, void (*release)(\s-1EV_P\s0), void (*acquire)(\s-1EV_P\s0))" 4
+.IX 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.
+.Sp
+However, \f(CW\*(C`ev_loop\*(C'\fR 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
+\&\f(CW\*(C`ev_unloop\*(C'\fR and \f(CW\*(C`av_async_send\*(C'\fR, another way is to set these \fIrelease\fR
+and \fIacquire\fR callbacks on the loop.
+.Sp
+When set, then \f(CW\*(C`release\*(C'\fR will be called just before the thread is
+suspended waiting for new events, and \f(CW\*(C`acquire\*(C'\fR is called just
+afterwards.
+.Sp
+Ideally, \f(CW\*(C`release\*(C'\fR will just call your mutex_unlock function, and
+\&\f(CW\*(C`acquire\*(C'\fR will just call the mutex_lock function again.
+.Sp
+While event loop modifications are allowed between invocations of
+\&\f(CW\*(C`release\*(C'\fR and \f(CW\*(C`acquire\*(C'\fR (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 \f(CW\*(C`ev_async\*(C'\fR watcher to wake up \f(CW\*(C`ev_loop\*(C'\fR when you want it
+to take note of any changes you made.
+.Sp
+In theory, threads executing \f(CW\*(C`ev_loop\*(C'\fR will be async-cancel safe between
+invocations of \f(CW\*(C`release\*(C'\fR and \f(CW\*(C`acquire\*(C'\fR.
+.Sp
+See also the locking example in the \f(CW\*(C`THREADS\*(C'\fR section later in this
+document.
+.IP "ev_set_userdata (loop, void *data)" 4
+.IX Item "ev_set_userdata (loop, void *data)"
+.PD 0
+.IP "ev_userdata (loop)" 4
+.IX Item "ev_userdata (loop)"
+.PD
+Set and retrieve a single \f(CW\*(C`void *\*(C'\fR associated with a loop. When
+\&\f(CW\*(C`ev_set_userdata\*(C'\fR has never been called, then \f(CW\*(C`ev_userdata\*(C'\fR returns
+\&\f(CW0.\fR
+.Sp
+These two functions can be used to associate arbitrary data with a loop,
+and are intended solely for the \f(CW\*(C`invoke_pending_cb\*(C'\fR, \f(CW\*(C`release\*(C'\fR and
+\&\f(CW\*(C`acquire\*(C'\fR callbacks described above, but of course can be (ab\-)used for
+any other purpose as well.
.IP "ev_loop_verify (loop)" 4
.IX Item "ev_loop_verify (loop)"
This function only does something when \f(CW\*(C`EV_VERIFY\*(C'\fR support has been
@@ -1126,7 +1205,7 @@ callbacks is well-written it can just attempt the operation and cope with
the error from \fIread()\fR or \fIwrite()\fR. This will not work in multi-threaded
programs, though, as the fd could already be closed and reused for another
thing, so beware.
-.Sh "\s-1GENERIC\s0 \s-1WATCHER\s0 \s-1FUNCTIONS\s0"
+.SS "\s-1GENERIC\s0 \s-1WATCHER\s0 \s-1FUNCTIONS\s0"
.IX Subsection "GENERIC WATCHER FUNCTIONS"
.ie n .IP """ev_init"" (ev_TYPE *watcher, callback)" 4
.el .IP "\f(CWev_init\fR (ev_TYPE *watcher, callback)" 4
@@ -1260,7 +1339,7 @@ watcher isn't pending it does nothing and returns \f(CW0\fR.
.Sp
Sometimes it can be useful to \*(L"poll\*(R" a watcher instead of waiting for its
callback to be invoked, which can be accomplished with this function.
-.Sh "\s-1ASSOCIATING\s0 \s-1CUSTOM\s0 \s-1DATA\s0 \s-1WITH\s0 A \s-1WATCHER\s0"
+.SS "\s-1ASSOCIATING\s0 \s-1CUSTOM\s0 \s-1DATA\s0 \s-1WITH\s0 A \s-1WATCHER\s0"
.IX Subsection "ASSOCIATING CUSTOM DATA WITH A WATCHER"
Each watcher has, by default, a member \f(CW\*(C`void *data\*(C'\fR that you can change
and read at any time: libev will completely ignore it. This can be used
@@ -1321,18 +1400,18 @@ programmers):
\& static void
\& t1_cb (EV_P_ ev_timer *w, int revents)
\& {
-\& struct my_biggy big = (struct my_biggy *
+\& struct my_biggy big = (struct my_biggy *)
\& (((char *)w) \- offsetof (struct my_biggy, t1));
\& }
\&
\& static void
\& t2_cb (EV_P_ ev_timer *w, int revents)
\& {
-\& struct my_biggy big = (struct my_biggy *
+\& struct my_biggy big = (struct my_biggy *)
\& (((char *)w) \- offsetof (struct my_biggy, t2));
\& }
.Ve
-.Sh "\s-1WATCHER\s0 \s-1PRIORITY\s0 \s-1MODELS\s0"
+.SS "\s-1WATCHER\s0 \s-1PRIORITY\s0 \s-1MODELS\s0"
.IX Subsection "WATCHER PRIORITY MODELS"
Many event loops support \fIwatcher priorities\fR, which are usually small
integers that influence the ordering of event callback invocation
@@ -1415,7 +1494,7 @@ other events are pending:
\& }
\&
\& static void
-\& idle\-cb (EV_P_ ev_idle *w, int revents)
+\& idle_cb (EV_P_ ev_idle *w, int revents)
\& {
\& // actual processing
\& read (STDIN_FILENO, ...);
@@ -1450,8 +1529,8 @@ means you can expect it to have some sensible content while the watcher
is active, but you can also modify it. Modifying it may not do something
sensible or take immediate effect (or do anything at all), but libev will
not crash or malfunction in any way.
-.ie n .Sh """ev_io"" \- is this file descriptor readable or writable?"
-.el .Sh "\f(CWev_io\fP \- is this file descriptor readable or writable?"
+.ie n .SS """ev_io"" \- is this file descriptor readable or writable?"
+.el .SS "\f(CWev_io\fP \- is this file descriptor readable or writable?"
.IX Subsection "ev_io - is this file descriptor readable or writable?"
I/O watchers check whether a file descriptor is readable or writable
in each iteration of the event loop, or, more precisely, when reading
@@ -1589,8 +1668,8 @@ attempt to read a whole line in the callback.
\& ev_io_start (loop, &stdin_readable);
\& ev_loop (loop, 0);
.Ve
-.ie n .Sh """ev_timer"" \- relative and optionally repeating timeouts"
-.el .Sh "\f(CWev_timer\fP \- relative and optionally repeating timeouts"
+.ie n .SS """ev_timer"" \- relative and optionally repeating timeouts"
+.el .SS "\f(CWev_timer\fP \- relative and optionally repeating timeouts"
.IX Subsection "ev_timer - 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.
@@ -1605,8 +1684,8 @@ The callback is guaranteed to be invoked only \fIafter\fR its timeout has
passed (not \fIat\fR, 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 with later time-out values (but this is no longer true when a
-callback calls \f(CW\*(C`ev_loop\*(C'\fR recursively).
+before ones of the same priority with later time-out values (but this is
+no longer true when a callback calls \f(CW\*(C`ev_loop\*(C'\fR recursively).
.PP
\fIBe smart about timeouts\fR
.IX Subsection "Be smart about timeouts"
@@ -1663,7 +1742,7 @@ member and \f(CW\*(C`ev_timer_again\*(C'\fR.
At start:
.Sp
.Vb 3
-\& ev_timer_init (timer, callback);
+\& ev_init (timer, callback);
\& timer\->repeat = 60.;
\& ev_timer_again (loop, timer);
.Ve
@@ -1742,7 +1821,7 @@ to the current time (meaning we just have some activity :), then call the
callback, which will \*(L"do the right thing\*(R" and start the timer:
.Sp
.Vb 3
-\& ev_timer_init (timer, callback);
+\& ev_init (timer, callback);
\& last_activity = ev_now (loop);
\& callback (loop, timer, EV_TIMEOUT);
.Ve
@@ -1816,6 +1895,37 @@ If the event loop is suspended for a long time, you can also force an
update of the time returned by \f(CW\*(C`ev_now ()\*(C'\fR by calling \f(CW\*(C`ev_now_update
()\*(C'\fR.
.PP
+\fIThe special problems of suspended animation\fR
+.IX Subsection "The special problems of suspended animation"
+.PP
+When you leave the server world it is quite customary to hit machines that
+can suspend/hibernate \- what happens to the clocks during such a suspend?
+.PP
+Some quick tests made with a Linux 2.6.28 indicate that a suspend freezes
+all processes, while the clocks (\f(CW\*(C`times\*(C'\fR, \f(CW\*(C`CLOCK_MONOTONIC\*(C'\fR) continue
+to run until the system is suspended, but they will not advance while the
+system is suspended. That means, on resume, it will be as if the program
+was frozen for a few seconds, but the suspend time will not be counted
+towards \f(CW\*(C`ev_timer\*(C'\fR when a monotonic clock source is used. The real time
+clock advanced as expected, but if it is used as sole clocksource, then a
+long suspend would be detected as a time jump by libev, and timers would
+be adjusted accordingly.
+.PP
+I would not be surprised to see different behaviour in different between
+operating systems, \s-1OS\s0 versions or even different hardware.
+.PP
+The other form of suspend (job control, or sending a \s-1SIGSTOP\s0) will see a
+time jump in the monotonic clocks and the realtime clock. If the program
+is suspended for a very long time, and monotonic clock sources are in use,
+then you can expect \f(CW\*(C`ev_timer\*(C'\fRs to expire as the full suspension time
+will be counted towards the timers. When no monotonic clock source is in
+use, then libev will again assume a timejump and adjust accordingly.
+.PP
+It might be beneficial for this latter case to call \f(CW\*(C`ev_suspend\*(C'\fR
+and \f(CW\*(C`ev_resume\*(C'\fR in code that handles \f(CW\*(C`SIGTSTP\*(C'\fR, to at least get
+deterministic behaviour in this case (you can do nothing against
+\&\f(CW\*(C`SIGSTOP\*(C'\fR).
+.PP
\fIWatcher-Specific Functions and Data Members\fR
.IX Subsection "Watcher-Specific Functions and Data Members"
.IP "ev_timer_init (ev_timer *, callback, ev_tstamp after, ev_tstamp repeat)" 4
@@ -1849,6 +1959,17 @@ If the timer is repeating, either start it if necessary (with the
.Sp
This sounds a bit complicated, see \*(L"Be smart about timeouts\*(R", above, for a
usage example.
+.IP "ev_timer_remaining (loop, ev_timer *)" 4
+.IX Item "ev_timer_remaining (loop, ev_timer *)"
+Returns the remaining time until a timer fires. If the timer is active,
+then this time is relative to the current event loop time, otherwise it's
+the timeout value currently configured.
+.Sp
+That is, after an \f(CW\*(C`ev_timer_set (w, 5, 7)\*(C'\fR, \f(CW\*(C`ev_timer_remaining\*(C'\fR returns
+\&\f(CW5\fR. When the timer is started and one second passes, \f(CW\*(C`ev_timer_remain\*(C'\fR
+will return \f(CW4\fR. When the timer expires and is restarted, it will return
+roughly \f(CW7\fR (likely slightly less as callback invocation takes some time,
+too), and so on.
.IP "ev_tstamp repeat [read\-write]" 4
.IX Item "ev_tstamp repeat [read-write]"
The current \f(CW\*(C`repeat\*(C'\fR value. Will be used each time the watcher times out
@@ -1891,8 +2012,8 @@ inactivity.
\& // reset the timeout to start ticking again at 10 seconds
\& ev_timer_again (&mytimer);
.Ve
-.ie n .Sh """ev_periodic"" \- to cron or not to cron?"
-.el .Sh "\f(CWev_periodic\fP \- to cron or not to cron?"
+.ie n .SS """ev_periodic"" \- to cron or not to cron?"
+.el .SS "\f(CWev_periodic\fP \- to cron or not to cron?"
.IX Subsection "ev_periodic - to cron or not to cron?"
Periodic watchers are also timers of a kind, but they are very versatile
(and unfortunately a bit complex).
@@ -2090,8 +2211,8 @@ Example: Call a callback every hour, starting now:
\& fmod (ev_now (loop), 3600.), 3600., 0);
\& ev_periodic_start (loop, &hourly_tick);
.Ve
-.ie n .Sh """ev_signal"" \- signal me when a signal gets signalled!"
-.el .Sh "\f(CWev_signal\fP \- signal me when a signal gets signalled!"
+.ie n .SS """ev_signal"" \- signal me when a signal gets signalled!"
+.el .SS "\f(CWev_signal\fP \- signal me when a signal gets signalled!"
.IX Subsection "ev_signal - signal me when a signal gets signalled!"
Signal watchers will trigger an event when the process receives a specific
signal one or more times. Even though signals are very asynchronous, libev
@@ -2145,8 +2266,8 @@ Example: Try to exit cleanly on \s-1SIGINT\s0.
\& ev_signal_init (&signal_watcher, sigint_cb, SIGINT);
\& ev_signal_start (loop, &signal_watcher);
.Ve
-.ie n .Sh """ev_child"" \- watch out for process status changes"
-.el .Sh "\f(CWev_child\fP \- watch out for process status changes"
+.ie n .SS """ev_child"" \- watch out for process status changes"
+.el .SS "\f(CWev_child\fP \- watch out for process status changes"
.IX Subsection "ev_child - watch out for process status changes"
Child watchers trigger when your process receives a \s-1SIGCHLD\s0 in response to
some child status changes (most typically when a child of yours dies or
@@ -2154,12 +2275,16 @@ exits). It is permissible to install a child watcher \fIafter\fR the child
has been forked (which implies it might have already exited), as long
as the event loop isn't entered (or is continued from a watcher), i.e.,
forking and then immediately registering a watcher for the child is fine,
-but forking and registering a watcher a few event loop iterations later is
-not.
+but forking and registering a watcher a few event loop iterations later or
+in the next callback invocation is not.
.PP
Only the default event loop is capable of handling signals, and therefore
you can only register child watchers in the default event loop.
.PP
+Due to some design glitches inside libev, child watchers will always be
+handled at maximum priority (their priority is set to \f(CW\*(C`EV_MAXPRI\*(C'\fR by
+libev)
+.PP
\fIProcess Interaction\fR
.IX Subsection "Process Interaction"
.PP
@@ -2247,8 +2372,8 @@ its completion.
\& ev_child_start (EV_DEFAULT_ &cw);
\& }
.Ve
-.ie n .Sh """ev_stat"" \- did the file attributes just change?"
-.el .Sh "\f(CWev_stat\fP \- did the file attributes just change?"
+.ie n .SS """ev_stat"" \- did the file attributes just change?"
+.el .SS "\f(CWev_stat\fP \- did the file attributes just change?"
.IX Subsection "ev_stat - did the file attributes just change?"
This watches a file system path for attribute changes. That is, it calls
\&\f(CW\*(C`stat\*(C'\fR on that path in regular intervals (or when the \s-1OS\s0 says it changed)
@@ -2472,8 +2597,8 @@ one might do the work both on \f(CW\*(C`ev_stat\*(C'\fR callback invocation \fIa
\& ev_stat_start (loop, &passwd);
\& ev_timer_init (&timer, timer_cb, 0., 1.02);
.Ve
-.ie n .Sh """ev_idle"" \- when you've got nothing better to do..."
-.el .Sh "\f(CWev_idle\fP \- when you've got nothing better to do..."
+.ie n .SS """ev_idle"" \- when you've got nothing better to do..."
+.el .SS "\f(CWev_idle\fP \- when you've got nothing better to do..."
.IX Subsection "ev_idle - when you've got nothing better to do..."
Idle watchers trigger events when no other events of the same or higher
priority are pending (prepare, check and other idle watchers do not count
@@ -2519,10 +2644,10 @@ callback, free it. Also, use no error checking, as usual.
\&
\& ev_idle *idle_watcher = malloc (sizeof (ev_idle));
\& ev_idle_init (idle_watcher, idle_cb);
-\& ev_idle_start (loop, idle_cb);
+\& ev_idle_start (loop, idle_watcher);
.Ve
-.ie n .Sh """ev_prepare""\fP and \f(CW""ev_check"" \- customise your event loop!"
-.el .Sh "\f(CWev_prepare\fP and \f(CWev_check\fP \- customise your event loop!"
+.ie n .SS """ev_prepare"" and ""ev_check"" \- customise your event loop!"
+.el .SS "\f(CWev_prepare\fP and \f(CWev_check\fP \- customise your event loop!"
.IX Subsection "ev_prepare and ev_check - customise your event loop!"
Prepare and check watchers are usually (but not always) used in pairs:
prepare watchers get invoked before the process blocks and check watchers
@@ -2622,7 +2747,7 @@ the callbacks for the IO/timeout watchers might not have been called yet.
\& adns_beforepoll (ads, fds, &nfd, &timeout, timeval_from (ev_time ()));
\&
\& /* the callback is illegal, but won\*(Aqt be called as we stop during check */
-\& ev_timer_init (&tw, 0, timeout * 1e\-3);
+\& ev_timer_init (&tw, 0, timeout * 1e\-3, 0.);
\& ev_timer_start (loop, &tw);
\&
\& // create one ev_io per pollfd
@@ -2723,8 +2848,8 @@ libglib event loop.
\& return got_events;
\& }
.Ve
-.ie n .Sh """ev_embed"" \- when one backend isn't enough..."
-.el .Sh "\f(CWev_embed\fP \- when one backend isn't enough..."
+.ie n .SS """ev_embed"" \- when one backend isn't enough..."
+.el .SS "\f(CWev_embed\fP \- when one backend isn't enough..."
.IX Subsection "ev_embed - when one backend isn't enough..."
This is a rather advanced watcher type that lets you embed one event loop
into another (currently only \f(CW\*(C`ev_io\*(C'\fR events are supported in the embedded
@@ -2856,8 +2981,8 @@ kqueue implementation). Store the kqueue/socket\-only event loop in
\&
\& // now use loop_socket for all sockets, and loop for everything else
.Ve
-.ie n .Sh """ev_fork"" \- the audacity to resume the event loop after a fork"
-.el .Sh "\f(CWev_fork\fP \- the audacity to resume the event loop after a fork"
+.ie n .SS """ev_fork"" \- the audacity to resume the event loop after a fork"
+.el .SS "\f(CWev_fork\fP \- the audacity to resume the event loop after a fork"
.IX Subsection "ev_fork - the audacity to resume the event loop after a fork"
Fork watchers are called when a \f(CW\*(C`fork ()\*(C'\fR was detected (usually because
whoever is a good citizen cared to tell libev about it by calling
@@ -2908,8 +3033,8 @@ also that in that case, you have to re-register any signal watchers.
Initialises and configures the fork watcher \- it has no parameters of any
kind. There is a \f(CW\*(C`ev_fork_set\*(C'\fR macro, but using it is utterly pointless,
believe me.
-.ie n .Sh """ev_async"" \- how to wake up another event loop"
-.el .Sh "\f(CWev_async\fP \- how to wake up another event loop"
+.ie n .SS """ev_async"" \- how to wake up another event loop"
+.el .SS "\f(CWev_async\fP \- how to wake up another event loop"
.IX Subsection "ev_async - how to wake up another event loop"
In general, you cannot use an \f(CW\*(C`ev_loop\*(C'\fR from multiple threads or other
asynchronous sources such as signal handlers (as opposed to multiple event
@@ -3157,16 +3282,16 @@ types of functors please contact the author (preferably after implementing
it).
.PP
Here is a list of things available in the \f(CW\*(C`ev\*(C'\fR namespace:
-.ie n .IP """ev::READ""\fR, \f(CW""ev::WRITE"" etc." 4
+.ie n .IP """ev::READ"", ""ev::WRITE"" etc." 4
.el .IP "\f(CWev::READ\fR, \f(CWev::WRITE\fR etc." 4
.IX Item "ev::READ, ev::WRITE etc."
These are just enum values with the same values as the \f(CW\*(C`EV_READ\*(C'\fR etc.
macros from \fIev.h\fR.
-.ie n .IP """ev::tstamp""\fR, \f(CW""ev::now""" 4
+.ie n .IP """ev::tstamp"", ""ev::now""" 4
.el .IP "\f(CWev::tstamp\fR, \f(CWev::now\fR" 4
.IX Item "ev::tstamp, ev::now"
Aliases to the same types/functions as with the \f(CW\*(C`ev_\*(C'\fR prefix.
-.ie n .IP """ev::io""\fR, \f(CW""ev::timer""\fR, \f(CW""ev::periodic""\fR, \f(CW""ev::idle""\fR, \f(CW""ev::sig"" etc." 4
+.ie n .IP """ev::io"", ""ev::timer"", ""ev::periodic"", ""ev::idle"", ""ev::sig"" etc." 4
.el .IP "\f(CWev::io\fR, \f(CWev::timer\fR, \f(CWev::periodic\fR, \f(CWev::idle\fR, \f(CWev::sig\fR etc." 4
.IX Item "ev::io, ev::timer, ev::periodic, ev::idle, ev::sig etc."
For each \f(CW\*(C`ev_TYPE\*(C'\fR watcher in \fIev.h\fR there is a corresponding class of
@@ -3286,7 +3411,7 @@ constructor already stores the event loop.
.IP "w\->stop ()" 4
.IX Item "w->stop ()"
Stops the watcher if it is active. Again, no \f(CW\*(C`loop\*(C'\fR argument.
-.ie n .IP "w\->again () (""ev::timer""\fR, \f(CW""ev::periodic"" only)" 4
+.ie n .IP "w\->again () (""ev::timer"", ""ev::periodic"" only)" 4
.el .IP "w\->again () (\f(CWev::timer\fR, \f(CWev::periodic\fR only)" 4
.IX Item "w->again () (ev::timer, ev::periodic only)"
For \f(CW\*(C`ev::timer\*(C'\fR and \f(CW\*(C`ev::periodic\*(C'\fR, this invokes the corresponding
@@ -3371,7 +3496,7 @@ functions and callbacks have an initial \f(CW\*(C`struct ev_loop *\*(C'\fR argum
.PP
To make it easier to write programs that cope with either variant, the
following macros are defined:
-.ie n .IP """EV_A""\fR, \f(CW""EV_A_""" 4
+.ie n .IP """EV_A"", ""EV_A_""" 4
.el .IP "\f(CWEV_A\fR, \f(CWEV_A_\fR" 4
.IX Item "EV_A, EV_A_"
This provides the loop \fIargument\fR for functions, if one is required (\*(L"ev
@@ -3386,7 +3511,7 @@ loop argument\*(R"). The \f(CW\*(C`EV_A\*(C'\fR form is used when this is the so
.Sp
It assumes the variable \f(CW\*(C`loop\*(C'\fR of type \f(CW\*(C`struct ev_loop *\*(C'\fR is in scope,
which is often provided by the following macro.
-.ie n .IP """EV_P""\fR, \f(CW""EV_P_""" 4
+.ie n .IP """EV_P"", ""EV_P_""" 4
.el .IP "\f(CWEV_P\fR, \f(CWEV_P_\fR" 4
.IX Item "EV_P, EV_P_"
This provides the loop \fIparameter\fR for functions, if one is required (\*(L"ev
@@ -3403,12 +3528,12 @@ loop parameter\*(R"). The \f(CW\*(C`EV_P\*(C'\fR form is used when this is the s
.Sp
It declares a parameter \f(CW\*(C`loop\*(C'\fR of type \f(CW\*(C`struct ev_loop *\*(C'\fR, quite
suitable for use with \f(CW\*(C`EV_A\*(C'\fR.
-.ie n .IP """EV_DEFAULT""\fR, \f(CW""EV_DEFAULT_""" 4
+.ie n .IP """EV_DEFAULT"", ""EV_DEFAULT_""" 4
.el .IP "\f(CWEV_DEFAULT\fR, \f(CWEV_DEFAULT_\fR" 4
.IX Item "EV_DEFAULT, EV_DEFAULT_"
Similar to the other two macros, this gives you the value of the default
loop, if multiple loops are supported (\*(L"ev loop default\*(R").
-.ie n .IP """EV_DEFAULT_UC""\fR, \f(CW""EV_DEFAULT_UC_""" 4
+.ie n .IP """EV_DEFAULT_UC"", ""EV_DEFAULT_UC_""" 4
.el .IP "\f(CWEV_DEFAULT_UC\fR, \f(CWEV_DEFAULT_UC_\fR" 4
.IX Item "EV_DEFAULT_UC, EV_DEFAULT_UC_"
Usage identical to \f(CW\*(C`EV_DEFAULT\*(C'\fR and \f(CW\*(C`EV_DEFAULT_\*(C'\fR, but requires that the
@@ -3446,7 +3571,7 @@ The goal is to enable you to just copy the necessary files into your
source directory without having to change even a single line in them, so
you can easily upgrade by simply copying (or having a checked-out copy of
libev somewhere in your source tree).
-.Sh "\s-1FILESETS\s0"
+.SS "\s-1FILESETS\s0"
.IX Subsection "FILESETS"
Depending on what features you need you need to include one or more sets of files
in your application.
@@ -3535,7 +3660,7 @@ For this of course you need the m4 file:
.Vb 1
\& libev.m4
.Ve
-.Sh "\s-1PREPROCESSOR\s0 \s-1SYMBOLS/MACROS\s0"
+.SS "\s-1PREPROCESSOR\s0 \s-1SYMBOLS/MACROS\s0"
.IX Subsection "PREPROCESSOR SYMBOLS/MACROS"
Libev can be configured via a variety of preprocessor symbols you have to
define before including any of its files. The default in the absence of
@@ -3744,9 +3869,19 @@ defined to be \f(CW0\fR, then they are not.
.IP "\s-1EV_MINIMAL\s0" 4
.IX Item "EV_MINIMAL"
If you need to shave off some kilobytes of code at the expense of some
-speed, define this symbol to \f(CW1\fR. 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 \s-1API\s0), define this symbol to \f(CW1\fR. 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.
+.Sp
+You can save even more by disabling watcher types you do not need
+and setting \f(CW\*(C`EV_MAXPRI\*(C'\fR == \f(CW\*(C`EV_MINPRI\*(C'\fR. Also, disabling \f(CW\*(C`assert\*(C'\fR
+(\f(CW\*(C`\-DNDEBUG\*(C'\fR) will usually reduce code size a lot.
+.Sp
+Defining \f(CW\*(C`EV_MINIMAL\*(C'\fR to \f(CW2\fR will additionally reduce the core \s-1API\s0 to
+provide a bare-bones event library. See \f(CW\*(C`ev.h\*(C'\fR for details on what parts
+of the \s-1API\s0 are still available, and do not complain if this subset changes
+over time.
.IP "\s-1EV_PID_HASHSIZE\s0" 4
.IX Item "EV_PID_HASHSIZE"
\&\f(CW\*(C`ev_child\*(C'\fR watchers use a small hash table to distribute workload by
@@ -3820,7 +3955,7 @@ definition and a statement, respectively. See the \fIev.h\fR header file for
their default definitions. One possible use for overriding these is to
avoid the \f(CW\*(C`struct ev_loop *\*(C'\fR as first argument in all cases, or to use
method calls instead of plain function calls in \*(C+.
-.Sh "\s-1EXPORTED\s0 \s-1API\s0 \s-1SYMBOLS\s0"
+.SS "\s-1EXPORTED\s0 \s-1API\s0 \s-1SYMBOLS\s0"
.IX Subsection "EXPORTED API SYMBOLS"
If you need to re-export the \s-1API\s0 (e.g. via a \s-1DLL\s0) and you need a list of
exported symbols, you can use the provided \fISymbol.*\fR files which list
@@ -3850,7 +3985,7 @@ This would create a file \fIwrap.h\fR which essentially looks like this:
\& #define ev_check_stop myprefix_ev_check_stop
\& ...
.Ve
-.Sh "\s-1EXAMPLES\s0"
+.SS "\s-1EXAMPLES\s0"
.IX Subsection "EXAMPLES"
For a real-world example of a program the includes libev
verbatim, you can have a look at the \s-1EV\s0 perl module
@@ -3885,7 +4020,7 @@ And a \fIev_cpp.C\fR implementation file that contains libev proper and is compi
.Ve
.SH "INTERACTION WITH OTHER PROGRAMS OR LIBRARIES"
.IX Header "INTERACTION WITH OTHER PROGRAMS OR LIBRARIES"
-.Sh "\s-1THREADS\s0 \s-1AND\s0 \s-1COROUTINES\s0"
+.SS "\s-1THREADS\s0 \s-1AND\s0 \s-1COROUTINES\s0"
.IX Subsection "THREADS AND COROUTINES"
\fI\s-1THREADS\s0\fR
.IX Subsection "THREADS"
@@ -3941,20 +4076,173 @@ work in the default loop by registering the signal watcher with the
default loop and triggering an \f(CW\*(C`ev_async\*(C'\fR watcher from the default loop
watcher callback into the event loop interested in the signal.
.PP
+\s-1THREAD\s0 \s-1LOCKING\s0 \s-1EXAMPLE\s0
+.IX Subsection "THREAD LOCKING EXAMPLE"
+.PP
+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.
+.PP
+For a real-world example, see the \f(CW\*(C`EV::Loop::Async\*(C'\fR perl module,
+which uses exactly this technique (which is suited for many high-level
+languages).
+.PP
+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.
+.PP
+First, you need to associate some data with the event loop:
+.PP
+.Vb 6
+\& 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);
+\& }
+.Ve
+.PP
+The callback for the \f(CW\*(C`ev_async\*(C'\fR 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:
+.PP
+.Vb 5
+\& static void
+\& async_cb (EV_P_ ev_async *w, int revents)
+\& {
+\& // just used for the side effects
+\& }
+.Ve
+.PP
+The \f(CW\*(C`l_release\*(C'\fR and \f(CW\*(C`l_acquire\*(C'\fR callbacks simply unlock/lock the mutex
+protecting the loop data, respectively.
+.PP
+.Vb 6
+\& 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);
+\& }
+.Ve
+.PP
+The event loop thread first acquires the mutex, and then jumps straight
+into \f(CW\*(C`ev_loop\*(C'\fR:
+.PP
+.Vb 4
+\& 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;
+\& }
+.Ve
+.PP
+Instead of invoking all pending watchers, the \f(CW\*(C`l_invoke\*(C'\fR callback will
+signal the main thread via some unspecified mechanism (signals? pipe
+writes? \f(CW\*(C`Async::Interrupt\*(C'\fR?) and then waits until all pending watchers
+have been called (in a while loop because a) spurious wakeups are possible
+and b) skipping inter-thread-communication when there are no pending
+watchers is very beneficial):
+.PP
+.Vb 4
+\& static void
+\& l_invoke (EV_P)
+\& {
+\& userdata *u = ev_userdata (EV_A);
+\&
+\& while (ev_pending_count (EV_A))
+\& {
+\& wake_up_other_thread_in_some_magic_or_not_so_magic_way ();
+\& pthread_cond_wait (&u\->invoke_cv, &u\->lock);
+\& }
+\& }
+.Ve
+.PP
+Now, whenever the main thread gets told to invoke pending watchers, it
+will grab the lock, call \f(CW\*(C`ev_invoke_pending\*(C'\fR and then signal the loop
+thread to continue:
+.PP
+.Vb 4
+\& 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);
+\& }
+.Ve
+.PP
+Whenever you want to start/stop a watcher or do other modifications to an
+event loop, you will now have to lock:
+.PP
+.Vb 2
+\& 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);
+.Ve
+.PP
+Note that sending the \f(CW\*(C`ev_async\*(C'\fR 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.
+.PP
\fI\s-1COROUTINES\s0\fR
.IX Subsection "COROUTINES"
.PP
Libev is very accommodating to coroutines (\*(L"cooperative threads\*(R"):
libev fully supports nesting calls to its functions from different
coroutines (e.g. you can call \f(CW\*(C`ev_loop\*(C'\fR 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 \f(CW\*(C`ev_periodic\*(C'\fR 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 \f(CW\*(C`ev_periodic\*(C'\fR reschedule callbacks.
.PP
Care has been taken to ensure that libev does not keep local state inside
\&\f(CW\*(C`ev_loop\*(C'\fR, and other calls do not usually allow for coroutine switches as
they do not call any callbacks.
-.Sh "\s-1COMPILER\s0 \s-1WARNINGS\s0"
+.SS "\s-1COMPILER\s0 \s-1WARNINGS\s0"
.IX Subsection "COMPILER WARNINGS"
Depending on your compiler and compiler settings, you might get no or a
lot of warnings when compiling libev code. Some people are apparently
@@ -3981,7 +4269,7 @@ While libev is written to generate as few warnings as possible,
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.
-.Sh "\s-1VALGRIND\s0"
+.SS "\s-1VALGRIND\s0"
.IX Subsection "VALGRIND"
Valgrind has a special section here because it is a popular tool that is
highly useful. Unfortunately, valgrind reports are very hard to interpret.
@@ -4016,7 +4304,7 @@ If you need, for some reason, empty reports from valgrind for your project
I suggest using suppression lists.
.SH "PORTABILITY NOTES"
.IX Header "PORTABILITY NOTES"
-.Sh "\s-1WIN32\s0 \s-1PLATFORM\s0 \s-1LIMITATIONS\s0 \s-1AND\s0 \s-1WORKAROUNDS\s0"
+.SS "\s-1WIN32\s0 \s-1PLATFORM\s0 \s-1LIMITATIONS\s0 \s-1AND\s0 \s-1WORKAROUNDS\s0"
.IX Subsection "WIN32 PLATFORM LIMITATIONS AND WORKAROUNDS"
Win32 doesn't support any of the standards (e.g. \s-1POSIX\s0) that libev
requires, and its I/O model is fundamentally incompatible with the \s-1POSIX\s0
@@ -4113,11 +4401,11 @@ runtime libraries. This might get you to about \f(CW512\fR or \f(CW2048\fR socke
(depending on windows version and/or the phase of the moon). To get more,
you need to wrap all I/O functions and provide your own fd management, but
the cost of calling select (O(nA\*^X)) will likely make this unworkable.
-.Sh "\s-1PORTABILITY\s0 \s-1REQUIREMENTS\s0"
+.SS "\s-1PORTABILITY\s0 \s-1REQUIREMENTS\s0"
.IX Subsection "PORTABILITY REQUIREMENTS"
In addition to a working ISO-C implementation and of course the
backend-specific APIs, libev relies on a few additional extensions:
-.ie n .IP """void (*)(ev_watcher_type *, int revents)""\fR must have compatible calling conventions regardless of \f(CW""ev_watcher_type *""." 4
+.ie n .IP """void (*)(ev_watcher_type *, int revents)"" must have compatible calling conventions regardless of ""ev_watcher_type *""." 4
.el .IP "\f(CWvoid (*)(ev_watcher_type *, int revents)\fR must have compatible calling conventions regardless of \f(CWev_watcher_type *\fR." 4
.IX Item "void (*)(ev_watcher_type *, int revents) must have compatible calling conventions regardless of ev_watcher_type *."
Libev assumes not only that all watcher pointers have the same internal
@@ -4159,7 +4447,9 @@ watchers.
The type \f(CW\*(C`double\*(C'\fR is used to represent timestamps. It is required to
have at least 51 bits of mantissa (and 9 bits of exponent), which is good
enough for at least into the year 4000. This requirement is fulfilled by
-implementations implementing \s-1IEEE\s0 754 (basically all existing ones).
+implementations implementing \s-1IEEE\s0 754, which is basically all existing
+ones. With \s-1IEEE\s0 754 doubles, you get microsecond accuracy until at least
+2200.
.PP
If you know of other additional requirements drop me a note.
.SH "ALGORITHMIC COMPLEXITIES"