/src/mpv/misc/thread_tools.c
Line | Count | Source |
1 | | /* Copyright (C) 2018 the mpv developers |
2 | | * |
3 | | * Permission to use, copy, modify, and/or distribute this software for any |
4 | | * purpose with or without fee is hereby granted, provided that the above |
5 | | * copyright notice and this permission notice appear in all copies. |
6 | | * |
7 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
8 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
9 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
10 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
11 | | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
12 | | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
13 | | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | | */ |
15 | | |
16 | | #include <assert.h> |
17 | | #include <errno.h> |
18 | | #include <stdatomic.h> |
19 | | #include <string.h> |
20 | | #include <sys/types.h> |
21 | | |
22 | | #ifdef _WIN32 |
23 | | #include <windows.h> |
24 | | #else |
25 | | #include <poll.h> |
26 | | #endif |
27 | | |
28 | | #include "common/common.h" |
29 | | #include "misc/linked_list.h" |
30 | | #include "osdep/io.h" |
31 | | #include "osdep/timer.h" |
32 | | |
33 | | #include "thread_tools.h" |
34 | | |
35 | | uintptr_t mp_waiter_wait(struct mp_waiter *waiter) |
36 | 284k | { |
37 | 284k | mp_mutex_lock(&waiter->lock); |
38 | 284k | while (!waiter->done) |
39 | 0 | mp_cond_wait(&waiter->wakeup, &waiter->lock); |
40 | 284k | mp_mutex_unlock(&waiter->lock); |
41 | | |
42 | 284k | uintptr_t ret = waiter->value; |
43 | | |
44 | | // We document that after mp_waiter_wait() the waiter object becomes |
45 | | // invalid. (It strictly returns only after mp_waiter_wakeup() has returned, |
46 | | // and the object is "single-shot".) So destroy it here. |
47 | | |
48 | | // Normally, we expect that the system uses futexes, in which case the |
49 | | // following functions will do nearly nothing. This is true for Windows |
50 | | // and Linux. But some lesser OSes still might allocate kernel objects |
51 | | // when initializing mutexes, so destroy them here. |
52 | 284k | mp_mutex_destroy(&waiter->lock); |
53 | 284k | mp_cond_destroy(&waiter->wakeup); |
54 | | |
55 | 284k | memset(waiter, 0xCA, sizeof(*waiter)); // for debugging |
56 | | |
57 | 284k | return ret; |
58 | 284k | } |
59 | | |
60 | | void mp_waiter_wakeup(struct mp_waiter *waiter, uintptr_t value) |
61 | 284k | { |
62 | 284k | mp_mutex_lock(&waiter->lock); |
63 | 284k | mp_assert(!waiter->done); |
64 | 284k | waiter->done = true; |
65 | 284k | waiter->value = value; |
66 | 284k | mp_cond_signal(&waiter->wakeup); |
67 | 284k | mp_mutex_unlock(&waiter->lock); |
68 | 284k | } |
69 | | |
70 | | bool mp_waiter_poll(struct mp_waiter *waiter) |
71 | 151k | { |
72 | 151k | mp_mutex_lock(&waiter->lock); |
73 | 151k | bool r = waiter->done; |
74 | 151k | mp_mutex_unlock(&waiter->lock); |
75 | 151k | return r; |
76 | 151k | } |
77 | | |
78 | | struct mp_cancel { |
79 | | mp_mutex lock; |
80 | | mp_cond wakeup; |
81 | | |
82 | | // Semaphore state and "mirrors". |
83 | | atomic_bool triggered; |
84 | | void (*cb)(void *ctx); |
85 | | void *cb_ctx; |
86 | | int wakeup_pipe[2]; |
87 | | void *win32_event; // actually HANDLE |
88 | | |
89 | | // Slave list. These are automatically notified as well. |
90 | | struct { |
91 | | struct mp_cancel *head, *tail; |
92 | | } slaves; |
93 | | |
94 | | // For slaves. Synchronization is managed by parent.lock! |
95 | | struct mp_cancel *parent; |
96 | | struct { |
97 | | struct mp_cancel *next, *prev; |
98 | | } siblings; |
99 | | }; |
100 | | |
101 | | static void cancel_destroy(void *p) |
102 | 385k | { |
103 | 385k | struct mp_cancel *c = p; |
104 | | |
105 | 385k | mp_assert(!c->slaves.head); // API user error |
106 | | |
107 | 385k | mp_cancel_set_parent(c, NULL); |
108 | | |
109 | 385k | if (c->wakeup_pipe[0] >= 0) { |
110 | 0 | close(c->wakeup_pipe[0]); |
111 | 0 | close(c->wakeup_pipe[1]); |
112 | 0 | } |
113 | | |
114 | | #ifdef _WIN32 |
115 | | if (c->win32_event) |
116 | | CloseHandle(c->win32_event); |
117 | | #endif |
118 | | |
119 | 385k | mp_mutex_destroy(&c->lock); |
120 | 385k | mp_cond_destroy(&c->wakeup); |
121 | 385k | } |
122 | | |
123 | | struct mp_cancel *mp_cancel_new(void *talloc_ctx) |
124 | 385k | { |
125 | 385k | struct mp_cancel *c = talloc_ptrtype(talloc_ctx, c); |
126 | 385k | talloc_set_destructor(c, cancel_destroy); |
127 | 385k | *c = (struct mp_cancel){ |
128 | 385k | .triggered = false, |
129 | 385k | .wakeup_pipe = {-1, -1}, |
130 | 385k | }; |
131 | 385k | mp_mutex_init(&c->lock); |
132 | 385k | mp_cond_init(&c->wakeup); |
133 | 385k | return c; |
134 | 385k | } |
135 | | |
136 | | static void trigger_locked(struct mp_cancel *c) |
137 | 609k | { |
138 | 609k | atomic_store(&c->triggered, true); |
139 | | |
140 | 609k | mp_cond_broadcast(&c->wakeup); // condition bound to c->triggered |
141 | | |
142 | 609k | if (c->cb) |
143 | 0 | c->cb(c->cb_ctx); |
144 | | |
145 | 610k | for (struct mp_cancel *sub = c->slaves.head; sub; sub = sub->siblings.next) |
146 | 485 | mp_cancel_trigger(sub); |
147 | | |
148 | 609k | if (c->wakeup_pipe[1] >= 0) |
149 | 0 | (void)write(c->wakeup_pipe[1], &(char){0}, 1); |
150 | | |
151 | | #ifdef _WIN32 |
152 | | if (c->win32_event) |
153 | | SetEvent(c->win32_event); |
154 | | #endif |
155 | 609k | } |
156 | | |
157 | | void mp_cancel_trigger(struct mp_cancel *c) |
158 | 609k | { |
159 | 609k | mp_mutex_lock(&c->lock); |
160 | 609k | trigger_locked(c); |
161 | 609k | mp_mutex_unlock(&c->lock); |
162 | 609k | } |
163 | | |
164 | | void mp_cancel_reset(struct mp_cancel *c) |
165 | 374k | { |
166 | 374k | mp_mutex_lock(&c->lock); |
167 | | |
168 | 374k | atomic_store(&c->triggered, false); |
169 | | |
170 | 374k | if (c->wakeup_pipe[0] >= 0) { |
171 | | // Flush it fully. |
172 | 0 | while (1) { |
173 | 0 | int r = read(c->wakeup_pipe[0], &(char[256]){0}, 256); |
174 | 0 | if (r <= 0 && !(r < 0 && errno == EINTR)) |
175 | 0 | break; |
176 | 0 | } |
177 | 0 | } |
178 | | |
179 | | #ifdef _WIN32 |
180 | | if (c->win32_event) |
181 | | ResetEvent(c->win32_event); |
182 | | #endif |
183 | | |
184 | 374k | mp_mutex_unlock(&c->lock); |
185 | 374k | } |
186 | | |
187 | | bool mp_cancel_test(struct mp_cancel *c) |
188 | 28.0M | { |
189 | 28.0M | return c ? atomic_load_explicit(&c->triggered, memory_order_relaxed) : false; |
190 | 28.0M | } |
191 | | |
192 | | bool mp_cancel_wait(struct mp_cancel *c, double timeout) |
193 | 0 | { |
194 | 0 | int64_t wait_until = mp_time_ns_add(mp_time_ns(), timeout); |
195 | 0 | mp_mutex_lock(&c->lock); |
196 | 0 | while (!mp_cancel_test(c)) { |
197 | 0 | if (mp_cond_timedwait_until(&c->wakeup, &c->lock, wait_until)) |
198 | 0 | break; |
199 | 0 | } |
200 | 0 | mp_mutex_unlock(&c->lock); |
201 | |
|
202 | 0 | return mp_cancel_test(c); |
203 | 0 | } |
204 | | |
205 | | // If a new notification mechanism was added, and the mp_cancel state was |
206 | | // already triggered, make sure the newly added mechanism is also triggered. |
207 | | static void retrigger_locked(struct mp_cancel *c) |
208 | 341k | { |
209 | 341k | if (mp_cancel_test(c)) |
210 | 54 | trigger_locked(c); |
211 | 341k | } |
212 | | |
213 | | void mp_cancel_set_cb(struct mp_cancel *c, void (*cb)(void *ctx), void *ctx) |
214 | 0 | { |
215 | 0 | mp_mutex_lock(&c->lock); |
216 | 0 | c->cb = cb; |
217 | 0 | c->cb_ctx = ctx; |
218 | 0 | retrigger_locked(c); |
219 | 0 | mp_mutex_unlock(&c->lock); |
220 | 0 | } |
221 | | |
222 | | void mp_cancel_set_parent(struct mp_cancel *slave, struct mp_cancel *parent) |
223 | 738k | { |
224 | | // We can access c->parent without synchronization, because: |
225 | | // - concurrent mp_cancel_set_parent() calls to slave are not allowed |
226 | | // - slave->parent needs to stay valid as long as the slave exists |
227 | 738k | if (slave->parent == parent) |
228 | 139k | return; |
229 | 599k | if (slave->parent) { |
230 | 341k | mp_mutex_lock(&slave->parent->lock); |
231 | 341k | LL_REMOVE(siblings, &slave->parent->slaves, slave); |
232 | 341k | mp_mutex_unlock(&slave->parent->lock); |
233 | 341k | } |
234 | 599k | slave->parent = parent; |
235 | 599k | if (slave->parent) { |
236 | 341k | mp_mutex_lock(&slave->parent->lock); |
237 | 341k | LL_APPEND(siblings, &slave->parent->slaves, slave); |
238 | 341k | retrigger_locked(slave->parent); |
239 | 341k | mp_mutex_unlock(&slave->parent->lock); |
240 | 341k | } |
241 | 599k | } |
242 | | |
243 | | int mp_cancel_get_fd(struct mp_cancel *c) |
244 | 0 | { |
245 | 0 | mp_mutex_lock(&c->lock); |
246 | 0 | if (c->wakeup_pipe[0] < 0) { |
247 | | #if defined(__GNUC__) && !defined(__clang__) |
248 | | # pragma GCC diagnostic push |
249 | | # pragma GCC diagnostic ignored "-Wstringop-overflow=" |
250 | | #endif |
251 | 0 | mp_make_wakeup_pipe(c->wakeup_pipe); |
252 | | #if defined(__GNUC__) && !defined(__clang__) |
253 | | # pragma GCC diagnostic pop |
254 | | #endif |
255 | 0 | retrigger_locked(c); |
256 | 0 | } |
257 | 0 | mp_mutex_unlock(&c->lock); |
258 | | |
259 | |
|
260 | 0 | return c->wakeup_pipe[0]; |
261 | 0 | } |
262 | | |
263 | | #ifdef _WIN32 |
264 | | void *mp_cancel_get_event(struct mp_cancel *c) |
265 | | { |
266 | | mp_mutex_lock(&c->lock); |
267 | | if (!c->win32_event) { |
268 | | c->win32_event = CreateEventW(NULL, TRUE, FALSE, NULL); |
269 | | retrigger_locked(c); |
270 | | } |
271 | | mp_mutex_unlock(&c->lock); |
272 | | |
273 | | return c->win32_event; |
274 | | } |
275 | | #endif |