mirror of https://github.com/mpv-player/mpv
Add Plan 9-style barriers
Plan 9 has a very interesting synchronization mechanism, the rendezvous() call. A good property of this is that you don't need to explicitly initialize and destroy a barrier object, unlike as with e.g. POSIX barriers (which are mandatory to begin with). Upon "meeting", they can exchange a value. This mechanism will be nice to synchronize certain stages of initialization between threads in the following commit. Unlike Plan 9 rendezvous(), this is not implemented with a hashtable, because that would require additional effort (especially if you want to make it actually scele). Unlike the Plan 9 variant, we use intptr_t instead of void* as type for the value, because I expect that we will be mostly passing a status code as value and not a pointer. Converting an integer to void* requires two cast (because the integer needs to be intptr_t), the other way around it's only one cast. We don't particularly care about performance in this case either. It's simply not important for our use-case. So a simple linked list is used for waiters, and on wakeup, all waiters are temporarily woken up.
This commit is contained in:
parent
8ed6d298c5
commit
559fe1daac
|
@ -0,0 +1,54 @@
|
|||
#include <pthread.h>
|
||||
|
||||
#include "rendezvous.h"
|
||||
|
||||
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t wakeup = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
static struct waiter *waiters;
|
||||
|
||||
struct waiter {
|
||||
void *tag;
|
||||
struct waiter *next;
|
||||
intptr_t *value;
|
||||
};
|
||||
|
||||
/* A barrier for 2 threads, which can exchange a value when they meet.
|
||||
* The first thread to call this function will block. As soon as two threads
|
||||
* are calling this function with the same tag value, they will unblock, and
|
||||
* on each thread the call return the value parameter of the _other_ thread.
|
||||
*
|
||||
* tag is an arbitrary value, but it must be an unique pointer. If there are
|
||||
* more than 2 threads using the same tag, things won't work. Typically, it
|
||||
* will have to point to a memory allocation or to the stack, while pointing
|
||||
* it to static data is always a bug.
|
||||
*
|
||||
* This shouldn't be used for performance critical code (uses a linked list
|
||||
* of _all_ waiters in the process, and temporarily wakes up _all_ waiters on
|
||||
* each second call).
|
||||
*
|
||||
* This is inspired by: http://9atom.org/magic/man2html/2/rendezvous */
|
||||
intptr_t mp_rendezvous(void *tag, intptr_t value)
|
||||
{
|
||||
struct waiter wait = { .tag = tag, .value = &value };
|
||||
pthread_mutex_lock(&lock);
|
||||
struct waiter **prev = &waiters;
|
||||
while (*prev) {
|
||||
if ((*prev)->tag == tag) {
|
||||
intptr_t tmp = *(*prev)->value;
|
||||
*(*prev)->value = value;
|
||||
value = tmp;
|
||||
(*prev)->value = NULL; // signals completion
|
||||
*prev = (*prev)->next; // unlink
|
||||
pthread_cond_broadcast(&wakeup);
|
||||
goto done;
|
||||
}
|
||||
prev = &(*prev)->next;
|
||||
}
|
||||
*prev = &wait;
|
||||
while (wait.value)
|
||||
pthread_cond_wait(&wakeup, &lock);
|
||||
done:
|
||||
pthread_mutex_unlock(&lock);
|
||||
return value;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef MP_RENDEZVOUS_H_
|
||||
#define MP_RENDEZVOUS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
intptr_t mp_rendezvous(void *tag, intptr_t value);
|
||||
|
||||
#endif
|
|
@ -177,6 +177,7 @@ SOURCES = audio/audio.c \
|
|||
input/keycodes.c \
|
||||
misc/charset_conv.c \
|
||||
misc/dispatch.c \
|
||||
misc/rendezvous.c \
|
||||
misc/ring.c \
|
||||
options/m_config.c \
|
||||
options/m_option.c \
|
||||
|
|
|
@ -201,6 +201,7 @@ def build(ctx):
|
|||
( "misc/charset_conv.c" ),
|
||||
( "misc/dispatch.c" ),
|
||||
( "misc/ring.c" ),
|
||||
( "misc/rendezvous.c" ),
|
||||
|
||||
## Options
|
||||
( "options/m_config.c" ),
|
||||
|
|
Loading…
Reference in New Issue