From a48d05c8f98513b3ba4d3ee178fcda4d871b8844 Mon Sep 17 00:00:00 2001 From: Ryan Macnak Date: Thu, 10 Mar 2022 23:48:05 +0000 Subject: [PATCH] [vm] Add build-time option to use absl mutexes in place of pthread mutexes. TEST=ci, cbuild Bug: b/220994782 Change-Id: Id1a15d0d9fcef192847b25a2549730eb1014a99e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/236760 Reviewed-by: Alexander Aprelev Commit-Queue: Ryan Macnak --- runtime/bin/builtin_impl_sources.gni | 2 + runtime/bin/thread.h | 4 +- runtime/bin/thread_absl.cc | 221 ++++++++++++++ runtime/bin/thread_absl.h | 76 +++++ runtime/bin/thread_android.cc | 4 +- runtime/bin/thread_fuchsia.cc | 4 +- runtime/bin/thread_linux.cc | 4 +- runtime/bin/thread_macos.cc | 4 +- runtime/bin/thread_win.cc | 4 +- runtime/tools/run_clang_tidy.dart | 2 + runtime/vm/os_thread.h | 4 +- runtime/vm/os_thread_absl.cc | 417 +++++++++++++++++++++++++++ runtime/vm/os_thread_absl.h | 77 +++++ runtime/vm/os_thread_android.cc | 4 +- runtime/vm/os_thread_fuchsia.cc | 4 +- runtime/vm/os_thread_linux.cc | 4 +- runtime/vm/os_thread_macos.cc | 2 +- runtime/vm/os_thread_win.cc | 4 +- runtime/vm/vm_sources.gni | 2 + 19 files changed, 822 insertions(+), 21 deletions(-) create mode 100644 runtime/bin/thread_absl.cc create mode 100644 runtime/bin/thread_absl.h create mode 100644 runtime/vm/os_thread_absl.cc create mode 100644 runtime/vm/os_thread_absl.h diff --git a/runtime/bin/builtin_impl_sources.gni b/runtime/bin/builtin_impl_sources.gni index ff6a6c30034..feb1ee80a3b 100644 --- a/runtime/bin/builtin_impl_sources.gni +++ b/runtime/bin/builtin_impl_sources.gni @@ -45,6 +45,8 @@ builtin_impl_sources = [ "isolate_data.h", "lockers.h", "thread.h", + "thread_absl.cc", + "thread_absl.h", "thread_android.cc", "thread_android.h", "thread_fuchsia.cc", diff --git a/runtime/bin/thread.h b/runtime/bin/thread.h index d6716c9c02a..bff42e9d1e3 100644 --- a/runtime/bin/thread.h +++ b/runtime/bin/thread.h @@ -16,7 +16,9 @@ class Monitor; } // namespace dart // Declare the OS-specific types ahead of defining the generic classes. -#if defined(DART_HOST_OS_ANDROID) +#if defined(DART_USE_ABSL) +#include "bin/thread_absl.h" +#elif defined(DART_HOST_OS_ANDROID) #include "bin/thread_android.h" #elif defined(DART_HOST_OS_FUCHSIA) #include "bin/thread_fuchsia.h" diff --git a/runtime/bin/thread_absl.cc b/runtime/bin/thread_absl.cc new file mode 100644 index 00000000000..5db7af16574 --- /dev/null +++ b/runtime/bin/thread_absl.cc @@ -0,0 +1,221 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "platform/globals.h" +#if defined(DART_USE_ABSL) + +#include // NOLINT +#include // NOLINT +#include // NOLINT + +#include "bin/thread.h" +#include "bin/thread_absl.h" +#include "platform/assert.h" +#include "platform/utils.h" + +namespace dart { +namespace bin { + +#define VALIDATE_PTHREAD_RESULT(result) \ + if (result != 0) { \ + const int kBufferSize = 1024; \ + char error_buf[kBufferSize]; \ + FATAL2("pthread error: %d (%s)", result, \ + Utils::StrError(result, error_buf, kBufferSize)); \ + } + +#ifdef DEBUG +#define RETURN_ON_PTHREAD_FAILURE(result) \ + if (result != 0) { \ + const int kBufferSize = 1024; \ + char error_buf[kBufferSize]; \ + fprintf(stderr, "%s:%d: pthread error: %d (%s)\n", __FILE__, __LINE__, \ + result, Utils::StrError(result, error_buf, kBufferSize)); \ + return result; \ + } +#else +#define RETURN_ON_PTHREAD_FAILURE(result) \ + if (result != 0) { \ + return result; \ + } +#endif + +class ThreadStartData { + public: + ThreadStartData(const char* name, + Thread::ThreadStartFunction function, + uword parameter) + : name_(name), function_(function), parameter_(parameter) {} + + const char* name() const { return name_; } + Thread::ThreadStartFunction function() const { return function_; } + uword parameter() const { return parameter_; } + + private: + const char* name_; + Thread::ThreadStartFunction function_; + uword parameter_; + + DISALLOW_COPY_AND_ASSIGN(ThreadStartData); +}; + +// Dispatch to the thread start function provided by the caller. This trampoline +// is used to ensure that the thread is properly destroyed if the thread just +// exits. +static void* ThreadStart(void* data_ptr) { + ThreadStartData* data = reinterpret_cast(data_ptr); + + const char* name = data->name(); + Thread::ThreadStartFunction function = data->function(); + uword parameter = data->parameter(); + delete data; + + // Set the thread name. There is 16 bytes limit on the name (including \0). + // pthread_setname_np ignores names that are too long rather than truncating. + char truncated_name[16]; + snprintf(truncated_name, sizeof(truncated_name), "%s", name); + pthread_setname_np(pthread_self(), truncated_name); + + // Call the supplied thread start function handing it its parameters. + function(parameter); + + return NULL; +} + +int Thread::Start(const char* name, + ThreadStartFunction function, + uword parameter) { + pthread_attr_t attr; + int result = pthread_attr_init(&attr); + RETURN_ON_PTHREAD_FAILURE(result); + + result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + RETURN_ON_PTHREAD_FAILURE(result); + + result = pthread_attr_setstacksize(&attr, Thread::GetMaxStackSize()); + RETURN_ON_PTHREAD_FAILURE(result); + + ThreadStartData* data = new ThreadStartData(name, function, parameter); + + pthread_t tid; + result = pthread_create(&tid, &attr, ThreadStart, data); + RETURN_ON_PTHREAD_FAILURE(result); + + result = pthread_attr_destroy(&attr); + RETURN_ON_PTHREAD_FAILURE(result); + + return 0; +} + +const ThreadLocalKey Thread::kUnsetThreadLocalKey = + static_cast(-1); +const ThreadId Thread::kInvalidThreadId = static_cast(0); + +ThreadLocalKey Thread::CreateThreadLocal() { + pthread_key_t key = kUnsetThreadLocalKey; + int result = pthread_key_create(&key, NULL); + VALIDATE_PTHREAD_RESULT(result); + ASSERT(key != kUnsetThreadLocalKey); + return key; +} + +void Thread::DeleteThreadLocal(ThreadLocalKey key) { + ASSERT(key != kUnsetThreadLocalKey); + int result = pthread_key_delete(key); + VALIDATE_PTHREAD_RESULT(result); +} + +void Thread::SetThreadLocal(ThreadLocalKey key, uword value) { + ASSERT(key != kUnsetThreadLocalKey); + int result = pthread_setspecific(key, reinterpret_cast(value)); + VALIDATE_PTHREAD_RESULT(result); +} + +intptr_t Thread::GetMaxStackSize() { + const int kStackSize = (128 * kWordSize * KB); + return kStackSize; +} + +ThreadId Thread::GetCurrentThreadId() { + return pthread_self(); +} + +intptr_t Thread::ThreadIdToIntPtr(ThreadId id) { + ASSERT(sizeof(id) == sizeof(intptr_t)); + return static_cast(id); +} + +bool Thread::Compare(ThreadId a, ThreadId b) { + return (pthread_equal(a, b) != 0); +} + +Mutex::Mutex() : data_() {} + +Mutex::~Mutex() {} + +ABSL_NO_THREAD_SAFETY_ANALYSIS +void Mutex::Lock() { + data_.mutex()->Lock(); +} + +ABSL_NO_THREAD_SAFETY_ANALYSIS +bool Mutex::TryLock() { + if (!data_.mutex()->TryLock()) { + return false; + } + return true; +} + +ABSL_NO_THREAD_SAFETY_ANALYSIS +void Mutex::Unlock() { + data_.mutex()->Unlock(); +} + +Monitor::Monitor() : data_() {} + +Monitor::~Monitor() {} + +ABSL_NO_THREAD_SAFETY_ANALYSIS +void Monitor::Enter() { + data_.mutex()->Lock(); +} + +ABSL_NO_THREAD_SAFETY_ANALYSIS +void Monitor::Exit() { + data_.mutex()->Unlock(); +} + +Monitor::WaitResult Monitor::Wait(int64_t millis) { + return WaitMicros(millis * kMicrosecondsPerMillisecond); +} + +ABSL_NO_THREAD_SAFETY_ANALYSIS +Monitor::WaitResult Monitor::WaitMicros(int64_t micros) { + Monitor::WaitResult retval = kNotified; + if (micros == kNoTimeout) { + // Wait forever. + data_.cond()->Wait(data_.mutex()); + } else { + if (data_.cond()->WaitWithTimeout(data_.mutex(), + absl::Microseconds(micros))) { + retval = kTimedOut; + } + } + return retval; +} + +ABSL_NO_THREAD_SAFETY_ANALYSIS +void Monitor::Notify() { + data_.cond()->Signal(); +} + +ABSL_NO_THREAD_SAFETY_ANALYSIS +void Monitor::NotifyAll() { + data_.cond()->SignalAll(); +} + +} // namespace bin +} // namespace dart + +#endif // defined(DART_USE_ABSL) diff --git a/runtime/bin/thread_absl.h b/runtime/bin/thread_absl.h new file mode 100644 index 00000000000..649f2e7f356 --- /dev/null +++ b/runtime/bin/thread_absl.h @@ -0,0 +1,76 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef RUNTIME_BIN_THREAD_ABSL_H_ +#define RUNTIME_BIN_THREAD_ABSL_H_ + +#if !defined(RUNTIME_BIN_THREAD_H_) +#error Do not include thread_absl.h directly; use thread.h instead. +#endif + +#include + +#include "platform/assert.h" +#include "platform/globals.h" +#include "third_party/absl/synchronization/mutex.h" + +namespace dart { +namespace bin { + +typedef pthread_key_t ThreadLocalKey; +typedef pthread_t ThreadId; + +class ThreadInlineImpl { + private: + ThreadInlineImpl() {} + ~ThreadInlineImpl() {} + + static uword GetThreadLocal(ThreadLocalKey key) { + static ThreadLocalKey kUnsetThreadLocalKey = static_cast(-1); + ASSERT(key != kUnsetThreadLocalKey); + return reinterpret_cast(pthread_getspecific(key)); + } + + friend class Thread; + + DISALLOW_ALLOCATION(); + DISALLOW_COPY_AND_ASSIGN(ThreadInlineImpl); +}; + +class MutexData { + private: + MutexData() : mutex_() {} + ~MutexData() {} + + absl::Mutex* mutex() { return &mutex_; } + + absl::Mutex mutex_; + + friend class Mutex; + + DISALLOW_ALLOCATION(); + DISALLOW_COPY_AND_ASSIGN(MutexData); +}; + +class MonitorData { + private: + MonitorData() : mutex_(), cond_() {} + ~MonitorData() {} + + absl::Mutex* mutex() { return &mutex_; } + absl::CondVar* cond() { return &cond_; } + + absl::Mutex mutex_; + absl::CondVar cond_; + + friend class Monitor; + + DISALLOW_ALLOCATION(); + DISALLOW_COPY_AND_ASSIGN(MonitorData); +}; + +} // namespace bin +} // namespace dart + +#endif // RUNTIME_BIN_THREAD_ABSL_H_ diff --git a/runtime/bin/thread_android.cc b/runtime/bin/thread_android.cc index 60c911af037..2848e467fe1 100644 --- a/runtime/bin/thread_android.cc +++ b/runtime/bin/thread_android.cc @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. #include "platform/globals.h" -#if defined(DART_HOST_OS_ANDROID) +#if defined(DART_HOST_OS_ANDROID) && !defined(DART_USE_ABSL) #include "bin/thread.h" #include "bin/thread_android.h" @@ -301,4 +301,4 @@ void Monitor::NotifyAll() { } // namespace bin } // namespace dart -#endif // defined(DART_HOST_OS_ANDROID) +#endif // defined(DART_HOST_OS_ANDROID) && !defined(DART_USE_ABSL) diff --git a/runtime/bin/thread_fuchsia.cc b/runtime/bin/thread_fuchsia.cc index 66b847d1551..ef839f1518b 100644 --- a/runtime/bin/thread_fuchsia.cc +++ b/runtime/bin/thread_fuchsia.cc @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. #include "platform/globals.h" -#if defined(DART_HOST_OS_FUCHSIA) +#if defined(DART_HOST_OS_FUCHSIA) && !defined(DART_USE_ABSL) #include "bin/thread.h" #include "bin/thread_fuchsia.h" @@ -305,4 +305,4 @@ void Monitor::NotifyAll() { } // namespace bin } // namespace dart -#endif // defined(DART_HOST_OS_FUCHSIA) +#endif // defined(DART_HOST_OS_FUCHSIA) && !defined(DART_USE_ABSL) diff --git a/runtime/bin/thread_linux.cc b/runtime/bin/thread_linux.cc index 1a46a3e37f4..bd8688d6ee2 100644 --- a/runtime/bin/thread_linux.cc +++ b/runtime/bin/thread_linux.cc @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. #include "platform/globals.h" -#if defined(DART_HOST_OS_LINUX) +#if defined(DART_HOST_OS_LINUX) && !defined(DART_USE_ABSL) #include "bin/thread.h" #include "bin/thread_linux.h" @@ -304,4 +304,4 @@ void Monitor::NotifyAll() { } // namespace bin } // namespace dart -#endif // defined(DART_HOST_OS_LINUX) +#endif // defined(DART_HOST_OS_LINUX) && !defined(DART_USE_ABSL) diff --git a/runtime/bin/thread_macos.cc b/runtime/bin/thread_macos.cc index a38c44129a8..3b22fb3118d 100644 --- a/runtime/bin/thread_macos.cc +++ b/runtime/bin/thread_macos.cc @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. #include "platform/globals.h" -#if defined(DART_HOST_OS_MACOS) +#if defined(DART_HOST_OS_MACOS) && !defined(DART_USE_ABSL) #include "bin/thread.h" #include "bin/thread_macos.h" @@ -294,4 +294,4 @@ void Monitor::NotifyAll() { } // namespace bin } // namespace dart -#endif // defined(DART_HOST_OS_MACOS) +#endif // defined(DART_HOST_OS_MACOS) && !defined(DART_USE_ABSL) diff --git a/runtime/bin/thread_win.cc b/runtime/bin/thread_win.cc index 53809f364e7..a0c20ba6ebc 100644 --- a/runtime/bin/thread_win.cc +++ b/runtime/bin/thread_win.cc @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. #include "platform/globals.h" -#if defined(DART_HOST_OS_WINDOWS) +#if defined(DART_HOST_OS_WINDOWS) && !defined(DART_USE_ABSL) #include "bin/thread.h" #include "bin/thread_win.h" @@ -192,4 +192,4 @@ void Monitor::NotifyAll() { } // namespace bin } // namespace dart -#endif // defined(DART_HOST_OS_WINDOWS) +#endif // defined(DART_HOST_OS_WINDOWS) && !defined(DART_USE_ABSL) diff --git a/runtime/tools/run_clang_tidy.dart b/runtime/tools/run_clang_tidy.dart index 84b66e8041a..2c12e4516fb 100644 --- a/runtime/tools/run_clang_tidy.dart +++ b/runtime/tools/run_clang_tidy.dart @@ -64,6 +64,7 @@ final Set excludedFiles = Set.from([ 'runtime/bin/socket_base_linux.h', 'runtime/bin/socket_base_macos.h', 'runtime/bin/socket_base_win.h', + 'runtime/bin/thread_absl.h', 'runtime/bin/thread_android.h', 'runtime/bin/thread_fuchsia.h', 'runtime/bin/thread_linux.h', @@ -100,6 +101,7 @@ final Set excludedFiles = Set.from([ 'runtime/vm/instructions_ia32.h', 'runtime/vm/instructions_riscv.h', 'runtime/vm/instructions_x64.h', + 'runtime/vm/os_thread_absl.h', 'runtime/vm/os_thread_android.h', 'runtime/vm/os_thread_fuchsia.h', 'runtime/vm/os_thread_linux.h', diff --git a/runtime/vm/os_thread.h b/runtime/vm/os_thread.h index 1f6a0e35f8f..5df7fb2994a 100644 --- a/runtime/vm/os_thread.h +++ b/runtime/vm/os_thread.h @@ -13,7 +13,9 @@ #include "vm/globals.h" // Declare the OS-specific types ahead of defining the generic classes. -#if defined(DART_HOST_OS_ANDROID) +#if defined(DART_USE_ABSL) +#include "vm/os_thread_absl.h" +#elif defined(DART_HOST_OS_ANDROID) #include "vm/os_thread_android.h" #elif defined(DART_HOST_OS_FUCHSIA) #include "vm/os_thread_fuchsia.h" diff --git a/runtime/vm/os_thread_absl.cc b/runtime/vm/os_thread_absl.cc new file mode 100644 index 00000000000..d922da8febd --- /dev/null +++ b/runtime/vm/os_thread_absl.cc @@ -0,0 +1,417 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "platform/globals.h" // NOLINT + +#if defined(DART_USE_ABSL) + +#include // NOLINT +#include +#include // NOLINT +#include // NOLINT +#include // NOLINT + +#include "platform/address_sanitizer.h" +#include "platform/assert.h" +#include "platform/safe_stack.h" +#include "platform/signal_blocker.h" +#include "platform/utils.h" +#include "vm/flags.h" +#include "vm/os_thread.h" + +namespace dart { + +DEFINE_FLAG(int, + worker_thread_priority, + kMinInt, + "The thread priority the VM should use for new worker threads."); + +#define VALIDATE_PTHREAD_RESULT(result) \ + if (result != 0) { \ + const int kBufferSize = 1024; \ + char error_buf[kBufferSize]; \ + FATAL2("pthread error: %d (%s)", result, \ + Utils::StrError(result, error_buf, kBufferSize)); \ + } + +// Variation of VALIDATE_PTHREAD_RESULT for named objects. +#if defined(PRODUCT) +#define VALIDATE_PTHREAD_RESULT_NAMED(result) VALIDATE_PTHREAD_RESULT(result) +#else +#define VALIDATE_PTHREAD_RESULT_NAMED(result) \ + if (result != 0) { \ + const int kBufferSize = 1024; \ + char error_buf[kBufferSize]; \ + FATAL3("[%s] pthread error: %d (%s)", name_, result, \ + Utils::StrError(result, error_buf, kBufferSize)); \ + } +#endif + +#if defined(DEBUG) +#define ASSERT_PTHREAD_SUCCESS(result) VALIDATE_PTHREAD_RESULT(result) +#else +// NOTE: This (currently) expands to a no-op. +#define ASSERT_PTHREAD_SUCCESS(result) ASSERT(result == 0) +#endif + +#ifdef DEBUG +#define RETURN_ON_PTHREAD_FAILURE(result) \ + if (result != 0) { \ + const int kBufferSize = 1024; \ + char error_buf[kBufferSize]; \ + fprintf(stderr, "%s:%d: pthread error: %d (%s)\n", __FILE__, __LINE__, \ + result, Utils::StrError(result, error_buf, kBufferSize)); \ + return result; \ + } +#else +#define RETURN_ON_PTHREAD_FAILURE(result) \ + if (result != 0) return result; +#endif + +class ThreadStartData { + public: + ThreadStartData(const char* name, + OSThread::ThreadStartFunction function, + uword parameter) + : name_(name), function_(function), parameter_(parameter) {} + + const char* name() const { return name_; } + OSThread::ThreadStartFunction function() const { return function_; } + uword parameter() const { return parameter_; } + + private: + const char* name_; + OSThread::ThreadStartFunction function_; + uword parameter_; + + DISALLOW_COPY_AND_ASSIGN(ThreadStartData); +}; + +// TODO(bkonyi): remove this call once the prebuilt SDK is updated. +// Spawned threads inherit their spawner's signal mask. We sometimes spawn +// threads for running Dart code from a thread that is blocking SIGPROF. +// This function explicitly unblocks SIGPROF so the profiler continues to +// sample this thread. +static void UnblockSIGPROF() { + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGPROF); + int r = pthread_sigmask(SIG_UNBLOCK, &set, NULL); + USE(r); + ASSERT(r == 0); + ASSERT(!CHECK_IS_BLOCKING(SIGPROF)); +} + +// Dispatch to the thread start function provided by the caller. This trampoline +// is used to ensure that the thread is properly destroyed if the thread just +// exits. +static void* ThreadStart(void* data_ptr) { + if (FLAG_worker_thread_priority != kMinInt) { + if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), + FLAG_worker_thread_priority) == -1) { + FATAL2("Setting thread priority to %d failed: errno = %d\n", + FLAG_worker_thread_priority, errno); + } + } + + ThreadStartData* data = reinterpret_cast(data_ptr); + + const char* name = data->name(); + OSThread::ThreadStartFunction function = data->function(); + uword parameter = data->parameter(); + delete data; + + // Set the thread name. There is 16 bytes limit on the name (including \0). + // pthread_setname_np ignores names that are too long rather than truncating. + char truncated_name[16]; + snprintf(truncated_name, ARRAY_SIZE(truncated_name), "%s", name); + pthread_setname_np(pthread_self(), truncated_name); + + // Create new OSThread object and set as TLS for new thread. + OSThread* thread = OSThread::CreateOSThread(); + if (thread != NULL) { + OSThread::SetCurrent(thread); + thread->set_name(name); + UnblockSIGPROF(); + // Call the supplied thread start function handing it its parameters. + function(parameter); + } + + return NULL; +} + +int OSThread::Start(const char* name, + ThreadStartFunction function, + uword parameter) { + pthread_attr_t attr; + int result = pthread_attr_init(&attr); + RETURN_ON_PTHREAD_FAILURE(result); + + result = pthread_attr_setstacksize(&attr, OSThread::GetMaxStackSize()); + RETURN_ON_PTHREAD_FAILURE(result); + + ThreadStartData* data = new ThreadStartData(name, function, parameter); + + pthread_t tid; + result = pthread_create(&tid, &attr, ThreadStart, data); + RETURN_ON_PTHREAD_FAILURE(result); + + result = pthread_attr_destroy(&attr); + RETURN_ON_PTHREAD_FAILURE(result); + + return 0; +} + +const ThreadId OSThread::kInvalidThreadId = static_cast(0); +const ThreadJoinId OSThread::kInvalidThreadJoinId = + static_cast(0); + +ThreadLocalKey OSThread::CreateThreadLocal(ThreadDestructor destructor) { + pthread_key_t key = kUnsetThreadLocalKey; + int result = pthread_key_create(&key, destructor); + VALIDATE_PTHREAD_RESULT(result); + ASSERT(key != kUnsetThreadLocalKey); + return key; +} + +void OSThread::DeleteThreadLocal(ThreadLocalKey key) { + ASSERT(key != kUnsetThreadLocalKey); + int result = pthread_key_delete(key); + VALIDATE_PTHREAD_RESULT(result); +} + +void OSThread::SetThreadLocal(ThreadLocalKey key, uword value) { + ASSERT(key != kUnsetThreadLocalKey); + int result = pthread_setspecific(key, reinterpret_cast(value)); + VALIDATE_PTHREAD_RESULT(result); +} + +intptr_t OSThread::GetMaxStackSize() { + const int kStackSize = (128 * kWordSize * KB); + return kStackSize; +} + +ThreadId OSThread::GetCurrentThreadId() { + return pthread_self(); +} + +#ifdef SUPPORT_TIMELINE +ThreadId OSThread::GetCurrentThreadTraceId() { + return syscall(__NR_gettid); +} +#endif // PRODUCT + +ThreadJoinId OSThread::GetCurrentThreadJoinId(OSThread* thread) { + ASSERT(thread != NULL); + // Make sure we're filling in the join id for the current thread. + ASSERT(thread->id() == GetCurrentThreadId()); + // Make sure the join_id_ hasn't been set, yet. + DEBUG_ASSERT(thread->join_id_ == kInvalidThreadJoinId); + pthread_t id = pthread_self(); +#if defined(DEBUG) + thread->join_id_ = id; +#endif + return id; +} + +void OSThread::Join(ThreadJoinId id) { + int result = pthread_join(id, NULL); + ASSERT(result == 0); +} + +intptr_t OSThread::ThreadIdToIntPtr(ThreadId id) { + ASSERT(sizeof(id) == sizeof(intptr_t)); + return static_cast(id); +} + +ThreadId OSThread::ThreadIdFromIntPtr(intptr_t id) { + return static_cast(id); +} + +bool OSThread::Compare(ThreadId a, ThreadId b) { + return pthread_equal(a, b) != 0; +} + +bool OSThread::GetCurrentStackBounds(uword* lower, uword* upper) { + pthread_attr_t attr; + // May fail on the main thread. + if (pthread_getattr_np(pthread_self(), &attr) != 0) { + return false; + } + + void* base; + size_t size; + int error = pthread_attr_getstack(&attr, &base, &size); + pthread_attr_destroy(&attr); + if (error != 0) { + return false; + } + + *lower = reinterpret_cast(base); + *upper = *lower + size; + return true; +} + +#if defined(USING_SAFE_STACK) +NO_SANITIZE_ADDRESS +NO_SANITIZE_SAFE_STACK +uword OSThread::GetCurrentSafestackPointer() { +#error "SAFE_STACK is unsupported on this platform" + return 0; +} + +NO_SANITIZE_ADDRESS +NO_SANITIZE_SAFE_STACK +void OSThread::SetCurrentSafestackPointer(uword ssp) { +#error "SAFE_STACK is unsupported on this platform" +} +#endif + +Mutex::Mutex(NOT_IN_PRODUCT(const char* name)) +#if !defined(PRODUCT) + : name_(name) +#endif +{ +#if defined(DEBUG) + // When running with assertions enabled we track the owner. + owner_ = OSThread::kInvalidThreadId; +#endif // defined(DEBUG) +} + +Mutex::~Mutex() { +#if defined(DEBUG) + // When running with assertions enabled we track the owner. + ASSERT(owner_ == OSThread::kInvalidThreadId); +#endif // defined(DEBUG) +} + +ABSL_NO_THREAD_SAFETY_ANALYSIS +void Mutex::Lock() { + data_.mutex()->Lock(); +#if defined(DEBUG) + // When running with assertions enabled we track the owner. + owner_ = OSThread::GetCurrentThreadId(); +#endif // defined(DEBUG) +} + +ABSL_NO_THREAD_SAFETY_ANALYSIS +bool Mutex::TryLock() { + if (!data_.mutex()->TryLock()) { + return false; + } +#if defined(DEBUG) + // When running with assertions enabled we track the owner. + owner_ = OSThread::GetCurrentThreadId(); +#endif // defined(DEBUG) + return true; +} + +ABSL_NO_THREAD_SAFETY_ANALYSIS +void Mutex::Unlock() { +#if defined(DEBUG) + // When running with assertions enabled we track the owner. + ASSERT(IsOwnedByCurrentThread()); + owner_ = OSThread::kInvalidThreadId; +#endif // defined(DEBUG) + data_.mutex()->Unlock(); +} + +Monitor::Monitor() { +#if defined(DEBUG) + // When running with assertions enabled we track the owner. + owner_ = OSThread::kInvalidThreadId; +#endif // defined(DEBUG) +} + +Monitor::~Monitor() { +#if defined(DEBUG) + // When running with assertions enabled we track the owner. + ASSERT(owner_ == OSThread::kInvalidThreadId); +#endif // defined(DEBUG) +} + +ABSL_NO_THREAD_SAFETY_ANALYSIS +bool Monitor::TryEnter() { + if (!data_.mutex()->TryLock()) { + return false; + } +#if defined(DEBUG) + // When running with assertions enabled we track the owner. + ASSERT(owner_ == OSThread::kInvalidThreadId); + owner_ = OSThread::GetCurrentThreadId(); +#endif // defined(DEBUG) + return true; +} + +ABSL_NO_THREAD_SAFETY_ANALYSIS +void Monitor::Enter() { + data_.mutex()->Lock(); +#if defined(DEBUG) + // When running with assertions enabled we track the owner. + ASSERT(owner_ == OSThread::kInvalidThreadId); + owner_ = OSThread::GetCurrentThreadId(); +#endif // defined(DEBUG) +} + +ABSL_NO_THREAD_SAFETY_ANALYSIS +void Monitor::Exit() { +#if defined(DEBUG) + // When running with assertions enabled we track the owner. + ASSERT(IsOwnedByCurrentThread()); + owner_ = OSThread::kInvalidThreadId; +#endif // defined(DEBUG) + data_.mutex()->Unlock(); +} + +Monitor::WaitResult Monitor::Wait(int64_t millis) { + Monitor::WaitResult retval = WaitMicros(millis * kMicrosecondsPerMillisecond); + return retval; +} + +ABSL_NO_THREAD_SAFETY_ANALYSIS +Monitor::WaitResult Monitor::WaitMicros(int64_t micros) { +#if defined(DEBUG) + // When running with assertions enabled we track the owner. + ASSERT(IsOwnedByCurrentThread()); + ThreadId saved_owner = owner_; + owner_ = OSThread::kInvalidThreadId; +#endif // defined(DEBUG) + + Monitor::WaitResult retval = kNotified; + if (micros == kNoTimeout) { + // Wait forever. + data_.cond()->Wait(data_.mutex()); + } else { + if (data_.cond()->WaitWithTimeout(data_.mutex(), + absl::Microseconds(micros))) { + retval = kTimedOut; + } + } + +#if defined(DEBUG) + // When running with assertions enabled we track the owner. + ASSERT(owner_ == OSThread::kInvalidThreadId); + owner_ = OSThread::GetCurrentThreadId(); + ASSERT(owner_ == saved_owner); +#endif // defined(DEBUG) + return retval; +} + +ABSL_NO_THREAD_SAFETY_ANALYSIS +void Monitor::Notify() { + // When running with assertions enabled we track the owner. + ASSERT(IsOwnedByCurrentThread()); + data_.cond()->Signal(); +} + +ABSL_NO_THREAD_SAFETY_ANALYSIS +void Monitor::NotifyAll() { + // When running with assertions enabled we track the owner. + ASSERT(IsOwnedByCurrentThread()); + xdata_.cond()->SignalAll(); +} + +} // namespace dart + +#endif // defined(DART_USE_ABSL) diff --git a/runtime/vm/os_thread_absl.h b/runtime/vm/os_thread_absl.h new file mode 100644 index 00000000000..e10793b22fc --- /dev/null +++ b/runtime/vm/os_thread_absl.h @@ -0,0 +1,77 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef RUNTIME_VM_OS_THREAD_ABSL_H_ +#define RUNTIME_VM_OS_THREAD_ABSL_H_ + +#if !defined(RUNTIME_VM_OS_THREAD_H_) +#error Do not include os_thread_absl.h directly; use os_thread.h instead. +#endif + +#include + +#include "platform/assert.h" +#include "platform/globals.h" +#include "third_party/absl/synchronization/mutex.h" + +namespace dart { + +typedef pthread_key_t ThreadLocalKey; +typedef pthread_t ThreadId; +typedef pthread_t ThreadJoinId; + +static const ThreadLocalKey kUnsetThreadLocalKey = + static_cast(-1); + +class ThreadInlineImpl { + private: + ThreadInlineImpl() {} + ~ThreadInlineImpl() {} + + static uword GetThreadLocal(ThreadLocalKey key) { + ASSERT(key != kUnsetThreadLocalKey); + return reinterpret_cast(pthread_getspecific(key)); + } + + friend class OSThread; + + DISALLOW_ALLOCATION(); + DISALLOW_COPY_AND_ASSIGN(ThreadInlineImpl); +}; + +class MutexData { + private: + MutexData() : mutex_() {} + ~MutexData() {} + + absl::Mutex* mutex() { return &mutex_; } + + absl::Mutex mutex_; + + friend class Mutex; + + DISALLOW_ALLOCATION(); + DISALLOW_COPY_AND_ASSIGN(MutexData); +}; + +class MonitorData { + private: + MonitorData() : mutex_(), cond_() {} + ~MonitorData() {} + + absl::Mutex* mutex() { return &mutex_; } + absl::CondVar* cond() { return &cond_; } + + absl::Mutex mutex_; + absl::CondVar cond_; + + friend class Monitor; + + DISALLOW_ALLOCATION(); + DISALLOW_COPY_AND_ASSIGN(MonitorData); +}; + +} // namespace dart + +#endif // RUNTIME_VM_OS_THREAD_ABSL_H_ diff --git a/runtime/vm/os_thread_android.cc b/runtime/vm/os_thread_android.cc index 8c09320f2a9..a9a1413a9d8 100644 --- a/runtime/vm/os_thread_android.cc +++ b/runtime/vm/os_thread_android.cc @@ -4,7 +4,7 @@ #include "platform/globals.h" // NOLINT -#if defined(DART_HOST_OS_ANDROID) +#if defined(DART_HOST_OS_ANDROID) && !defined(DART_USE_ABSL) #include "vm/os_thread.h" @@ -491,4 +491,4 @@ void Monitor::NotifyAll() { } // namespace dart -#endif // defined(DART_HOST_OS_ANDROID) +#endif // defined(DART_HOST_OS_ANDROID) && !defined(DART_USE_ABSL) diff --git a/runtime/vm/os_thread_fuchsia.cc b/runtime/vm/os_thread_fuchsia.cc index 1eb8a3a0f14..3cf8193f839 100644 --- a/runtime/vm/os_thread_fuchsia.cc +++ b/runtime/vm/os_thread_fuchsia.cc @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. #include "platform/globals.h" // NOLINT -#if defined(DART_HOST_OS_FUCHSIA) +#if defined(DART_HOST_OS_FUCHSIA) && !defined(DART_USE_ABSL) #include "vm/os.h" #include "vm/os_thread.h" @@ -492,4 +492,4 @@ void Monitor::NotifyAll() { } // namespace dart -#endif // defined(DART_HOST_OS_FUCHSIA) +#endif // defined(DART_HOST_OS_FUCHSIA) && !defined(DART_USE_ABSL) diff --git a/runtime/vm/os_thread_linux.cc b/runtime/vm/os_thread_linux.cc index 409b03b00cc..ea447908116 100644 --- a/runtime/vm/os_thread_linux.cc +++ b/runtime/vm/os_thread_linux.cc @@ -4,7 +4,7 @@ #include "platform/globals.h" // NOLINT -#if defined(DART_HOST_OS_LINUX) +#if defined(DART_HOST_OS_LINUX) && !defined(DART_USE_ABSL) #include "vm/os_thread.h" @@ -497,4 +497,4 @@ void Monitor::NotifyAll() { } // namespace dart -#endif // defined(DART_HOST_OS_LINUX) +#endif // defined(DART_HOST_OS_LINUX) && !defined(DART_USE_ABSL) diff --git a/runtime/vm/os_thread_macos.cc b/runtime/vm/os_thread_macos.cc index 401fdc79a52..cbbc2261615 100644 --- a/runtime/vm/os_thread_macos.cc +++ b/runtime/vm/os_thread_macos.cc @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. #include "platform/globals.h" // NOLINT -#if defined(DART_HOST_OS_MACOS) +#if defined(DART_HOST_OS_MACOS) && !defined(DART_USE_ABSL) #include "vm/os_thread.h" diff --git a/runtime/vm/os_thread_win.cc b/runtime/vm/os_thread_win.cc index 8b859a83afb..49eda86bf54 100644 --- a/runtime/vm/os_thread_win.cc +++ b/runtime/vm/os_thread_win.cc @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. #include "platform/globals.h" // NOLINT -#if defined(DART_HOST_OS_WINDOWS) +#if defined(DART_HOST_OS_WINDOWS) && !defined(DART_USE_ABSL) #include "vm/growable_array.h" #include "vm/lockers.h" @@ -530,4 +530,4 @@ PIMAGE_TLS_CALLBACK p_thread_callback_dart = OnDartThreadExit; #endif // _WIN64 } // extern "C" -#endif // defined(DART_HOST_OS_WINDOWS) +#endif // defined(DART_HOST_OS_WINDOWS) && !defined(DART_USE_ABSL) diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni index 059703c8b50..0a2264ae127 100644 --- a/runtime/vm/vm_sources.gni +++ b/runtime/vm/vm_sources.gni @@ -210,6 +210,8 @@ vm_sources = [ "os_macos.cc", "os_thread.cc", "os_thread.h", + "os_thread_absl.cc", + "os_thread_absl.h", "os_thread_android.cc", "os_thread_android.h", "os_thread_fuchsia.cc",