From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- js/src/threading/posix/ConditionVariable.cpp | 180 ++++++++++++++++++++++++++ js/src/threading/posix/MutexImpl.cpp | 81 ++++++++++++ js/src/threading/posix/MutexPlatformData.h | 19 +++ js/src/threading/posix/Thread.cpp | 182 +++++++++++++++++++++++++++ 4 files changed, 462 insertions(+) create mode 100644 js/src/threading/posix/ConditionVariable.cpp create mode 100644 js/src/threading/posix/MutexImpl.cpp create mode 100644 js/src/threading/posix/MutexPlatformData.h create mode 100644 js/src/threading/posix/Thread.cpp (limited to 'js/src/threading/posix') diff --git a/js/src/threading/posix/ConditionVariable.cpp b/js/src/threading/posix/ConditionVariable.cpp new file mode 100644 index 000000000..35a90c604 --- /dev/null +++ b/js/src/threading/posix/ConditionVariable.cpp @@ -0,0 +1,180 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/CheckedInt.h" + +#include +#include +#include +#include +#include + +#include "threading/ConditionVariable.h" +#include "threading/Mutex.h" +#include "threading/posix/MutexPlatformData.h" + +using mozilla::CheckedInt; +using mozilla::TimeDuration; +using mozilla::TimeStamp; + +static const long NanoSecPerSec = 1000000000; + +// Android 32-bit & macOS 10.12 has the clock functions, but not pthread_condattr_setclock. +#if defined(HAVE_CLOCK_MONOTONIC) && \ + !(defined(__ANDROID__) && !defined(__LP64__)) && !defined(__APPLE__) +# define USE_CLOCK_API +#endif + +#ifdef USE_CLOCK_API +// The C++ specification defines std::condition_variable::wait_for in terms of +// std::chrono::steady_clock, which is closest to CLOCK_MONOTONIC. +static const clockid_t WhichClock = CLOCK_MONOTONIC; + +// While timevaladd is widely available to work with timevals, the newer +// timespec structure is largely lacking such conveniences. Thankfully, the +// utilities available in MFBT make implementing our own quite easy. +static void +moz_timespecadd(struct timespec* lhs, struct timespec* rhs, struct timespec* result) +{ + // Add nanoseconds. This may wrap, but not above 2 billion. + MOZ_RELEASE_ASSERT(lhs->tv_nsec < NanoSecPerSec); + MOZ_RELEASE_ASSERT(rhs->tv_nsec < NanoSecPerSec); + result->tv_nsec = lhs->tv_nsec + rhs->tv_nsec; + + // Add seconds, checking for overflow in the platform specific time_t type. + CheckedInt sec = CheckedInt(lhs->tv_sec) + rhs->tv_sec; + + // If nanoseconds overflowed, carry the result over into seconds. + if (result->tv_nsec >= NanoSecPerSec) { + MOZ_RELEASE_ASSERT(result->tv_nsec < 2 * NanoSecPerSec); + result->tv_nsec -= NanoSecPerSec; + sec += 1; + } + + // Extracting the value asserts that there was no overflow. + MOZ_RELEASE_ASSERT(sec.isValid()); + result->tv_sec = sec.value(); +} +#endif + +struct js::ConditionVariable::PlatformData +{ + pthread_cond_t ptCond; +}; + +js::ConditionVariable::ConditionVariable() +{ + pthread_cond_t* ptCond = &platformData()->ptCond; + +#ifdef USE_CLOCK_API + pthread_condattr_t attr; + int r0 = pthread_condattr_init(&attr); + MOZ_RELEASE_ASSERT(!r0); + + int r1 = pthread_condattr_setclock(&attr, WhichClock); + MOZ_RELEASE_ASSERT(!r1); + + int r2 = pthread_cond_init(ptCond, &attr); + MOZ_RELEASE_ASSERT(!r2); + + int r3 = pthread_condattr_destroy(&attr); + MOZ_RELEASE_ASSERT(!r3); +#else + int r = pthread_cond_init(ptCond, NULL); + MOZ_RELEASE_ASSERT(!r); +#endif +} + +js::ConditionVariable::~ConditionVariable() +{ + int r = pthread_cond_destroy(&platformData()->ptCond); + MOZ_RELEASE_ASSERT(r == 0); +} + +void +js::ConditionVariable::notify_one() +{ + int r = pthread_cond_signal(&platformData()->ptCond); + MOZ_RELEASE_ASSERT(r == 0); +} + +void +js::ConditionVariable::notify_all() +{ + int r = pthread_cond_broadcast(&platformData()->ptCond); + MOZ_RELEASE_ASSERT(r == 0); +} + +void +js::ConditionVariable::wait(UniqueLock& lock) +{ + pthread_cond_t* ptCond = &platformData()->ptCond; + pthread_mutex_t* ptMutex = &lock.lock.platformData()->ptMutex; + + int r = pthread_cond_wait(ptCond, ptMutex); + MOZ_RELEASE_ASSERT(r == 0); +} + +js::CVStatus +js::ConditionVariable::wait_until(UniqueLock& lock, + const TimeStamp& abs_time) +{ + return wait_for(lock, abs_time - TimeStamp::Now()); +} + +js::CVStatus +js::ConditionVariable::wait_for(UniqueLock& lock, + const TimeDuration& a_rel_time) +{ + if (a_rel_time == TimeDuration::Forever()) { + wait(lock); + return CVStatus::NoTimeout; + } + + pthread_cond_t* ptCond = &platformData()->ptCond; + pthread_mutex_t* ptMutex = &lock.lock.platformData()->ptMutex; + int r; + + // Clamp to 0, as time_t is unsigned. + TimeDuration rel_time = a_rel_time < TimeDuration::FromSeconds(0) + ? TimeDuration::FromSeconds(0) + : a_rel_time; + + // Convert the duration to a timespec. + struct timespec rel_ts; + rel_ts.tv_sec = static_cast(rel_time.ToSeconds()); + rel_ts.tv_nsec = static_cast(rel_time.ToMicroseconds() * 1000.0) % NanoSecPerSec; + +#ifdef USE_CLOCK_API + struct timespec now_ts; + r = clock_gettime(WhichClock, &now_ts); + MOZ_RELEASE_ASSERT(!r); + + struct timespec abs_ts; + moz_timespecadd(&now_ts, &rel_ts, &abs_ts); + + r = pthread_cond_timedwait(ptCond, ptMutex, &abs_ts); +#else + // Our non-clock-supporting platforms, OS X and Android, do support waiting + // on a condition variable with a relative timeout. + r = pthread_cond_timedwait_relative_np(ptCond, ptMutex, &rel_ts); +#endif + + if (r == 0) { + return CVStatus::NoTimeout; + } + MOZ_RELEASE_ASSERT(r == ETIMEDOUT); + return CVStatus::Timeout; +} + +js::ConditionVariable::PlatformData* +js::ConditionVariable::platformData() +{ + static_assert(sizeof platformData_ >= sizeof(PlatformData), + "platformData_ is too small"); + return reinterpret_cast(platformData_); +} diff --git a/js/src/threading/posix/MutexImpl.cpp b/js/src/threading/posix/MutexImpl.cpp new file mode 100644 index 000000000..1d406400f --- /dev/null +++ b/js/src/threading/posix/MutexImpl.cpp @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include + +#include "js/Utility.h" + +#include "threading/Mutex.h" +#include "threading/posix/MutexPlatformData.h" + +#define TRY_CALL_PTHREADS(call, msg) \ + { \ + int result = (call); \ + if (result != 0) { \ + errno = result; \ + perror(msg); \ + MOZ_CRASH(msg); \ + } \ + } + +js::detail::MutexImpl::MutexImpl() +{ + AutoEnterOOMUnsafeRegion oom; + platformData_ = js_new(); + if (!platformData_) + oom.crash("js::detail::MutexImpl::MutexImpl"); + + pthread_mutexattr_t* attrp = nullptr; + +#ifdef DEBUG + pthread_mutexattr_t attr; + + TRY_CALL_PTHREADS(pthread_mutexattr_init(&attr), + "js::detail::MutexImpl::MutexImpl: pthread_mutexattr_init failed"); + + TRY_CALL_PTHREADS(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK), + "js::detail::MutexImpl::MutexImpl: pthread_mutexattr_settype failed"); + + attrp = &attr; +#endif + + TRY_CALL_PTHREADS(pthread_mutex_init(&platformData()->ptMutex, attrp), + "js::detail::MutexImpl::MutexImpl: pthread_mutex_init failed"); + +#ifdef DEBUG + TRY_CALL_PTHREADS(pthread_mutexattr_destroy(&attr), + "js::detail::MutexImpl::MutexImpl: pthread_mutexattr_destroy failed"); +#endif +} + +js::detail::MutexImpl::~MutexImpl() +{ + if (!platformData_) + return; + + TRY_CALL_PTHREADS(pthread_mutex_destroy(&platformData()->ptMutex), + "js::detail::MutexImpl::~MutexImpl: pthread_mutex_destroy failed"); + + js_delete(platformData()); +} + +void +js::detail::MutexImpl::lock() +{ + TRY_CALL_PTHREADS(pthread_mutex_lock(&platformData()->ptMutex), + "js::detail::MutexImpl::lock: pthread_mutex_lock failed"); +} + +void +js::detail::MutexImpl::unlock() +{ + TRY_CALL_PTHREADS(pthread_mutex_unlock(&platformData()->ptMutex), + "js::detail::MutexImpl::unlock: pthread_mutex_unlock failed"); +} + +#undef TRY_CALL_PTHREADS diff --git a/js/src/threading/posix/MutexPlatformData.h b/js/src/threading/posix/MutexPlatformData.h new file mode 100644 index 000000000..487d89681 --- /dev/null +++ b/js/src/threading/posix/MutexPlatformData.h @@ -0,0 +1,19 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef platform_win_MutexPlatformData_h +#define platform_win_MutexPlatformData_h + +#include + +#include "threading/Mutex.h" + +struct js::detail::MutexImpl::PlatformData +{ + pthread_mutex_t ptMutex; +}; + +#endif // platform_win_MutexPlatformData_h diff --git a/js/src/threading/posix/Thread.cpp b/js/src/threading/posix/Thread.cpp new file mode 100644 index 000000000..2572cc727 --- /dev/null +++ b/js/src/threading/posix/Thread.cpp @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" + +#include +#include +#include +#include + +#if defined(__APPLE__) && defined(__MACH__) +#include +#endif + +#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) +#include +#endif + +#if defined(__linux__) +#include +#endif + +#include "threading/Thread.h" + +class js::Thread::Id::PlatformData +{ + friend class js::Thread; + friend js::Thread::Id js::ThisThread::GetId(); + + pthread_t ptThread; + + // pthread_t does not have a default initializer, so we have to carry a bool + // to tell whether it is safe to compare or not. + bool hasThread; +}; + +/* static */ js::HashNumber +js::Thread::Hasher::hash(const Lookup& l) +{ + return mozilla::HashBytes(&l.platformData()->ptThread, sizeof(pthread_t)); +} + +inline js::Thread::Id::PlatformData* +js::Thread::Id::platformData() +{ + static_assert(sizeof platformData_ >= sizeof(PlatformData), + "platformData_ is too small"); + return reinterpret_cast(platformData_); +} + +inline const js::Thread::Id::PlatformData* +js::Thread::Id::platformData() const +{ + static_assert(sizeof platformData_ >= sizeof(PlatformData), + "platformData_ is too small"); + return reinterpret_cast(platformData_); +} + +js::Thread::Id::Id() +{ + platformData()->hasThread = false; +} + +bool +js::Thread::Id::operator==(const Id& aOther) const +{ + const PlatformData& self = *platformData(); + const PlatformData& other = *aOther.platformData(); + return (!self.hasThread && !other.hasThread) || + (self.hasThread == other.hasThread && + pthread_equal(self.ptThread, other.ptThread)); +} + +js::Thread::Thread(Thread&& aOther) +{ + id_ = aOther.id_; + aOther.id_ = Id(); +} + +js::Thread& +js::Thread::operator=(Thread&& aOther) +{ + MOZ_RELEASE_ASSERT(!joinable()); + id_ = aOther.id_; + aOther.id_ = Id(); + return *this; +} + +bool +js::Thread::create(void* (*aMain)(void*), void* aArg) +{ + pthread_attr_t attrs; + int r = pthread_attr_init(&attrs); + MOZ_RELEASE_ASSERT(!r); + if (options_.stackSize()) { + r = pthread_attr_setstacksize(&attrs, options_.stackSize()); + MOZ_RELEASE_ASSERT(!r); + } + r = pthread_create(&id_.platformData()->ptThread, &attrs, aMain, aArg); + if (r) { + // |pthread_create| may leave id_ in an undefined state. + id_ = Id(); + return false; + } + id_.platformData()->hasThread = true; + return true; +} + +void +js::Thread::join() +{ + MOZ_RELEASE_ASSERT(joinable()); + int r = pthread_join(id_.platformData()->ptThread, nullptr); + MOZ_RELEASE_ASSERT(!r); + id_ = Id(); +} + +void +js::Thread::detach() +{ + MOZ_RELEASE_ASSERT(joinable()); + int r = pthread_detach(id_.platformData()->ptThread); + MOZ_RELEASE_ASSERT(!r); + id_ = Id(); +} + +js::Thread::Id +js::ThisThread::GetId() +{ + js::Thread::Id id; + id.platformData()->ptThread = pthread_self(); + id.platformData()->hasThread = true; + return id; +} + +void +js::ThisThread::SetName(const char* name) +{ + MOZ_RELEASE_ASSERT(name); + +#if (defined(__APPLE__) && defined(__MACH__)) || defined(__linux__) + // On linux and OS X the name may not be longer than 16 bytes, including + // the null terminator. Truncate the name to 15 characters. + char nameBuf[16]; + + strncpy(nameBuf, name, sizeof nameBuf - 1); + nameBuf[sizeof nameBuf - 1] = '\0'; + name = nameBuf; +#endif + + int rv; +#ifdef XP_DARWIN + rv = pthread_setname_np(name); +#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) + pthread_set_name_np(pthread_self(), name); + rv = 0; +#elif defined(__NetBSD__) + rv = pthread_setname_np(pthread_self(), "%s", (void*)name); +#else + rv = pthread_setname_np(pthread_self(), name); +#endif + MOZ_RELEASE_ASSERT(!rv); +} + +void +js::ThisThread::GetName(char* nameBuffer, size_t len) +{ + MOZ_RELEASE_ASSERT(len >= 16); + + int rv = -1; +#ifdef HAVE_PTHREAD_GETNAME_NP + rv = pthread_getname_np(pthread_self(), nameBuffer, len); +#elif defined(__linux__) + rv = prctl(PR_GET_NAME, reinterpret_cast(nameBuffer)); +#endif + + if (rv) + nameBuffer[0] = '\0'; +} -- cgit v1.2.3