]> git.alsa-project.org Git - alsa-utils.git/commitdiff
axfer: add an implementation of waiter for epoll(7)
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Tue, 13 Nov 2018 06:41:44 +0000 (15:41 +0900)
committerTakashi Iwai <tiwai@suse.de>
Tue, 13 Nov 2018 11:04:51 +0000 (12:04 +0100)
This commit adds support of waiter for Linux specific epoll(7) system call.
For portability to the other Unix-like systems such as xBSD, modification
of Makefile.am may be required for conditional build, but this commit
includes no changes for it.

Below lines are examples to use this option:
$ axfer transfer --waiter-type=epoll -M -P -d 2 -D hw:0,3 /dev/urandom -f dat -vvv
$ axfer transfer --waiter-type=epoll -M -C -d 2 -D hw:1,0 /dev/null -r 48000 -vvv

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
axfer/Makefile.am
axfer/waiter-epoll.c [new file with mode: 0644]
axfer/waiter.c
axfer/waiter.h

index 867007bc28ea8569d551a7c915959951a45bd9eb..ab1d0b4425cd07a6c2aec3a56aae2ee6628447f9 100644 (file)
@@ -52,4 +52,5 @@ axfer_SOURCES = \
        waiter.h \
        waiter.c \
        waiter-poll.c \
-       waiter-select.c
+       waiter-select.c \
+       waiter-epoll.c
diff --git a/axfer/waiter-epoll.c b/axfer/waiter-epoll.c
new file mode 100644 (file)
index 0000000..8f084f1
--- /dev/null
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// waiter-epoll.c - Waiter for event notification by epoll(7).
+//
+// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+//
+// Licensed under the terms of the GNU General Public License, version 2.
+
+#include "waiter.h"
+#include "misc.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/epoll.h>
+
+struct epoll_state {
+       int epfd;
+       struct epoll_event *events;
+       unsigned int ev_count;
+};
+
+static int epoll_prepare(struct waiter_context *waiter)
+{
+       struct epoll_state *state = waiter->private_data;
+       int i;
+
+       state->ev_count = waiter->pfd_count;
+       state->events = calloc(state->ev_count, sizeof(*state->events));
+       if (state->events == NULL)
+               return -ENOMEM;
+
+       state->epfd = epoll_create(1);
+       if (state->epfd < 0)
+               return -errno;
+
+       for (i = 0; i < waiter->pfd_count; ++i) {
+               struct epoll_event ev = {
+                       .data.fd = waiter->pfds[i].fd,
+                       .events = waiter->pfds[i].events,
+               };
+               if (epoll_ctl(state->epfd, EPOLL_CTL_ADD, ev.data.fd, &ev) < 0)
+                       return -errno;
+       }
+
+       return 0;
+}
+
+static int epoll_wait_event(struct waiter_context *waiter, int timeout_msec)
+{
+       struct epoll_state *state = waiter->private_data;
+       unsigned int ev_count;
+       int i, j;
+       int err;
+
+       memset(state->events, 0, state->ev_count * sizeof(*state->events));
+       err = epoll_wait(state->epfd, state->events, state->ev_count,
+                        timeout_msec);
+       if (err < 0)
+               return -errno;
+       ev_count = (unsigned int)err;
+
+       if (ev_count > 0) {
+               // Reconstruct data of pollfd structure.
+               for (i = 0; i < ev_count; ++i) {
+                       struct epoll_event *ev = &state->events[i];
+                       for (j = 0; j < waiter->pfd_count; ++j) {
+                               if (waiter->pfds[i].fd == ev->data.fd) {
+                                       waiter->pfds[i].revents = ev->events;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       return ev_count;
+}
+
+static void epoll_release(struct waiter_context *waiter)
+{
+       struct epoll_state *state = waiter->private_data;
+       int i;
+
+       for (i = 0; i < waiter->pfd_count; ++i) {
+               int fd = waiter->pfds[i].fd;
+               epoll_ctl(state->epfd, EPOLL_CTL_DEL, fd, NULL);
+       }
+
+       free(state->events);
+       state->events = NULL;
+
+       close(state->epfd);
+
+       state->ev_count = 0;
+       state->epfd = 0;
+}
+
+const struct waiter_data waiter_epoll = {
+       .ops = {
+               .prepare        = epoll_prepare,
+               .wait_event     = epoll_wait_event,
+               .release        = epoll_release,
+       },
+       .private_size = sizeof(struct epoll_state),
+};
index 08428e36afbc8ca9b5fe0fb507b968412a458df3..1e9c811579f68f02e4a3e7d27e4b65912b4457de 100644 (file)
@@ -18,6 +18,7 @@ static const char *const waiter_type_labels[] = {
        [WAITER_TYPE_DEFAULT] = "default",
        [WAITER_TYPE_POLL] = "poll",
        [WAITER_TYPE_SELECT] = "select",
+       [WAITER_TYPE_EPOLL] = "epoll",
 };
 
 enum waiter_type waiter_type_from_label(const char *label)
@@ -46,6 +47,7 @@ int waiter_context_init(struct waiter_context *waiter,
        } entries[] = {
                {WAITER_TYPE_POLL,      &waiter_poll},
                {WAITER_TYPE_SELECT,    &waiter_select},
+               {WAITER_TYPE_EPOLL,     &waiter_epoll},
        };
        int i;
 
index fec16b77f6f59e84cea4de974dbe8b42c60da358..db18e33e337f5a3ddf4b8977d8fe7d3f4ae18e15 100644 (file)
@@ -15,6 +15,7 @@ enum waiter_type {
        WAITER_TYPE_DEFAULT = 0,
        WAITER_TYPE_POLL,
        WAITER_TYPE_SELECT,
+       WAITER_TYPE_EPOLL,
        WAITER_TYPE_COUNT,
 };
 
@@ -55,5 +56,6 @@ struct waiter_data {
 
 extern const struct waiter_data waiter_poll;
 extern const struct waiter_data waiter_select;
+extern const struct waiter_data waiter_epoll;
 
 #endif