Browse Source

*** empty log message ***

master
Marc Alexander Lehmann 11 years ago
parent
commit
edd55e34df
  1. 343
      ev.pod

343
ev.pod

@ -508,7 +508,9 @@ events to filter out spurious ones, recreating the set when required. Last
not least, it also refuses to work with some file descriptors which work
perfectly fine with C<select> (files, many character devices...).
Epoll is truly the train wreck analog among event poll mechanisms.
Epoll is truly the train wreck analog among event poll mechanisms,
a frankenpoll, cobbled together in a hurry, no thought to design or
interaction with others.
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
@ -1616,26 +1618,19 @@ fd as you want (as long as you don't confuse yourself). Setting all file
descriptors to non-blocking mode is also usually a good idea (but not
required if you know what you are doing).
If you cannot use non-blocking mode, then force the use of a
known-to-be-good backend (at the time of this writing, this includes only
C<EVBACKEND_SELECT> and C<EVBACKEND_POLL>). The same applies to file
descriptors for which non-blocking operation makes no sense (such as
files) - libev doesn't guarantee any specific behaviour in that case.
Another thing you have to watch out for is that it is quite easy to
receive "spurious" readiness notifications, that is your callback might
receive "spurious" readiness notifications, that is, your callback might
be called with C<EV_READ> but a subsequent C<read>(2) will actually block
because there is no data. Not only are some backends known to create a
lot of those (for example Solaris ports), it is very easy to get into
this situation even with a relatively standard program structure. Thus
it is best to always use non-blocking I/O: An extra C<read>(2) returning
C<EAGAIN> is far preferable to a program hanging until some data arrives.
because there is no data. It is very easy to get into this situation even
with a relatively standard program structure. Thus it is best to always
use non-blocking I/O: An extra C<read>(2) returning C<EAGAIN> is far
preferable to a program hanging until some data arrives.
If you cannot run the fd in non-blocking mode (for example you should
not play around with an Xlib connection), then you have to separately
re-test whether a file descriptor is really ready with a known-to-be good
interface such as poll (fortunately in our Xlib example, Xlib already
does this on its own, so its quite safe to use). Some people additionally
interface such as poll (fortunately in the case of Xlib, it already does
this on its own, so its quite safe to use). Some people additionally
use C<SIGALRM> and an interval timer, just to be sure you won't block
indefinitely.
@ -1673,16 +1668,48 @@ There is no workaround possible except not registering events
for potentially C<dup ()>'ed file descriptors, or to resort to
C<EVBACKEND_SELECT> or C<EVBACKEND_POLL>.
=head3 The special problem of files
Many people try to use C<select> (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 it's own, but in the case of files, there is no such thing: the disk
will not send data on it's 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<epoll> on Linux works with F</dev/random> but not with
F</dev/urandom>), 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<fork ()> at all or exhibit
useless behaviour. Libev fully supports fork, but needs to be told about
it in the child.
it in the child if you want to continue to use it in the child.
To support fork in your programs, you either have to call
C<ev_default_fork ()> or C<ev_loop_fork ()> after a fork in the child,
enable C<EVFLAG_FORKCHECK>, or resort to C<EVBACKEND_SELECT> or
C<EVBACKEND_POLL>.
To support fork in your child processes, you have to call C<ev_loop_fork
()> after a fork in the child, enable C<EVFLAG_FORKCHECK>, or resort to
C<EVBACKEND_SELECT> or C<EVBACKEND_POLL>.
=head3 The special problem of SIGPIPE
@ -3472,6 +3499,144 @@ To exit from any of these loops, just set the corresponding exit variable:
// exit both
exit_main_loop = exit_nested_loop = 1;
=item Thread locking example
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.
For a real-world example, see the C<EV::Loop::Async> perl module,
which uses exactly this technique (which is suited for many high-level
languages).
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.
First, you need to associate some data with the event loop:
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);
}
The callback for the C<ev_async> 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:
static void
async_cb (EV_P_ ev_async *w, int revents)
{
// just used for the side effects
}
The C<l_release> and C<l_acquire> callbacks simply unlock/lock the mutex
protecting the loop data, respectively.
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);
}
The event loop thread first acquires the mutex, and then jumps straight
into C<ev_run>:
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_run (EV_A_ 0);
l_release (EV_A);
return 0;
}
Instead of invoking all pending watchers, the C<l_invoke> callback will
signal the main thread via some unspecified mechanism (signals? pipe
writes? C<Async::Interrupt>?) 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):
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);
}
}
Now, whenever the main thread gets told to invoke pending watchers, it
will grab the lock, call C<ev_invoke_pending> and then signal the loop
thread to continue:
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);
}
Whenever you want to start/stop a watcher or do other modifications to an
event loop, you will now have to lock:
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);
Note that sending the C<ev_async> 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.
=back
@ -4473,143 +4638,7 @@ watcher callback into the event loop interested in the signal.
=back
=head4 THREAD LOCKING EXAMPLE
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.
For a real-world example, see the C<EV::Loop::Async> perl module,
which uses exactly this technique (which is suited for many high-level
languages).
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.
First, you need to associate some data with the event loop:
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);
}
The callback for the C<ev_async> 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:
static void
async_cb (EV_P_ ev_async *w, int revents)
{
// just used for the side effects
}
The C<l_release> and C<l_acquire> callbacks simply unlock/lock the mutex
protecting the loop data, respectively.
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);
}
The event loop thread first acquires the mutex, and then jumps straight
into C<ev_run>:
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_run (EV_A_ 0);
l_release (EV_A);
return 0;
}
Instead of invoking all pending watchers, the C<l_invoke> callback will
signal the main thread via some unspecified mechanism (signals? pipe
writes? C<Async::Interrupt>?) 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):
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);
}
}
Now, whenever the main thread gets told to invoke pending watchers, it
will grab the lock, call C<ev_invoke_pending> and then signal the loop
thread to continue:
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);
}
Whenever you want to start/stop a watcher or do other modifications to an
event loop, you will now have to lock:
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);
Note that sending the C<ev_async> 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.
See also L<Thread locking example>.
=head3 COROUTINES

Loading…
Cancel
Save