summaryrefslogtreecommitdiff
path: root/compat/w32pthreads.h (plain)
blob: 4ac2a995b84b300d57b811ba3d3c3c4980b6a2ae
1/*
2 * Copyright (C) 2010-2011 x264 project
3 *
4 * Authors: Steven Walters <kemuri9@gmail.com>
5 * Pegasys Inc. <http://www.pegasys-inc.com>
6 *
7 * This file is part of FFmpeg.
8 *
9 * FFmpeg is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * FFmpeg is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with FFmpeg; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24/**
25 * @file
26 * w32threads to pthreads wrapper
27 */
28
29#ifndef COMPAT_W32PTHREADS_H
30#define COMPAT_W32PTHREADS_H
31
32/* Build up a pthread-like API using underlying Windows API. Have only static
33 * methods so as to not conflict with a potentially linked in pthread-win32
34 * library.
35 * As most functions here are used without checking return values,
36 * only implement return values as necessary. */
37
38#define WIN32_LEAN_AND_MEAN
39#include <windows.h>
40#include <process.h>
41
42#if _WIN32_WINNT < 0x0600 && defined(__MINGW32__)
43#undef MemoryBarrier
44#define MemoryBarrier __sync_synchronize
45#endif
46
47#include "libavutil/attributes.h"
48#include "libavutil/common.h"
49#include "libavutil/internal.h"
50#include "libavutil/mem.h"
51
52typedef struct pthread_t {
53 void *handle;
54 void *(*func)(void* arg);
55 void *arg;
56 void *ret;
57} pthread_t;
58
59/* the conditional variable api for windows 6.0+ uses critical sections and
60 * not mutexes */
61typedef CRITICAL_SECTION pthread_mutex_t;
62
63/* This is the CONDITION_VARIABLE typedef for using Windows' native
64 * conditional variables on kernels 6.0+. */
65#if HAVE_CONDITION_VARIABLE_PTR
66typedef CONDITION_VARIABLE pthread_cond_t;
67#else
68typedef struct pthread_cond_t {
69 void *Ptr;
70} pthread_cond_t;
71#endif
72
73#if _WIN32_WINNT >= 0x0600
74#define InitializeCriticalSection(x) InitializeCriticalSectionEx(x, 0, 0)
75#define WaitForSingleObject(a, b) WaitForSingleObjectEx(a, b, FALSE)
76#endif
77
78static av_unused unsigned __stdcall attribute_align_arg win32thread_worker(void *arg)
79{
80 pthread_t *h = arg;
81 h->ret = h->func(h->arg);
82 return 0;
83}
84
85static av_unused int pthread_create(pthread_t *thread, const void *unused_attr,
86 void *(*start_routine)(void*), void *arg)
87{
88 thread->func = start_routine;
89 thread->arg = arg;
90#if HAVE_WINRT
91 thread->handle = (void*)CreateThread(NULL, 0, win32thread_worker, thread,
92 0, NULL);
93#else
94 thread->handle = (void*)_beginthreadex(NULL, 0, win32thread_worker, thread,
95 0, NULL);
96#endif
97 return !thread->handle;
98}
99
100static av_unused int pthread_join(pthread_t thread, void **value_ptr)
101{
102 DWORD ret = WaitForSingleObject(thread.handle, INFINITE);
103 if (ret != WAIT_OBJECT_0) {
104 if (ret == WAIT_ABANDONED)
105 return EINVAL;
106 else
107 return EDEADLK;
108 }
109 if (value_ptr)
110 *value_ptr = thread.ret;
111 CloseHandle(thread.handle);
112 return 0;
113}
114
115static inline int pthread_mutex_init(pthread_mutex_t *m, void* attr)
116{
117 InitializeCriticalSection(m);
118 return 0;
119}
120static inline int pthread_mutex_destroy(pthread_mutex_t *m)
121{
122 DeleteCriticalSection(m);
123 return 0;
124}
125static inline int pthread_mutex_lock(pthread_mutex_t *m)
126{
127 EnterCriticalSection(m);
128 return 0;
129}
130static inline int pthread_mutex_unlock(pthread_mutex_t *m)
131{
132 LeaveCriticalSection(m);
133 return 0;
134}
135
136#if _WIN32_WINNT >= 0x0600
137typedef INIT_ONCE pthread_once_t;
138#define PTHREAD_ONCE_INIT INIT_ONCE_STATIC_INIT
139
140static av_unused int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
141{
142 BOOL pending = FALSE;
143 InitOnceBeginInitialize(once_control, 0, &pending, NULL);
144 if (pending)
145 init_routine();
146 InitOnceComplete(once_control, 0, NULL);
147 return 0;
148}
149
150static inline int pthread_cond_init(pthread_cond_t *cond, const void *unused_attr)
151{
152 InitializeConditionVariable(cond);
153 return 0;
154}
155
156/* native condition variables do not destroy */
157static inline int pthread_cond_destroy(pthread_cond_t *cond)
158{
159 return 0;
160}
161
162static inline int pthread_cond_broadcast(pthread_cond_t *cond)
163{
164 WakeAllConditionVariable(cond);
165 return 0;
166}
167
168static inline int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
169{
170 SleepConditionVariableCS(cond, mutex, INFINITE);
171 return 0;
172}
173
174static inline int pthread_cond_signal(pthread_cond_t *cond)
175{
176 WakeConditionVariable(cond);
177 return 0;
178}
179
180#else // _WIN32_WINNT < 0x0600
181
182/* atomic init state of dynamically loaded functions */
183static LONG w32thread_init_state = 0;
184static av_unused void w32thread_init(void);
185
186/* for pre-Windows 6.0 platforms, define INIT_ONCE struct,
187 * compatible to the one used in the native API */
188
189typedef union pthread_once_t {
190 void * Ptr; ///< For the Windows 6.0+ native functions
191 LONG state; ///< For the pre-Windows 6.0 compat code
192} pthread_once_t;
193
194#define PTHREAD_ONCE_INIT {0}
195
196/* function pointers to init once API on windows 6.0+ kernels */
197static BOOL (WINAPI *initonce_begin)(pthread_once_t *lpInitOnce, DWORD dwFlags, BOOL *fPending, void **lpContext);
198static BOOL (WINAPI *initonce_complete)(pthread_once_t *lpInitOnce, DWORD dwFlags, void *lpContext);
199
200/* pre-Windows 6.0 compat using a spin-lock */
201static inline void w32thread_once_fallback(LONG volatile *state, void (*init_routine)(void))
202{
203 switch (InterlockedCompareExchange(state, 1, 0)) {
204 /* Initial run */
205 case 0:
206 init_routine();
207 InterlockedExchange(state, 2);
208 break;
209 /* Another thread is running init */
210 case 1:
211 while (1) {
212 MemoryBarrier();
213 if (*state == 2)
214 break;
215 Sleep(0);
216 }
217 break;
218 /* Initialization complete */
219 case 2:
220 break;
221 }
222}
223
224static av_unused int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
225{
226 w32thread_once_fallback(&w32thread_init_state, w32thread_init);
227
228 /* Use native functions on Windows 6.0+ */
229 if (initonce_begin && initonce_complete) {
230 BOOL pending = FALSE;
231 initonce_begin(once_control, 0, &pending, NULL);
232 if (pending)
233 init_routine();
234 initonce_complete(once_control, 0, NULL);
235 return 0;
236 }
237
238 w32thread_once_fallback(&once_control->state, init_routine);
239 return 0;
240}
241
242/* for pre-Windows 6.0 platforms we need to define and use our own condition
243 * variable and api */
244
245typedef struct win32_cond_t {
246 pthread_mutex_t mtx_broadcast;
247 pthread_mutex_t mtx_waiter_count;
248 volatile int waiter_count;
249 HANDLE semaphore;
250 HANDLE waiters_done;
251 volatile int is_broadcast;
252} win32_cond_t;
253
254/* function pointers to conditional variable API on windows 6.0+ kernels */
255static void (WINAPI *cond_broadcast)(pthread_cond_t *cond);
256static void (WINAPI *cond_init)(pthread_cond_t *cond);
257static void (WINAPI *cond_signal)(pthread_cond_t *cond);
258static BOOL (WINAPI *cond_wait)(pthread_cond_t *cond, pthread_mutex_t *mutex,
259 DWORD milliseconds);
260
261static av_unused int pthread_cond_init(pthread_cond_t *cond, const void *unused_attr)
262{
263 win32_cond_t *win32_cond = NULL;
264
265 w32thread_once_fallback(&w32thread_init_state, w32thread_init);
266
267 if (cond_init) {
268 cond_init(cond);
269 return 0;
270 }
271
272 /* non native condition variables */
273 win32_cond = av_mallocz(sizeof(win32_cond_t));
274 if (!win32_cond)
275 return ENOMEM;
276 cond->Ptr = win32_cond;
277 win32_cond->semaphore = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
278 if (!win32_cond->semaphore)
279 return ENOMEM;
280 win32_cond->waiters_done = CreateEvent(NULL, TRUE, FALSE, NULL);
281 if (!win32_cond->waiters_done)
282 return ENOMEM;
283
284 pthread_mutex_init(&win32_cond->mtx_waiter_count, NULL);
285 pthread_mutex_init(&win32_cond->mtx_broadcast, NULL);
286 return 0;
287}
288
289static av_unused int pthread_cond_destroy(pthread_cond_t *cond)
290{
291 win32_cond_t *win32_cond = cond->Ptr;
292 /* native condition variables do not destroy */
293 if (cond_init)
294 return 0;
295
296 /* non native condition variables */
297 CloseHandle(win32_cond->semaphore);
298 CloseHandle(win32_cond->waiters_done);
299 pthread_mutex_destroy(&win32_cond->mtx_waiter_count);
300 pthread_mutex_destroy(&win32_cond->mtx_broadcast);
301 av_freep(&win32_cond);
302 cond->Ptr = NULL;
303 return 0;
304}
305
306static av_unused int pthread_cond_broadcast(pthread_cond_t *cond)
307{
308 win32_cond_t *win32_cond = cond->Ptr;
309 int have_waiter;
310
311 if (cond_broadcast) {
312 cond_broadcast(cond);
313 return 0;
314 }
315
316 /* non native condition variables */
317 pthread_mutex_lock(&win32_cond->mtx_broadcast);
318 pthread_mutex_lock(&win32_cond->mtx_waiter_count);
319 have_waiter = 0;
320
321 if (win32_cond->waiter_count) {
322 win32_cond->is_broadcast = 1;
323 have_waiter = 1;
324 }
325
326 if (have_waiter) {
327 ReleaseSemaphore(win32_cond->semaphore, win32_cond->waiter_count, NULL);
328 pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
329 WaitForSingleObject(win32_cond->waiters_done, INFINITE);
330 ResetEvent(win32_cond->waiters_done);
331 win32_cond->is_broadcast = 0;
332 } else
333 pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
334 pthread_mutex_unlock(&win32_cond->mtx_broadcast);
335 return 0;
336}
337
338static av_unused int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
339{
340 win32_cond_t *win32_cond = cond->Ptr;
341 int last_waiter;
342 if (cond_wait) {
343 cond_wait(cond, mutex, INFINITE);
344 return 0;
345 }
346
347 /* non native condition variables */
348 pthread_mutex_lock(&win32_cond->mtx_broadcast);
349 pthread_mutex_lock(&win32_cond->mtx_waiter_count);
350 win32_cond->waiter_count++;
351 pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
352 pthread_mutex_unlock(&win32_cond->mtx_broadcast);
353
354 // unlock the external mutex
355 pthread_mutex_unlock(mutex);
356 WaitForSingleObject(win32_cond->semaphore, INFINITE);
357
358 pthread_mutex_lock(&win32_cond->mtx_waiter_count);
359 win32_cond->waiter_count--;
360 last_waiter = !win32_cond->waiter_count || !win32_cond->is_broadcast;
361 pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
362
363 if (last_waiter)
364 SetEvent(win32_cond->waiters_done);
365
366 // lock the external mutex
367 return pthread_mutex_lock(mutex);
368}
369
370static av_unused int pthread_cond_signal(pthread_cond_t *cond)
371{
372 win32_cond_t *win32_cond = cond->Ptr;
373 int have_waiter;
374 if (cond_signal) {
375 cond_signal(cond);
376 return 0;
377 }
378
379 pthread_mutex_lock(&win32_cond->mtx_broadcast);
380
381 /* non-native condition variables */
382 pthread_mutex_lock(&win32_cond->mtx_waiter_count);
383 have_waiter = win32_cond->waiter_count;
384 pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
385
386 if (have_waiter) {
387 ReleaseSemaphore(win32_cond->semaphore, 1, NULL);
388 WaitForSingleObject(win32_cond->waiters_done, INFINITE);
389 ResetEvent(win32_cond->waiters_done);
390 }
391
392 pthread_mutex_unlock(&win32_cond->mtx_broadcast);
393 return 0;
394}
395#endif
396
397static av_unused void w32thread_init(void)
398{
399#if _WIN32_WINNT < 0x0600
400 HANDLE kernel_dll = GetModuleHandle(TEXT("kernel32.dll"));
401 /* if one is available, then they should all be available */
402 cond_init =
403 (void*)GetProcAddress(kernel_dll, "InitializeConditionVariable");
404 cond_broadcast =
405 (void*)GetProcAddress(kernel_dll, "WakeAllConditionVariable");
406 cond_signal =
407 (void*)GetProcAddress(kernel_dll, "WakeConditionVariable");
408 cond_wait =
409 (void*)GetProcAddress(kernel_dll, "SleepConditionVariableCS");
410 initonce_begin =
411 (void*)GetProcAddress(kernel_dll, "InitOnceBeginInitialize");
412 initonce_complete =
413 (void*)GetProcAddress(kernel_dll, "InitOnceComplete");
414#endif
415
416}
417
418#endif /* COMPAT_W32PTHREADS_H */
419