| /* |
| * 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); |
| } |