sout: sdi: fix channels to pairs setup
[vlc.git] / src / misc / interrupt.c
blobd2a04a72b3c86030988d2903059526731a4e70a3
1 /*****************************************************************************
2 * interrupt.c:
3 *****************************************************************************
4 * Copyright (C) 2015 RĂ©mi Denis-Courmont
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 /** @ingroup interrupt */
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
26 #include <assert.h>
27 #include <errno.h>
28 #include <limits.h>
29 #include <stdbool.h>
30 #include <stdlib.h>
32 #ifdef HAVE_POLL
33 #include <poll.h>
34 #endif
35 #ifdef HAVE_SYS_EVENTFD_H
36 # include <sys/eventfd.h>
37 #endif
39 #include <vlc_common.h>
40 #include <vlc_fs.h> /* vlc_pipe */
41 #include <vlc_network.h> /* vlc_accept */
43 #include "interrupt.h"
44 #include "libvlc.h"
46 static thread_local vlc_interrupt_t *vlc_interrupt_var;
48 /**
49 * Initializes an interruption context.
51 void vlc_interrupt_init(vlc_interrupt_t *ctx)
53 vlc_mutex_init(&ctx->lock);
54 ctx->interrupted = false;
55 atomic_init(&ctx->killed, false);
56 ctx->callback = NULL;
59 vlc_interrupt_t *vlc_interrupt_create(void)
61 vlc_interrupt_t *ctx = malloc(sizeof (*ctx));
62 if (likely(ctx != NULL))
63 vlc_interrupt_init(ctx);
64 return ctx;
67 /**
68 * Deinitializes an interruption context.
69 * The context shall no longer be used by any thread.
71 void vlc_interrupt_deinit(vlc_interrupt_t *ctx)
73 assert(ctx->callback == NULL);
74 vlc_mutex_destroy(&ctx->lock);
77 void vlc_interrupt_destroy(vlc_interrupt_t *ctx)
79 assert(ctx != NULL);
80 vlc_interrupt_deinit(ctx);
81 free(ctx);
84 void vlc_interrupt_raise(vlc_interrupt_t *ctx)
86 assert(ctx != NULL);
88 /* This function must be reentrant. But the callback typically is not
89 * reentrant. The lock ensures that all calls to the callback for a given
90 * context are serialized. The lock also protects against invalid memory
91 * accesses to the callback pointer proper, and the interrupted flag. */
92 vlc_mutex_lock(&ctx->lock);
93 ctx->interrupted = true;
94 if (ctx->callback != NULL)
95 ctx->callback(ctx->data);
96 vlc_mutex_unlock(&ctx->lock);
99 vlc_interrupt_t *vlc_interrupt_set(vlc_interrupt_t *newctx)
101 vlc_interrupt_t *oldctx = vlc_interrupt_var;
103 vlc_interrupt_var = newctx;
104 return oldctx;
108 * Prepares to enter interruptible wait.
109 * @param cb callback to interrupt the wait (i.e. wake up the thread)
110 * @param data opaque data pointer for the callback
111 * @note Any <b>successful</b> call <b>must</b> be paired with a call to
112 * vlc_interrupt_finish().
114 static void vlc_interrupt_prepare(vlc_interrupt_t *ctx,
115 void (*cb)(void *), void *data)
117 assert(ctx != NULL);
118 assert(ctx == vlc_interrupt_var);
120 vlc_mutex_lock(&ctx->lock);
121 assert(ctx->callback == NULL);
122 ctx->callback = cb;
123 ctx->data = data;
125 if (unlikely(ctx->interrupted))
126 cb(data);
127 vlc_mutex_unlock(&ctx->lock);
131 * Cleans up after an interruptible wait: waits for any pending invocations of
132 * the callback previously registed with vlc_interrupt_prepare(), and rechecks
133 * for any pending interruption.
135 * @warning As this function waits for ongoing callback invocation to complete,
136 * the caller must not hold any resource necessary for the callback to run.
137 * Otherwise a deadlock may occur.
139 * @return EINTR if an interruption occurred, zero otherwise
141 static int vlc_interrupt_finish(vlc_interrupt_t *ctx)
143 int ret = 0;
145 assert(ctx != NULL);
146 assert(ctx == vlc_interrupt_var);
148 /* Wait for pending callbacks to prevent access by other threads. */
149 vlc_mutex_lock(&ctx->lock);
150 ctx->callback = NULL;
151 if (ctx->interrupted)
153 ret = EINTR;
154 ctx->interrupted = false;
156 vlc_mutex_unlock(&ctx->lock);
157 return ret;
160 void vlc_interrupt_register(void (*cb)(void *), void *opaque)
162 vlc_interrupt_t *ctx = vlc_interrupt_var;
163 if (ctx != NULL)
164 vlc_interrupt_prepare(ctx, cb, opaque);
167 int vlc_interrupt_unregister(void)
169 vlc_interrupt_t *ctx = vlc_interrupt_var;
170 return (ctx != NULL) ? vlc_interrupt_finish(ctx) : 0;
173 static void vlc_interrupt_cleanup(void *opaque)
175 vlc_interrupt_finish(opaque);
178 void vlc_interrupt_kill(vlc_interrupt_t *ctx)
180 assert(ctx != NULL);
182 atomic_store(&ctx->killed, true);
183 vlc_interrupt_raise(ctx);
186 bool vlc_killed(void)
188 vlc_interrupt_t *ctx = vlc_interrupt_var;
190 return (ctx != NULL) && atomic_load(&ctx->killed);
193 static void vlc_interrupt_sem(void *opaque)
195 vlc_sem_post(opaque);
198 int vlc_sem_wait_i11e(vlc_sem_t *sem)
200 vlc_interrupt_t *ctx = vlc_interrupt_var;
201 if (ctx == NULL)
202 return vlc_sem_wait(sem), 0;
204 vlc_interrupt_prepare(ctx, vlc_interrupt_sem, sem);
206 vlc_cleanup_push(vlc_interrupt_cleanup, ctx);
207 vlc_sem_wait(sem);
208 vlc_cleanup_pop();
210 return vlc_interrupt_finish(ctx);
213 static void vlc_mwait_i11e_wake(void *opaque)
215 vlc_cond_signal(opaque);
218 static void vlc_mwait_i11e_cleanup(void *opaque)
220 vlc_interrupt_t *ctx = opaque;
221 vlc_cond_t *cond = ctx->data;
223 vlc_mutex_unlock(&ctx->lock);
224 vlc_interrupt_finish(ctx);
225 vlc_cond_destroy(cond);
228 int vlc_mwait_i11e(vlc_tick_t deadline)
230 vlc_interrupt_t *ctx = vlc_interrupt_var;
231 if (ctx == NULL)
232 return vlc_tick_wait(deadline), 0;
234 vlc_cond_t wait;
235 vlc_cond_init(&wait);
237 vlc_interrupt_prepare(ctx, vlc_mwait_i11e_wake, &wait);
239 vlc_mutex_lock(&ctx->lock);
240 vlc_cleanup_push(vlc_mwait_i11e_cleanup, ctx);
241 while (!ctx->interrupted
242 && vlc_cond_timedwait(&wait, &ctx->lock, deadline) == 0);
243 vlc_cleanup_pop();
244 vlc_mutex_unlock(&ctx->lock);
246 int ret = vlc_interrupt_finish(ctx);
247 vlc_cond_destroy(&wait);
248 return ret;
251 static void vlc_interrupt_forward_wake(void *opaque)
253 void **data = opaque;
254 vlc_interrupt_t *to = data[0];
255 vlc_interrupt_t *from = data[1];
257 (atomic_load(&from->killed) ? vlc_interrupt_kill
258 : vlc_interrupt_raise)(to);
261 void vlc_interrupt_forward_start(vlc_interrupt_t *to, void *data[2])
263 data[0] = data[1] = NULL;
265 vlc_interrupt_t *from = vlc_interrupt_var;
266 if (from == NULL)
267 return;
269 assert(from != to);
270 data[0] = to;
271 data[1] = from;
272 vlc_interrupt_prepare(from, vlc_interrupt_forward_wake, data);
275 int vlc_interrupt_forward_stop(void *const data[2])
277 vlc_interrupt_t *from = data[1];
278 if (from == NULL)
279 return 0;
281 assert(from->callback == vlc_interrupt_forward_wake);
282 assert(from->data == data);
283 return vlc_interrupt_finish(from);
286 #ifndef _WIN32
287 static void vlc_poll_i11e_wake(void *opaque)
289 uint64_t value = 1;
290 int *fd = opaque;
291 int canc;
293 canc = vlc_savecancel();
294 write(fd[1], &value, sizeof (value));
295 vlc_restorecancel(canc);
298 static void vlc_poll_i11e_cleanup(void *opaque)
300 vlc_interrupt_t *ctx = opaque;
301 int *fd = ctx->data;
303 vlc_interrupt_finish(ctx);
304 if (fd[1] != fd[0])
305 vlc_close(fd[1]);
306 vlc_close(fd[0]);
309 static int vlc_poll_i11e_inner(struct pollfd *restrict fds, unsigned nfds,
310 int timeout, vlc_interrupt_t *ctx,
311 struct pollfd *restrict ufd)
313 int fd[2];
314 int ret = -1;
315 int canc;
317 /* TODO: cache this */
318 # if defined (HAVE_EVENTFD) && defined (EFD_CLOEXEC)
319 canc = vlc_savecancel();
320 fd[0] = eventfd(0, EFD_CLOEXEC);
321 vlc_restorecancel(canc);
322 if (fd[0] != -1)
323 fd[1] = fd[0];
324 else
325 # endif
326 if (vlc_pipe(fd))
328 vlc_testcancel();
329 errno = ENOMEM;
330 return -1;
333 for (unsigned i = 0; i < nfds; i++)
335 ufd[i].fd = fds[i].fd;
336 ufd[i].events = fds[i].events;
338 ufd[nfds].fd = fd[0];
339 ufd[nfds].events = POLLIN;
341 vlc_interrupt_prepare(ctx, vlc_poll_i11e_wake, fd);
343 vlc_cleanup_push(vlc_poll_i11e_cleanup, ctx);
344 ret = poll(ufd, nfds + 1, timeout);
346 for (unsigned i = 0; i < nfds; i++)
347 fds[i].revents = ufd[i].revents;
349 if (ret > 0 && ufd[nfds].revents)
351 uint64_t dummy;
353 read(fd[0], &dummy, sizeof (dummy));
354 ret--;
356 vlc_cleanup_pop();
358 if (vlc_interrupt_finish(ctx))
360 errno = EINTR;
361 ret = -1;
364 canc = vlc_savecancel();
365 if (fd[1] != fd[0])
366 vlc_close(fd[1]);
367 vlc_close(fd[0]);
368 vlc_restorecancel(canc);
369 return ret;
372 int vlc_poll_i11e(struct pollfd *fds, unsigned nfds, int timeout)
374 vlc_interrupt_t *ctx = vlc_interrupt_var;
375 if (ctx == NULL)
376 return poll(fds, nfds, timeout);
378 int ret;
380 if (likely(nfds < 255))
381 { /* Fast path with stack allocation */
382 struct pollfd ufd[nfds + 1];
384 ret = vlc_poll_i11e_inner(fds, nfds, timeout, ctx, ufd);
386 else
387 { /* Slow path but poll() is slow with large nfds anyway. */
388 struct pollfd *ufd = vlc_alloc(nfds + 1, sizeof (*ufd));
389 if (unlikely(ufd == NULL))
390 return -1; /* ENOMEM */
392 vlc_cleanup_push(free, ufd);
393 ret = vlc_poll_i11e_inner(fds, nfds, timeout, ctx, ufd);
394 vlc_cleanup_pop();
395 free(ufd);
397 return ret;
400 # include <fcntl.h>
401 # include <sys/uio.h>
402 # ifdef HAVE_SYS_SOCKET_H
403 # include <sys/socket.h>
404 # endif
407 /* There are currently no ways to atomically force a non-blocking read or write
408 * operations. Even for sockets, the MSG_DONTWAIT flag is non-standard.
410 * So in the event that more than one thread tries to read or write on the same
411 * file at the same time, there is a race condition where these functions might
412 * block in spite of an interruption. This should never happen in practice.
416 * Wrapper for readv() that returns the EINTR error upon VLC I/O interruption.
417 * @warning This function ignores the non-blocking file flag.
419 ssize_t vlc_readv_i11e(int fd, struct iovec *iov, int count)
421 struct pollfd ufd;
423 ufd.fd = fd;
424 ufd.events = POLLIN;
426 if (vlc_poll_i11e(&ufd, 1, -1) < 0)
427 return -1;
428 return readv(fd, iov, count);
432 * Wrapper for writev() that returns the EINTR error upon VLC I/O interruption.
434 * @note Like writev(), once some but not all bytes are written, the function
435 * might wait for write completion, regardless of signals and interruptions.
436 * @warning This function ignores the non-blocking file flag.
438 ssize_t vlc_writev_i11e(int fd, const struct iovec *iov, int count)
440 struct pollfd ufd;
442 ufd.fd = fd;
443 ufd.events = POLLOUT;
445 if (vlc_poll_i11e(&ufd, 1, -1) < 0)
446 return -1;
447 return writev(fd, iov, count);
451 * Wrapper for read() that returns the EINTR error upon VLC I/O interruption.
452 * @warning This function ignores the non-blocking file flag.
454 ssize_t vlc_read_i11e(int fd, void *buf, size_t count)
456 struct iovec iov = { .iov_base = buf, .iov_len = count };
457 return vlc_readv_i11e(fd, &iov, 1);
461 * Wrapper for write() that returns the EINTR error upon VLC I/O interruption.
463 * @note Like write(), once some but not all bytes are written, the function
464 * might wait for write completion, regardless of signals and interruptions.
465 * @warning This function ignores the non-blocking file flag.
467 ssize_t vlc_write_i11e(int fd, const void *buf, size_t count)
469 struct iovec iov = { .iov_base = (void*)buf, .iov_len = count };
470 return vlc_writev_i11e(fd, &iov, 1);
473 ssize_t vlc_recvmsg_i11e(int fd, struct msghdr *msg, int flags)
475 struct pollfd ufd;
477 ufd.fd = fd;
478 ufd.events = POLLIN;
480 if (vlc_poll_i11e(&ufd, 1, -1) < 0)
481 return -1;
482 /* NOTE: MSG_OOB and MSG_PEEK should work fine here.
483 * MSG_WAITALL is not supported at this point. */
484 return recvmsg(fd, msg, flags);
487 ssize_t vlc_recvfrom_i11e(int fd, void *buf, size_t len, int flags,
488 struct sockaddr *addr, socklen_t *addrlen)
490 struct iovec iov = { .iov_base = buf, .iov_len = len };
491 struct msghdr msg = {
492 .msg_name = addr,
493 .msg_namelen = (addrlen != NULL) ? *addrlen : 0,
494 .msg_iov = &iov,
495 .msg_iovlen = 1,
498 ssize_t ret = vlc_recvmsg_i11e(fd, &msg, flags);
499 if (ret >= 0 && addrlen != NULL)
500 *addrlen = msg.msg_namelen;
501 return ret;
504 ssize_t vlc_sendmsg_i11e(int fd, const struct msghdr *msg, int flags)
506 struct pollfd ufd;
508 ufd.fd = fd;
509 ufd.events = POLLOUT;
511 if (vlc_poll_i11e(&ufd, 1, -1) < 0)
512 return -1;
513 /* NOTE: MSG_EOR, MSG_OOB and MSG_NOSIGNAL should all work fine here. */
514 return sendmsg(fd, msg, flags);
517 ssize_t vlc_sendto_i11e(int fd, const void *buf, size_t len, int flags,
518 const struct sockaddr *addr, socklen_t addrlen)
520 struct iovec iov = { .iov_base = (void *)buf, .iov_len = len };
521 struct msghdr msg = {
522 .msg_name = (struct sockaddr *)addr,
523 .msg_namelen = addrlen,
524 .msg_iov = &iov,
525 .msg_iovlen = 1,
528 return vlc_sendmsg_i11e(fd, &msg, flags);
531 int vlc_accept_i11e(int fd, struct sockaddr *addr, socklen_t *addrlen,
532 bool blocking)
534 struct pollfd ufd;
536 ufd.fd = fd;
537 ufd.events = POLLIN;
539 if (vlc_poll_i11e(&ufd, 1, -1) < 0)
540 return -1;
542 return vlc_accept(fd, addr, addrlen, blocking);
545 #else /* _WIN32 */
547 static void CALLBACK vlc_poll_i11e_wake_self(ULONG_PTR data)
549 (void) data; /* Nothing to do */
552 static void vlc_poll_i11e_wake(void *opaque)
554 #if !VLC_WINSTORE_APP || _WIN32_WINNT >= 0x0A00
555 HANDLE th = opaque;
556 QueueUserAPC(vlc_poll_i11e_wake_self, th, 0);
557 #else
558 (void) opaque;
559 #endif
562 static void vlc_poll_i11e_cleanup(void *opaque)
564 vlc_interrupt_t *ctx = opaque;
565 HANDLE th = ctx->data;
567 vlc_interrupt_finish(ctx);
568 CloseHandle(th);
571 int vlc_poll_i11e(struct pollfd *fds, unsigned nfds, int timeout)
573 vlc_interrupt_t *ctx = vlc_interrupt_var;
574 if (ctx == NULL)
575 return vlc_poll(fds, nfds, timeout);
577 int ret = -1;
578 HANDLE th;
580 if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
581 GetCurrentProcess(), &th, 0, FALSE,
582 DUPLICATE_SAME_ACCESS))
584 errno = ENOMEM;
585 return -1;
588 vlc_interrupt_prepare(ctx, vlc_poll_i11e_wake, th);
590 vlc_cleanup_push(vlc_poll_i11e_cleanup, ctx);
591 ret = vlc_poll(fds, nfds, timeout);
592 vlc_cleanup_pop();
594 if (vlc_interrupt_finish(ctx))
596 errno = EINTR;
597 ret = -1;
600 CloseHandle(th);
601 return ret;
604 ssize_t vlc_readv_i11e(int fd, struct iovec *iov, int count)
606 (void) fd; (void) iov; (void) count;
607 vlc_assert_unreachable();
610 ssize_t vlc_writev_i11e(int fd, const struct iovec *iov, int count)
612 (void) fd; (void) iov; (void) count;
613 vlc_assert_unreachable();
616 ssize_t vlc_read_i11e(int fd, void *buf, size_t count)
618 return read(fd, buf, count);
621 ssize_t vlc_write_i11e(int fd, const void *buf, size_t count)
623 return write(fd, buf, count);
626 ssize_t vlc_recvmsg_i11e(int fd, struct msghdr *msg, int flags)
628 (void) fd; (void) msg; (void) flags;
629 vlc_assert_unreachable();
632 ssize_t vlc_recvfrom_i11e(int fd, void *buf, size_t len, int flags,
633 struct sockaddr *addr, socklen_t *addrlen)
635 struct pollfd ufd;
637 ufd.fd = fd;
638 ufd.events = POLLIN;
640 if (vlc_poll_i11e(&ufd, 1, -1) < 0)
641 return -1;
643 ssize_t ret = recvfrom(fd, buf, len, flags, addr, addrlen);
644 if (ret < 0 && WSAGetLastError() == WSAEWOULDBLOCK)
645 errno = EAGAIN;
646 return ret;
649 ssize_t vlc_sendmsg_i11e(int fd, const struct msghdr *msg, int flags)
651 (void) fd; (void) msg; (void) flags;
652 vlc_assert_unreachable();
655 ssize_t vlc_sendto_i11e(int fd, const void *buf, size_t len, int flags,
656 const struct sockaddr *addr, socklen_t addrlen)
658 struct pollfd ufd;
660 ufd.fd = fd;
661 ufd.events = POLLOUT;
663 if (vlc_poll_i11e(&ufd, 1, -1) < 0)
664 return -1;
666 ssize_t ret = sendto(fd, buf, len, flags, addr, addrlen);
667 if (ret < 0 && WSAGetLastError() == WSAEWOULDBLOCK)
668 errno = EAGAIN;
669 return ret;
672 int vlc_accept_i11e(int fd, struct sockaddr *addr, socklen_t *addrlen,
673 bool blocking)
675 struct pollfd ufd;
677 ufd.fd = fd;
678 ufd.events = POLLIN;
680 if (vlc_poll_i11e(&ufd, 1, -1) < 0)
681 return -1;
683 int cfd = vlc_accept(fd, addr, addrlen, blocking);
684 if (cfd < 0 && WSAGetLastError() == WSAEWOULDBLOCK)
685 errno = EAGAIN;
686 return cfd;
689 #endif