|
|
|
@ -2386,8 +2386,8 @@ queue:
|
|
|
|
|
=item queueing from a signal handler context
|
|
|
|
|
|
|
|
|
|
To implement race-free queueing, you simply add to the queue in the signal
|
|
|
|
|
handler but you block the signal handler in the watcher callback. Here is an example that does that for
|
|
|
|
|
some fictitious SIGUSR1 handler:
|
|
|
|
|
handler but you block the signal handler in the watcher callback. Here is
|
|
|
|
|
an example that does that for some fictitious SIGUSR1 handler:
|
|
|
|
|
|
|
|
|
|
static ev_async mysig;
|
|
|
|
|
|
|
|
|
@ -3315,11 +3315,11 @@ And a F<ev_cpp.C> implementation file that contains libev proper and is compiled
|
|
|
|
|
=head3 THREADS
|
|
|
|
|
|
|
|
|
|
All libev functions are reentrant and thread-safe unless explicitly
|
|
|
|
|
documented otherwise, but it uses no locking itself. This means that you
|
|
|
|
|
can use as many loops as you want in parallel, as long as there are no
|
|
|
|
|
concurrent calls into any libev function with the same loop parameter
|
|
|
|
|
(C<ev_default_*> calls have an implicit default loop parameter, of
|
|
|
|
|
course): libev guarantees that different event loops share no data
|
|
|
|
|
documented otherwise, but libev implements no locking itself. This means
|
|
|
|
|
that you can use as many loops as you want in parallel, as long as there
|
|
|
|
|
are no concurrent calls into any libev function with the same loop
|
|
|
|
|
parameter (C<ev_default_*> calls have an implicit default loop parameter,
|
|
|
|
|
of course): libev guarantees that different event loops share no data
|
|
|
|
|
structures that need any locking.
|
|
|
|
|
|
|
|
|
|
Or to put it differently: calls with different loop parameters can be done
|
|
|
|
@ -3371,15 +3371,16 @@ watcher callback into the event loop interested in the signal.
|
|
|
|
|
|
|
|
|
|
=head3 COROUTINES
|
|
|
|
|
|
|
|
|
|
Libev is much more accommodating to coroutines ("cooperative threads"):
|
|
|
|
|
libev fully supports nesting calls to it's functions from different
|
|
|
|
|
Libev is very accommodating to coroutines ("cooperative threads"):
|
|
|
|
|
libev fully supports nesting calls to its functions from different
|
|
|
|
|
coroutines (e.g. you can call C<ev_loop> on the same loop from two
|
|
|
|
|
different coroutines and switch freely between both coroutines running the
|
|
|
|
|
different coroutines, and switch freely between both coroutines running the
|
|
|
|
|
loop, as long as you don't confuse yourself). The only exception is that
|
|
|
|
|
you must not do this from C<ev_periodic> reschedule callbacks.
|
|
|
|
|
|
|
|
|
|
Care has been taken to ensure that libev does not keep local state inside
|
|
|
|
|
C<ev_loop>, and other calls do not usually allow coroutine switches.
|
|
|
|
|
C<ev_loop>, and other calls do not usually allow for coroutine switches as
|
|
|
|
|
they do not clal any callbacks.
|
|
|
|
|
|
|
|
|
|
=head2 COMPILER WARNINGS
|
|
|
|
|
|
|
|
|
@ -3443,77 +3444,6 @@ If you need, for some reason, empty reports from valgrind for your project
|
|
|
|
|
I suggest using suppression lists.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
=head1 COMPLEXITIES
|
|
|
|
|
|
|
|
|
|
In this section the complexities of (many of) the algorithms used inside
|
|
|
|
|
libev will be explained. For complexity discussions about backends see the
|
|
|
|
|
documentation for C<ev_default_init>.
|
|
|
|
|
|
|
|
|
|
All of the following are about amortised time: If an array needs to be
|
|
|
|
|
extended, libev needs to realloc and move the whole array, but this
|
|
|
|
|
happens asymptotically never with higher number of elements, so O(1) might
|
|
|
|
|
mean it might do a lengthy realloc operation in rare cases, but on average
|
|
|
|
|
it is much faster and asymptotically approaches constant time.
|
|
|
|
|
|
|
|
|
|
=over 4
|
|
|
|
|
|
|
|
|
|
=item Starting and stopping timer/periodic watchers: O(log skipped_other_timers)
|
|
|
|
|
|
|
|
|
|
This means that, when you have a watcher that triggers in one hour and
|
|
|
|
|
there are 100 watchers that would trigger before that then inserting will
|
|
|
|
|
have to skip roughly seven (C<ld 100>) of these watchers.
|
|
|
|
|
|
|
|
|
|
=item Changing timer/periodic watchers (by autorepeat or calling again): O(log skipped_other_timers)
|
|
|
|
|
|
|
|
|
|
That means that changing a timer costs less than removing/adding them
|
|
|
|
|
as only the relative motion in the event queue has to be paid for.
|
|
|
|
|
|
|
|
|
|
=item Starting io/check/prepare/idle/signal/child/fork/async watchers: O(1)
|
|
|
|
|
|
|
|
|
|
These just add the watcher into an array or at the head of a list.
|
|
|
|
|
|
|
|
|
|
=item Stopping check/prepare/idle/fork/async watchers: O(1)
|
|
|
|
|
|
|
|
|
|
=item Stopping an io/signal/child watcher: O(number_of_watchers_for_this_(fd/signal/pid % EV_PID_HASHSIZE))
|
|
|
|
|
|
|
|
|
|
These watchers are stored in lists then need to be walked to find the
|
|
|
|
|
correct watcher to remove. The lists are usually short (you don't usually
|
|
|
|
|
have many watchers waiting for the same fd or signal).
|
|
|
|
|
|
|
|
|
|
=item Finding the next timer in each loop iteration: O(1)
|
|
|
|
|
|
|
|
|
|
By virtue of using a binary or 4-heap, the next timer is always found at a
|
|
|
|
|
fixed position in the storage array.
|
|
|
|
|
|
|
|
|
|
=item Each change on a file descriptor per loop iteration: O(number_of_watchers_for_this_fd)
|
|
|
|
|
|
|
|
|
|
A change means an I/O watcher gets started or stopped, which requires
|
|
|
|
|
libev to recalculate its status (and possibly tell the kernel, depending
|
|
|
|
|
on backend and whether C<ev_io_set> was used).
|
|
|
|
|
|
|
|
|
|
=item Activating one watcher (putting it into the pending state): O(1)
|
|
|
|
|
|
|
|
|
|
=item Priority handling: O(number_of_priorities)
|
|
|
|
|
|
|
|
|
|
Priorities are implemented by allocating some space for each
|
|
|
|
|
priority. When doing priority-based operations, libev usually has to
|
|
|
|
|
linearly search all the priorities, but starting/stopping and activating
|
|
|
|
|
watchers becomes O(1) with respect to priority handling.
|
|
|
|
|
|
|
|
|
|
=item Sending an ev_async: O(1)
|
|
|
|
|
|
|
|
|
|
=item Processing ev_async_send: O(number_of_async_watchers)
|
|
|
|
|
|
|
|
|
|
=item Processing signals: O(max_signal_number)
|
|
|
|
|
|
|
|
|
|
Sending involves a system call I<iff> there were no other C<ev_async_send>
|
|
|
|
|
calls in the current loop iteration. Checking for async and signal events
|
|
|
|
|
involves iterating over all running async watchers or all signal numbers.
|
|
|
|
|
|
|
|
|
|
=back
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
=head1 PORTABILITY NOTES
|
|
|
|
|
|
|
|
|
|
=head2 WIN32 PLATFORM LIMITATIONS AND WORKAROUNDS
|
|
|
|
@ -3669,6 +3599,77 @@ implementations implementing IEEE 754 (basically all existing ones).
|
|
|
|
|
If you know of other additional requirements drop me a note.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
=head1 ALGORITHMIC COMPLEXITIES
|
|
|
|
|
|
|
|
|
|
In this section the complexities of (many of) the algorithms used inside
|
|
|
|
|
libev will be documented. For complexity discussions about backends see
|
|
|
|
|
the documentation for C<ev_default_init>.
|
|
|
|
|
|
|
|
|
|
All of the following are about amortised time: If an array needs to be
|
|
|
|
|
extended, libev needs to realloc and move the whole array, but this
|
|
|
|
|
happens asymptotically rarer with higher number of elements, so O(1) might
|
|
|
|
|
mean that libev does a lengthy realloc operation in rare cases, but on
|
|
|
|
|
average it is much faster and asymptotically approaches constant time.
|
|
|
|
|
|
|
|
|
|
=over 4
|
|
|
|
|
|
|
|
|
|
=item Starting and stopping timer/periodic watchers: O(log skipped_other_timers)
|
|
|
|
|
|
|
|
|
|
This means that, when you have a watcher that triggers in one hour and
|
|
|
|
|
there are 100 watchers that would trigger before that, then inserting will
|
|
|
|
|
have to skip roughly seven (C<ld 100>) of these watchers.
|
|
|
|
|
|
|
|
|
|
=item Changing timer/periodic watchers (by autorepeat or calling again): O(log skipped_other_timers)
|
|
|
|
|
|
|
|
|
|
That means that changing a timer costs less than removing/adding them,
|
|
|
|
|
as only the relative motion in the event queue has to be paid for.
|
|
|
|
|
|
|
|
|
|
=item Starting io/check/prepare/idle/signal/child/fork/async watchers: O(1)
|
|
|
|
|
|
|
|
|
|
These just add the watcher into an array or at the head of a list.
|
|
|
|
|
|
|
|
|
|
=item Stopping check/prepare/idle/fork/async watchers: O(1)
|
|
|
|
|
|
|
|
|
|
=item Stopping an io/signal/child watcher: O(number_of_watchers_for_this_(fd/signal/pid % EV_PID_HASHSIZE))
|
|
|
|
|
|
|
|
|
|
These watchers are stored in lists, so they need to be walked to find the
|
|
|
|
|
correct watcher to remove. The lists are usually short (you don't usually
|
|
|
|
|
have many watchers waiting for the same fd or signal: one is typical, two
|
|
|
|
|
is rare).
|
|
|
|
|
|
|
|
|
|
=item Finding the next timer in each loop iteration: O(1)
|
|
|
|
|
|
|
|
|
|
By virtue of using a binary or 4-heap, the next timer is always found at a
|
|
|
|
|
fixed position in the storage array.
|
|
|
|
|
|
|
|
|
|
=item Each change on a file descriptor per loop iteration: O(number_of_watchers_for_this_fd)
|
|
|
|
|
|
|
|
|
|
A change means an I/O watcher gets started or stopped, which requires
|
|
|
|
|
libev to recalculate its status (and possibly tell the kernel, depending
|
|
|
|
|
on backend and whether C<ev_io_set> was used).
|
|
|
|
|
|
|
|
|
|
=item Activating one watcher (putting it into the pending state): O(1)
|
|
|
|
|
|
|
|
|
|
=item Priority handling: O(number_of_priorities)
|
|
|
|
|
|
|
|
|
|
Priorities are implemented by allocating some space for each
|
|
|
|
|
priority. When doing priority-based operations, libev usually has to
|
|
|
|
|
linearly search all the priorities, but starting/stopping and activating
|
|
|
|
|
watchers becomes O(1) with respect to priority handling.
|
|
|
|
|
|
|
|
|
|
=item Sending an ev_async: O(1)
|
|
|
|
|
|
|
|
|
|
=item Processing ev_async_send: O(number_of_async_watchers)
|
|
|
|
|
|
|
|
|
|
=item Processing signals: O(max_signal_number)
|
|
|
|
|
|
|
|
|
|
Sending involves a system call I<iff> there were no other C<ev_async_send>
|
|
|
|
|
calls in the current loop iteration. Checking for async and signal events
|
|
|
|
|
involves iterating over all running async watchers or all signal numbers.
|
|
|
|
|
|
|
|
|
|
=back
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
=head1 AUTHOR
|
|
|
|
|
|
|
|
|
|
Marc Lehmann <libev@schmorp.de>.
|
|
|
|
|