|
|
|
@ -2072,6 +2072,91 @@ C<ev_async_sent> calls).
|
|
|
|
|
Unlike C<ev_signal> watchers, C<ev_async> works with any event loop, not
|
|
|
|
|
just the default loop.
|
|
|
|
|
|
|
|
|
|
=head3 Queueing
|
|
|
|
|
|
|
|
|
|
C<ev_async> does not support queueing of data in any way. The reason
|
|
|
|
|
is that the author does not know of a simple (or any) algorithm for a
|
|
|
|
|
multiple-writer-single-reader queue that works in all cases and doesn't
|
|
|
|
|
need elaborate support such as pthreads.
|
|
|
|
|
|
|
|
|
|
That means that if you want to queue data, you have to provide your own
|
|
|
|
|
queue. And here is how you would implement locking:
|
|
|
|
|
|
|
|
|
|
=over 4
|
|
|
|
|
|
|
|
|
|
=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 fictitiuous SIGUSR1 handler:
|
|
|
|
|
|
|
|
|
|
static ev_async mysig;
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
sigusr1_handler (void)
|
|
|
|
|
{
|
|
|
|
|
sometype data;
|
|
|
|
|
|
|
|
|
|
// no locking etc.
|
|
|
|
|
queue_put (data);
|
|
|
|
|
ev_async_send (DEFAULT_LOOP, &mysig);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
mysig_cb (EV_P_ ev_async *w, int revents)
|
|
|
|
|
{
|
|
|
|
|
sometype data;
|
|
|
|
|
sigset_t block, prev;
|
|
|
|
|
|
|
|
|
|
sigemptyset (&block);
|
|
|
|
|
sigaddset (&block, SIGUSR1);
|
|
|
|
|
sigprocmask (SIG_BLOCK, &block, &prev);
|
|
|
|
|
|
|
|
|
|
while (queue_get (&data))
|
|
|
|
|
process (data);
|
|
|
|
|
|
|
|
|
|
if (sigismember (&prev, SIGUSR1)
|
|
|
|
|
sigprocmask (SIG_UNBLOCK, &block, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(Note: pthreads in theory requires you to use C<pthread_setmask>
|
|
|
|
|
instead of C<sigprocmask> when you use threads, but libev doesn't do it
|
|
|
|
|
either...).
|
|
|
|
|
|
|
|
|
|
=item queueing from a thread context
|
|
|
|
|
|
|
|
|
|
The strategy for threads is different, as you cannot (easily) block
|
|
|
|
|
threads but you can easily preempt them, so to queue safely you need to
|
|
|
|
|
emply a traditional mutex lock, such as in this pthread example:
|
|
|
|
|
|
|
|
|
|
static ev_async mysig;
|
|
|
|
|
static pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
otherthread (void)
|
|
|
|
|
{
|
|
|
|
|
// only need to lock the actual queueing operation
|
|
|
|
|
pthread_mutex_lock (&mymutex);
|
|
|
|
|
queue_put (data);
|
|
|
|
|
pthread_mutex_unlock (&mymutex);
|
|
|
|
|
|
|
|
|
|
ev_async_send (DEFAULT_LOOP, &mysig);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
mysig_cb (EV_P_ ev_async *w, int revents)
|
|
|
|
|
{
|
|
|
|
|
pthread_mutex_lock (&mymutex);
|
|
|
|
|
|
|
|
|
|
while (queue_get (&data))
|
|
|
|
|
process (data);
|
|
|
|
|
|
|
|
|
|
pthread_mutex_unlock (&mymutex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
=back
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
=head3 Watcher-Specific Functions and Data Members
|
|
|
|
|
|
|
|
|
|
=over 4
|
|
|
|
|