From 22c002138d8b82a021564ddab447fafd90852e75 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 6 May 2018 13:00:05 +0200 Subject: misc: add a synchronization helper This is almost like rendezvous(), except it allows async wakeup, and does not require global state. It will be used by a later commit. struct mp_waiter is intended to be allocated on the stack, and uses an initializer including PTHREAD_MUTEX_INITIALIZER. This is the first case in mpv that it uses PTHREAD_MUTEX_INITIALIZER for stack-allocated mutexes. It seems POSIX still does not allow this formally, but since POSIX is worth less than used toilet paper, I don't really care. Modern OSes use futexes, which means you can make _every_ memory location a lock, and this code tries to make use of it, without using OS specific code. The name of the source file is rather generic, because I intend to dump further small helpers there (or maybe move mp_rendezvous() to it). --- misc/thread_tools.h | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 misc/thread_tools.h (limited to 'misc/thread_tools.h') diff --git a/misc/thread_tools.h b/misc/thread_tools.h new file mode 100644 index 0000000000..54f396dead --- /dev/null +++ b/misc/thread_tools.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include + +// This is basically a single-shot semaphore, intended as light-weight solution +// for just making a thread wait for another thread. +struct mp_waiter { + // All fields are considered private. Use MP_WAITER_INITIALIZER to init. + pthread_mutex_t lock; + pthread_cond_t wakeup; + bool done; + uintptr_t value; +}; + +// Initialize a mp_waiter object for use with mp_waiter_*(). +#define MP_WAITER_INITIALIZER { \ + .lock = PTHREAD_MUTEX_INITIALIZER, \ + .wakeup = PTHREAD_COND_INITIALIZER, \ + } + +// Block until some other thread calls mp_waiter_wakeup(). The function returns +// the value argument of that wakeup call. After this, the waiter object must +// not be used anymore. Although you can reinit it with MP_WAITER_INITIALIZER +// (then you must make sure nothing calls mp_waiter_wakeup() before this). +uintptr_t mp_waiter_wait(struct mp_waiter *waiter); + +// Unblock the thread waiting with mp_waiter_wait(), and make it return the +// provided value. If the other thread did not enter that call yet, it will +// return immediately once it does (mp_waiter_wakeup() always returns +// immediately). Calling this more than once is not allowed. +void mp_waiter_wakeup(struct mp_waiter *waiter, uintptr_t value); + +// Query whether the waiter was woken up. If true, mp_waiter_wait() will return +// immediately. This is useful if you want to use another way to block and +// wakeup (in parallel to mp_waiter). +// You still need to call mp_waiter_wait() to free resources. +bool mp_waiter_poll(struct mp_waiter *waiter); -- cgit v1.2.3