michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "vm/PosixNSPR.h" michael@0: michael@0: #include "js/Utility.h" michael@0: michael@0: #ifdef JS_POSIX_NSPR michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) michael@0: #include michael@0: #endif michael@0: michael@0: class nspr::Thread michael@0: { michael@0: pthread_t pthread_; michael@0: void (*start)(void *arg); michael@0: void *arg; michael@0: bool joinable; michael@0: michael@0: public: michael@0: Thread(void (*start)(void *arg), void *arg, bool joinable) michael@0: : start(start), arg(arg), joinable(joinable) {} michael@0: michael@0: static void *ThreadRoutine(void *arg); michael@0: michael@0: pthread_t &pthread() { return pthread_; } michael@0: }; michael@0: michael@0: static pthread_key_t gSelfThreadIndex; michael@0: static nspr::Thread gMainThread(nullptr, nullptr, false); michael@0: michael@0: void * michael@0: nspr::Thread::ThreadRoutine(void *arg) michael@0: { michael@0: Thread *self = static_cast(arg); michael@0: pthread_setspecific(gSelfThreadIndex, self); michael@0: self->start(self->arg); michael@0: if (!self->joinable) michael@0: js_delete(self); michael@0: return nullptr; michael@0: } michael@0: michael@0: static bool gInitialized; michael@0: michael@0: void michael@0: DummyDestructor(void *) michael@0: { michael@0: } michael@0: michael@0: /* Should be called from the main thread. */ michael@0: static void michael@0: Initialize() michael@0: { michael@0: gInitialized = true; michael@0: michael@0: if (pthread_key_create(&gSelfThreadIndex, DummyDestructor)) { michael@0: MOZ_CRASH(); michael@0: return; michael@0: } michael@0: michael@0: pthread_setspecific(gSelfThreadIndex, &gMainThread); michael@0: } michael@0: michael@0: PRThread * michael@0: PR_CreateThread(PRThreadType type, michael@0: void (*start)(void *arg), michael@0: void *arg, michael@0: PRThreadPriority priority, michael@0: PRThreadScope scope, michael@0: PRThreadState state, michael@0: uint32_t stackSize) michael@0: { michael@0: JS_ASSERT(type == PR_USER_THREAD); michael@0: JS_ASSERT(priority == PR_PRIORITY_NORMAL); michael@0: michael@0: if (!gInitialized) { michael@0: /* michael@0: * We assume that the first call to PR_CreateThread happens on the main michael@0: * thread. michael@0: */ michael@0: Initialize(); michael@0: } michael@0: michael@0: pthread_attr_t attr; michael@0: if (pthread_attr_init(&attr)) michael@0: return nullptr; michael@0: michael@0: if (stackSize && pthread_attr_setstacksize(&attr, stackSize)) { michael@0: pthread_attr_destroy(&attr); michael@0: return nullptr; michael@0: } michael@0: michael@0: nspr::Thread *t = js_new(start, arg, michael@0: state != PR_UNJOINABLE_THREAD); michael@0: if (!t) { michael@0: pthread_attr_destroy(&attr); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (pthread_create(&t->pthread(), &attr, &nspr::Thread::ThreadRoutine, t)) { michael@0: pthread_attr_destroy(&attr); michael@0: js_delete(t); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (state == PR_UNJOINABLE_THREAD) { michael@0: if (pthread_detach(t->pthread())) { michael@0: pthread_attr_destroy(&attr); michael@0: js_delete(t); michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: pthread_attr_destroy(&attr); michael@0: michael@0: return t; michael@0: } michael@0: michael@0: PRStatus michael@0: PR_JoinThread(PRThread *thread) michael@0: { michael@0: if (pthread_join(thread->pthread(), nullptr)) michael@0: return PR_FAILURE; michael@0: michael@0: js_delete(thread); michael@0: michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: PRThread * michael@0: PR_GetCurrentThread() michael@0: { michael@0: if (!gInitialized) michael@0: Initialize(); michael@0: michael@0: return (PRThread *)pthread_getspecific(gSelfThreadIndex); michael@0: } michael@0: michael@0: PRStatus michael@0: PR_SetCurrentThreadName(const char *name) michael@0: { michael@0: int result; michael@0: #ifdef XP_MACOSX michael@0: result = pthread_setname_np(name); michael@0: #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) michael@0: pthread_set_name_np(pthread_self(), name); michael@0: result = 0; michael@0: #elif defined(__NetBSD__) michael@0: result = pthread_setname_np(pthread_self(), "%s", (void *)name); michael@0: #else michael@0: result = pthread_setname_np(pthread_self(), name); michael@0: #endif michael@0: if (result) michael@0: return PR_FAILURE; michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: static const size_t MaxTLSKeyCount = 32; michael@0: static size_t gTLSKeyCount; michael@0: static pthread_key_t gTLSKeys[MaxTLSKeyCount]; michael@0: michael@0: PRStatus michael@0: PR_NewThreadPrivateIndex(unsigned *newIndex, PRThreadPrivateDTOR destructor) michael@0: { michael@0: /* michael@0: * We only call PR_NewThreadPrivateIndex from the main thread, so there's no michael@0: * need to lock the table of TLS keys. michael@0: */ michael@0: JS_ASSERT(PR_GetCurrentThread() == &gMainThread); michael@0: michael@0: pthread_key_t key; michael@0: if (pthread_key_create(&key, destructor)) michael@0: return PR_FAILURE; michael@0: michael@0: JS_ASSERT(gTLSKeyCount + 1 < MaxTLSKeyCount); michael@0: michael@0: gTLSKeys[gTLSKeyCount] = key; michael@0: *newIndex = gTLSKeyCount; michael@0: gTLSKeyCount++; michael@0: michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: PRStatus michael@0: PR_SetThreadPrivate(unsigned index, void *priv) michael@0: { michael@0: if (index >= gTLSKeyCount) michael@0: return PR_FAILURE; michael@0: if (pthread_setspecific(gTLSKeys[index], priv)) michael@0: return PR_FAILURE; michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: void * michael@0: PR_GetThreadPrivate(unsigned index) michael@0: { michael@0: if (index >= gTLSKeyCount) michael@0: return nullptr; michael@0: return pthread_getspecific(gTLSKeys[index]); michael@0: } michael@0: michael@0: PRStatus michael@0: PR_CallOnce(PRCallOnceType *once, PRCallOnceFN func) michael@0: { michael@0: MOZ_ASSUME_UNREACHABLE("PR_CallOnce unimplemented"); michael@0: } michael@0: michael@0: PRStatus michael@0: PR_CallOnceWithArg(PRCallOnceType *once, PRCallOnceWithArgFN func, void *arg) michael@0: { michael@0: MOZ_ASSUME_UNREACHABLE("PR_CallOnceWithArg unimplemented"); michael@0: } michael@0: michael@0: class nspr::Lock michael@0: { michael@0: pthread_mutex_t mutex_; michael@0: michael@0: public: michael@0: Lock() {} michael@0: pthread_mutex_t &mutex() { return mutex_; } michael@0: }; michael@0: michael@0: PRLock * michael@0: PR_NewLock() michael@0: { michael@0: nspr::Lock *lock = js_new(); michael@0: if (!lock) michael@0: return nullptr; michael@0: michael@0: if (pthread_mutex_init(&lock->mutex(), nullptr)) { michael@0: js_delete(lock); michael@0: return nullptr; michael@0: } michael@0: michael@0: return lock; michael@0: } michael@0: michael@0: void michael@0: PR_DestroyLock(PRLock *lock) michael@0: { michael@0: pthread_mutex_destroy(&lock->mutex()); michael@0: js_delete(lock); michael@0: } michael@0: michael@0: void michael@0: PR_Lock(PRLock *lock) michael@0: { michael@0: pthread_mutex_lock(&lock->mutex()); michael@0: } michael@0: michael@0: PRStatus michael@0: PR_Unlock(PRLock *lock) michael@0: { michael@0: if (pthread_mutex_unlock(&lock->mutex())) michael@0: return PR_FAILURE; michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: class nspr::CondVar michael@0: { michael@0: pthread_cond_t cond_; michael@0: nspr::Lock *lock_; michael@0: michael@0: public: michael@0: CondVar(nspr::Lock *lock) : lock_(lock) {} michael@0: pthread_cond_t &cond() { return cond_; } michael@0: nspr::Lock *lock() { return lock_; } michael@0: }; michael@0: michael@0: PRCondVar * michael@0: PR_NewCondVar(PRLock *lock) michael@0: { michael@0: nspr::CondVar *cvar = js_new(lock); michael@0: if (!cvar) michael@0: return nullptr; michael@0: michael@0: if (pthread_cond_init(&cvar->cond(), nullptr)) { michael@0: js_delete(cvar); michael@0: return nullptr; michael@0: } michael@0: michael@0: return cvar; michael@0: } michael@0: michael@0: void michael@0: PR_DestroyCondVar(PRCondVar *cvar) michael@0: { michael@0: pthread_cond_destroy(&cvar->cond()); michael@0: js_delete(cvar); michael@0: } michael@0: michael@0: PRStatus michael@0: PR_NotifyCondVar(PRCondVar *cvar) michael@0: { michael@0: if (pthread_cond_signal(&cvar->cond())) michael@0: return PR_FAILURE; michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: PRStatus michael@0: PR_NotifyAllCondVar(PRCondVar *cvar) michael@0: { michael@0: if (pthread_cond_broadcast(&cvar->cond())) michael@0: return PR_FAILURE; michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: uint32_t michael@0: PR_MillisecondsToInterval(uint32_t milli) michael@0: { michael@0: return milli; michael@0: } michael@0: michael@0: static const uint64_t TicksPerSecond = 1000; michael@0: static const uint64_t NanoSecondsInSeconds = 1000000000; michael@0: static const uint64_t MicroSecondsInSeconds = 1000000; michael@0: michael@0: uint32_t michael@0: PR_TicksPerSecond() michael@0: { michael@0: return TicksPerSecond; michael@0: } michael@0: michael@0: PRStatus michael@0: PR_WaitCondVar(PRCondVar *cvar, uint32_t timeout) michael@0: { michael@0: if (timeout == PR_INTERVAL_NO_TIMEOUT) { michael@0: if (pthread_cond_wait(&cvar->cond(), &cvar->lock()->mutex())) michael@0: return PR_FAILURE; michael@0: return PR_SUCCESS; michael@0: } else { michael@0: struct timespec ts; michael@0: struct timeval tv; michael@0: michael@0: gettimeofday(&tv, nullptr); michael@0: ts.tv_sec = tv.tv_sec; michael@0: ts.tv_nsec = tv.tv_usec * (NanoSecondsInSeconds / MicroSecondsInSeconds); michael@0: michael@0: ts.tv_nsec += timeout * (NanoSecondsInSeconds / TicksPerSecond); michael@0: while (uint64_t(ts.tv_nsec) >= NanoSecondsInSeconds) { michael@0: ts.tv_nsec -= NanoSecondsInSeconds; michael@0: ts.tv_sec++; michael@0: } michael@0: michael@0: int result = pthread_cond_timedwait(&cvar->cond(), &cvar->lock()->mutex(), &ts); michael@0: if (result == 0 || result == ETIMEDOUT) michael@0: return PR_SUCCESS; michael@0: return PR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: #endif /* JS_POSIX_NSPR */