summaryrefslogtreecommitdiffstats
path: root/misc
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2018-05-06 13:00:05 +0200
committerwm4 <wm4@nowhere>2018-05-24 19:56:34 +0200
commit22c002138d8b82a021564ddab447fafd90852e75 (patch)
tree233503a74b229f24bdedfc70b18d29f6354b46ae /misc
parent383da1bfd59fa77366d5e61612da4da8d4f1cf6c (diff)
downloadmpv-22c002138d8b82a021564ddab447fafd90852e75.tar.bz2
mpv-22c002138d8b82a021564ddab447fafd90852e75.tar.xz
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).
Diffstat (limited to 'misc')
-rw-r--r--misc/thread_tools.c62
-rw-r--r--misc/thread_tools.h39
2 files changed, 101 insertions, 0 deletions
diff --git a/misc/thread_tools.c b/misc/thread_tools.c
new file mode 100644
index 0000000000..4bcb952267
--- /dev/null
+++ b/misc/thread_tools.c
@@ -0,0 +1,62 @@
+/* Copyright (C) 2018 the mpv developers
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "thread_tools.h"
+
+uintptr_t mp_waiter_wait(struct mp_waiter *waiter)
+{
+ pthread_mutex_lock(&waiter->lock);
+ while (!waiter->done)
+ pthread_cond_wait(&waiter->wakeup, &waiter->lock);
+ pthread_mutex_unlock(&waiter->lock);
+
+ uintptr_t ret = waiter->value;
+
+ // We document that after mp_waiter_wait() the waiter object becomes
+ // invalid. (It strictly returns only after mp_waiter_wakeup() has returned,
+ // and the object is "single-shot".) So destroy it here.
+
+ // Normally, we expect that the system uses futexes, in which case the
+ // following functions will do nearly nothing. This is true for Windows
+ // and Linux. But some lesser OSes still might allocate kernel objects
+ // when initializing mutexes, so destroy them here.
+ pthread_mutex_destroy(&waiter->lock);
+ pthread_cond_destroy(&waiter->wakeup);
+
+ memset(waiter, 0xCA, sizeof(*waiter)); // for debugging
+
+ return ret;
+}
+
+void mp_waiter_wakeup(struct mp_waiter *waiter, uintptr_t value)
+{
+ pthread_mutex_lock(&waiter->lock);
+ assert(!waiter->done);
+ waiter->done = true;
+ waiter->value = value;
+ pthread_cond_signal(&waiter->wakeup);
+ pthread_mutex_unlock(&waiter->lock);
+}
+
+bool mp_waiter_poll(struct mp_waiter *waiter)
+{
+ pthread_mutex_lock(&waiter->lock);
+ bool r = waiter->done;
+ pthread_mutex_unlock(&waiter->lock);
+ return r;
+}
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 <stdint.h>
+#include <stdbool.h>
+#include <pthread.h>
+
+// 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);