blob: 55403a8c9e6f58bd163a502e48891181a5df5fae [file] [log] [blame]
// Copyright 2011 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
// Multi-threaded worker
//
// Author: Skal (pascal.massimino@gmail.com)
#if defined(STARBOARD)
#include "starboard/log.h"
#include "starboard/memory.h"
#else
#include <string.h> // for SbMemorySet()
#endif
#include "./thread.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
#ifdef WEBP_USE_THREAD
#if defined(_WIN32)
//------------------------------------------------------------------------------
// simplistic pthread emulation layer
#include <process.h>
// _beginthreadex requires __stdcall
#define THREADFN unsigned int __stdcall
#define THREAD_RETURN(val) (unsigned int)((DWORD_PTR)val)
static int pthread_create(pthread_t* const thread, const void* attr,
unsigned int (__stdcall *start)(void*), void* arg) {
(void)attr;
*thread = (pthread_t)_beginthreadex(NULL, /* void *security */
0, /* unsigned stack_size */
start,
arg,
0, /* unsigned initflag */
NULL); /* unsigned *thrdaddr */
if (*thread == NULL) return 1;
SetThreadPriority(*thread, THREAD_PRIORITY_ABOVE_NORMAL);
return 0;
}
static int pthread_join(pthread_t thread, void** value_ptr) {
(void)value_ptr;
return (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0 ||
CloseHandle(thread) == 0);
}
// Mutex
static int pthread_mutex_init(pthread_mutex_t* const mutex, void* mutexattr) {
(void)mutexattr;
InitializeCriticalSection(mutex);
return 0;
}
static int pthread_mutex_lock(pthread_mutex_t* const mutex) {
EnterCriticalSection(mutex);
return 0;
}
static int pthread_mutex_unlock(pthread_mutex_t* const mutex) {
LeaveCriticalSection(mutex);
return 0;
}
static int pthread_mutex_destroy(pthread_mutex_t* const mutex) {
DeleteCriticalSection(mutex);
return 0;
}
// Condition
static int pthread_cond_destroy(pthread_cond_t* const condition) {
int ok = 1;
ok &= (CloseHandle(condition->waiting_sem_) != 0);
ok &= (CloseHandle(condition->received_sem_) != 0);
ok &= (CloseHandle(condition->signal_event_) != 0);
return !ok;
}
static int pthread_cond_init(pthread_cond_t* const condition, void* cond_attr) {
(void)cond_attr;
condition->waiting_sem_ = CreateSemaphore(NULL, 0, 1, NULL);
condition->received_sem_ = CreateSemaphore(NULL, 0, 1, NULL);
condition->signal_event_ = CreateEvent(NULL, FALSE, FALSE, NULL);
if (condition->waiting_sem_ == NULL ||
condition->received_sem_ == NULL ||
condition->signal_event_ == NULL) {
pthread_cond_destroy(condition);
return 1;
}
return 0;
}
static int pthread_cond_signal(pthread_cond_t* const condition) {
int ok = 1;
if (WaitForSingleObject(condition->waiting_sem_, 0) == WAIT_OBJECT_0) {
// a thread is waiting in pthread_cond_wait: allow it to be notified
ok = SetEvent(condition->signal_event_);
// wait until the event is consumed so the signaler cannot consume
// the event via its own pthread_cond_wait.
ok &= (WaitForSingleObject(condition->received_sem_, INFINITE) !=
WAIT_OBJECT_0);
}
return !ok;
}
static int pthread_cond_wait(pthread_cond_t* const condition,
pthread_mutex_t* const mutex) {
int ok;
// note that there is a consumer available so the signal isn't dropped in
// pthread_cond_signal
if (!ReleaseSemaphore(condition->waiting_sem_, 1, NULL))
return 1;
// now unlock the mutex so pthread_cond_signal may be issued
pthread_mutex_unlock(mutex);
ok = (WaitForSingleObject(condition->signal_event_, INFINITE) ==
WAIT_OBJECT_0);
ok &= ReleaseSemaphore(condition->received_sem_, 1, NULL);
pthread_mutex_lock(mutex);
return !ok;
}
#else // _WIN32
# define THREADFN void*
# define THREAD_RETURN(val) val
#endif
//------------------------------------------------------------------------------
static THREADFN WebPWorkerThreadLoop(void *ptr) { // thread loop
WebPWorker* const worker = (WebPWorker*)ptr;
int done = 0;
while (!done) {
pthread_mutex_lock(&worker->mutex_);
while (worker->status_ == OK) { // wait in idling mode
pthread_cond_wait(&worker->condition_, &worker->mutex_);
}
if (worker->status_ == WORK) {
if (worker->hook) {
worker->had_error |= !worker->hook(worker->data1, worker->data2);
}
worker->status_ = OK;
} else if (worker->status_ == NOT_OK) { // finish the worker
done = 1;
}
// signal to the main thread that we're done (for Sync())
pthread_cond_signal(&worker->condition_);
pthread_mutex_unlock(&worker->mutex_);
}
return THREAD_RETURN(NULL); // Thread is finished
}
// main thread state control
static void WebPWorkerChangeState(WebPWorker* const worker,
WebPWorkerStatus new_status) {
// no-op when attempting to change state on a thread that didn't come up
if (worker->status_ < OK) return;
pthread_mutex_lock(&worker->mutex_);
// wait for the worker to finish
while (worker->status_ != OK) {
pthread_cond_wait(&worker->condition_, &worker->mutex_);
}
// assign new status and release the working thread if needed
if (new_status != OK) {
worker->status_ = new_status;
pthread_cond_signal(&worker->condition_);
}
pthread_mutex_unlock(&worker->mutex_);
}
#endif
//------------------------------------------------------------------------------
void WebPWorkerInit(WebPWorker* const worker) {
SbMemorySet(worker, 0, sizeof(*worker));
worker->status_ = NOT_OK;
}
int WebPWorkerSync(WebPWorker* const worker) {
#ifdef WEBP_USE_THREAD
WebPWorkerChangeState(worker, OK);
#endif
SB_DCHECK(worker->status_ <= OK);
return !worker->had_error;
}
int WebPWorkerReset(WebPWorker* const worker) {
int ok = 1;
worker->had_error = 0;
if (worker->status_ < OK) {
#ifdef WEBP_USE_THREAD
if (pthread_mutex_init(&worker->mutex_, NULL) ||
pthread_cond_init(&worker->condition_, NULL)) {
return 0;
}
pthread_mutex_lock(&worker->mutex_);
ok = !pthread_create(&worker->thread_, NULL, WebPWorkerThreadLoop, worker);
if (ok) worker->status_ = OK;
pthread_mutex_unlock(&worker->mutex_);
#else
worker->status_ = OK;
#endif
} else if (worker->status_ > OK) {
ok = WebPWorkerSync(worker);
}
SB_DCHECK(!ok || (worker->status_ == OK));
return ok;
}
void WebPWorkerLaunch(WebPWorker* const worker) {
#ifdef WEBP_USE_THREAD
WebPWorkerChangeState(worker, WORK);
#else
if (worker->hook)
worker->had_error |= !worker->hook(worker->data1, worker->data2);
#endif
}
void WebPWorkerEnd(WebPWorker* const worker) {
if (worker->status_ >= OK) {
#ifdef WEBP_USE_THREAD
WebPWorkerChangeState(worker, NOT_OK);
pthread_join(worker->thread_, NULL);
pthread_mutex_destroy(&worker->mutex_);
pthread_cond_destroy(&worker->condition_);
#else
worker->status_ = NOT_OK;
#endif
}
SB_DCHECK(worker->status_ == NOT_OK);
}
//------------------------------------------------------------------------------
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
#endif