libavcodec/w32pthreads.h
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010-2011 x264 project
00003  *
00004  * Authors: Steven Walters <kemuri9@gmail.com>
00005  *          Pegasys Inc. <http://www.pegasys-inc.com>
00006  *
00007  * This file is part of Libav.
00008  *
00009  * Libav is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Lesser General Public
00011  * License as published by the Free Software Foundation; either
00012  * version 2.1 of the License, or (at your option) any later version.
00013  *
00014  * Libav is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  * Lesser General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU Lesser General Public
00020  * License along with Libav; if not, write to the Free Software
00021  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00022  */
00023 
00029 #ifndef AVCODEC_W32PTHREADS_H
00030 #define AVCODEC_W32PTHREADS_H
00031 
00032 /* Build up a pthread-like API using underlying Windows API. Have only static
00033  * methods so as to not conflict with a potentially linked in pthread-win32
00034  * library.
00035  * As most functions here are used without checking return values,
00036  * only implement return values as necessary. */
00037 
00038 #define WIN32_LEAN_AND_MEAN
00039 #include <windows.h>
00040 #include <process.h>
00041 
00042 typedef struct {
00043     void *handle;
00044     void *(*func)(void* arg);
00045     void *arg;
00046     void *ret;
00047 } pthread_t;
00048 
00049 /* the conditional variable api for windows 6.0+ uses critical sections and
00050  * not mutexes */
00051 typedef CRITICAL_SECTION pthread_mutex_t;
00052 
00053 /* This is the CONDITIONAL_VARIABLE typedef for using Window's native
00054  * conditional variables on kernels 6.0+.
00055  * MinGW does not currently have this typedef. */
00056 typedef struct {
00057     void *ptr;
00058 } pthread_cond_t;
00059 
00060 /* function pointers to conditional variable API on windows 6.0+ kernels */
00061 static void (WINAPI *cond_broadcast)(pthread_cond_t *cond);
00062 static void (WINAPI *cond_init)(pthread_cond_t *cond);
00063 static void (WINAPI *cond_signal)(pthread_cond_t *cond);
00064 static BOOL (WINAPI *cond_wait)(pthread_cond_t *cond, pthread_mutex_t *mutex,
00065                                 DWORD milliseconds);
00066 
00067 static unsigned __stdcall attribute_align_arg win32thread_worker(void *arg)
00068 {
00069     pthread_t *h = arg;
00070     h->ret = h->func(h->arg);
00071     return 0;
00072 }
00073 
00074 static int pthread_create(pthread_t *thread, const void *unused_attr,
00075                           void *(*start_routine)(void*), void *arg)
00076 {
00077     thread->func   = start_routine;
00078     thread->arg    = arg;
00079     thread->handle = (void*)_beginthreadex(NULL, 0, win32thread_worker, thread,
00080                                            0, NULL);
00081     return !thread->handle;
00082 }
00083 
00084 static void pthread_join(pthread_t thread, void **value_ptr)
00085 {
00086     DWORD ret = WaitForSingleObject(thread.handle, INFINITE);
00087     if (ret != WAIT_OBJECT_0)
00088         return;
00089     if (value_ptr)
00090         *value_ptr = thread.ret;
00091     CloseHandle(thread.handle);
00092 }
00093 
00094 static inline int pthread_mutex_init(pthread_mutex_t *m, void* attr)
00095 {
00096     InitializeCriticalSection(m);
00097     return 0;
00098 }
00099 static inline int pthread_mutex_destroy(pthread_mutex_t *m)
00100 {
00101     DeleteCriticalSection(m);
00102     return 0;
00103 }
00104 static inline int pthread_mutex_lock(pthread_mutex_t *m)
00105 {
00106     EnterCriticalSection(m);
00107     return 0;
00108 }
00109 static inline int pthread_mutex_unlock(pthread_mutex_t *m)
00110 {
00111     LeaveCriticalSection(m);
00112     return 0;
00113 }
00114 
00115 /* for pre-Windows 6.0 platforms we need to define and use our own condition
00116  * variable and api */
00117 typedef struct {
00118     pthread_mutex_t mtx_broadcast;
00119     pthread_mutex_t mtx_waiter_count;
00120     volatile int waiter_count;
00121     HANDLE semaphore;
00122     HANDLE waiters_done;
00123     volatile int is_broadcast;
00124 } win32_cond_t;
00125 
00126 static void pthread_cond_init(pthread_cond_t *cond, const void *unused_attr)
00127 {
00128     win32_cond_t *win32_cond = NULL;
00129     if (cond_init) {
00130         cond_init(cond);
00131         return;
00132     }
00133 
00134     /* non native condition variables */
00135     win32_cond = av_mallocz(sizeof(win32_cond_t));
00136     if (!win32_cond)
00137         return;
00138     cond->ptr = win32_cond;
00139     win32_cond->semaphore = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
00140     if (!win32_cond->semaphore)
00141         return;
00142     win32_cond->waiters_done = CreateEvent(NULL, TRUE, FALSE, NULL);
00143     if (!win32_cond->waiters_done)
00144         return;
00145 
00146     pthread_mutex_init(&win32_cond->mtx_waiter_count, NULL);
00147     pthread_mutex_init(&win32_cond->mtx_broadcast, NULL);
00148 }
00149 
00150 static void pthread_cond_destroy(pthread_cond_t *cond)
00151 {
00152     win32_cond_t *win32_cond = cond->ptr;
00153     /* native condition variables do not destroy */
00154     if (cond_init)
00155         return;
00156 
00157     /* non native condition variables */
00158     CloseHandle(win32_cond->semaphore);
00159     CloseHandle(win32_cond->waiters_done);
00160     pthread_mutex_destroy(&win32_cond->mtx_waiter_count);
00161     pthread_mutex_destroy(&win32_cond->mtx_broadcast);
00162     av_freep(&win32_cond);
00163     cond->ptr = NULL;
00164 }
00165 
00166 static void pthread_cond_broadcast(pthread_cond_t *cond)
00167 {
00168     win32_cond_t *win32_cond = cond->ptr;
00169     int have_waiter;
00170 
00171     if (cond_broadcast) {
00172         cond_broadcast(cond);
00173         return;
00174     }
00175 
00176     /* non native condition variables */
00177     pthread_mutex_lock(&win32_cond->mtx_broadcast);
00178     pthread_mutex_lock(&win32_cond->mtx_waiter_count);
00179     have_waiter = 0;
00180 
00181     if (win32_cond->waiter_count) {
00182         win32_cond->is_broadcast = 1;
00183         have_waiter = 1;
00184     }
00185 
00186     if (have_waiter) {
00187         ReleaseSemaphore(win32_cond->semaphore, win32_cond->waiter_count, NULL);
00188         pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
00189         WaitForSingleObject(win32_cond->waiters_done, INFINITE);
00190         ResetEvent(win32_cond->waiters_done);
00191         win32_cond->is_broadcast = 0;
00192     } else
00193         pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
00194     pthread_mutex_unlock(&win32_cond->mtx_broadcast);
00195 }
00196 
00197 static void pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
00198 {
00199     win32_cond_t *win32_cond = cond->ptr;
00200     int last_waiter;
00201     if (cond_wait) {
00202         cond_wait(cond, mutex, INFINITE);
00203         return;
00204     }
00205 
00206     /* non native condition variables */
00207     pthread_mutex_lock(&win32_cond->mtx_broadcast);
00208     pthread_mutex_lock(&win32_cond->mtx_waiter_count);
00209     win32_cond->waiter_count++;
00210     pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
00211     pthread_mutex_unlock(&win32_cond->mtx_broadcast);
00212 
00213     // unlock the external mutex
00214     pthread_mutex_unlock(mutex);
00215     WaitForSingleObject(win32_cond->semaphore, INFINITE);
00216 
00217     pthread_mutex_lock(&win32_cond->mtx_waiter_count);
00218     win32_cond->waiter_count--;
00219     last_waiter = !win32_cond->waiter_count || !win32_cond->is_broadcast;
00220     pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
00221 
00222     if (last_waiter)
00223         SetEvent(win32_cond->waiters_done);
00224 
00225     // lock the external mutex
00226     return pthread_mutex_lock(mutex);
00227 }
00228 
00229 static void pthread_cond_signal(pthread_cond_t *cond)
00230 {
00231     win32_cond_t *win32_cond = cond->ptr;
00232     int have_waiter;
00233     if (cond_signal) {
00234         cond_signal(cond);
00235         return;
00236     }
00237 
00238     pthread_mutex_lock(&win32_cond->mtx_broadcast);
00239 
00240     /* non-native condition variables */
00241     pthread_mutex_lock(&win32_cond->mtx_waiter_count);
00242     have_waiter = win32_cond->waiter_count;
00243     pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
00244 
00245     if (have_waiter) {
00246         ReleaseSemaphore(win32_cond->semaphore, 1, NULL);
00247         WaitForSingleObject(win32_cond->waiters_done, INFINITE);
00248         ResetEvent(win32_cond->waiters_done);
00249     }
00250 
00251     pthread_mutex_unlock(&win32_cond->mtx_broadcast);
00252 }
00253 
00254 static void w32thread_init(void)
00255 {
00256     HANDLE kernel_dll = GetModuleHandle(TEXT("kernel32.dll"));
00257     /* if one is available, then they should all be available */
00258     cond_init      =
00259         (void*)GetProcAddress(kernel_dll, "InitializeConditionVariable");
00260     cond_broadcast =
00261         (void*)GetProcAddress(kernel_dll, "WakeAllConditionVariable");
00262     cond_signal    =
00263         (void*)GetProcAddress(kernel_dll, "WakeConditionVariable");
00264     cond_wait      =
00265         (void*)GetProcAddress(kernel_dll, "SleepConditionVariableCS");
00266 }
00267 
00268 #endif /* AVCODEC_W32PTHREADS_H */