/src/mpv/misc/rendezvous.c
Line | Count | Source (jump to first uncovered line) |
1 | | |
2 | | #include "rendezvous.h" |
3 | | |
4 | | #include "osdep/threads.h" |
5 | | |
6 | | static mp_static_mutex lock = MP_STATIC_MUTEX_INITIALIZER; |
7 | | static mp_cond wakeup = MP_STATIC_COND_INITIALIZER; |
8 | | |
9 | | static struct waiter *waiters; |
10 | | |
11 | | struct waiter { |
12 | | void *tag; |
13 | | struct waiter *next; |
14 | | intptr_t *value; |
15 | | }; |
16 | | |
17 | | /* A barrier for 2 threads, which can exchange a value when they meet. |
18 | | * The first thread to call this function will block. As soon as two threads |
19 | | * are calling this function with the same tag value, they will unblock, and |
20 | | * on each thread the call returns the value parameter of the _other_ thread. |
21 | | * |
22 | | * tag is an arbitrary value, but it must be an unique pointer. If there are |
23 | | * more than 2 threads using the same tag, things won't work. Typically, it |
24 | | * will have to point to a memory allocation or to the stack, while pointing |
25 | | * it to static data is always a bug. |
26 | | * |
27 | | * This shouldn't be used for performance critical code (uses a linked list |
28 | | * of _all_ waiters in the process, and temporarily wakes up _all_ waiters on |
29 | | * each second call). |
30 | | * |
31 | | * This is inspired by: https://man.cat-v.org/plan_9/2/rendezvous */ |
32 | | intptr_t mp_rendezvous(void *tag, intptr_t value) |
33 | 130k | { |
34 | 130k | struct waiter wait = { .tag = tag, .value = &value }; |
35 | 130k | mp_mutex_lock(&lock); |
36 | 130k | struct waiter **prev = &waiters; |
37 | 130k | while (*prev) { |
38 | 65.3k | if ((*prev)->tag == tag) { |
39 | 65.3k | intptr_t tmp = *(*prev)->value; |
40 | 65.3k | *(*prev)->value = value; |
41 | 65.3k | value = tmp; |
42 | 65.3k | (*prev)->value = NULL; // signals completion |
43 | 65.3k | *prev = (*prev)->next; // unlink |
44 | 65.3k | mp_cond_broadcast(&wakeup); |
45 | 65.3k | goto done; |
46 | 65.3k | } |
47 | 0 | prev = &(*prev)->next; |
48 | 0 | } |
49 | 65.3k | *prev = &wait; |
50 | 130k | while (wait.value) |
51 | 65.3k | mp_cond_wait(&wakeup, &lock); |
52 | 130k | done: |
53 | 130k | mp_mutex_unlock(&lock); |
54 | 130k | return value; |
55 | 65.3k | } |