/*
* 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,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#define _GNU_SOURCE
#define _LGPL_SOURCE
#include <assert.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <sys/time.h>
+#include <stdbool.h>
#include <common/defaults.h>
#include <common/error.h>
}
if (!poll_max_size) {
- ERR("poll_max_size not initialized yet");
- goto error;
+ if (lttng_poll_set_max_size()) {
+ goto error;
+ }
}
/* Don't bust the limit here */
return -1;
}
+/*
+ * Modify an fd's events..
+ */
+int compat_poll_mod(struct lttng_poll_event *events, int fd,
+ uint32_t req_events)
+{
+ int i;
+ struct compat_poll_event_array *current;
+
+ if (events == NULL || events->current.nb_fd == 0 ||
+ events->current.events == NULL || fd < 0) {
+ ERR("Bad compat poll mod arguments");
+ goto error;
+ }
+
+ current = &events->current;
+
+ for (i = 0; i < current->nb_fd; i++) {
+ if (current->events[i].fd == fd) {
+ current->events[i].events = req_events;
+ events->need_update = 1;
+ break;
+ }
+ }
+
+ /*
+ * The epoll flavor doesn't flag modifying a non-included FD as an
+ * error.
+ */
+
+ return 0;
+
+error:
+ return -1;
+}
+
/*
* Remove a fd from the pollfd structure.
*/
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;
}
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;
events->need_update = 1;
+end:
return 0;
error:
/*
* Wait on poll() with timeout. Blocking call.
*/
-int compat_poll_wait(struct lttng_poll_event *events, int timeout)
+int compat_poll_wait(struct lttng_poll_event *events, int timeout,
+ bool interruptible)
{
- int ret;
+ int ret, active_fd_count;
+ int idle_pfd_index = 0;
+ size_t i;
if (events == NULL || events->current.events == NULL) {
ERR("poll wait arguments error");
}
}
- ret = poll(events->wait.events, events->wait.nb_fd, timeout);
+ do {
+ ret = poll(events->wait.events, events->wait.nb_fd, timeout);
+ } 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.
+ * Swap all active pollfd structs to the beginning of the
+ * array to emulate compat-epoll behaviour. This algorithm takes
+ * advantage of poll's returned value and the burst nature of active
+ * events on the file descriptors. The while loop guarantees that
+ * idle_pfd will always point to an idle fd.
*/
- return events->wait.nb_fd;
+ if (active_fd_count == events->wait.nb_fd) {
+ goto end;
+ }
+ while (idle_pfd_index < active_fd_count &&
+ events->wait.events[idle_pfd_index].revents != 0) {
+ idle_pfd_index++;
+ }
+
+ for (i = idle_pfd_index + 1; idle_pfd_index < active_fd_count;
+ i++) {
+ struct pollfd swap_pfd;
+ struct pollfd *idle_pfd = &events->wait.events[idle_pfd_index];
+ struct pollfd *current_pfd = &events->wait.events[i];
+
+ if (idle_pfd->revents != 0) {
+ swap_pfd = *current_pfd;
+ *current_pfd = *idle_pfd;
+ *idle_pfd = swap_pfd;
+ idle_pfd_index++;
+ }
+ }
+
+end:
+ return ret;
error:
return -1;