2 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
4 * This source code is subject to the terms of the BSD 2 Clause License and
5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6 * was not distributed with this source code in the LICENSE file, you can
7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8 * Media Patent License 1.0 was not distributed with this source code in the
9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
12 // Multi-threaded worker
15 // https://chromium.googlesource.com/webm/libwebp
20 #include "./aom_config.h"
26 // Set maximum decode threads to be 8 due to the limit of frame buffers
27 // and not enough semaphores in the emulation layer on windows.
28 #define MAX_DECODE_THREADS 8
30 #if CONFIG_MULTITHREAD
32 #if defined(_WIN32) && !HAVE_PTHREAD_H
33 #include <errno.h> // NOLINT
34 #include <process.h> // NOLINT
35 #include <windows.h> // NOLINT
36 typedef HANDLE pthread_t
;
37 typedef CRITICAL_SECTION pthread_mutex_t
;
39 #if _WIN32_WINNT >= 0x0600 // Windows Vista / Server 2008 or greater
40 #define USE_WINDOWS_CONDITION_VARIABLE
41 typedef CONDITION_VARIABLE pthread_cond_t
;
48 #endif // _WIN32_WINNT >= 0x600
50 #ifndef WINAPI_FAMILY_PARTITION
51 #define WINAPI_PARTITION_DESKTOP 1
52 #define WINAPI_FAMILY_PARTITION(x) x
55 #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
56 #define USE_CREATE_THREAD
59 //------------------------------------------------------------------------------
60 // simplistic pthread emulation layer
62 // _beginthreadex requires __stdcall
63 #define THREADFN unsigned int __stdcall
64 #define THREAD_RETURN(val) (unsigned int)((DWORD_PTR)val)
66 #if _WIN32_WINNT >= 0x0501 // Windows XP or greater
67 #define WaitForSingleObject(obj, timeout) \
68 WaitForSingleObjectEx(obj, timeout, FALSE /*bAlertable*/)
71 static INLINE
int pthread_create(pthread_t
*const thread
, const void *attr
,
72 unsigned int(__stdcall
*start
)(void *),
75 #ifdef USE_CREATE_THREAD
76 *thread
= CreateThread(NULL
, /* lpThreadAttributes */
78 start
, arg
, 0, /* dwStackSize */
79 NULL
); /* lpThreadId */
81 *thread
= (pthread_t
)_beginthreadex(NULL
, /* void *security */
82 0, /* unsigned stack_size */
83 start
, arg
, 0, /* unsigned initflag */
84 NULL
); /* unsigned *thrdaddr */
86 if (*thread
== NULL
) return 1;
87 SetThreadPriority(*thread
, THREAD_PRIORITY_ABOVE_NORMAL
);
91 static INLINE
int pthread_join(pthread_t thread
, void **value_ptr
) {
93 return (WaitForSingleObject(thread
, INFINITE
) != WAIT_OBJECT_0
||
94 CloseHandle(thread
) == 0);
98 static INLINE
int pthread_mutex_init(pthread_mutex_t
*const mutex
,
101 #if _WIN32_WINNT >= 0x0600 // Windows Vista / Server 2008 or greater
102 InitializeCriticalSectionEx(mutex
, 0 /*dwSpinCount*/, 0 /*Flags*/);
104 InitializeCriticalSection(mutex
);
109 static INLINE
int pthread_mutex_trylock(pthread_mutex_t
*const mutex
) {
110 return TryEnterCriticalSection(mutex
) ? 0 : EBUSY
;
113 static INLINE
int pthread_mutex_lock(pthread_mutex_t
*const mutex
) {
114 EnterCriticalSection(mutex
);
118 static INLINE
int pthread_mutex_unlock(pthread_mutex_t
*const mutex
) {
119 LeaveCriticalSection(mutex
);
123 static INLINE
int pthread_mutex_destroy(pthread_mutex_t
*const mutex
) {
124 DeleteCriticalSection(mutex
);
129 static INLINE
int pthread_cond_destroy(pthread_cond_t
*const condition
) {
131 #ifdef USE_WINDOWS_CONDITION_VARIABLE
134 ok
&= (CloseHandle(condition
->waiting_sem_
) != 0);
135 ok
&= (CloseHandle(condition
->received_sem_
) != 0);
136 ok
&= (CloseHandle(condition
->signal_event_
) != 0);
141 static INLINE
int pthread_cond_init(pthread_cond_t
*const condition
,
144 #ifdef USE_WINDOWS_CONDITION_VARIABLE
145 InitializeConditionVariable(condition
);
147 condition
->waiting_sem_
= CreateSemaphore(NULL
, 0, MAX_DECODE_THREADS
, NULL
);
148 condition
->received_sem_
= CreateSemaphore(NULL
, 0, MAX_DECODE_THREADS
, NULL
);
149 condition
->signal_event_
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
150 if (condition
->waiting_sem_
== NULL
|| condition
->received_sem_
== NULL
||
151 condition
->signal_event_
== NULL
) {
152 pthread_cond_destroy(condition
);
159 static INLINE
int pthread_cond_signal(pthread_cond_t
*const condition
) {
161 #ifdef USE_WINDOWS_CONDITION_VARIABLE
162 WakeConditionVariable(condition
);
164 if (WaitForSingleObject(condition
->waiting_sem_
, 0) == WAIT_OBJECT_0
) {
165 // a thread is waiting in pthread_cond_wait: allow it to be notified
166 ok
= SetEvent(condition
->signal_event_
);
167 // wait until the event is consumed so the signaler cannot consume
168 // the event via its own pthread_cond_wait.
169 ok
&= (WaitForSingleObject(condition
->received_sem_
, INFINITE
) !=
176 static INLINE
int pthread_cond_wait(pthread_cond_t
*const condition
,
177 pthread_mutex_t
*const mutex
) {
179 #ifdef USE_WINDOWS_CONDITION_VARIABLE
180 ok
= SleepConditionVariableCS(condition
, mutex
, INFINITE
);
182 // note that there is a consumer available so the signal isn't dropped in
183 // pthread_cond_signal
184 if (!ReleaseSemaphore(condition
->waiting_sem_
, 1, NULL
)) return 1;
185 // now unlock the mutex so pthread_cond_signal may be issued
186 pthread_mutex_unlock(mutex
);
187 ok
= (WaitForSingleObject(condition
->signal_event_
, INFINITE
) ==
189 ok
&= ReleaseSemaphore(condition
->received_sem_
, 1, NULL
);
190 pthread_mutex_lock(mutex
);
194 #elif defined(__OS2__)
196 #include <os2.h> // NOLINT
198 #include <errno.h> // NOLINT
199 #include <stdlib.h> // NOLINT
200 #include <sys/builtin.h> // NOLINT
202 #define pthread_t TID
203 #define pthread_mutex_t HMTX
208 volatile unsigned wait_count_
;
211 //------------------------------------------------------------------------------
212 // simplistic pthread emulation layer
214 #define THREADFN void *
215 #define THREAD_RETURN(val) (val)
218 void *(*start_
)(void *);
222 static void thread_start(void *arg
) {
223 thread_arg targ
= *(thread_arg
*)arg
;
226 targ
.start_(targ
.arg_
);
229 static INLINE
int pthread_create(pthread_t
*const thread
, const void *attr
,
230 void *(*start
)(void *), void *arg
) {
232 thread_arg
*targ
= (thread_arg
*)malloc(sizeof(*targ
));
233 if (targ
== NULL
) return 1;
237 targ
->start_
= start
;
239 tid
= (pthread_t
)_beginthread(thread_start
, NULL
, 1024 * 1024, targ
);
249 static INLINE
int pthread_join(pthread_t thread
, void **value_ptr
) {
251 return DosWaitThread(&thread
, DCWW_WAIT
) != 0;
255 static INLINE
int pthread_mutex_init(pthread_mutex_t
*const mutex
,
258 return DosCreateMutexSem(NULL
, mutex
, 0, FALSE
) != 0;
261 static INLINE
int pthread_mutex_trylock(pthread_mutex_t
*const mutex
) {
262 return DosRequestMutexSem(*mutex
, SEM_IMMEDIATE_RETURN
) == 0 ? 0 : EBUSY
;
265 static INLINE
int pthread_mutex_lock(pthread_mutex_t
*const mutex
) {
266 return DosRequestMutexSem(*mutex
, SEM_INDEFINITE_WAIT
) != 0;
269 static INLINE
int pthread_mutex_unlock(pthread_mutex_t
*const mutex
) {
270 return DosReleaseMutexSem(*mutex
) != 0;
273 static INLINE
int pthread_mutex_destroy(pthread_mutex_t
*const mutex
) {
274 return DosCloseMutexSem(*mutex
) != 0;
278 static INLINE
int pthread_cond_destroy(pthread_cond_t
*const condition
) {
280 ok
&= DosCloseEventSem(condition
->event_sem_
) == 0;
281 ok
&= DosCloseEventSem(condition
->ack_sem_
) == 0;
285 static INLINE
int pthread_cond_init(pthread_cond_t
*const condition
,
291 DosCreateEventSem(NULL
, &condition
->event_sem_
, DCE_POSTONE
, FALSE
) == 0;
292 ok
&= DosCreateEventSem(NULL
, &condition
->ack_sem_
, DCE_POSTONE
, FALSE
) == 0;
294 pthread_cond_destroy(condition
);
297 condition
->wait_count_
= 0;
301 static INLINE
int pthread_cond_signal(pthread_cond_t
*const condition
) {
304 if (!__atomic_cmpxchg32(&condition
->wait_count_
, 0, 0)) {
305 ok
&= DosPostEventSem(condition
->event_sem_
) == 0;
306 ok
&= DosWaitEventSem(condition
->ack_sem_
, SEM_INDEFINITE_WAIT
) == 0;
312 static INLINE
int pthread_cond_broadcast(pthread_cond_t
*const condition
) {
315 while (!__atomic_cmpxchg32(&condition
->wait_count_
, 0, 0))
316 ok
&= pthread_cond_signal(condition
) == 0;
321 static INLINE
int pthread_cond_wait(pthread_cond_t
*const condition
,
322 pthread_mutex_t
*const mutex
) {
325 __atomic_increment(&condition
->wait_count_
);
327 ok
&= pthread_mutex_unlock(mutex
) == 0;
329 ok
&= DosWaitEventSem(condition
->event_sem_
, SEM_INDEFINITE_WAIT
) == 0;
331 __atomic_decrement(&condition
->wait_count_
);
333 ok
&= DosPostEventSem(condition
->ack_sem_
) == 0;
335 pthread_mutex_lock(mutex
);
340 #include <pthread.h> // NOLINT
341 #define THREADFN void *
342 #define THREAD_RETURN(val) val
345 #endif // CONFIG_MULTITHREAD
347 // State of the worker thread object
349 NOT_OK
= 0, // object is unusable
351 WORK
// busy finishing the current task
354 // Function to be called by the worker thread. Takes two opaque pointers as
355 // arguments (data1 and data2), and should return false in case of error.
356 typedef int (*AVxWorkerHook
)(void *, void *);
358 // Platform-dependent implementation details for the worker.
359 typedef struct AVxWorkerImpl AVxWorkerImpl
;
361 // Synchronization object used to launch job in the worker thread
363 AVxWorkerImpl
*impl_
;
364 AVxWorkerStatus status_
;
365 AVxWorkerHook hook
; // hook to call
366 void *data1
; // first argument passed to 'hook'
367 void *data2
; // second argument passed to 'hook'
368 int had_error
; // return value of the last call to 'hook'
371 // The interface for all thread-worker related functions. All these functions
372 // must be implemented.
374 // Must be called first, before any other method.
375 void (*init
)(AVxWorker
*const worker
);
376 // Must be called to initialize the object and spawn the thread. Re-entrant.
377 // Will potentially launch the thread. Returns false in case of error.
378 int (*reset
)(AVxWorker
*const worker
);
379 // Makes sure the previous work is finished. Returns true if worker->had_error
380 // was not set and no error condition was triggered by the working thread.
381 int (*sync
)(AVxWorker
*const worker
);
382 // Triggers the thread to call hook() with data1 and data2 arguments. These
383 // hook/data1/data2 values can be changed at any time before calling this
384 // function, but not be changed afterward until the next call to Sync().
385 void (*launch
)(AVxWorker
*const worker
);
386 // This function is similar to launch() except that it calls the
387 // hook directly instead of using a thread. Convenient to bypass the thread
388 // mechanism while still using the AVxWorker structs. sync() must
389 // still be called afterward (for error reporting).
390 void (*execute
)(AVxWorker
*const worker
);
391 // Kill the thread and terminate the object. To use the object again, one
392 // must call reset() again.
393 void (*end
)(AVxWorker
*const worker
);
394 } AVxWorkerInterface
;
396 // Install a new set of threading functions, overriding the defaults. This
397 // should be done before any workers are started, i.e., before any encoding or
398 // decoding takes place. The contents of the interface struct are copied, it
399 // is safe to free the corresponding memory after this call. This function is
400 // not thread-safe. Return false in case of invalid pointer or methods.
401 int aom_set_worker_interface(const AVxWorkerInterface
*const winterface
);
403 // Retrieve the currently set thread worker interface.
404 const AVxWorkerInterface
*aom_get_worker_interface(void);
406 //------------------------------------------------------------------------------
412 #endif // AOM_THREAD_H_