]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/libsystemd/sd-event/sd-event.c
sd-event: use DIV_ROUND_UP where appropriate
[thirdparty/systemd.git] / src / libsystemd / sd-event / sd-event.c
index 202e9eab163ade54eaaa3904b4e9d0e9259847db..1987f279eb3f733c64ef6f7347fe7a7c3855bfea 100644 (file)
@@ -9,11 +9,13 @@
 #include "sd-id128.h"
 
 #include "alloc-util.h"
+#include "event-source.h"
 #include "fd-util.h"
 #include "fs-util.h"
 #include "hashmap.h"
 #include "list.h"
 #include "macro.h"
+#include "memory-util.h"
 #include "missing.h"
 #include "prioq.h"
 #include "process-util.h"
 #include "string-table.h"
 #include "string-util.h"
 #include "time-util.h"
-#include "util.h"
 
 #define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC)
 
-typedef enum EventSourceType {
-        SOURCE_IO,
-        SOURCE_TIME_REALTIME,
-        SOURCE_TIME_BOOTTIME,
-        SOURCE_TIME_MONOTONIC,
-        SOURCE_TIME_REALTIME_ALARM,
-        SOURCE_TIME_BOOTTIME_ALARM,
-        SOURCE_SIGNAL,
-        SOURCE_CHILD,
-        SOURCE_DEFER,
-        SOURCE_POST,
-        SOURCE_EXIT,
-        SOURCE_WATCHDOG,
-        SOURCE_INOTIFY,
-        _SOURCE_EVENT_SOURCE_TYPE_MAX,
-        _SOURCE_EVENT_SOURCE_TYPE_INVALID = -1
-} EventSourceType;
-
 static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = {
         [SOURCE_IO] = "io",
         [SOURCE_TIME_REALTIME] = "realtime",
@@ -62,183 +45,8 @@ static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX]
 
 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int);
 
-/* All objects we use in epoll events start with this value, so that
- * we know how to dispatch it */
-typedef enum WakeupType {
-        WAKEUP_NONE,
-        WAKEUP_EVENT_SOURCE,
-        WAKEUP_CLOCK_DATA,
-        WAKEUP_SIGNAL_DATA,
-        WAKEUP_INOTIFY_DATA,
-        _WAKEUP_TYPE_MAX,
-        _WAKEUP_TYPE_INVALID = -1,
-} WakeupType;
-
 #define EVENT_SOURCE_IS_TIME(t) IN_SET((t), SOURCE_TIME_REALTIME, SOURCE_TIME_BOOTTIME, SOURCE_TIME_MONOTONIC, SOURCE_TIME_REALTIME_ALARM, SOURCE_TIME_BOOTTIME_ALARM)
 
-struct inode_data;
-
-struct sd_event_source {
-        WakeupType wakeup;
-
-        unsigned n_ref;
-
-        sd_event *event;
-        void *userdata;
-        sd_event_handler_t prepare;
-
-        char *description;
-
-        EventSourceType type:5;
-        signed int enabled:3;
-        bool pending:1;
-        bool dispatching:1;
-        bool floating:1;
-
-        int64_t priority;
-        unsigned pending_index;
-        unsigned prepare_index;
-        uint64_t pending_iteration;
-        uint64_t prepare_iteration;
-
-        sd_event_destroy_t destroy_callback;
-
-        LIST_FIELDS(sd_event_source, sources);
-
-        union {
-                struct {
-                        sd_event_io_handler_t callback;
-                        int fd;
-                        uint32_t events;
-                        uint32_t revents;
-                        bool registered:1;
-                        bool owned:1;
-                } io;
-                struct {
-                        sd_event_time_handler_t callback;
-                        usec_t next, accuracy;
-                        unsigned earliest_index;
-                        unsigned latest_index;
-                } time;
-                struct {
-                        sd_event_signal_handler_t callback;
-                        struct signalfd_siginfo siginfo;
-                        int sig;
-                } signal;
-                struct {
-                        sd_event_child_handler_t callback;
-                        siginfo_t siginfo;
-                        pid_t pid;
-                        int options;
-                } child;
-                struct {
-                        sd_event_handler_t callback;
-                } defer;
-                struct {
-                        sd_event_handler_t callback;
-                } post;
-                struct {
-                        sd_event_handler_t callback;
-                        unsigned prioq_index;
-                } exit;
-                struct {
-                        sd_event_inotify_handler_t callback;
-                        uint32_t mask;
-                        struct inode_data *inode_data;
-                        LIST_FIELDS(sd_event_source, by_inode_data);
-                } inotify;
-        };
-};
-
-struct clock_data {
-        WakeupType wakeup;
-        int fd;
-
-        /* For all clocks we maintain two priority queues each, one
-         * ordered for the earliest times the events may be
-         * dispatched, and one ordered by the latest times they must
-         * have been dispatched. The range between the top entries in
-         * the two prioqs is the time window we can freely schedule
-         * wakeups in */
-
-        Prioq *earliest;
-        Prioq *latest;
-        usec_t next;
-
-        bool needs_rearm:1;
-};
-
-struct signal_data {
-        WakeupType wakeup;
-
-        /* For each priority we maintain one signal fd, so that we
-         * only have to dequeue a single event per priority at a
-         * time. */
-
-        int fd;
-        int64_t priority;
-        sigset_t sigset;
-        sd_event_source *current;
-};
-
-/* A structure listing all event sources currently watching a specific inode */
-struct inode_data {
-        /* The identifier for the inode, the combination of the .st_dev + .st_ino fields of the file */
-        ino_t ino;
-        dev_t dev;
-
-        /* An fd of the inode to watch. The fd is kept open until the next iteration of the loop, so that we can
-         * rearrange the priority still until then, as we need the original inode to change the priority as we need to
-         * add a watch descriptor to the right inotify for the priority which we can only do if we have a handle to the
-         * original inode. We keep a list of all inode_data objects with an open fd in the to_close list (see below) of
-         * the sd-event object, so that it is efficient to close everything, before entering the next event loop
-         * iteration. */
-        int fd;
-
-        /* The inotify "watch descriptor" */
-        int wd;
-
-        /* The combination of the mask of all inotify watches on this inode we manage. This is also the mask that has
-         * most recently been set on the watch descriptor. */
-        uint32_t combined_mask;
-
-        /* All event sources subscribed to this inode */
-        LIST_HEAD(sd_event_source, event_sources);
-
-        /* The inotify object we watch this inode with */
-        struct inotify_data *inotify_data;
-
-        /* A linked list of all inode data objects with fds to close (see above) */
-        LIST_FIELDS(struct inode_data, to_close);
-};
-
-/* A structure encapsulating an inotify fd */
-struct inotify_data {
-        WakeupType wakeup;
-
-        /* For each priority we maintain one inotify fd, so that we only have to dequeue a single event per priority at
-         * a time */
-
-        int fd;
-        int64_t priority;
-
-        Hashmap *inodes; /* The inode_data structures keyed by dev+ino */
-        Hashmap *wd;     /* The inode_data structures keyed by the watch descriptor for each */
-
-        /* The buffer we read inotify events into */
-        union inotify_event_buffer buffer;
-        size_t buffer_filled; /* fill level of the buffer */
-
-        /* How many event sources are currently marked pending for this inotify. We won't read new events off the
-         * inotify fd as long as there are still pending events on the inotify (because we have no strategy of queuing
-         * the events locally if they can't be coalesced). */
-        unsigned n_pending;
-
-        /* A linked list of all inotify objects with data already read, that still need processing. We keep this list
-         * to make it efficient to figure out what inotify objects to process data on next. */
-        LIST_FIELDS(struct inotify_data, buffered);
-};
-
 struct sd_event {
         unsigned n_ref;
 
@@ -314,6 +122,7 @@ static sd_event *event_resolve(sd_event *e) {
 
 static int pending_prioq_compare(const void *a, const void *b) {
         const sd_event_source *x = a, *y = b;
+        int r;
 
         assert(x->pending);
         assert(y->pending);
@@ -325,22 +134,17 @@ static int pending_prioq_compare(const void *a, const void *b) {
                 return 1;
 
         /* Lower priority values first */
-        if (x->priority < y->priority)
-                return -1;
-        if (x->priority > y->priority)
-                return 1;
+        r = CMP(x->priority, y->priority);
+        if (r != 0)
+                return r;
 
         /* Older entries first */
-        if (x->pending_iteration < y->pending_iteration)
-                return -1;
-        if (x->pending_iteration > y->pending_iteration)
-                return 1;
-
-        return 0;
+        return CMP(x->pending_iteration, y->pending_iteration);
 }
 
 static int prepare_prioq_compare(const void *a, const void *b) {
         const sd_event_source *x = a, *y = b;
+        int r;
 
         assert(x->prepare);
         assert(y->prepare);
@@ -354,18 +158,12 @@ static int prepare_prioq_compare(const void *a, const void *b) {
         /* Move most recently prepared ones last, so that we can stop
          * preparing as soon as we hit one that has already been
          * prepared in the current iteration */
-        if (x->prepare_iteration < y->prepare_iteration)
-                return -1;
-        if (x->prepare_iteration > y->prepare_iteration)
-                return 1;
+        r = CMP(x->prepare_iteration, y->prepare_iteration);
+        if (r != 0)
+                return r;
 
         /* Lower priority values first */
-        if (x->priority < y->priority)
-                return -1;
-        if (x->priority > y->priority)
-                return 1;
-
-        return 0;
+        return CMP(x->priority, y->priority);
 }
 
 static int earliest_time_prioq_compare(const void *a, const void *b) {
@@ -387,12 +185,7 @@ static int earliest_time_prioq_compare(const void *a, const void *b) {
                 return 1;
 
         /* Order by time */
-        if (x->time.next < y->time.next)
-                return -1;
-        if (x->time.next > y->time.next)
-                return 1;
-
-        return 0;
+        return CMP(x->time.next, y->time.next);
 }
 
 static usec_t time_event_source_latest(const sd_event_source *s) {
@@ -418,12 +211,7 @@ static int latest_time_prioq_compare(const void *a, const void *b) {
                 return 1;
 
         /* Order by time */
-        if (time_event_source_latest(x) < time_event_source_latest(y))
-                return -1;
-        if (time_event_source_latest(x) > time_event_source_latest(y))
-                return 1;
-
-        return 0;
+        return CMP(time_event_source_latest(x), time_event_source_latest(y));
 }
 
 static int exit_prioq_compare(const void *a, const void *b) {
@@ -439,12 +227,7 @@ static int exit_prioq_compare(const void *a, const void *b) {
                 return 1;
 
         /* Lower priority values first */
-        if (x->priority < y->priority)
-                return -1;
-        if (x->priority > y->priority)
-                return 1;
-
-        return 0;
+        return CMP(x->priority, y->priority);
 }
 
 static void free_clock_data(struct clock_data *d) {
@@ -687,6 +470,17 @@ static struct clock_data* event_get_clock_data(sd_event *e, EventSourceType t) {
         }
 }
 
+static void event_free_signal_data(sd_event *e, struct signal_data *d) {
+        assert(e);
+
+        if (!d)
+                return;
+
+        hashmap_remove(e->signal_data, &d->priority);
+        safe_close(d->fd);
+        free(d);
+}
+
 static int event_make_signal_data(
                 sd_event *e,
                 int sig,
@@ -776,11 +570,8 @@ static int event_make_signal_data(
         return 0;
 
 fail:
-        if (added) {
-                d->fd = safe_close(d->fd);
-                hashmap_remove(e->signal_data, &d->priority);
-                free(d);
-        }
+        if (added)
+                event_free_signal_data(e, d);
 
         return r;
 }
@@ -799,11 +590,8 @@ static void event_unmask_signal_data(sd_event *e, struct signal_data *d, int sig
         assert_se(sigdelset(&d->sigset, sig) >= 0);
 
         if (sigisemptyset(&d->sigset)) {
-
                 /* If all the mask is all-zero we can get rid of the structure */
-                hashmap_remove(e->signal_data, &d->priority);
-                safe_close(d->fd);
-                free(d);
+                event_free_signal_data(e, d);
                 return;
         }
 
@@ -950,7 +738,7 @@ static void source_disconnect(sd_event_source *s) {
                          * continued to being watched. That's because inotify doesn't really have an API for that: we
                          * can only change watch masks with access to the original inode either by fd or by path. But
                          * paths aren't stable, and keeping an O_PATH fd open all the time would mean wasting an fd
-                         * continously and keeping the mount busy which we can't really do. We could reconstruct the
+                         * continuously and keeping the mount busy which we can't really do. We could reconstruct the
                          * original inode from /proc/self/fdinfo/$INOTIFY_FD (as all watch descriptors are listed
                          * there), but given the need for open_by_handle_at() which is privileged and not universally
                          * available this would be quite an incomplete solution. Hence we go the other way, leave the
@@ -1130,7 +918,7 @@ static void initialize_perturb(sd_event *e) {
         sd_id128_t bootid = {};
 
         /* When we sleep for longer, we try to realign the wakeup to
-           the same time wihtin each minute/second/250ms, so that
+           the same time within each minute/second/250ms, so that
            events all across the system can be coalesced into a single
            CPU wakeup. However, let's take some system-specific
            randomness for this value, so that in a network of systems
@@ -1588,38 +1376,27 @@ static int event_make_inotify_data(
         return 1;
 }
 
-static int inode_data_compare(const void *a, const void *b) {
-        const struct inode_data *x = a, *y = b;
+static int inode_data_compare(const struct inode_data *x, const struct inode_data *y) {
+        int r;
 
         assert(x);
         assert(y);
 
-        if (x->dev < y->dev)
-                return -1;
-        if (x->dev > y->dev)
-                return 1;
-
-        if (x->ino < y->ino)
-                return -1;
-        if (x->ino > y->ino)
-                return 1;
+        r = CMP(x->dev, y->dev);
+        if (r != 0)
+                return r;
 
-        return 0;
+        return CMP(x->ino, y->ino);
 }
 
-static void inode_data_hash_func(const void *p, struct siphash *state) {
-        const struct inode_data *d = p;
-
-        assert(p);
+static void inode_data_hash_func(const struct inode_data *d, struct siphash *state) {
+        assert(d);
 
         siphash24_compress(&d->dev, sizeof(d->dev), state);
         siphash24_compress(&d->ino, sizeof(d->ino), state);
 }
 
-const struct hash_ops inode_data_hash_ops = {
-        .hash = inode_data_hash_func,
-        .compare = inode_data_compare
-};
+DEFINE_PRIVATE_HASH_OPS(inode_data_hash_ops, struct inode_data, inode_data_hash_func, inode_data_compare);
 
 static void event_free_inode_data(
                 sd_event *e,
@@ -1746,7 +1523,7 @@ static uint32_t inode_data_determine_mask(struct inode_data *d) {
          *
          * Note that we add all sources to the mask here, regardless whether enabled, disabled or oneshot. That's
          * because we cannot change the mask anymore after the event source was created once, since the kernel has no
-         * API for that. Hence we need to subscribe to the maximum mask we ever might be interested in, and supress
+         * API for that. Hence we need to subscribe to the maximum mask we ever might be interested in, and suppress
          * events we don't care for client-side. */
 
         LIST_FOREACH(inotify.by_inode_data, s, d->event_sources) {
@@ -1917,9 +1694,11 @@ _public_ int sd_event_source_set_description(sd_event_source *s, const char *des
 _public_ int sd_event_source_get_description(sd_event_source *s, const char **description) {
         assert_return(s, -EINVAL);
         assert_return(description, -EINVAL);
-        assert_return(s->description, -ENXIO);
         assert_return(!event_pid_changed(s->event), -ECHILD);
 
+        if (!s->description)
+                return -ENXIO;
+
         *description = s->description;
         return 0;
 }
@@ -2171,11 +1950,11 @@ fail:
 
 _public_ int sd_event_source_get_enabled(sd_event_source *s, int *m) {
         assert_return(s, -EINVAL);
-        assert_return(m, -EINVAL);
         assert_return(!event_pid_changed(s->event), -ECHILD);
 
-        *m = s->enabled;
-        return 0;
+        if (m)
+                *m = s->enabled;
+        return s->enabled != SD_EVENT_OFF;
 }
 
 _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
@@ -3337,7 +3116,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
                 timeout = 0;
 
         m = epoll_wait(e->epoll_fd, ev_queue, ev_queue_max,
-                       timeout == (uint64_t) -1 ? -1 : (int) ((timeout + USEC_PER_MSEC - 1) / USEC_PER_MSEC));
+                       timeout == (uint64_t) -1 ? -1 : (int) DIV_ROUND_UP(timeout, USEC_PER_MSEC));
         if (m < 0) {
                 if (errno == EINTR) {
                         e->state = SD_EVENT_PENDING;
@@ -3732,3 +3511,31 @@ _public_ int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_d
 
         return !!s->destroy_callback;
 }
+
+_public_ int sd_event_source_get_floating(sd_event_source *s) {
+        assert_return(s, -EINVAL);
+
+        return s->floating;
+}
+
+_public_ int sd_event_source_set_floating(sd_event_source *s, int b) {
+        assert_return(s, -EINVAL);
+
+        if (s->floating == !!b)
+                return 0;
+
+        if (!s->event) /* Already disconnected */
+                return -ESTALE;
+
+        s->floating = b;
+
+        if (b) {
+                sd_event_source_ref(s);
+                sd_event_unref(s->event);
+        } else {
+                sd_event_ref(s->event);
+                sd_event_source_unref(s);
+        }
+
+        return 1;
+}