955 lines
24 KiB
C
955 lines
24 KiB
C
|
/*
|
||
|
* The copyright in this software is being made available under the 2-clauses
|
||
|
* BSD License, included below. This software may be subject to other third
|
||
|
* party and contributor rights, including patent rights, and no such rights
|
||
|
* are granted under this license.
|
||
|
*
|
||
|
* Copyright (c) 2016, Even Rouault
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions
|
||
|
* are met:
|
||
|
* 1. Redistributions of source code must retain the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer.
|
||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer in the
|
||
|
* documentation and/or other materials provided with the distribution.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
|
||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
#include <assert.h>
|
||
|
|
||
|
#ifdef MUTEX_win32
|
||
|
|
||
|
/* Some versions of x86_64-w64-mingw32-gc -m32 resolve InterlockedCompareExchange() */
|
||
|
/* as __sync_val_compare_and_swap_4 but fails to link it. As this protects against */
|
||
|
/* a rather unlikely race, skip it */
|
||
|
#if !(defined(__MINGW32__) && defined(__i386__))
|
||
|
#define HAVE_INTERLOCKED_COMPARE_EXCHANGE 1
|
||
|
#endif
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include <process.h>
|
||
|
|
||
|
#include "opj_includes.h"
|
||
|
|
||
|
OPJ_BOOL OPJ_CALLCONV opj_has_thread_support(void)
|
||
|
{
|
||
|
return OPJ_TRUE;
|
||
|
}
|
||
|
|
||
|
int OPJ_CALLCONV opj_get_num_cpus(void)
|
||
|
{
|
||
|
SYSTEM_INFO info;
|
||
|
DWORD dwNum;
|
||
|
GetSystemInfo(&info);
|
||
|
dwNum = info.dwNumberOfProcessors;
|
||
|
if (dwNum < 1) {
|
||
|
return 1;
|
||
|
}
|
||
|
return (int)dwNum;
|
||
|
}
|
||
|
|
||
|
struct opj_mutex_t {
|
||
|
CRITICAL_SECTION cs;
|
||
|
};
|
||
|
|
||
|
opj_mutex_t* opj_mutex_create(void)
|
||
|
{
|
||
|
opj_mutex_t* mutex = (opj_mutex_t*) opj_malloc(sizeof(opj_mutex_t));
|
||
|
if (!mutex) {
|
||
|
return NULL;
|
||
|
}
|
||
|
InitializeCriticalSectionAndSpinCount(&(mutex->cs), 4000);
|
||
|
return mutex;
|
||
|
}
|
||
|
|
||
|
void opj_mutex_lock(opj_mutex_t* mutex)
|
||
|
{
|
||
|
EnterCriticalSection(&(mutex->cs));
|
||
|
}
|
||
|
|
||
|
void opj_mutex_unlock(opj_mutex_t* mutex)
|
||
|
{
|
||
|
LeaveCriticalSection(&(mutex->cs));
|
||
|
}
|
||
|
|
||
|
void opj_mutex_destroy(opj_mutex_t* mutex)
|
||
|
{
|
||
|
if (!mutex) {
|
||
|
return;
|
||
|
}
|
||
|
DeleteCriticalSection(&(mutex->cs));
|
||
|
opj_free(mutex);
|
||
|
}
|
||
|
|
||
|
struct opj_cond_waiter_list_t {
|
||
|
HANDLE hEvent;
|
||
|
struct opj_cond_waiter_list_t* next;
|
||
|
};
|
||
|
typedef struct opj_cond_waiter_list_t opj_cond_waiter_list_t;
|
||
|
|
||
|
struct opj_cond_t {
|
||
|
opj_mutex_t *internal_mutex;
|
||
|
opj_cond_waiter_list_t *waiter_list;
|
||
|
};
|
||
|
|
||
|
static DWORD TLSKey = 0;
|
||
|
static volatile LONG inTLSLockedSection = 0;
|
||
|
static volatile int TLSKeyInit = OPJ_FALSE;
|
||
|
|
||
|
opj_cond_t* opj_cond_create(void)
|
||
|
{
|
||
|
opj_cond_t* cond = (opj_cond_t*) opj_malloc(sizeof(opj_cond_t));
|
||
|
if (!cond) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* Make sure that the TLS key is allocated in a thread-safe way */
|
||
|
/* We cannot use a global mutex/critical section since its creation itself would not be */
|
||
|
/* thread-safe, so use InterlockedCompareExchange trick */
|
||
|
while (OPJ_TRUE) {
|
||
|
|
||
|
#if HAVE_INTERLOCKED_COMPARE_EXCHANGE
|
||
|
if (InterlockedCompareExchange(&inTLSLockedSection, 1, 0) == 0)
|
||
|
#endif
|
||
|
{
|
||
|
if (!TLSKeyInit) {
|
||
|
TLSKey = TlsAlloc();
|
||
|
TLSKeyInit = OPJ_TRUE;
|
||
|
}
|
||
|
#if HAVE_INTERLOCKED_COMPARE_EXCHANGE
|
||
|
InterlockedCompareExchange(&inTLSLockedSection, 0, 1);
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (TLSKey == TLS_OUT_OF_INDEXES) {
|
||
|
opj_free(cond);
|
||
|
return NULL;
|
||
|
}
|
||
|
cond->internal_mutex = opj_mutex_create();
|
||
|
if (cond->internal_mutex == NULL) {
|
||
|
opj_free(cond);
|
||
|
return NULL;
|
||
|
}
|
||
|
cond->waiter_list = NULL;
|
||
|
return cond;
|
||
|
}
|
||
|
|
||
|
void opj_cond_wait(opj_cond_t* cond, opj_mutex_t* mutex)
|
||
|
{
|
||
|
opj_cond_waiter_list_t* item;
|
||
|
HANDLE hEvent = (HANDLE) TlsGetValue(TLSKey);
|
||
|
if (hEvent == NULL) {
|
||
|
hEvent = CreateEvent(NULL, /* security attributes */
|
||
|
0, /* manual reset = no */
|
||
|
0, /* initial state = unsignaled */
|
||
|
NULL /* no name */);
|
||
|
assert(hEvent);
|
||
|
|
||
|
TlsSetValue(TLSKey, hEvent);
|
||
|
}
|
||
|
|
||
|
/* Insert the waiter into the waiter list of the condition */
|
||
|
opj_mutex_lock(cond->internal_mutex);
|
||
|
|
||
|
item = (opj_cond_waiter_list_t*)opj_malloc(sizeof(opj_cond_waiter_list_t));
|
||
|
assert(item != NULL);
|
||
|
|
||
|
item->hEvent = hEvent;
|
||
|
item->next = cond->waiter_list;
|
||
|
|
||
|
cond->waiter_list = item;
|
||
|
|
||
|
opj_mutex_unlock(cond->internal_mutex);
|
||
|
|
||
|
/* Release the client mutex before waiting for the event being signaled */
|
||
|
opj_mutex_unlock(mutex);
|
||
|
|
||
|
/* Ideally we would check that we do not get WAIT_FAILED but it is hard */
|
||
|
/* to report a failure. */
|
||
|
WaitForSingleObject(hEvent, INFINITE);
|
||
|
|
||
|
/* Reacquire the client mutex */
|
||
|
opj_mutex_lock(mutex);
|
||
|
}
|
||
|
|
||
|
void opj_cond_signal(opj_cond_t* cond)
|
||
|
{
|
||
|
opj_cond_waiter_list_t* psIter;
|
||
|
|
||
|
/* Signal the first registered event, and remove it from the list */
|
||
|
opj_mutex_lock(cond->internal_mutex);
|
||
|
|
||
|
psIter = cond->waiter_list;
|
||
|
if (psIter != NULL) {
|
||
|
SetEvent(psIter->hEvent);
|
||
|
cond->waiter_list = psIter->next;
|
||
|
opj_free(psIter);
|
||
|
}
|
||
|
|
||
|
opj_mutex_unlock(cond->internal_mutex);
|
||
|
}
|
||
|
|
||
|
void opj_cond_destroy(opj_cond_t* cond)
|
||
|
{
|
||
|
if (!cond) {
|
||
|
return;
|
||
|
}
|
||
|
opj_mutex_destroy(cond->internal_mutex);
|
||
|
assert(cond->waiter_list == NULL);
|
||
|
opj_free(cond);
|
||
|
}
|
||
|
|
||
|
struct opj_thread_t {
|
||
|
opj_thread_fn thread_fn;
|
||
|
void* user_data;
|
||
|
HANDLE hThread;
|
||
|
};
|
||
|
|
||
|
unsigned int __stdcall opj_thread_callback_adapter(void *info)
|
||
|
{
|
||
|
opj_thread_t* thread = (opj_thread_t*) info;
|
||
|
HANDLE hEvent = NULL;
|
||
|
|
||
|
thread->thread_fn(thread->user_data);
|
||
|
|
||
|
/* Free the handle possible allocated by a cond */
|
||
|
while (OPJ_TRUE) {
|
||
|
/* Make sure TLSKey is not being created just at that moment... */
|
||
|
#if HAVE_INTERLOCKED_COMPARE_EXCHANGE
|
||
|
if (InterlockedCompareExchange(&inTLSLockedSection, 1, 0) == 0)
|
||
|
#endif
|
||
|
{
|
||
|
if (TLSKeyInit) {
|
||
|
hEvent = (HANDLE) TlsGetValue(TLSKey);
|
||
|
}
|
||
|
#if HAVE_INTERLOCKED_COMPARE_EXCHANGE
|
||
|
InterlockedCompareExchange(&inTLSLockedSection, 0, 1);
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (hEvent) {
|
||
|
CloseHandle(hEvent);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
opj_thread_t* opj_thread_create(opj_thread_fn thread_fn, void* user_data)
|
||
|
{
|
||
|
opj_thread_t* thread;
|
||
|
|
||
|
assert(thread_fn);
|
||
|
|
||
|
thread = (opj_thread_t*) opj_malloc(sizeof(opj_thread_t));
|
||
|
if (!thread) {
|
||
|
return NULL;
|
||
|
}
|
||
|
thread->thread_fn = thread_fn;
|
||
|
thread->user_data = user_data;
|
||
|
|
||
|
thread->hThread = (HANDLE)_beginthreadex(NULL, 0,
|
||
|
opj_thread_callback_adapter, thread, 0, NULL);
|
||
|
|
||
|
if (thread->hThread == NULL) {
|
||
|
opj_free(thread);
|
||
|
return NULL;
|
||
|
}
|
||
|
return thread;
|
||
|
}
|
||
|
|
||
|
void opj_thread_join(opj_thread_t* thread)
|
||
|
{
|
||
|
WaitForSingleObject(thread->hThread, INFINITE);
|
||
|
CloseHandle(thread->hThread);
|
||
|
|
||
|
opj_free(thread);
|
||
|
}
|
||
|
|
||
|
#elif MUTEX_pthread
|
||
|
|
||
|
#include <pthread.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
/* Moved after all system includes, and in particular pthread.h, so as to */
|
||
|
/* avoid poisoning issuing with malloc() use in pthread.h with ulibc (#1013) */
|
||
|
#include "opj_includes.h"
|
||
|
|
||
|
OPJ_BOOL OPJ_CALLCONV opj_has_thread_support(void)
|
||
|
{
|
||
|
return OPJ_TRUE;
|
||
|
}
|
||
|
|
||
|
int OPJ_CALLCONV opj_get_num_cpus(void)
|
||
|
{
|
||
|
#ifdef _SC_NPROCESSORS_ONLN
|
||
|
return (int)sysconf(_SC_NPROCESSORS_ONLN);
|
||
|
#else
|
||
|
return 1;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
struct opj_mutex_t {
|
||
|
pthread_mutex_t mutex;
|
||
|
};
|
||
|
|
||
|
opj_mutex_t* opj_mutex_create(void)
|
||
|
{
|
||
|
opj_mutex_t* mutex = (opj_mutex_t*) opj_calloc(1U, sizeof(opj_mutex_t));
|
||
|
if (mutex != NULL) {
|
||
|
if (pthread_mutex_init(&mutex->mutex, NULL) != 0) {
|
||
|
opj_free(mutex);
|
||
|
mutex = NULL;
|
||
|
}
|
||
|
}
|
||
|
return mutex;
|
||
|
}
|
||
|
|
||
|
void opj_mutex_lock(opj_mutex_t* mutex)
|
||
|
{
|
||
|
pthread_mutex_lock(&(mutex->mutex));
|
||
|
}
|
||
|
|
||
|
void opj_mutex_unlock(opj_mutex_t* mutex)
|
||
|
{
|
||
|
pthread_mutex_unlock(&(mutex->mutex));
|
||
|
}
|
||
|
|
||
|
void opj_mutex_destroy(opj_mutex_t* mutex)
|
||
|
{
|
||
|
if (!mutex) {
|
||
|
return;
|
||
|
}
|
||
|
pthread_mutex_destroy(&(mutex->mutex));
|
||
|
opj_free(mutex);
|
||
|
}
|
||
|
|
||
|
struct opj_cond_t {
|
||
|
pthread_cond_t cond;
|
||
|
};
|
||
|
|
||
|
opj_cond_t* opj_cond_create(void)
|
||
|
{
|
||
|
opj_cond_t* cond = (opj_cond_t*) opj_malloc(sizeof(opj_cond_t));
|
||
|
if (!cond) {
|
||
|
return NULL;
|
||
|
}
|
||
|
if (pthread_cond_init(&(cond->cond), NULL) != 0) {
|
||
|
opj_free(cond);
|
||
|
return NULL;
|
||
|
}
|
||
|
return cond;
|
||
|
}
|
||
|
|
||
|
void opj_cond_wait(opj_cond_t* cond, opj_mutex_t* mutex)
|
||
|
{
|
||
|
pthread_cond_wait(&(cond->cond), &(mutex->mutex));
|
||
|
}
|
||
|
|
||
|
void opj_cond_signal(opj_cond_t* cond)
|
||
|
{
|
||
|
int ret = pthread_cond_signal(&(cond->cond));
|
||
|
(void)ret;
|
||
|
assert(ret == 0);
|
||
|
}
|
||
|
|
||
|
void opj_cond_destroy(opj_cond_t* cond)
|
||
|
{
|
||
|
if (!cond) {
|
||
|
return;
|
||
|
}
|
||
|
pthread_cond_destroy(&(cond->cond));
|
||
|
opj_free(cond);
|
||
|
}
|
||
|
|
||
|
|
||
|
struct opj_thread_t {
|
||
|
opj_thread_fn thread_fn;
|
||
|
void* user_data;
|
||
|
pthread_t thread;
|
||
|
};
|
||
|
|
||
|
static void* opj_thread_callback_adapter(void* info)
|
||
|
{
|
||
|
opj_thread_t* thread = (opj_thread_t*) info;
|
||
|
thread->thread_fn(thread->user_data);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
opj_thread_t* opj_thread_create(opj_thread_fn thread_fn, void* user_data)
|
||
|
{
|
||
|
pthread_attr_t attr;
|
||
|
opj_thread_t* thread;
|
||
|
|
||
|
assert(thread_fn);
|
||
|
|
||
|
thread = (opj_thread_t*) opj_malloc(sizeof(opj_thread_t));
|
||
|
if (!thread) {
|
||
|
return NULL;
|
||
|
}
|
||
|
thread->thread_fn = thread_fn;
|
||
|
thread->user_data = user_data;
|
||
|
|
||
|
pthread_attr_init(&attr);
|
||
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
||
|
if (pthread_create(&(thread->thread), &attr,
|
||
|
opj_thread_callback_adapter, (void *) thread) != 0) {
|
||
|
opj_free(thread);
|
||
|
return NULL;
|
||
|
}
|
||
|
return thread;
|
||
|
}
|
||
|
|
||
|
void opj_thread_join(opj_thread_t* thread)
|
||
|
{
|
||
|
void* status;
|
||
|
pthread_join(thread->thread, &status);
|
||
|
|
||
|
opj_free(thread);
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
/* Stub implementation */
|
||
|
|
||
|
#include "opj_includes.h"
|
||
|
|
||
|
OPJ_BOOL OPJ_CALLCONV opj_has_thread_support(void)
|
||
|
{
|
||
|
return OPJ_FALSE;
|
||
|
}
|
||
|
|
||
|
int OPJ_CALLCONV opj_get_num_cpus(void)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
opj_mutex_t* opj_mutex_create(void)
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void opj_mutex_lock(opj_mutex_t* mutex)
|
||
|
{
|
||
|
(void) mutex;
|
||
|
}
|
||
|
|
||
|
void opj_mutex_unlock(opj_mutex_t* mutex)
|
||
|
{
|
||
|
(void) mutex;
|
||
|
}
|
||
|
|
||
|
void opj_mutex_destroy(opj_mutex_t* mutex)
|
||
|
{
|
||
|
(void) mutex;
|
||
|
}
|
||
|
|
||
|
opj_cond_t* opj_cond_create(void)
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void opj_cond_wait(opj_cond_t* cond, opj_mutex_t* mutex)
|
||
|
{
|
||
|
(void) cond;
|
||
|
(void) mutex;
|
||
|
}
|
||
|
|
||
|
void opj_cond_signal(opj_cond_t* cond)
|
||
|
{
|
||
|
(void) cond;
|
||
|
}
|
||
|
|
||
|
void opj_cond_destroy(opj_cond_t* cond)
|
||
|
{
|
||
|
(void) cond;
|
||
|
}
|
||
|
|
||
|
opj_thread_t* opj_thread_create(opj_thread_fn thread_fn, void* user_data)
|
||
|
{
|
||
|
(void) thread_fn;
|
||
|
(void) user_data;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void opj_thread_join(opj_thread_t* thread)
|
||
|
{
|
||
|
(void) thread;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
typedef struct {
|
||
|
int key;
|
||
|
void* value;
|
||
|
opj_tls_free_func opj_free_func;
|
||
|
} opj_tls_key_val_t;
|
||
|
|
||
|
struct opj_tls_t {
|
||
|
opj_tls_key_val_t* key_val;
|
||
|
int key_val_count;
|
||
|
};
|
||
|
|
||
|
static opj_tls_t* opj_tls_new(void)
|
||
|
{
|
||
|
return (opj_tls_t*) opj_calloc(1, sizeof(opj_tls_t));
|
||
|
}
|
||
|
|
||
|
static void opj_tls_destroy(opj_tls_t* tls)
|
||
|
{
|
||
|
int i;
|
||
|
if (!tls) {
|
||
|
return;
|
||
|
}
|
||
|
for (i = 0; i < tls->key_val_count; i++) {
|
||
|
if (tls->key_val[i].opj_free_func) {
|
||
|
tls->key_val[i].opj_free_func(tls->key_val[i].value);
|
||
|
}
|
||
|
}
|
||
|
opj_free(tls->key_val);
|
||
|
opj_free(tls);
|
||
|
}
|
||
|
|
||
|
void* opj_tls_get(opj_tls_t* tls, int key)
|
||
|
{
|
||
|
int i;
|
||
|
for (i = 0; i < tls->key_val_count; i++) {
|
||
|
if (tls->key_val[i].key == key) {
|
||
|
return tls->key_val[i].value;
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
OPJ_BOOL opj_tls_set(opj_tls_t* tls, int key, void* value,
|
||
|
opj_tls_free_func opj_free_func)
|
||
|
{
|
||
|
opj_tls_key_val_t* new_key_val;
|
||
|
int i;
|
||
|
|
||
|
if (tls->key_val_count == INT_MAX) {
|
||
|
return OPJ_FALSE;
|
||
|
}
|
||
|
for (i = 0; i < tls->key_val_count; i++) {
|
||
|
if (tls->key_val[i].key == key) {
|
||
|
if (tls->key_val[i].opj_free_func) {
|
||
|
tls->key_val[i].opj_free_func(tls->key_val[i].value);
|
||
|
}
|
||
|
tls->key_val[i].value = value;
|
||
|
tls->key_val[i].opj_free_func = opj_free_func;
|
||
|
return OPJ_TRUE;
|
||
|
}
|
||
|
}
|
||
|
new_key_val = (opj_tls_key_val_t*) opj_realloc(tls->key_val,
|
||
|
((size_t)tls->key_val_count + 1U) * sizeof(opj_tls_key_val_t));
|
||
|
if (!new_key_val) {
|
||
|
return OPJ_FALSE;
|
||
|
}
|
||
|
tls->key_val = new_key_val;
|
||
|
new_key_val[tls->key_val_count].key = key;
|
||
|
new_key_val[tls->key_val_count].value = value;
|
||
|
new_key_val[tls->key_val_count].opj_free_func = opj_free_func;
|
||
|
tls->key_val_count ++;
|
||
|
return OPJ_TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
typedef struct {
|
||
|
opj_job_fn job_fn;
|
||
|
void *user_data;
|
||
|
} opj_worker_thread_job_t;
|
||
|
|
||
|
typedef struct {
|
||
|
opj_thread_pool_t *tp;
|
||
|
opj_thread_t *thread;
|
||
|
int marked_as_waiting;
|
||
|
|
||
|
opj_mutex_t *mutex;
|
||
|
opj_cond_t *cond;
|
||
|
} opj_worker_thread_t;
|
||
|
|
||
|
typedef enum {
|
||
|
OPJWTS_OK,
|
||
|
OPJWTS_STOP,
|
||
|
OPJWTS_ERROR
|
||
|
} opj_worker_thread_state;
|
||
|
|
||
|
struct opj_job_list_t {
|
||
|
opj_worker_thread_job_t* job;
|
||
|
struct opj_job_list_t* next;
|
||
|
};
|
||
|
typedef struct opj_job_list_t opj_job_list_t;
|
||
|
|
||
|
struct opj_worker_thread_list_t {
|
||
|
opj_worker_thread_t* worker_thread;
|
||
|
struct opj_worker_thread_list_t* next;
|
||
|
};
|
||
|
typedef struct opj_worker_thread_list_t opj_worker_thread_list_t;
|
||
|
|
||
|
struct opj_thread_pool_t {
|
||
|
opj_worker_thread_t* worker_threads;
|
||
|
int worker_threads_count;
|
||
|
opj_cond_t* cond;
|
||
|
opj_mutex_t* mutex;
|
||
|
volatile opj_worker_thread_state state;
|
||
|
opj_job_list_t* job_queue;
|
||
|
volatile int pending_jobs_count;
|
||
|
opj_worker_thread_list_t* waiting_worker_thread_list;
|
||
|
int waiting_worker_thread_count;
|
||
|
opj_tls_t* tls;
|
||
|
int signaling_threshold;
|
||
|
};
|
||
|
|
||
|
static OPJ_BOOL opj_thread_pool_setup(opj_thread_pool_t* tp, int num_threads);
|
||
|
static opj_worker_thread_job_t* opj_thread_pool_get_next_job(
|
||
|
opj_thread_pool_t* tp,
|
||
|
opj_worker_thread_t* worker_thread,
|
||
|
OPJ_BOOL signal_job_finished);
|
||
|
|
||
|
opj_thread_pool_t* opj_thread_pool_create(int num_threads)
|
||
|
{
|
||
|
opj_thread_pool_t* tp;
|
||
|
|
||
|
tp = (opj_thread_pool_t*) opj_calloc(1, sizeof(opj_thread_pool_t));
|
||
|
if (!tp) {
|
||
|
return NULL;
|
||
|
}
|
||
|
tp->state = OPJWTS_OK;
|
||
|
|
||
|
if (num_threads <= 0) {
|
||
|
tp->tls = opj_tls_new();
|
||
|
if (!tp->tls) {
|
||
|
opj_free(tp);
|
||
|
tp = NULL;
|
||
|
}
|
||
|
return tp;
|
||
|
}
|
||
|
|
||
|
tp->mutex = opj_mutex_create();
|
||
|
if (!tp->mutex) {
|
||
|
opj_free(tp);
|
||
|
return NULL;
|
||
|
}
|
||
|
if (!opj_thread_pool_setup(tp, num_threads)) {
|
||
|
opj_thread_pool_destroy(tp);
|
||
|
return NULL;
|
||
|
}
|
||
|
return tp;
|
||
|
}
|
||
|
|
||
|
static void opj_worker_thread_function(void* user_data)
|
||
|
{
|
||
|
opj_worker_thread_t* worker_thread;
|
||
|
opj_thread_pool_t* tp;
|
||
|
opj_tls_t* tls;
|
||
|
OPJ_BOOL job_finished = OPJ_FALSE;
|
||
|
|
||
|
worker_thread = (opj_worker_thread_t*) user_data;
|
||
|
tp = worker_thread->tp;
|
||
|
tls = opj_tls_new();
|
||
|
|
||
|
while (OPJ_TRUE) {
|
||
|
opj_worker_thread_job_t* job = opj_thread_pool_get_next_job(tp, worker_thread,
|
||
|
job_finished);
|
||
|
if (job == NULL) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (job->job_fn) {
|
||
|
job->job_fn(job->user_data, tls);
|
||
|
}
|
||
|
opj_free(job);
|
||
|
job_finished = OPJ_TRUE;
|
||
|
}
|
||
|
|
||
|
opj_tls_destroy(tls);
|
||
|
}
|
||
|
|
||
|
static OPJ_BOOL opj_thread_pool_setup(opj_thread_pool_t* tp, int num_threads)
|
||
|
{
|
||
|
int i;
|
||
|
OPJ_BOOL bRet = OPJ_TRUE;
|
||
|
|
||
|
assert(num_threads > 0);
|
||
|
|
||
|
tp->cond = opj_cond_create();
|
||
|
if (tp->cond == NULL) {
|
||
|
return OPJ_FALSE;
|
||
|
}
|
||
|
|
||
|
tp->worker_threads = (opj_worker_thread_t*) opj_calloc((size_t)num_threads,
|
||
|
sizeof(opj_worker_thread_t));
|
||
|
if (tp->worker_threads == NULL) {
|
||
|
return OPJ_FALSE;
|
||
|
}
|
||
|
tp->worker_threads_count = num_threads;
|
||
|
|
||
|
for (i = 0; i < num_threads; i++) {
|
||
|
tp->worker_threads[i].tp = tp;
|
||
|
|
||
|
tp->worker_threads[i].mutex = opj_mutex_create();
|
||
|
if (tp->worker_threads[i].mutex == NULL) {
|
||
|
tp->worker_threads_count = i;
|
||
|
bRet = OPJ_FALSE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
tp->worker_threads[i].cond = opj_cond_create();
|
||
|
if (tp->worker_threads[i].cond == NULL) {
|
||
|
opj_mutex_destroy(tp->worker_threads[i].mutex);
|
||
|
tp->worker_threads_count = i;
|
||
|
bRet = OPJ_FALSE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
tp->worker_threads[i].marked_as_waiting = OPJ_FALSE;
|
||
|
|
||
|
tp->worker_threads[i].thread = opj_thread_create(opj_worker_thread_function,
|
||
|
&(tp->worker_threads[i]));
|
||
|
if (tp->worker_threads[i].thread == NULL) {
|
||
|
opj_mutex_destroy(tp->worker_threads[i].mutex);
|
||
|
opj_cond_destroy(tp->worker_threads[i].cond);
|
||
|
tp->worker_threads_count = i;
|
||
|
bRet = OPJ_FALSE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Wait all threads to be started */
|
||
|
/* printf("waiting for all threads to be started\n"); */
|
||
|
opj_mutex_lock(tp->mutex);
|
||
|
while (tp->waiting_worker_thread_count < tp->worker_threads_count) {
|
||
|
opj_cond_wait(tp->cond, tp->mutex);
|
||
|
}
|
||
|
opj_mutex_unlock(tp->mutex);
|
||
|
/* printf("all threads started\n"); */
|
||
|
|
||
|
if (tp->state == OPJWTS_ERROR) {
|
||
|
bRet = OPJ_FALSE;
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
void opj_waiting()
|
||
|
{
|
||
|
printf("waiting!\n");
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
static opj_worker_thread_job_t* opj_thread_pool_get_next_job(
|
||
|
opj_thread_pool_t* tp,
|
||
|
opj_worker_thread_t* worker_thread,
|
||
|
OPJ_BOOL signal_job_finished)
|
||
|
{
|
||
|
while (OPJ_TRUE) {
|
||
|
opj_job_list_t* top_job_iter;
|
||
|
|
||
|
opj_mutex_lock(tp->mutex);
|
||
|
|
||
|
if (signal_job_finished) {
|
||
|
signal_job_finished = OPJ_FALSE;
|
||
|
tp->pending_jobs_count --;
|
||
|
/*printf("tp=%p, remaining jobs: %d\n", tp, tp->pending_jobs_count);*/
|
||
|
if (tp->pending_jobs_count <= tp->signaling_threshold) {
|
||
|
opj_cond_signal(tp->cond);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (tp->state == OPJWTS_STOP) {
|
||
|
opj_mutex_unlock(tp->mutex);
|
||
|
return NULL;
|
||
|
}
|
||
|
top_job_iter = tp->job_queue;
|
||
|
if (top_job_iter) {
|
||
|
opj_worker_thread_job_t* job;
|
||
|
tp->job_queue = top_job_iter->next;
|
||
|
|
||
|
job = top_job_iter->job;
|
||
|
opj_mutex_unlock(tp->mutex);
|
||
|
opj_free(top_job_iter);
|
||
|
return job;
|
||
|
}
|
||
|
|
||
|
/* opj_waiting(); */
|
||
|
if (!worker_thread->marked_as_waiting) {
|
||
|
opj_worker_thread_list_t* item;
|
||
|
|
||
|
worker_thread->marked_as_waiting = OPJ_TRUE;
|
||
|
tp->waiting_worker_thread_count ++;
|
||
|
assert(tp->waiting_worker_thread_count <= tp->worker_threads_count);
|
||
|
|
||
|
item = (opj_worker_thread_list_t*) opj_malloc(sizeof(opj_worker_thread_list_t));
|
||
|
if (item == NULL) {
|
||
|
tp->state = OPJWTS_ERROR;
|
||
|
opj_cond_signal(tp->cond);
|
||
|
|
||
|
opj_mutex_unlock(tp->mutex);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
item->worker_thread = worker_thread;
|
||
|
item->next = tp->waiting_worker_thread_list;
|
||
|
tp->waiting_worker_thread_list = item;
|
||
|
}
|
||
|
|
||
|
/* printf("signaling that worker thread is ready\n"); */
|
||
|
opj_cond_signal(tp->cond);
|
||
|
|
||
|
opj_mutex_lock(worker_thread->mutex);
|
||
|
opj_mutex_unlock(tp->mutex);
|
||
|
|
||
|
/* printf("waiting for job\n"); */
|
||
|
opj_cond_wait(worker_thread->cond, worker_thread->mutex);
|
||
|
|
||
|
opj_mutex_unlock(worker_thread->mutex);
|
||
|
/* printf("got job\n"); */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
OPJ_BOOL opj_thread_pool_submit_job(opj_thread_pool_t* tp,
|
||
|
opj_job_fn job_fn,
|
||
|
void* user_data)
|
||
|
{
|
||
|
opj_worker_thread_job_t* job;
|
||
|
opj_job_list_t* item;
|
||
|
|
||
|
if (tp->mutex == NULL) {
|
||
|
job_fn(user_data, tp->tls);
|
||
|
return OPJ_TRUE;
|
||
|
}
|
||
|
|
||
|
job = (opj_worker_thread_job_t*)opj_malloc(sizeof(opj_worker_thread_job_t));
|
||
|
if (job == NULL) {
|
||
|
return OPJ_FALSE;
|
||
|
}
|
||
|
job->job_fn = job_fn;
|
||
|
job->user_data = user_data;
|
||
|
|
||
|
item = (opj_job_list_t*) opj_malloc(sizeof(opj_job_list_t));
|
||
|
if (item == NULL) {
|
||
|
opj_free(job);
|
||
|
return OPJ_FALSE;
|
||
|
}
|
||
|
item->job = job;
|
||
|
|
||
|
opj_mutex_lock(tp->mutex);
|
||
|
|
||
|
tp->signaling_threshold = 100 * tp->worker_threads_count;
|
||
|
while (tp->pending_jobs_count > tp->signaling_threshold) {
|
||
|
/* printf("%d jobs enqueued. Waiting\n", tp->pending_jobs_count); */
|
||
|
opj_cond_wait(tp->cond, tp->mutex);
|
||
|
/* printf("...%d jobs enqueued.\n", tp->pending_jobs_count); */
|
||
|
}
|
||
|
|
||
|
item->next = tp->job_queue;
|
||
|
tp->job_queue = item;
|
||
|
tp->pending_jobs_count ++;
|
||
|
|
||
|
if (tp->waiting_worker_thread_list) {
|
||
|
opj_worker_thread_t* worker_thread;
|
||
|
opj_worker_thread_list_t* next;
|
||
|
opj_worker_thread_list_t* to_opj_free;
|
||
|
|
||
|
worker_thread = tp->waiting_worker_thread_list->worker_thread;
|
||
|
|
||
|
assert(worker_thread->marked_as_waiting);
|
||
|
worker_thread->marked_as_waiting = OPJ_FALSE;
|
||
|
|
||
|
next = tp->waiting_worker_thread_list->next;
|
||
|
to_opj_free = tp->waiting_worker_thread_list;
|
||
|
tp->waiting_worker_thread_list = next;
|
||
|
tp->waiting_worker_thread_count --;
|
||
|
|
||
|
opj_mutex_lock(worker_thread->mutex);
|
||
|
opj_mutex_unlock(tp->mutex);
|
||
|
opj_cond_signal(worker_thread->cond);
|
||
|
opj_mutex_unlock(worker_thread->mutex);
|
||
|
|
||
|
opj_free(to_opj_free);
|
||
|
} else {
|
||
|
opj_mutex_unlock(tp->mutex);
|
||
|
}
|
||
|
|
||
|
return OPJ_TRUE;
|
||
|
}
|
||
|
|
||
|
void opj_thread_pool_wait_completion(opj_thread_pool_t* tp,
|
||
|
int max_remaining_jobs)
|
||
|
{
|
||
|
if (tp->mutex == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (max_remaining_jobs < 0) {
|
||
|
max_remaining_jobs = 0;
|
||
|
}
|
||
|
opj_mutex_lock(tp->mutex);
|
||
|
tp->signaling_threshold = max_remaining_jobs;
|
||
|
while (tp->pending_jobs_count > max_remaining_jobs) {
|
||
|
/*printf("tp=%p, jobs before wait = %d, max_remaining_jobs = %d\n", tp, tp->pending_jobs_count, max_remaining_jobs);*/
|
||
|
opj_cond_wait(tp->cond, tp->mutex);
|
||
|
/*printf("tp=%p, jobs after wait = %d\n", tp, tp->pending_jobs_count);*/
|
||
|
}
|
||
|
opj_mutex_unlock(tp->mutex);
|
||
|
}
|
||
|
|
||
|
int opj_thread_pool_get_thread_count(opj_thread_pool_t* tp)
|
||
|
{
|
||
|
return tp->worker_threads_count;
|
||
|
}
|
||
|
|
||
|
void opj_thread_pool_destroy(opj_thread_pool_t* tp)
|
||
|
{
|
||
|
if (!tp) {
|
||
|
return;
|
||
|
}
|
||
|
if (tp->cond) {
|
||
|
int i;
|
||
|
opj_thread_pool_wait_completion(tp, 0);
|
||
|
|
||
|
opj_mutex_lock(tp->mutex);
|
||
|
tp->state = OPJWTS_STOP;
|
||
|
opj_mutex_unlock(tp->mutex);
|
||
|
|
||
|
for (i = 0; i < tp->worker_threads_count; i++) {
|
||
|
opj_mutex_lock(tp->worker_threads[i].mutex);
|
||
|
opj_cond_signal(tp->worker_threads[i].cond);
|
||
|
opj_mutex_unlock(tp->worker_threads[i].mutex);
|
||
|
opj_thread_join(tp->worker_threads[i].thread);
|
||
|
opj_cond_destroy(tp->worker_threads[i].cond);
|
||
|
opj_mutex_destroy(tp->worker_threads[i].mutex);
|
||
|
}
|
||
|
|
||
|
opj_free(tp->worker_threads);
|
||
|
|
||
|
while (tp->waiting_worker_thread_list != NULL) {
|
||
|
opj_worker_thread_list_t* next = tp->waiting_worker_thread_list->next;
|
||
|
opj_free(tp->waiting_worker_thread_list);
|
||
|
tp->waiting_worker_thread_list = next;
|
||
|
}
|
||
|
|
||
|
opj_cond_destroy(tp->cond);
|
||
|
}
|
||
|
opj_mutex_destroy(tp->mutex);
|
||
|
opj_tls_destroy(tp->tls);
|
||
|
opj_free(tp);
|
||
|
}
|