You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

402 lines
12KB

  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 Libav.
  8. *
  9. * Libav 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. * Libav 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 Libav; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. */
  23. /**
  24. * @file
  25. * w32threads to pthreads wrapper
  26. */
  27. #ifndef LIBAV_COMPAT_W32PTHREADS_H
  28. #define LIBAV_COMPAT_W32PTHREADS_H
  29. /* Build up a pthread-like API using underlying Windows API. Have only static
  30. * methods so as to not conflict with a potentially linked in pthread-win32
  31. * library.
  32. * As most functions here are used without checking return values,
  33. * only implement return values as necessary. */
  34. #define WIN32_LEAN_AND_MEAN
  35. #include <windows.h>
  36. #include <process.h>
  37. #if _WIN32_WINNT < 0x0600 && defined(__MINGW32__)
  38. #undef MemoryBarrier
  39. #define MemoryBarrier __sync_synchronize
  40. #endif
  41. #include "libavutil/attributes.h"
  42. #include "libavutil/internal.h"
  43. #include "libavutil/mem.h"
  44. typedef struct pthread_t {
  45. void *handle;
  46. void *(*func)(void* arg);
  47. void *arg;
  48. void *ret;
  49. } pthread_t;
  50. /* the conditional variable api for windows 6.0+ uses critical sections and
  51. * not mutexes */
  52. typedef CRITICAL_SECTION pthread_mutex_t;
  53. /* This is the CONDITION_VARIABLE typedef for using Windows' native
  54. * conditional variables on kernels 6.0+. */
  55. #if HAVE_CONDITION_VARIABLE_PTR
  56. typedef CONDITION_VARIABLE pthread_cond_t;
  57. #else
  58. typedef struct pthread_cond_t {
  59. void *Ptr;
  60. } pthread_cond_t;
  61. #endif
  62. #if _WIN32_WINNT >= 0x0600
  63. #define InitializeCriticalSection(x) InitializeCriticalSectionEx(x, 0, 0)
  64. #define WaitForSingleObject(a, b) WaitForSingleObjectEx(a, b, FALSE)
  65. #endif
  66. static av_unused unsigned __stdcall attribute_align_arg win32thread_worker(void *arg)
  67. {
  68. pthread_t *h = arg;
  69. h->ret = h->func(h->arg);
  70. return 0;
  71. }
  72. static av_unused int pthread_create(pthread_t *thread, const void *unused_attr,
  73. void *(*start_routine)(void*), void *arg)
  74. {
  75. thread->func = start_routine;
  76. thread->arg = arg;
  77. thread->handle = (void*)_beginthreadex(NULL, 0, win32thread_worker, thread,
  78. 0, NULL);
  79. return !thread->handle;
  80. }
  81. static av_unused void pthread_join(pthread_t thread, void **value_ptr)
  82. {
  83. DWORD ret = WaitForSingleObject(thread.handle, INFINITE);
  84. if (ret != WAIT_OBJECT_0)
  85. return;
  86. if (value_ptr)
  87. *value_ptr = thread.ret;
  88. CloseHandle(thread.handle);
  89. }
  90. static inline int pthread_mutex_init(pthread_mutex_t *m, void* attr)
  91. {
  92. InitializeCriticalSection(m);
  93. return 0;
  94. }
  95. static inline int pthread_mutex_destroy(pthread_mutex_t *m)
  96. {
  97. DeleteCriticalSection(m);
  98. return 0;
  99. }
  100. static inline int pthread_mutex_lock(pthread_mutex_t *m)
  101. {
  102. EnterCriticalSection(m);
  103. return 0;
  104. }
  105. static inline int pthread_mutex_unlock(pthread_mutex_t *m)
  106. {
  107. LeaveCriticalSection(m);
  108. return 0;
  109. }
  110. #if _WIN32_WINNT >= 0x0600
  111. typedef INIT_ONCE pthread_once_t;
  112. #define PTHREAD_ONCE_INIT INIT_ONCE_STATIC_INIT
  113. static av_unused int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
  114. {
  115. BOOL pending = FALSE;
  116. InitOnceBeginInitialize(once_control, 0, &pending, NULL);
  117. if (pending)
  118. init_routine();
  119. InitOnceComplete(once_control, 0, NULL);
  120. return 0;
  121. }
  122. static inline void pthread_cond_init(pthread_cond_t *cond, const void *unused_attr)
  123. {
  124. InitializeConditionVariable(cond);
  125. }
  126. /* native condition variables do not destroy */
  127. static inline void pthread_cond_destroy(pthread_cond_t *cond)
  128. {
  129. return;
  130. }
  131. static inline void pthread_cond_broadcast(pthread_cond_t *cond)
  132. {
  133. WakeAllConditionVariable(cond);
  134. }
  135. static inline int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
  136. {
  137. SleepConditionVariableCS(cond, mutex, INFINITE);
  138. return 0;
  139. }
  140. static inline void pthread_cond_signal(pthread_cond_t *cond)
  141. {
  142. WakeConditionVariable(cond);
  143. }
  144. #else // _WIN32_WINNT < 0x0600
  145. /* atomic init state of dynamically loaded functions */
  146. static LONG w32thread_init_state = 0;
  147. static av_unused void w32thread_init(void);
  148. /* for pre-Windows 6.0 platforms, define INIT_ONCE struct,
  149. * compatible to the one used in the native API */
  150. typedef union pthread_once_t {
  151. void * Ptr; ///< For the Windows 6.0+ native functions
  152. LONG state; ///< For the pre-Windows 6.0 compat code
  153. } pthread_once_t;
  154. #define PTHREAD_ONCE_INIT {0}
  155. /* function pointers to init once API on windows 6.0+ kernels */
  156. static BOOL (WINAPI *initonce_begin)(pthread_once_t *lpInitOnce, DWORD dwFlags, BOOL *fPending, void **lpContext);
  157. static BOOL (WINAPI *initonce_complete)(pthread_once_t *lpInitOnce, DWORD dwFlags, void *lpContext);
  158. /* pre-Windows 6.0 compat using a spin-lock */
  159. static inline void w32thread_once_fallback(LONG volatile *state, void (*init_routine)(void))
  160. {
  161. switch (InterlockedCompareExchange(state, 1, 0)) {
  162. /* Initial run */
  163. case 0:
  164. init_routine();
  165. InterlockedExchange(state, 2);
  166. break;
  167. /* Another thread is running init */
  168. case 1:
  169. while (1) {
  170. MemoryBarrier();
  171. if (*state == 2)
  172. break;
  173. Sleep(0);
  174. }
  175. break;
  176. /* Initialization complete */
  177. case 2:
  178. break;
  179. }
  180. }
  181. static av_unused int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
  182. {
  183. w32thread_once_fallback(&w32thread_init_state, w32thread_init);
  184. /* Use native functions on Windows 6.0+ */
  185. if (initonce_begin && initonce_complete) {
  186. BOOL pending = FALSE;
  187. initonce_begin(once_control, 0, &pending, NULL);
  188. if (pending)
  189. init_routine();
  190. initonce_complete(once_control, 0, NULL);
  191. return 0;
  192. }
  193. w32thread_once_fallback(&once_control->state, init_routine);
  194. return 0;
  195. }
  196. /* for pre-Windows 6.0 platforms we need to define and use our own condition
  197. * variable and api */
  198. typedef struct win32_cond_t {
  199. pthread_mutex_t mtx_broadcast;
  200. pthread_mutex_t mtx_waiter_count;
  201. volatile int waiter_count;
  202. HANDLE semaphore;
  203. HANDLE waiters_done;
  204. volatile int is_broadcast;
  205. } win32_cond_t;
  206. /* function pointers to conditional variable API on windows 6.0+ kernels */
  207. static void (WINAPI *cond_broadcast)(pthread_cond_t *cond);
  208. static void (WINAPI *cond_init)(pthread_cond_t *cond);
  209. static void (WINAPI *cond_signal)(pthread_cond_t *cond);
  210. static BOOL (WINAPI *cond_wait)(pthread_cond_t *cond, pthread_mutex_t *mutex,
  211. DWORD milliseconds);
  212. static av_unused void pthread_cond_init(pthread_cond_t *cond, const void *unused_attr)
  213. {
  214. win32_cond_t *win32_cond = NULL;
  215. w32thread_once_fallback(&w32thread_init_state, w32thread_init);
  216. if (cond_init) {
  217. cond_init(cond);
  218. return;
  219. }
  220. /* non native condition variables */
  221. win32_cond = av_mallocz(sizeof(win32_cond_t));
  222. if (!win32_cond)
  223. return;
  224. cond->Ptr = win32_cond;
  225. win32_cond->semaphore = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
  226. if (!win32_cond->semaphore)
  227. return;
  228. win32_cond->waiters_done = CreateEvent(NULL, TRUE, FALSE, NULL);
  229. if (!win32_cond->waiters_done)
  230. return;
  231. pthread_mutex_init(&win32_cond->mtx_waiter_count, NULL);
  232. pthread_mutex_init(&win32_cond->mtx_broadcast, NULL);
  233. }
  234. static av_unused void pthread_cond_destroy(pthread_cond_t *cond)
  235. {
  236. win32_cond_t *win32_cond = cond->Ptr;
  237. /* native condition variables do not destroy */
  238. if (cond_init)
  239. return;
  240. /* non native condition variables */
  241. CloseHandle(win32_cond->semaphore);
  242. CloseHandle(win32_cond->waiters_done);
  243. pthread_mutex_destroy(&win32_cond->mtx_waiter_count);
  244. pthread_mutex_destroy(&win32_cond->mtx_broadcast);
  245. av_freep(&win32_cond);
  246. cond->Ptr = NULL;
  247. }
  248. static av_unused void pthread_cond_broadcast(pthread_cond_t *cond)
  249. {
  250. win32_cond_t *win32_cond = cond->Ptr;
  251. int have_waiter;
  252. if (cond_broadcast) {
  253. cond_broadcast(cond);
  254. return;
  255. }
  256. /* non native condition variables */
  257. pthread_mutex_lock(&win32_cond->mtx_broadcast);
  258. pthread_mutex_lock(&win32_cond->mtx_waiter_count);
  259. have_waiter = 0;
  260. if (win32_cond->waiter_count) {
  261. win32_cond->is_broadcast = 1;
  262. have_waiter = 1;
  263. }
  264. if (have_waiter) {
  265. ReleaseSemaphore(win32_cond->semaphore, win32_cond->waiter_count, NULL);
  266. pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
  267. WaitForSingleObject(win32_cond->waiters_done, INFINITE);
  268. ResetEvent(win32_cond->waiters_done);
  269. win32_cond->is_broadcast = 0;
  270. } else
  271. pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
  272. pthread_mutex_unlock(&win32_cond->mtx_broadcast);
  273. }
  274. static av_unused int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
  275. {
  276. win32_cond_t *win32_cond = cond->Ptr;
  277. int last_waiter;
  278. if (cond_wait) {
  279. cond_wait(cond, mutex, INFINITE);
  280. return 0;
  281. }
  282. /* non native condition variables */
  283. pthread_mutex_lock(&win32_cond->mtx_broadcast);
  284. pthread_mutex_lock(&win32_cond->mtx_waiter_count);
  285. win32_cond->waiter_count++;
  286. pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
  287. pthread_mutex_unlock(&win32_cond->mtx_broadcast);
  288. // unlock the external mutex
  289. pthread_mutex_unlock(mutex);
  290. WaitForSingleObject(win32_cond->semaphore, INFINITE);
  291. pthread_mutex_lock(&win32_cond->mtx_waiter_count);
  292. win32_cond->waiter_count--;
  293. last_waiter = !win32_cond->waiter_count || !win32_cond->is_broadcast;
  294. pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
  295. if (last_waiter)
  296. SetEvent(win32_cond->waiters_done);
  297. // lock the external mutex
  298. return pthread_mutex_lock(mutex);
  299. }
  300. static av_unused void pthread_cond_signal(pthread_cond_t *cond)
  301. {
  302. win32_cond_t *win32_cond = cond->Ptr;
  303. int have_waiter;
  304. if (cond_signal) {
  305. cond_signal(cond);
  306. return;
  307. }
  308. pthread_mutex_lock(&win32_cond->mtx_broadcast);
  309. /* non-native condition variables */
  310. pthread_mutex_lock(&win32_cond->mtx_waiter_count);
  311. have_waiter = win32_cond->waiter_count;
  312. pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
  313. if (have_waiter) {
  314. ReleaseSemaphore(win32_cond->semaphore, 1, NULL);
  315. WaitForSingleObject(win32_cond->waiters_done, INFINITE);
  316. ResetEvent(win32_cond->waiters_done);
  317. }
  318. pthread_mutex_unlock(&win32_cond->mtx_broadcast);
  319. }
  320. #endif
  321. static av_unused void w32thread_init(void)
  322. {
  323. #if _WIN32_WINNT < 0x0600
  324. HANDLE kernel_dll = GetModuleHandle(TEXT("kernel32.dll"));
  325. /* if one is available, then they should all be available */
  326. cond_init = (void (WINAPI*)(pthread_cond_t *))
  327. GetProcAddress(kernel_dll, "InitializeConditionVariable");
  328. cond_broadcast = (void (WINAPI*)(pthread_cond_t *))
  329. GetProcAddress(kernel_dll, "WakeAllConditionVariable");
  330. cond_signal = (void (WINAPI*)(pthread_cond_t *))
  331. GetProcAddress(kernel_dll, "WakeConditionVariable");
  332. cond_wait = (BOOL (WINAPI*)(pthread_cond_t *, pthread_mutex_t *, DWORD))
  333. GetProcAddress(kernel_dll, "SleepConditionVariableCS");
  334. initonce_begin = (BOOL (WINAPI*)(pthread_once_t *, DWORD, BOOL *, void **))
  335. GetProcAddress(kernel_dll, "InitOnceBeginInitialize");
  336. initonce_complete = (BOOL (WINAPI*)(pthread_once_t *, DWORD, void *))
  337. GetProcAddress(kernel_dll, "InitOnceComplete");
  338. #endif
  339. }
  340. #endif /* LIBAV_COMPAT_W32PTHREADS_H */