@ -16,6 +16,7 @@ TODO: accept() documentation
- do not use sscanf to parse linux version number (smaller, faster,
no sscanf dependency).
- new EV_CHILD_ENABLE and EV_SIGNAL_ENABLE configurable settings.
- add section on accept() problems to the manpage.
3.9 Thu Dec 31 07:59:59 CET 2009
- signalfd is no longer used by default and has to be requested
@ -1540,6 +1540,44 @@ 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<accept> function (for example,
found in port-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<accept> to fail with C<ENFILE> 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<EAGAIN> and C<EWOULDBLOCK>, 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<ev_io> watcher on the listening fd "for a while", which reduces CPU
If your program is single-threaded, then you could also keep a dummy file
descriptor for overload situations (e.g. by opening F</dev/null>), and
when you run into C<ENFILE> or C<EMFILE>, close it, run C<accept>,
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<exit>, as
is often done with C<malloc> failures, but this results in an easy
opportunity for a DoS attack.
=head3 Watcher-Specific Functions