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 | |
52 | typedef 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 */ |
61 | typedef 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 |
66 | typedef CONDITION_VARIABLE pthread_cond_t; |
67 | #else |
68 | typedef 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 | |
78 | static 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 | |
85 | static 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 | |
100 | static 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 | |
115 | static inline int pthread_mutex_init(pthread_mutex_t *m, void* attr) |
116 | { |
117 | InitializeCriticalSection(m); |
118 | return 0; |
119 | } |
120 | static inline int pthread_mutex_destroy(pthread_mutex_t *m) |
121 | { |
122 | DeleteCriticalSection(m); |
123 | return 0; |
124 | } |
125 | static inline int pthread_mutex_lock(pthread_mutex_t *m) |
126 | { |
127 | EnterCriticalSection(m); |
128 | return 0; |
129 | } |
130 | static inline int pthread_mutex_unlock(pthread_mutex_t *m) |
131 | { |
132 | LeaveCriticalSection(m); |
133 | return 0; |
134 | } |
135 | |
136 | #if _WIN32_WINNT >= 0x0600 |
137 | typedef INIT_ONCE pthread_once_t; |
138 | #define PTHREAD_ONCE_INIT INIT_ONCE_STATIC_INIT |
139 | |
140 | static 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 | |
150 | static 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 */ |
157 | static inline int pthread_cond_destroy(pthread_cond_t *cond) |
158 | { |
159 | return 0; |
160 | } |
161 | |
162 | static inline int pthread_cond_broadcast(pthread_cond_t *cond) |
163 | { |
164 | WakeAllConditionVariable(cond); |
165 | return 0; |
166 | } |
167 | |
168 | static inline int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) |
169 | { |
170 | SleepConditionVariableCS(cond, mutex, INFINITE); |
171 | return 0; |
172 | } |
173 | |
174 | static 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 */ |
183 | static LONG w32thread_init_state = 0; |
184 | static 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 | |
189 | typedef 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 */ |
197 | static BOOL (WINAPI *initonce_begin)(pthread_once_t *lpInitOnce, DWORD dwFlags, BOOL *fPending, void **lpContext); |
198 | static BOOL (WINAPI *initonce_complete)(pthread_once_t *lpInitOnce, DWORD dwFlags, void *lpContext); |
199 | |
200 | /* pre-Windows 6.0 compat using a spin-lock */ |
201 | static 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 | |
224 | static 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 | |
245 | typedef 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 */ |
255 | static void (WINAPI *cond_broadcast)(pthread_cond_t *cond); |
256 | static void (WINAPI *cond_init)(pthread_cond_t *cond); |
257 | static void (WINAPI *cond_signal)(pthread_cond_t *cond); |
258 | static BOOL (WINAPI *cond_wait)(pthread_cond_t *cond, pthread_mutex_t *mutex, |
259 | DWORD milliseconds); |
260 | |
261 | static 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 | |
289 | static 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 | |
306 | static 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 | |
338 | static 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 | |
370 | static 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 | |
397 | static 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 |