win32thread.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /*****************************************************************************
  2. * win32thread.c: windows threading
  3. *****************************************************************************
  4. * Copyright (C) 2010-2018 x264 project
  5. *
  6. * Authors: Steven Walters <kemuri9@gmail.com>
  7. * Pegasys Inc. <http://www.pegasys-inc.com>
  8. * Henrik Gramner <henrik@gramner.com>
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 2 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, write to the Free Software
  22. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
  23. *
  24. * This program is also available under a commercial proprietary license.
  25. * For more information, contact us at licensing@x264.com.
  26. *****************************************************************************/
  27. /* Microsoft's way of supporting systems with >64 logical cpus can be found at
  28. * http://www.microsoft.com/whdc/system/Sysinternals/MoreThan64proc.mspx */
  29. /* Based on the agreed standing that x264 does not need to utilize >64 logical cpus,
  30. * this API does not detect nor utilize more than 64 cpus for systems that have them. */
  31. #include "base.h"
  32. #if HAVE_WINRT
  33. /* _beginthreadex() is technically the correct option, but it's only available for Desktop applications.
  34. * Using CreateThread() as an alternative works on Windows Store and Windows Phone 8.1+ as long as we're
  35. * using a dynamically linked MSVCRT which happens to be a requirement for WinRT applications anyway */
  36. #define _beginthreadex CreateThread
  37. #define InitializeCriticalSectionAndSpinCount(a, b) InitializeCriticalSectionEx(a, b, CRITICAL_SECTION_NO_DEBUG_INFO)
  38. #define WaitForSingleObject(a, b) WaitForSingleObjectEx(a, b, FALSE)
  39. #else
  40. #include <process.h>
  41. #endif
  42. /* number of times to spin a thread about to block on a locked mutex before retrying and sleeping if still locked */
  43. #define X264_SPIN_COUNT 0
  44. /* global mutex for replacing MUTEX_INITIALIZER instances */
  45. static x264_pthread_mutex_t static_mutex;
  46. /* _beginthreadex requires that the start routine is __stdcall */
  47. static unsigned __stdcall win32thread_worker( void *arg )
  48. {
  49. x264_pthread_t *h = arg;
  50. *h->p_ret = h->func( h->arg );
  51. return 0;
  52. }
  53. int x264_pthread_create( x264_pthread_t *thread, const x264_pthread_attr_t *attr,
  54. void *(*start_routine)( void* ), void *arg )
  55. {
  56. thread->func = start_routine;
  57. thread->arg = arg;
  58. thread->p_ret = &thread->ret;
  59. thread->ret = NULL;
  60. thread->handle = (void*)_beginthreadex( NULL, 0, win32thread_worker, thread, 0, NULL );
  61. return !thread->handle;
  62. }
  63. int x264_pthread_join( x264_pthread_t thread, void **value_ptr )
  64. {
  65. DWORD ret = WaitForSingleObject( thread.handle, INFINITE );
  66. if( ret != WAIT_OBJECT_0 )
  67. return -1;
  68. if( value_ptr )
  69. *value_ptr = *thread.p_ret;
  70. CloseHandle( thread.handle );
  71. return 0;
  72. }
  73. int x264_pthread_mutex_init( x264_pthread_mutex_t *mutex, const x264_pthread_mutexattr_t *attr )
  74. {
  75. return !InitializeCriticalSectionAndSpinCount( mutex, X264_SPIN_COUNT );
  76. }
  77. int x264_pthread_mutex_destroy( x264_pthread_mutex_t *mutex )
  78. {
  79. DeleteCriticalSection( mutex );
  80. return 0;
  81. }
  82. int x264_pthread_mutex_lock( x264_pthread_mutex_t *mutex )
  83. {
  84. static const x264_pthread_mutex_t init = X264_PTHREAD_MUTEX_INITIALIZER;
  85. if( !memcmp( mutex, &init, sizeof(x264_pthread_mutex_t) ) )
  86. {
  87. int ret = 0;
  88. EnterCriticalSection( &static_mutex );
  89. if( !memcmp( mutex, &init, sizeof(x264_pthread_mutex_t) ) )
  90. ret = x264_pthread_mutex_init( mutex, NULL );
  91. LeaveCriticalSection( &static_mutex );
  92. if( ret )
  93. return ret;
  94. }
  95. EnterCriticalSection( mutex );
  96. return 0;
  97. }
  98. int x264_pthread_mutex_unlock( x264_pthread_mutex_t *mutex )
  99. {
  100. LeaveCriticalSection( mutex );
  101. return 0;
  102. }
  103. void x264_win32_threading_destroy( void )
  104. {
  105. x264_pthread_mutex_destroy( &static_mutex );
  106. memset( &static_mutex, 0, sizeof(static_mutex) );
  107. }
  108. #if HAVE_WINRT
  109. int x264_pthread_cond_init( x264_pthread_cond_t *cond, const x264_pthread_condattr_t *attr )
  110. {
  111. InitializeConditionVariable( cond );
  112. return 0;
  113. }
  114. int x264_pthread_cond_destroy( x264_pthread_cond_t *cond )
  115. {
  116. return 0;
  117. }
  118. int x264_pthread_cond_broadcast( x264_pthread_cond_t *cond )
  119. {
  120. WakeAllConditionVariable( cond );
  121. return 0;
  122. }
  123. int x264_pthread_cond_signal( x264_pthread_cond_t *cond )
  124. {
  125. WakeConditionVariable( cond );
  126. return 0;
  127. }
  128. int x264_pthread_cond_wait( x264_pthread_cond_t *cond, x264_pthread_mutex_t *mutex )
  129. {
  130. return !SleepConditionVariableCS( cond, mutex, INFINITE );
  131. }
  132. int x264_win32_threading_init( void )
  133. {
  134. return x264_pthread_mutex_init( &static_mutex, NULL );
  135. }
  136. int x264_pthread_num_processors_np( void )
  137. {
  138. SYSTEM_INFO si;
  139. GetNativeSystemInfo(&si);
  140. return si.dwNumberOfProcessors;
  141. }
  142. #else
  143. static struct
  144. {
  145. /* function pointers to conditional variable API on windows 6.0+ kernels */
  146. void (WINAPI *cond_broadcast)( x264_pthread_cond_t *cond );
  147. void (WINAPI *cond_init)( x264_pthread_cond_t *cond );
  148. void (WINAPI *cond_signal)( x264_pthread_cond_t *cond );
  149. BOOL (WINAPI *cond_wait)( x264_pthread_cond_t *cond, x264_pthread_mutex_t *mutex, DWORD milliseconds );
  150. } thread_control;
  151. /* for pre-Windows 6.0 platforms we need to define and use our own condition variable and api */
  152. typedef struct
  153. {
  154. x264_pthread_mutex_t mtx_broadcast;
  155. x264_pthread_mutex_t mtx_waiter_count;
  156. volatile int waiter_count;
  157. HANDLE semaphore;
  158. HANDLE waiters_done;
  159. volatile int is_broadcast;
  160. } x264_win32_cond_t;
  161. int x264_pthread_cond_init( x264_pthread_cond_t *cond, const x264_pthread_condattr_t *attr )
  162. {
  163. if( thread_control.cond_init )
  164. {
  165. thread_control.cond_init( cond );
  166. return 0;
  167. }
  168. /* non native condition variables */
  169. x264_win32_cond_t *win32_cond = calloc( 1, sizeof(x264_win32_cond_t) );
  170. if( !win32_cond )
  171. return -1;
  172. cond->Ptr = win32_cond;
  173. win32_cond->semaphore = CreateSemaphoreW( NULL, 0, 0x7fffffff, NULL );
  174. if( !win32_cond->semaphore )
  175. return -1;
  176. if( x264_pthread_mutex_init( &win32_cond->mtx_waiter_count, NULL ) )
  177. return -1;
  178. if( x264_pthread_mutex_init( &win32_cond->mtx_broadcast, NULL ) )
  179. return -1;
  180. win32_cond->waiters_done = CreateEventW( NULL, FALSE, FALSE, NULL );
  181. if( !win32_cond->waiters_done )
  182. return -1;
  183. return 0;
  184. }
  185. int x264_pthread_cond_destroy( x264_pthread_cond_t *cond )
  186. {
  187. /* native condition variables do not destroy */
  188. if( thread_control.cond_init )
  189. return 0;
  190. /* non native condition variables */
  191. x264_win32_cond_t *win32_cond = cond->Ptr;
  192. CloseHandle( win32_cond->semaphore );
  193. CloseHandle( win32_cond->waiters_done );
  194. x264_pthread_mutex_destroy( &win32_cond->mtx_broadcast );
  195. x264_pthread_mutex_destroy( &win32_cond->mtx_waiter_count );
  196. free( win32_cond );
  197. return 0;
  198. }
  199. int x264_pthread_cond_broadcast( x264_pthread_cond_t *cond )
  200. {
  201. if( thread_control.cond_broadcast )
  202. {
  203. thread_control.cond_broadcast( cond );
  204. return 0;
  205. }
  206. /* non native condition variables */
  207. x264_win32_cond_t *win32_cond = cond->Ptr;
  208. x264_pthread_mutex_lock( &win32_cond->mtx_broadcast );
  209. x264_pthread_mutex_lock( &win32_cond->mtx_waiter_count );
  210. int have_waiter = 0;
  211. if( win32_cond->waiter_count )
  212. {
  213. win32_cond->is_broadcast = 1;
  214. have_waiter = 1;
  215. }
  216. if( have_waiter )
  217. {
  218. ReleaseSemaphore( win32_cond->semaphore, win32_cond->waiter_count, NULL );
  219. x264_pthread_mutex_unlock( &win32_cond->mtx_waiter_count );
  220. WaitForSingleObject( win32_cond->waiters_done, INFINITE );
  221. win32_cond->is_broadcast = 0;
  222. }
  223. else
  224. x264_pthread_mutex_unlock( &win32_cond->mtx_waiter_count );
  225. return x264_pthread_mutex_unlock( &win32_cond->mtx_broadcast );
  226. }
  227. int x264_pthread_cond_signal( x264_pthread_cond_t *cond )
  228. {
  229. if( thread_control.cond_signal )
  230. {
  231. thread_control.cond_signal( cond );
  232. return 0;
  233. }
  234. /* non-native condition variables */
  235. x264_win32_cond_t *win32_cond = cond->Ptr;
  236. x264_pthread_mutex_lock( &win32_cond->mtx_broadcast );
  237. x264_pthread_mutex_lock( &win32_cond->mtx_waiter_count );
  238. int have_waiter = win32_cond->waiter_count;
  239. x264_pthread_mutex_unlock( &win32_cond->mtx_waiter_count );
  240. if( have_waiter )
  241. {
  242. ReleaseSemaphore( win32_cond->semaphore, 1, NULL );
  243. WaitForSingleObject( win32_cond->waiters_done, INFINITE );
  244. }
  245. return x264_pthread_mutex_unlock( &win32_cond->mtx_broadcast );
  246. }
  247. int x264_pthread_cond_wait( x264_pthread_cond_t *cond, x264_pthread_mutex_t *mutex )
  248. {
  249. if( thread_control.cond_wait )
  250. return !thread_control.cond_wait( cond, mutex, INFINITE );
  251. /* non native condition variables */
  252. x264_win32_cond_t *win32_cond = cond->Ptr;
  253. x264_pthread_mutex_lock( &win32_cond->mtx_broadcast );
  254. x264_pthread_mutex_lock( &win32_cond->mtx_waiter_count );
  255. win32_cond->waiter_count++;
  256. x264_pthread_mutex_unlock( &win32_cond->mtx_waiter_count );
  257. x264_pthread_mutex_unlock( &win32_cond->mtx_broadcast );
  258. // unlock the external mutex
  259. x264_pthread_mutex_unlock( mutex );
  260. WaitForSingleObject( win32_cond->semaphore, INFINITE );
  261. x264_pthread_mutex_lock( &win32_cond->mtx_waiter_count );
  262. win32_cond->waiter_count--;
  263. int last_waiter = !win32_cond->waiter_count || !win32_cond->is_broadcast;
  264. x264_pthread_mutex_unlock( &win32_cond->mtx_waiter_count );
  265. if( last_waiter )
  266. SetEvent( win32_cond->waiters_done );
  267. // lock the external mutex
  268. return x264_pthread_mutex_lock( mutex );
  269. }
  270. int x264_win32_threading_init( void )
  271. {
  272. /* find function pointers to API functions, if they exist */
  273. HANDLE kernel_dll = GetModuleHandleW( L"kernel32.dll" );
  274. thread_control.cond_init = (void*)GetProcAddress( kernel_dll, "InitializeConditionVariable" );
  275. if( thread_control.cond_init )
  276. {
  277. /* we're on a windows 6.0+ kernel, acquire the rest of the functions */
  278. thread_control.cond_broadcast = (void*)GetProcAddress( kernel_dll, "WakeAllConditionVariable" );
  279. thread_control.cond_signal = (void*)GetProcAddress( kernel_dll, "WakeConditionVariable" );
  280. thread_control.cond_wait = (void*)GetProcAddress( kernel_dll, "SleepConditionVariableCS" );
  281. }
  282. return x264_pthread_mutex_init( &static_mutex, NULL );
  283. }
  284. int x264_pthread_num_processors_np( void )
  285. {
  286. DWORD_PTR system_cpus, process_cpus = 0;
  287. int cpus = 0;
  288. /* GetProcessAffinityMask returns affinities of 0 when the process has threads in multiple processor groups.
  289. * On platforms that support processor grouping, use GetThreadGroupAffinity to get the current thread's affinity instead. */
  290. #if ARCH_X86_64
  291. /* find function pointers to API functions specific to x86_64 platforms, if they exist */
  292. HANDLE kernel_dll = GetModuleHandleW( L"kernel32.dll" );
  293. BOOL (*get_thread_affinity)( HANDLE thread, void *group_affinity ) = (void*)GetProcAddress( kernel_dll, "GetThreadGroupAffinity" );
  294. if( get_thread_affinity )
  295. {
  296. /* running on a platform that supports >64 logical cpus */
  297. struct /* GROUP_AFFINITY */
  298. {
  299. ULONG_PTR mask; // KAFFINITY = ULONG_PTR
  300. USHORT group;
  301. USHORT reserved[3];
  302. } thread_affinity;
  303. if( get_thread_affinity( GetCurrentThread(), &thread_affinity ) )
  304. process_cpus = thread_affinity.mask;
  305. }
  306. #endif
  307. if( !process_cpus )
  308. GetProcessAffinityMask( GetCurrentProcess(), &process_cpus, &system_cpus );
  309. for( DWORD_PTR bit = 1; bit; bit <<= 1 )
  310. cpus += !!(process_cpus & bit);
  311. return cpus ? cpus : 1;
  312. }
  313. #endif