blob: 89767bef3970b4ceb0010576c72a7cbabe60bf6a [file] [log] [blame]
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/client_porting/pr_starboard/pr_starboard.h"
#include "nb/thread_local_object.h"
#include "starboard/condition_variable.h"
#include "starboard/log.h"
#include "starboard/memory.h"
#include "starboard/mutex.h"
#include "starboard/once.h"
#include "starboard/queue.h"
#include "starboard/thread.h"
#include "starboard/time.h"
#include "starboard/types.h"
namespace {
typedef starboard::Queue<PRThread*> SetupSignalQueue;
typedef nb::ThreadLocalObject<PRThread> ThreadLocalPRThread;
SB_ONCE_INITIALIZE_FUNCTION(ThreadLocalPRThread,
g_local_pr_thread);
// Utility function to convert a PRInterval to signed 64 bit integer
// microseconds.
int64_t PR_IntervalToMicrosecondsInt64(PRIntervalTime ticks) {
uint32_t microseconds_as_uint32 = PR_IntervalToMicroseconds(ticks);
int64_t microseconds_as_int64 = static_cast<int64_t>(microseconds_as_uint32);
return microseconds_as_int64;
}
// Struct to bundle up arguments to be passed into SbThreadCreate.
struct ThreadEntryPointWrapperContext {
ThreadEntryPointWrapperContext(void* pr_context,
PRThreadEntryPoint pr_entry_point,
SetupSignalQueue* setup_signal_queue)
: pr_context(pr_context),
pr_entry_point(pr_entry_point),
setup_signal_queue(setup_signal_queue) {}
void* pr_context;
PRThreadEntryPoint pr_entry_point;
SetupSignalQueue* setup_signal_queue;
};
void* ThreadEntryPointWrapper(void* context_as_void_pointer) {
ThreadEntryPointWrapperContext* context =
reinterpret_cast<ThreadEntryPointWrapperContext*>(
context_as_void_pointer);
void* pr_context = context->pr_context;
PRThreadEntryPoint pr_entry_point = context->pr_entry_point;
SetupSignalQueue* setup_signal_queue = context->setup_signal_queue;
delete context;
SB_DCHECK(g_local_pr_thread()->GetIfExists() == NULL);
PRThread* pr_thread = g_local_pr_thread()->GetOrCreate(SbThreadGetCurrent());
SB_DCHECK(pr_thread);
setup_signal_queue->Put(pr_thread);
pr_entry_point(pr_context);
return NULL;
}
} // namespace
PRLock* PR_NewLock() {
PRLock* lock = new PRLock();
if (!SbMutexCreate(lock)) {
delete lock;
return NULL;
}
return lock;
}
void PR_DestroyLock(PRLock* lock) {
SB_DCHECK(lock);
SbMutexDestroy(lock);
delete lock;
}
PRCondVar* PR_NewCondVar(PRLock* lock) {
SB_DCHECK(lock);
PRCondVar* cvar = new PRCondVar();
if (!SbConditionVariableCreate(&cvar->sb_condition_variable, lock)) {
delete cvar;
return NULL;
}
cvar->lock = lock;
return cvar;
}
void PR_DestroyCondVar(PRCondVar* cvar) {
SbConditionVariableDestroy(&cvar->sb_condition_variable);
delete cvar;
}
PRStatus PR_WaitCondVar(PRCondVar* cvar, PRIntervalTime timeout) {
SbConditionVariableResult result;
if (timeout == PR_INTERVAL_NO_WAIT) {
result = SbConditionVariableWaitTimed(&cvar->sb_condition_variable,
cvar->lock, 0);
} else if (timeout == PR_INTERVAL_NO_TIMEOUT) {
result = SbConditionVariableWait(&cvar->sb_condition_variable, cvar->lock);
} else {
int64_t microseconds = PR_IntervalToMicrosecondsInt64(timeout);
result = SbConditionVariableWaitTimed(&cvar->sb_condition_variable,
cvar->lock, microseconds);
}
return pr_starboard::ToPRStatus(result != kSbConditionVariableFailed);
}
PRThread* PR_GetCurrentThread() {
return g_local_pr_thread()->GetOrCreate(SbThreadGetCurrent());
}
uint32_t PR_snprintf(char* out, uint32_t outlen, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
uint32_t ret = PR_vsnprintf(out, outlen, fmt, args);
va_end(args);
return ret;
}
PRThread* PR_CreateThread(PRThreadType type,
PRThreadEntryPoint start,
void* arg,
PRThreadPriority priority,
PRThreadScope scope,
PRThreadState state,
PRUint32 stackSize) {
int64_t sb_stack_size = static_cast<int64_t>(stackSize);
SbThreadPriority sb_priority;
switch (priority) {
case PR_PRIORITY_LOW:
sb_priority = kSbThreadPriorityLow;
break;
case PR_PRIORITY_NORMAL:
sb_priority = kSbThreadPriorityNormal;
break;
case PR_PRIORITY_HIGH:
sb_priority = kSbThreadPriorityHigh;
break;
case PR_PRIORITY_LAST:
sb_priority = kSbThreadPriorityHighest;
break;
default:
sb_priority = kSbThreadNoPriority;
}
SbThreadAffinity sb_affinity = kSbThreadNoAffinity;
SB_DCHECK(state == PR_JOINABLE_THREAD || state == PR_UNJOINABLE_THREAD);
bool sb_joinable = (state == PR_JOINABLE_THREAD);
// A queue that serves two purposes. The first is that it is a way for our
// child thread to signal to us that it is done with its wrapper level
// setup, and that we can safely return. The second, is that it is a
// channel for it to pass us the address of the child thread's |PRThread|
// object.
SetupSignalQueue setup_signal_queue;
// This heap allocated context object is freed after the initial setup of
// |ThreadEntryPointWrapper| is complete, right before the nspr level entry
// point is run.
ThreadEntryPointWrapperContext* context =
new ThreadEntryPointWrapperContext(arg, start, &setup_signal_queue);
SbThreadCreate(sb_stack_size, sb_priority, sb_affinity, sb_joinable, NULL,
ThreadEntryPointWrapper, context);
// Now we must wait for the setup section of |ThreadEntryPointWrapper| to
// run and pass us the initialized |pr_thread|.
PRThread* child_pr_thread = setup_signal_queue.Get();
SB_DCHECK(child_pr_thread);
SB_DCHECK(SbThreadIsValid(child_pr_thread->sb_thread));
return child_pr_thread;
}
PRStatus PR_CallOnceWithArg(PRCallOnceType* once,
PRCallOnceWithArgFN func,
void* arg) {
SB_NOTREACHED() << "Not implemented";
return PR_FAILURE;
}
PRStatus PR_NewThreadPrivateIndex(PRTLSIndex* newIndex,
PRThreadPrivateDTOR destructor) {
SbThreadLocalKey key = SbThreadCreateLocalKey(destructor);
if (!SbThreadIsValidLocalKey(key)) {
return pr_starboard::ToPRStatus(false);
}
*newIndex = key;
return pr_starboard::ToPRStatus(true);
}
PRIntervalTime PR_MillisecondsToInterval(PRUint32 milli) {
PRUint64 tock = static_cast<PRUint64>(milli);
PRUint64 msecPerSec = static_cast<PRInt64>(PR_MSEC_PER_SEC);
PRUint64 rounding = static_cast<PRInt64>(PR_MSEC_PER_SEC >> 1);
PRUint64 tps = static_cast<PRInt64>(PR_TicksPerSecond());
tock *= tps;
tock += rounding;
tock /= msecPerSec;
PRUint64 ticks = static_cast<PRUint64>(tock);
return ticks;
}
PRUint32 PR_IntervalToMicroseconds(PRIntervalTime ticks) {
PRUint64 tock = static_cast<PRInt64>(ticks);
PRUint64 usecPerSec = static_cast<PRInt64>(PR_USEC_PER_SEC);
PRUint64 tps = static_cast<PRInt64>(PR_TicksPerSecond());
PRUint64 rounding = static_cast<PRUint64>(tps) >> 1;
tock *= usecPerSec;
tock += rounding;
tock /= tps;
PRUint32 micro = static_cast<PRUint32>(tock);
return micro;
}