summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@googlemail.com>2012-05-19 13:33:05 +0200
committerDavid Herrmann <dh.herrmann@googlemail.com>2012-05-19 13:33:05 +0200
commit56d79325239987080840d5f0449712f31fa8ad26 (patch)
tree1399124ac31f9c35b4a44dce1b1806fbb839299e
parent2eb172acb4e6d4994c171092d51503e8c2bbc635 (diff)
eloop: add more comments
Add comments describing each source and the nesting features. Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
-rw-r--r--src/eloop.c119
1 files changed, 113 insertions, 6 deletions
diff --git a/src/eloop.c b/src/eloop.c
index 0481a45..dda5e22 100644
--- a/src/eloop.c
+++ b/src/eloop.c
@@ -51,6 +51,21 @@
#define LOG_SUBSYSTEM "eloop"
+/*
+ * Event Loop
+ * An event loop is an object where you can register event sources. If you then
+ * sleep on the event loop, you will be woken up if a single event source is
+ * firing up. An event loop itself is an event source so you can nest them.
+ * \efd: The epoll file descriptor.
+ * \ref: refcnt of this object
+ * \fd: Event source around \efd so you can nest event loops
+ * \cnt: Counter source used for idle events
+ * \sig_list: Shared signal sources
+ * \idlers: List of idle sources
+ * \cur_fds: Current dispatch array of fds
+ * \cur_fds_cnt: size of \cur_fds
+ * \exit: true if we should exit the main loop
+ */
struct ev_eloop {
int efd;
unsigned long ref;
@@ -65,6 +80,17 @@ struct ev_eloop {
bool exit;
};
+/*
+ * FD sources
+ * File descriptors are used internally for all kinds of sources.
+ * \ref: refcnt for object
+ * \fd: the actual file desciptor
+ * \mask: the event mask for this fd (EV_READABLE, EV_WRITABLE, ...)
+ * \cb: the user callback
+ * \data: the user data
+ * \enabled: true if the object is currently enabled
+ * \loop: NULL or pointer to eloop if bound
+ */
struct ev_fd {
unsigned long ref;
int fd;
@@ -76,6 +102,15 @@ struct ev_fd {
struct ev_eloop *loop;
};
+/*
+ * Timer sources
+ * Based on timerfd this allows firing events based on relative timeouts.
+ * \ref: refcnt of this object
+ * \cb: user callback
+ * \data: user data
+ * \fd: the timerfd file desciptor
+ * \efd: fd-source for \fd
+ */
struct ev_timer {
unsigned long ref;
ev_timer_cb cb;
@@ -85,6 +120,16 @@ struct ev_timer {
struct ev_fd *efd;
};
+/*
+ * Counter Sources
+ * Counter sources fire if they are non-zero. They are based on the eventfd
+ * syscall in linux.
+ * \ref: refcnt of counter object
+ * \cb: user callback
+ * \data: user data
+ * \fd: eventfd file desciptor
+ * \efd: fd-source for \fd
+ */
struct ev_counter {
unsigned long ref;
ev_counter_cb cb;
@@ -94,6 +139,15 @@ struct ev_counter {
struct ev_fd *efd;
};
+/*
+ * Shared Signal Sources
+ * A shared signal allows multiple listeners for the same signal. All listeners
+ * are called if the signal is catched.
+ * \list: list integration into ev_eloop object
+ * \fd: the signalfd file desciptor for this signal
+ * \signum: the actual signal number
+ * \hook: list of registered user callbacks for this signal
+ */
struct ev_signal_shared {
struct kmscon_dlist list;
@@ -104,6 +158,22 @@ struct ev_signal_shared {
/*
* Shared signals
+ * signalfd allows us to conveniently listen for incoming signals. However, if
+ * multiple signalfds are registered for the same signal, then only one of them
+ * will get signaled. To avoid this restriction, we provide shared signals.
+ * That means, the user can register for a signal and if no other user is
+ * registered for this signal, yet, we create a new shared signal. Otherwise,
+ * we add the user to the existing shared signals.
+ * If the signal is catched, we simply call all users that are registered for
+ * this signal.
+ * To avoid side-effects, we automatically block all signals for the current
+ * thread when a signalfd is created. We never unblock the signal. However,
+ * most modern linux user-space programs avoid signal handlers, anyway, so you
+ * can use signalfd only.
+ *
+ * As special note, we automatically handle SIGCHLD signals here and wait for
+ * all pending child exits. This, however, is only activated when at least one
+ * user has registered for SIGCHLD callbacks.
*/
static void sig_child()
@@ -141,7 +211,7 @@ static void shared_signal_cb(struct ev_fd *fd, int mask, void *data)
if (mask & EV_READABLE) {
len = read(fd->fd, &info, sizeof(info));
if (len != sizeof(info))
- log_warn("cannot read signalfd");
+ log_warn("cannot read signalfd (%d): %m", errno);
else
kmscon_hook_call(sig->hook, sig->fd->loop, &info);
@@ -175,7 +245,7 @@ static int signal_new(struct ev_signal_shared **out, struct ev_eloop *loop,
sigemptyset(&mask);
sigaddset(&mask, signum);
- fd = signalfd(-1, &mask, SFD_CLOEXEC);
+ fd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK);
if (fd < 0) {
ret = -errno;
goto err_hook;
@@ -214,14 +284,38 @@ static void signal_free(struct ev_signal_shared *sig)
close(fd);
kmscon_hook_free(sig->hook);
free(sig);
- /* We do not unblock the signal here as there may be other subsystems
- * which blocked this signal so we do not want to interfere. If you need
- * a clean sigmask then do it yourself.
+ /*
+ * We do not unblock the signal here as there may be other subsystems
+ * which blocked this signal so we do not want to interfere. If you
+ * need a clean sigmask then do it yourself.
*/
}
/*
* Eloop mainloop
+ * The main eloop object is responsible for correctly dispatching all events.
+ * You can register fd, idle or signal sources with it. All other kinds of
+ * sources are based on these. In fact, event idle and signal sources are based
+ * on fd sources.
+ * As special feature, you can retrieve an fd of an eloop object, too, and pass
+ * it to your own event loop. If this fd is readable, then call
+ * ev_eloop_dispatch() to make this loop dispatch all pending events.
+ *
+ * There is one restriction when nesting eloops, though. You cannot share
+ * signals across eloop boundaries. That is, if you have registered for shared
+ * signals in two eloops for the _same_ signal, then only one eloop will
+ * receive the signal (and this is pretty random).
+ * However, such a setup is most often broken in design and hence should never
+ * occur. Even shared signals are quite rare.
+ * Anyway, you must take this into account when nesting eloops.
+ *
+ * For the curious reader: We implement idle sources with counter sources. That
+ * is, whenever there is an idle source we increase the counter source. Hence,
+ * the next dispatch call will call the counter source and this will call all
+ * registered idle source. If the idle sources do not unregister them, then we
+ * directly increase the counter again and the next dispatch round will call
+ * all idle sources again. This, however, has the side-effect that idle sources
+ * are _not_ called before other fd events but are rather mixed in between.
*/
static void eloop_event(struct ev_fd *fd, int mask, void *data)
@@ -354,7 +448,6 @@ int ev_eloop_dispatch(struct ev_eloop *loop, int timeout)
if (!loop || loop->exit)
return -EINVAL;
- /* dispatch fd events */
count = epoll_wait(loop->efd, ep, 32, timeout);
if (count < 0) {
if (errno == EINTR) {
@@ -511,6 +604,12 @@ void ev_eloop_rm_eloop(struct ev_eloop *rm)
/*
* FD sources
+ * This allows adding file descriptors to an eloop. A file descriptor is the
+ * most basic kind of source and used for all other source types.
+ * By default a source is always enabled but you can easily disable the source
+ * by calling ev_fd_disable(). This will have the effect, that the source is
+ * still registered with the eloop but will not wake up the thread or get
+ * called until you enable it again.
*/
int ev_fd_new(struct ev_fd **out, int rfd, int mask, ev_fd_cb cb, void *data)
@@ -1199,6 +1298,10 @@ void ev_eloop_rm_counter(struct ev_counter *cnt)
/*
* Shared signals
+ * This allows registering for shared signal events. See description of the
+ * shared signal object above for more information how this works. Also see the
+ * eloop description to see some drawbacks when nesting eloop objects with the
+ * same shared signal sources.
*/
int ev_eloop_register_signal_cb(struct ev_eloop *loop, int signum,
@@ -1248,6 +1351,10 @@ void ev_eloop_unregister_signal_cb(struct ev_eloop *loop, int signum,
/*
* Idle sources
+ * Idle sources are called everytime when a next dispatch round is started.
+ * That means, unless there is no idle source registered, the thread will
+ * _never_ go to sleep. So please unregister your idle source if no longer
+ * needed.
*/
int ev_eloop_register_idle_cb(struct ev_eloop *eloop, ev_idle_cb cb,