w32pthreads.h 12 KB
Newer Older
1 2 3 4 5 6
/*
 * Copyright (C) 2010-2011 x264 project
 *
 * Authors: Steven Walters <kemuri9@gmail.com>
 *          Pegasys Inc. <http://www.pegasys-inc.com>
 *
7
 * This file is part of FFmpeg.
8
 *
9
 * FFmpeg is free software; you can redistribute it and/or
10 11 12 13
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
14
 * FFmpeg is distributed in the hope that it will be useful,
15 16 17 18 19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * 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 22 23 24 25 26 27 28
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
 * @file
 * w32threads to pthreads wrapper
 */

29 30
#ifndef COMPAT_W32PTHREADS_H
#define COMPAT_W32PTHREADS_H
31 32 33 34 35 36 37 38 39 40 41

/* Build up a pthread-like API using underlying Windows API. Have only static
 * methods so as to not conflict with a potentially linked in pthread-win32
 * library.
 * As most functions here are used without checking return values,
 * only implement return values as necessary. */

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <process.h>

42
#if _WIN32_WINNT < 0x0600 && defined(__MINGW32__)
43 44
#undef MemoryBarrier
#define MemoryBarrier __sync_synchronize
45 46
#endif

47
#include "libavutil/attributes.h"
48
#include "libavutil/common.h"
49 50
#include "libavutil/internal.h"
#include "libavutil/mem.h"
51

52
typedef struct pthread_t {
53 54 55 56 57 58 59 60 61 62
    void *handle;
    void *(*func)(void* arg);
    void *arg;
    void *ret;
} pthread_t;

/* the conditional variable api for windows 6.0+ uses critical sections and
 * not mutexes */
typedef CRITICAL_SECTION pthread_mutex_t;

63 64 65 66 67
/* This is the CONDITION_VARIABLE typedef for using Windows' native
 * conditional variables on kernels 6.0+. */
#if HAVE_CONDITION_VARIABLE_PTR
typedef CONDITION_VARIABLE pthread_cond_t;
#else
68
typedef struct pthread_cond_t {
69
    void *Ptr;
70
} pthread_cond_t;
71
#endif
72

73
#if _WIN32_WINNT >= 0x0600
74 75
#define InitializeCriticalSection(x) InitializeCriticalSectionEx(x, 0, 0)
#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 81 82 83 84
{
    pthread_t *h = arg;
    h->ret = h->func(h->arg);
    return 0;
}

85 86
static av_unused int pthread_create(pthread_t *thread, const void *unused_attr,
                                    void *(*start_routine)(void*), void *arg)
87 88 89
{
    thread->func   = start_routine;
    thread->arg    = arg;
wang-bin's avatar
wang-bin committed
90 91 92 93
#if HAVE_WINRT
    thread->handle = (void*)CreateThread(NULL, 0, win32thread_worker, thread,
                                           0, NULL);
#else
94 95
    thread->handle = (void*)_beginthreadex(NULL, 0, win32thread_worker, thread,
                                           0, NULL);
wang-bin's avatar
wang-bin committed
96
#endif
97 98 99
    return !thread->handle;
}

100
static av_unused int pthread_join(pthread_t thread, void **value_ptr)
101 102
{
    DWORD ret = WaitForSingleObject(thread.handle, INFINITE);
103 104 105 106 107 108
    if (ret != WAIT_OBJECT_0) {
        if (ret == WAIT_ABANDONED)
            return EINVAL;
        else
            return EDEADLK;
    }
109 110 111
    if (value_ptr)
        *value_ptr = thread.ret;
    CloseHandle(thread.handle);
112
    return 0;
113 114
}

115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
static inline int pthread_mutex_init(pthread_mutex_t *m, void* attr)
{
    InitializeCriticalSection(m);
    return 0;
}
static inline int pthread_mutex_destroy(pthread_mutex_t *m)
{
    DeleteCriticalSection(m);
    return 0;
}
static inline int pthread_mutex_lock(pthread_mutex_t *m)
{
    EnterCriticalSection(m);
    return 0;
}
static inline int pthread_mutex_unlock(pthread_mutex_t *m)
{
    LeaveCriticalSection(m);
    return 0;
}
135

136
#if _WIN32_WINNT >= 0x0600
137 138 139 140 141 142 143 144 145 146 147 148 149
typedef INIT_ONCE pthread_once_t;
#define PTHREAD_ONCE_INIT INIT_ONCE_STATIC_INIT

static av_unused int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
{
    BOOL pending = FALSE;
    InitOnceBeginInitialize(once_control, 0, &pending, NULL);
    if (pending)
        init_routine();
    InitOnceComplete(once_control, 0, NULL);
    return 0;
}

150 151 152 153 154 155 156
static inline int pthread_cond_init(pthread_cond_t *cond, const void *unused_attr)
{
    InitializeConditionVariable(cond);
    return 0;
}

/* 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 169 170 171 172 173
}

static inline int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
{
    SleepConditionVariableCS(cond, mutex, INFINITE);
    return 0;
}

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 183 184 185

/* atomic init state of dynamically loaded functions */
static LONG w32thread_init_state = 0;
static av_unused void w32thread_init(void);

186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
/* for pre-Windows 6.0 platforms, define INIT_ONCE struct,
 * compatible to the one used in the native API */

typedef union pthread_once_t  {
    void * Ptr;    ///< For the Windows 6.0+ native functions
    LONG state;    ///< For the pre-Windows 6.0 compat code
} pthread_once_t;

#define PTHREAD_ONCE_INIT {0}

/* function pointers to init once API on windows 6.0+ kernels */
static BOOL (WINAPI *initonce_begin)(pthread_once_t *lpInitOnce, DWORD dwFlags, BOOL *fPending, void **lpContext);
static BOOL (WINAPI *initonce_complete)(pthread_once_t *lpInitOnce, DWORD dwFlags, void *lpContext);

/* pre-Windows 6.0 compat using a spin-lock */
static inline void w32thread_once_fallback(LONG volatile *state, void (*init_routine)(void))
{
    switch (InterlockedCompareExchange(state, 1, 0)) {
    /* Initial run */
    case 0:
        init_routine();
        InterlockedExchange(state, 2);
        break;
    /* Another thread is running init */
    case 1:
        while (1) {
            MemoryBarrier();
            if (*state == 2)
                break;
            Sleep(0);
        }
        break;
    /* Initialization complete */
    case 2:
        break;
    }
}

static av_unused int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
{
226 227
    w32thread_once_fallback(&w32thread_init_state, w32thread_init);

228 229 230 231 232 233 234 235 236 237 238 239 240 241
    /* Use native functions on Windows 6.0+ */
    if (initonce_begin && initonce_complete) {
        BOOL pending = FALSE;
        initonce_begin(once_control, 0, &pending, NULL);
        if (pending)
            init_routine();
        initonce_complete(once_control, 0, NULL);
        return 0;
    }

    w32thread_once_fallback(&once_control->state, init_routine);
    return 0;
}

242 243
/* for pre-Windows 6.0 platforms we need to define and use our own condition
 * variable and api */
244

245
typedef struct  win32_cond_t {
246
    pthread_mutex_t mtx_broadcast;
247 248 249
    pthread_mutex_t mtx_waiter_count;
    volatile int waiter_count;
    HANDLE semaphore;
250
    HANDLE waiters_done;
251
    volatile int is_broadcast;
252 253
} win32_cond_t;

254 255 256 257 258 259 260
/* function pointers to conditional variable API on windows 6.0+ kernels */
static void (WINAPI *cond_broadcast)(pthread_cond_t *cond);
static void (WINAPI *cond_init)(pthread_cond_t *cond);
static void (WINAPI *cond_signal)(pthread_cond_t *cond);
static BOOL (WINAPI *cond_wait)(pthread_cond_t *cond, pthread_mutex_t *mutex,
                                DWORD milliseconds);

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 266

    w32thread_once_fallback(&w32thread_init_state, w32thread_init);

267
    if (cond_init) {
268
        cond_init(cond);
269
        return 0;
270 271 272 273 274
    }

    /* non native condition variables */
    win32_cond = av_mallocz(sizeof(win32_cond_t));
    if (!win32_cond)
275
        return ENOMEM;
276
    cond->Ptr = win32_cond;
277 278
    win32_cond->semaphore = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
    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 297

    /* non native condition variables */
    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 310
    int have_waiter;

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 320
    have_waiter = 0;

321
    if (win32_cond->waiter_count) {
322 323
        win32_cond->is_broadcast = 1;
        have_waiter = 1;
324
    }
325 326 327 328 329

    if (have_waiter) {
        ReleaseSemaphore(win32_cond->semaphore, win32_cond->waiter_count, NULL);
        pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
        WaitForSingleObject(win32_cond->waiters_done, INFINITE);
330
        ResetEvent(win32_cond->waiters_done);
331 332 333 334
        win32_cond->is_broadcast = 0;
    } else
        pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
    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 350 351
    pthread_mutex_lock(&win32_cond->mtx_waiter_count);
    win32_cond->waiter_count++;
    pthread_mutex_unlock(&win32_cond->mtx_waiter_count);
352
    pthread_mutex_unlock(&win32_cond->mtx_broadcast);
353

354
    // unlock the external mutex
355 356
    pthread_mutex_unlock(mutex);
    WaitForSingleObject(win32_cond->semaphore, INFINITE);
357 358 359

    pthread_mutex_lock(&win32_cond->mtx_waiter_count);
    win32_cond->waiter_count--;
360
    last_waiter = !win32_cond->waiter_count || !win32_cond->is_broadcast;
361 362 363 364 365 366 367
    pthread_mutex_unlock(&win32_cond->mtx_waiter_count);

    if (last_waiter)
        SetEvent(win32_cond->waiters_done);

    // lock the external mutex
    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 380
    pthread_mutex_lock(&win32_cond->mtx_broadcast);

381 382
    /* non-native condition variables */
    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 389 390 391 392
        WaitForSingleObject(win32_cond->waiters_done, INFINITE);
        ResetEvent(win32_cond->waiters_done);
    }

    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 401 402 403 404 405 406 407 408 409
    HANDLE kernel_dll = GetModuleHandle(TEXT("kernel32.dll"));
    /* if one is available, then they should all be available */
    cond_init      =
        (void*)GetProcAddress(kernel_dll, "InitializeConditionVariable");
    cond_broadcast =
        (void*)GetProcAddress(kernel_dll, "WakeAllConditionVariable");
    cond_signal    =
        (void*)GetProcAddress(kernel_dll, "WakeConditionVariable");
    cond_wait      =
        (void*)GetProcAddress(kernel_dll, "SleepConditionVariableCS");
410 411 412 413
    initonce_begin =
        (void*)GetProcAddress(kernel_dll, "InitOnceBeginInitialize");
    initonce_complete =
        (void*)GetProcAddress(kernel_dll, "InitOnceComplete");
414 415
#endif

416 417
}

418
#endif /* COMPAT_W32PTHREADS_H */