[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 <aam@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/bin/builtin_impl_sources.gni b/runtime/bin/builtin_impl_sources.gni
index ff6a6c3..feb1ee8 100644
--- a/runtime/bin/builtin_impl_sources.gni
+++ b/runtime/bin/builtin_impl_sources.gni
@@ -45,6 +45,8 @@
   "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 d6716c9..bff42e9 100644
--- a/runtime/bin/thread.h
+++ b/runtime/bin/thread.h
@@ -16,7 +16,9 @@
 }  // 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 0000000..5db7af1
--- /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 <errno.h>         // NOLINT
+#include <sys/resource.h>  // NOLINT
+#include <sys/time.h>      // 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<ThreadStartData*>(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<pthread_key_t>(-1);
+const ThreadId Thread::kInvalidThreadId = static_cast<ThreadId>(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<void*>(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<intptr_t>(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 0000000..649f2e7
--- /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 <pthread.h>
+
+#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<pthread_key_t>(-1);
+    ASSERT(key != kUnsetThreadLocalKey);
+    return reinterpret_cast<uword>(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 60c911a..2848e46 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 @@
 }  // 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 66b847d..ef839f1 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 @@
 }  // 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 1a46a3e..bd8688d 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 @@
 }  // 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 a38c441..3b22fb3 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 @@
 }  // 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 53809f3..a0c20ba 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 @@
 }  // 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 84b66e8..2c12e45 100644
--- a/runtime/tools/run_clang_tidy.dart
+++ b/runtime/tools/run_clang_tidy.dart
@@ -64,6 +64,7 @@
   '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 @@
   '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 1f6a0e3..5df7fb2 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 0000000..d922da8
--- /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 <errno.h>  // NOLINT
+#include <stdio.h>
+#include <sys/resource.h>  // NOLINT
+#include <sys/syscall.h>   // NOLINT
+#include <sys/time.h>      // 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<ThreadStartData*>(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<ThreadId>(0);
+const ThreadJoinId OSThread::kInvalidThreadJoinId =
+    static_cast<ThreadJoinId>(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<void*>(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<intptr_t>(id);
+}
+
+ThreadId OSThread::ThreadIdFromIntPtr(intptr_t id) {
+  return static_cast<ThreadId>(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<uword>(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 0000000..e10793b
--- /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 <pthread.h>
+
+#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<pthread_key_t>(-1);
+
+class ThreadInlineImpl {
+ private:
+  ThreadInlineImpl() {}
+  ~ThreadInlineImpl() {}
+
+  static uword GetThreadLocal(ThreadLocalKey key) {
+    ASSERT(key != kUnsetThreadLocalKey);
+    return reinterpret_cast<uword>(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 8c09320..a9a1413 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 @@
 
 }  // 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 1eb8a3a..3cf8193 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 @@
 
 }  // 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 409b03b..ea44790 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 @@
 
 }  // 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 401fdc7..cbbc226 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 8b859a8..49eda86 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 @@
 #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 059703c..0a2264a 100644
--- a/runtime/vm/vm_sources.gni
+++ b/runtime/vm/vm_sources.gni
@@ -210,6 +210,8 @@
   "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",