[vlc-commits] input: add vlc_poll_i11e() for interruptible poll()
Rémi Denis-Courmont
git at videolan.org
Wed Jul 1 18:22:09 CEST 2015
vlc | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Tue Jun 30 23:52:51 2015 +0300| [bf6dcb5d0b1ca1c9241764de1054b0418c82ad01] | committer: Rémi Denis-Courmont
input: add vlc_poll_i11e() for interruptible poll()
This is a generic and inefficient but functional implementation.
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=bf6dcb5d0b1ca1c9241764de1054b0418c82ad01
---
include/vlc_interrupt.h | 22 ++++++
src/libvlccore.sym | 1 +
src/misc/interrupt.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 194 insertions(+)
diff --git a/include/vlc_interrupt.h b/include/vlc_interrupt.h
index fc075a2..fedbcd8 100644
--- a/include/vlc_interrupt.h
+++ b/include/vlc_interrupt.h
@@ -26,6 +26,9 @@
#ifndef VLC_INTERRUPT_H
# define VLC_INTERRUPT_H 1
# include <vlc_threads.h>
+
+struct pollfd;
+
/**
* @defgroup interrupt Interruptible sleep
* @{
@@ -50,6 +53,25 @@
VLC_API int vlc_sem_wait_i11e(vlc_sem_t *);
/**
+ * Waits for file descriptors I/O events, a timeout, a signal or a VLC I/O
+ * interruption. Except for VLC I/O interruptions, this function behaves
+ * just like the standard poll().
+ *
+ * @note This function is always a cancellation point (as poll()).
+ * @see poll() manual page
+ *
+ * @param fds table of events to wait for
+ * @param nfds number of entries in the table
+ * @param timeout time to wait in milliseconds or -1 for infinite
+ *
+ * @return A strictly positive result represent the number of pending events.
+ * 0 is returned if the time-out is reached without events.
+ * -1 is returned if a VLC I/O interrupt occurs (and errno is set to EINTR)
+ * or if an error occurs.
+ */
+VLC_API int vlc_poll_i11e(struct pollfd *, unsigned, int);
+
+/**
* @}
* @defgroup interrupt_context Interrupt context signaling and manipulation
* @{
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index d71e43b..7f10d51 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -539,6 +539,7 @@ vlc_ngettext
vlc_iconv
vlc_iconv_close
vlc_iconv_open
+vlc_poll_i11e
vlc_sem_wait_i11e
vlc_interrupt_create
vlc_interrupt_destroy
diff --git a/src/misc/interrupt.c b/src/misc/interrupt.c
index ec02601..f24ab34 100644
--- a/src/misc/interrupt.c
+++ b/src/misc/interrupt.c
@@ -29,7 +29,13 @@
#include <stdbool.h>
#include <stdlib.h>
+#ifdef HAVE_POLL
+#include <poll.h>
+#endif
+
#include <vlc_common.h>
+#include <vlc_fs.h> /* vlc_pipe */
+
#include "interrupt.h"
#include "libvlc.h"
@@ -231,3 +237,168 @@ int vlc_sem_wait_i11e(vlc_sem_t *sem)
return vlc_interrupt_finish(ctx);
}
+
+#ifndef _WIN32
+static void vlc_poll_i11e_wake(void *opaque)
+{
+ uint64_t value = 1;
+ int *fd = opaque;
+ int canc;
+
+ canc = vlc_savecancel();
+ write(fd[1], &value, sizeof (value));
+ vlc_restorecancel(canc);
+}
+
+static void vlc_poll_i11e_cleanup(void *opaque)
+{
+ vlc_interrupt_t *ctx = opaque;
+ int *fd = ctx->data;
+
+ vlc_interrupt_finish(ctx);
+ close(fd[1]);
+ close(fd[0]);
+}
+
+static int vlc_poll_i11e_inner(struct pollfd *restrict fds, unsigned nfds,
+ int timeout, vlc_interrupt_t *ctx,
+ struct pollfd *restrict ufd)
+{
+ int fd[2];
+ int ret = -1;
+ int canc;
+
+ /* TODO: cache this */
+ /* TODO: use eventfd on Linux */
+ if (vlc_pipe(fd))
+ {
+ vlc_testcancel();
+ errno = ENOMEM;
+ return -1;
+ }
+
+ for (unsigned i = 0; i < nfds; i++)
+ {
+ ufd[i].fd = fds[i].fd;
+ ufd[i].events = fds[i].events;
+ }
+ ufd[nfds].fd = fd[0];
+ ufd[nfds].events = POLLIN;
+
+ if (vlc_interrupt_prepare(ctx, vlc_poll_i11e_wake, fd))
+ {
+ vlc_testcancel();
+ errno = EINTR;
+ goto out;
+ }
+
+ vlc_cleanup_push(vlc_poll_i11e_cleanup, ctx);
+ ret = poll(ufd, nfds + 1, timeout);
+
+ for (unsigned i = 0; i < nfds; i++)
+ fds[i].revents = ufd[i].revents;
+
+ if (ret > 0 && ufd[nfds].revents)
+ {
+ uint64_t dummy;
+
+ read(fd[0], &dummy, sizeof (dummy));
+ ret--;
+ }
+ vlc_cleanup_pop();
+
+ if (vlc_interrupt_finish(ctx))
+ {
+ errno = EINTR;
+ ret = -1;
+ }
+out:
+ canc = vlc_savecancel();
+ close(fd[1]);
+ close(fd[0]);
+ vlc_restorecancel(canc);
+ return ret;
+}
+
+int vlc_poll_i11e(struct pollfd *fds, unsigned nfds, int timeout)
+{
+ vlc_interrupt_t *ctx = vlc_threadvar_get(vlc_interrupt_var);
+ if (ctx == NULL)
+ return poll(fds, nfds, timeout);
+
+ int ret;
+
+ struct pollfd *ufd = malloc((nfds + 1) * sizeof (*ufd));
+ if (unlikely(ufd == NULL))
+ return -1; /* ENOMEM */
+
+ vlc_cleanup_push(free, ufd);
+ ret = vlc_poll_i11e_inner(fds, nfds, timeout, ctx, ufd);
+ vlc_cleanup_pop();
+ free(ufd);
+ return ret;
+}
+
+#else /* _WIN32 */
+
+static void CALLBACK vlc_poll_i11e_wake_self(ULONG_PTR data)
+{
+ (void) data; /* Nothing to do */
+}
+
+static void vlc_poll_i11e_wake(void *opaque)
+{
+#if !VLC_WINSTORE_APP
+ HANDLE th = opaque;
+ QueueUserAPC(vlc_poll_i11e_wake_self, th, 0);
+#else
+ (void) opaque;
+#endif
+}
+
+static void vlc_poll_i11e_cleanup(void *opaque)
+{
+ vlc_interrupt_t *ctx = opaque;
+ HANDLE th = ctx->data;
+
+ vlc_interrupt_finish(ctx);
+ CloseHandle(th);
+}
+
+int vlc_poll_i11e(struct pollfd *fds, unsigned nfds, int timeout)
+{
+ vlc_interrupt_t *ctx = vlc_threadvar_get(vlc_interrupt_var);
+ if (ctx == NULL)
+ return vlc_poll(fds, nfds, timeout);
+
+ int ret = -1;
+ HANDLE th;
+
+ if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
+ GetCurrentProcess(), &th, 0, FALSE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (vlc_interrupt_prepare(ctx, vlc_poll_i11e_wake, th))
+ {
+ errno = EINTR;
+ goto out;
+ }
+
+ vlc_cleanup_push(vlc_poll_i11e_cleanup, th);
+ ret = vlc_poll(fds, nfds, timeout);
+ vlc_cleanup_pop();
+
+ if (vlc_interrupt_finish(ctx))
+ {
+ errno = EINTR;
+ ret = -1;
+ }
+out:
+ CloseHandle(th);
+ return ret;
+}
+#endif
More information about the vlc-commits
mailing list