Fix: common: poll: compat_poll_wait never finishes
[lttng-tools.git] / src / common / compat / compat-poll.c
index 2b459e42f9fed14d565d371963e233c50c59e168..499a2762307f61c16023d574fba7aa337c10c6f9 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C)  2011 - David Goulet <david.goulet@polymtl.ca>
+ * Copyright (C)  2019 - Yannick Lamarre <ylamarre@efficios.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License, version 2 only,
 
 #include "poll.h"
 
-unsigned int poll_max_size;
+
+/*
+ * Maximum number of fd we can monitor.
+ *
+ * For poll(2), the max fds must not exceed RLIMIT_NOFILE given by
+ * getrlimit(2).
+ */
+static unsigned int poll_max_size;
 
 /*
  * Resize the epoll events structure of the new size.
@@ -102,6 +110,7 @@ error:
 /*
  * Create pollfd data structure.
  */
+LTTNG_HIDDEN
 int compat_poll_create(struct lttng_poll_event *events, int size)
 {
        struct compat_poll_event_array *current, *wait;
@@ -154,6 +163,7 @@ error:
 /*
  * Add fd to pollfd data structure with requested events.
  */
+LTTNG_HIDDEN
 int compat_poll_add(struct lttng_poll_event *events, int fd,
                uint32_t req_events)
 {
@@ -200,14 +210,15 @@ error:
 /*
  * Modify an fd's events..
  */
+LTTNG_HIDDEN
 int compat_poll_mod(struct lttng_poll_event *events, int fd,
                uint32_t req_events)
 {
-       int ret, i;
-       bool fd_found = false;
+       int i;
        struct compat_poll_event_array *current;
 
-       if (events == NULL || events->current.events == NULL || fd < 0) {
+       if (events == NULL || events->current.nb_fd == 0 ||
+                       events->current.events == NULL || fd < 0) {
                ERR("Bad compat poll mod arguments");
                goto error;
        }
@@ -216,16 +227,16 @@ int compat_poll_mod(struct lttng_poll_event *events, int fd,
 
        for (i = 0; i < current->nb_fd; i++) {
                if (current->events[i].fd == fd) {
-                       fd_found = true;
                        current->events[i].events = req_events;
                        events->need_update = 1;
                        break;
                }
        }
 
-       if (!fd_found) {
-               goto error;
-       }
+       /*
+        * The epoll flavor doesn't flag modifying a non-included FD as an
+        * error.
+        */
 
        return 0;
 
@@ -236,13 +247,15 @@ error:
 /*
  * Remove a fd from the pollfd structure.
  */
+LTTNG_HIDDEN
 int compat_poll_del(struct lttng_poll_event *events, int fd)
 {
-       int new_size, i, count = 0, ret;
+       int i, count = 0, ret;
+       uint32_t new_size;
        struct compat_poll_event_array *current;
 
-       if (events == NULL || events->current.events == NULL || fd < 0) {
-               ERR("Wrong arguments for poll del");
+       if (events == NULL || events->current.nb_fd == 0 ||
+                       events->current.events == NULL || fd < 0) {
                goto error;
        }
 
@@ -257,13 +270,20 @@ int compat_poll_del(struct lttng_poll_event *events, int fd)
                        count++;
                }
        }
+
+       /* The fd was not in our set, return no error as with epoll. */
+       if (current->nb_fd == count) {
+               goto end;
+       }
+
        /* No fd duplicate should be ever added into array. */
        assert(current->nb_fd - 1 == count);
        current->nb_fd = count;
 
        /* Resize array if needed. */
        new_size = 1U << utils_get_count_order_u32(current->nb_fd);
-       if (new_size != current->alloc_size && new_size >= current->init_size) {
+       if (new_size != current->alloc_size && new_size >= current->init_size
+                       && current->nb_fd != 0) {
                ret = resize_poll_event(current, new_size);
                if (ret < 0) {
                        goto error;
@@ -272,6 +292,7 @@ int compat_poll_del(struct lttng_poll_event *events, int fd)
 
        events->need_update = 1;
 
+end:
        return 0;
 
 error:
@@ -281,9 +302,12 @@ error:
 /*
  * Wait on poll() with timeout. Blocking call.
  */
-int compat_poll_wait(struct lttng_poll_event *events, int timeout)
+LTTNG_HIDDEN
+int compat_poll_wait(struct lttng_poll_event *events, int timeout,
+               bool interruptible)
 {
-       int ret;
+       int ret, active_fd_count;
+       size_t pos = 0, consecutive_entries = 0, non_idle_pos;
 
        if (events == NULL || events->current.events == NULL) {
                ERR("poll wait arguments error");
@@ -308,18 +332,49 @@ int compat_poll_wait(struct lttng_poll_event *events, int timeout)
 
        do {
                ret = poll(events->wait.events, events->wait.nb_fd, timeout);
-       } while (ret == -1 && errno == EINTR);
+       } while (!interruptible && ret == -1 && errno == EINTR);
        if (ret < 0) {
-               /* At this point, every error is fatal */
-               PERROR("poll wait");
+               if (errno != EINTR) {
+                       PERROR("poll wait");
+               }
                goto error;
        }
 
+       active_fd_count = ret;
+
        /*
-        * poll() should always iterate on all FDs since we handle the pollset in
-        * user space and after poll returns, we have to try every fd for a match.
+        * Move all active pollfd structs to the beginning of the
+        * array to emulate compat-epoll behaviour.
         */
-       return events->wait.nb_fd;
+       if (active_fd_count == events->wait.nb_fd) {
+               goto end;
+       }
+
+       while (consecutive_entries != active_fd_count) {
+               struct pollfd *current = &events->wait.events[pos];
+               struct pollfd idle_entry;
+
+               if (current->revents != 0) {
+                       consecutive_entries++;
+                       pos++;
+                       continue;
+               }
+
+               non_idle_pos = pos;
+
+               /* Look for next non-idle entry. */
+               while (events->wait.events[++non_idle_pos].revents == 0);
+
+               /* Swap idle and non-idle entries. */
+               idle_entry = *current;
+               *current = events->wait.events[non_idle_pos];
+               events->wait.events[non_idle_pos] = idle_entry;
+
+               consecutive_entries++;
+               pos++;
+       }
+end:
+       return ret;
 
 error:
        return -1;
@@ -328,6 +383,7 @@ error:
 /*
  * Setup poll set maximum size.
  */
+LTTNG_HIDDEN
 int compat_poll_set_max_size(void)
 {
        int ret, retval = 0;
This page took 0.030332 seconds and 4 git commands to generate.