1 /*****************************************************************************
2 * win32thread.c: windows threading
3 *****************************************************************************
4 * Copyright (C) 2010 x264 project
6 * Authors: Steven Walters <kemuri9@gmail.com>
7 * Pegasys Inc. <http://www.pegasys-inc.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program 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
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
23 * This program is also available under a commercial proprietary license.
24 * For more information, contact us at licensing@x264.com.
25 *****************************************************************************/
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 */
30 /* Based on the agreed standing that x264 does not need to utilize >64 logical cpus,
31 * this API does not detect nor utilize more than 64 cpus for systems that have them. */
36 /* number of times to spin a thread about to block on a locked mutex before retrying and sleeping if still locked */
37 #define X264_SPIN_COUNT 0
39 /* GROUP_AFFINITY struct */
42 ULONG_PTR mask; // KAFFINITY = ULONG_PTR
45 } x264_group_affinity_t;
49 /* global mutex for replacing MUTEX_INITIALIZER instances */
50 x264_pthread_mutex_t static_mutex;
52 /* function pointers to conditional variable API on windows 6.0+ kernels */
53 void (WINAPI *cond_broadcast)( x264_pthread_cond_t *cond );
54 void (WINAPI *cond_init)( x264_pthread_cond_t *cond );
55 void (WINAPI *cond_signal)( x264_pthread_cond_t *cond );
56 BOOL (WINAPI *cond_wait)( x264_pthread_cond_t *cond, x264_pthread_mutex_t *mutex, DWORD milliseconds );
57 } x264_win32thread_control_t;
59 static x264_win32thread_control_t thread_control;
61 /* _beginthreadex requires that the start routine is __stdcall */
62 static __stdcall unsigned x264_win32thread_worker( void *arg )
64 x264_pthread_t *h = arg;
65 h->ret = h->func( h->arg );
69 int x264_pthread_create( x264_pthread_t *thread, const x264_pthread_attr_t *attr,
70 void *(*start_routine)( void* ), void *arg )
72 thread->func = start_routine;
74 thread->handle = (void*)_beginthreadex( NULL, 0, x264_win32thread_worker, thread, 0, NULL );
75 return !thread->handle;
78 int x264_pthread_join( x264_pthread_t thread, void **value_ptr )
80 DWORD ret = WaitForSingleObject( thread.handle, INFINITE );
81 if( ret != WAIT_OBJECT_0 )
84 *value_ptr = thread.ret;
85 CloseHandle( thread.handle );
89 int x264_pthread_mutex_init( x264_pthread_mutex_t *mutex, const x264_pthread_mutexattr_t *attr )
91 return !InitializeCriticalSectionAndSpinCount( mutex, X264_SPIN_COUNT );
94 int x264_pthread_mutex_destroy( x264_pthread_mutex_t *mutex )
96 DeleteCriticalSection( mutex );
100 int x264_pthread_mutex_lock( x264_pthread_mutex_t *mutex )
102 static x264_pthread_mutex_t init = X264_PTHREAD_MUTEX_INITIALIZER;
103 if( !memcmp( mutex, &init, sizeof(x264_pthread_mutex_t) ) )
104 *mutex = thread_control.static_mutex;
105 EnterCriticalSection( mutex );
109 int x264_pthread_mutex_unlock( x264_pthread_mutex_t *mutex )
111 LeaveCriticalSection( mutex );
115 /* for pre-Windows 6.0 platforms we need to define and use our own condition variable and api */
118 x264_pthread_mutex_t mtx_broadcast;
119 x264_pthread_mutex_t mtx_waiter_count;
126 int x264_pthread_cond_init( x264_pthread_cond_t *cond, const x264_pthread_condattr_t *attr )
128 if( thread_control.cond_init )
130 thread_control.cond_init( cond );
134 /* non native condition variables */
135 x264_win32_cond_t *win32_cond = calloc( 1, sizeof(x264_win32_cond_t) );
138 cond->ptr = win32_cond;
139 win32_cond->semaphore = CreateSemaphore( NULL, 0, 0x7fffffff, NULL );
140 if( !win32_cond->semaphore )
143 if( x264_pthread_mutex_init( &win32_cond->mtx_waiter_count, NULL ) )
145 if( x264_pthread_mutex_init( &win32_cond->mtx_broadcast, NULL ) )
148 win32_cond->waiters_done = CreateEvent( NULL, FALSE, FALSE, NULL );
149 if( !win32_cond->waiters_done )
155 int x264_pthread_cond_destroy( x264_pthread_cond_t *cond )
157 /* native condition variables do not destroy */
158 if( thread_control.cond_init )
161 /* non native condition variables */
162 x264_win32_cond_t *win32_cond = cond->ptr;
163 CloseHandle( win32_cond->semaphore );
164 CloseHandle( win32_cond->waiters_done );
165 x264_pthread_mutex_destroy( &win32_cond->mtx_broadcast );
166 x264_pthread_mutex_destroy( &win32_cond->mtx_waiter_count );
172 int x264_pthread_cond_broadcast( x264_pthread_cond_t *cond )
174 if( thread_control.cond_broadcast )
176 thread_control.cond_broadcast( cond );
180 /* non native condition variables */
181 x264_win32_cond_t *win32_cond = cond->ptr;
182 x264_pthread_mutex_lock( &win32_cond->mtx_broadcast );
183 x264_pthread_mutex_lock( &win32_cond->mtx_waiter_count );
186 if( win32_cond->waiter_count )
188 win32_cond->is_broadcast = 1;
194 ReleaseSemaphore( win32_cond->semaphore, win32_cond->waiter_count, NULL );
195 x264_pthread_mutex_unlock( &win32_cond->mtx_waiter_count );
196 WaitForSingleObject( win32_cond->waiters_done, INFINITE );
197 win32_cond->is_broadcast = 0;
200 x264_pthread_mutex_unlock( &win32_cond->mtx_waiter_count );
201 return x264_pthread_mutex_unlock( &win32_cond->mtx_broadcast );
204 int x264_pthread_cond_signal( x264_pthread_cond_t *cond )
206 if( thread_control.cond_signal )
208 thread_control.cond_signal( cond );
212 /* non-native condition variables */
213 x264_win32_cond_t *win32_cond = cond->ptr;
214 x264_pthread_mutex_lock( &win32_cond->mtx_waiter_count );
215 int have_waiter = win32_cond->waiter_count;
216 x264_pthread_mutex_unlock( &win32_cond->mtx_waiter_count );
219 ReleaseSemaphore( win32_cond->semaphore, 1, NULL );
223 int x264_pthread_cond_wait( x264_pthread_cond_t *cond, x264_pthread_mutex_t *mutex )
225 if( thread_control.cond_wait )
226 return !thread_control.cond_wait( cond, mutex, INFINITE );
228 /* non native condition variables */
229 x264_win32_cond_t *win32_cond = cond->ptr;
231 x264_pthread_mutex_lock( &win32_cond->mtx_broadcast );
232 x264_pthread_mutex_unlock( &win32_cond->mtx_broadcast );
234 x264_pthread_mutex_lock( &win32_cond->mtx_waiter_count );
235 win32_cond->waiter_count++;
236 x264_pthread_mutex_unlock( &win32_cond->mtx_waiter_count );
238 // unlock the external mutex
239 x264_pthread_mutex_unlock( mutex );
240 WaitForSingleObject( win32_cond->semaphore, INFINITE );
242 x264_pthread_mutex_lock( &win32_cond->mtx_waiter_count );
243 win32_cond->waiter_count--;
244 int last_waiter = !win32_cond->waiter_count && win32_cond->is_broadcast;
245 x264_pthread_mutex_unlock( &win32_cond->mtx_waiter_count );
248 SetEvent( win32_cond->waiters_done );
250 // lock the external mutex
251 return x264_pthread_mutex_lock( mutex );
254 int x264_win32_threading_init( void )
256 /* find function pointers to API functions, if they exist */
257 HANDLE kernel_dll = GetModuleHandle( TEXT( "kernel32.dll" ) );
258 thread_control.cond_init = (void*)GetProcAddress( kernel_dll, "InitializeConditionVariable" );
259 if( thread_control.cond_init )
261 /* we're on a windows 6.0+ kernel, acquire the rest of the functions */
262 thread_control.cond_broadcast = (void*)GetProcAddress( kernel_dll, "WakeAllConditionVariable" );
263 thread_control.cond_signal = (void*)GetProcAddress( kernel_dll, "WakeConditionVariable" );
264 thread_control.cond_wait = (void*)GetProcAddress( kernel_dll, "SleepConditionVariableCS" );
266 return x264_pthread_mutex_init( &thread_control.static_mutex, NULL );
269 void x264_win32_threading_destroy( void )
271 x264_pthread_mutex_destroy( &thread_control.static_mutex );
272 memset( &thread_control, 0, sizeof(x264_win32thread_control_t) );
275 int x264_pthread_num_processors_np()
277 DWORD_PTR system_cpus, process_cpus = 0;
280 /* GetProcessAffinityMask returns affinities of 0 when the process has threads in multiple processor groups.
281 * On platforms that support processor grouping, use GetThreadGroupAffinity to get the current thread's affinity instead. */
283 /* find function pointers to API functions specific to x86_64 platforms, if they exist */
284 HANDLE kernel_dll = GetModuleHandle( TEXT( "kernel32.dll" ) );
285 BOOL (*get_thread_affinity)( HANDLE thread, x264_group_affinity_t *group_affinity ) = (void*)GetProcAddress( kernel_dll, "GetThreadGroupAffinity" );
286 if( get_thread_affinity )
288 /* running on a platform that supports >64 logical cpus */
289 x264_group_affinity_t thread_affinity;
290 if( get_thread_affinity( GetCurrentThread(), &thread_affinity ) )
291 process_cpus = thread_affinity.mask;
295 GetProcessAffinityMask( GetCurrentProcess(), &process_cpus, &system_cpus );
296 for( DWORD_PTR bit = 1; bit; bit <<= 1 )
297 cpus += !!(process_cpus & bit);
299 return cpus ? cpus : 1;