Add most of base/ build/ buildtools/ testing/ third_party/googletest/
Enough to make ./tools/gn/bootstrap/bootstrap.py work on Linux.
Change-Id: I94de95f1ce87dd3672d1a99c62254edee8be45bd
Reviewed-on: https://gn-review.googlesource.com/1100
Reviewed-by: Petr Hosek <phosek@google.com>
Commit-Queue: Scott Graham <scottmg@chromium.org>
diff --git a/base/threading/OWNERS b/base/threading/OWNERS
new file mode 100644
index 0000000..4198e99
--- /dev/null
+++ b/base/threading/OWNERS
@@ -0,0 +1,2 @@
+# For thread_resrictions.*
+jam@chromium.org
diff --git a/base/threading/platform_thread.h b/base/threading/platform_thread.h
new file mode 100644
index 0000000..faeb858
--- /dev/null
+++ b/base/threading/platform_thread.h
@@ -0,0 +1,240 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// WARNING: You should *NOT* be using this class directly. PlatformThread is
+// the low-level platform-specific abstraction to the OS's threading interface.
+// You should instead be using a message-loop driven Thread, see thread.h.
+
+#ifndef BASE_THREADING_PLATFORM_THREAD_H_
+#define BASE_THREADING_PLATFORM_THREAD_H_
+
+#include <stddef.h>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_types.h"
+#elif defined(OS_FUCHSIA)
+#include <zircon/types.h>
+#elif defined(OS_MACOSX)
+#include <mach/mach_types.h>
+#elif defined(OS_POSIX)
+#include <pthread.h>
+#include <unistd.h>
+#endif
+
+namespace base {
+
+// Used for logging. Always an integer value.
+#if defined(OS_WIN)
+typedef DWORD PlatformThreadId;
+#elif defined(OS_FUCHSIA)
+typedef zx_handle_t PlatformThreadId;
+#elif defined(OS_MACOSX)
+typedef mach_port_t PlatformThreadId;
+#elif defined(OS_POSIX)
+typedef pid_t PlatformThreadId;
+#endif
+
+// Used for thread checking and debugging.
+// Meant to be as fast as possible.
+// These are produced by PlatformThread::CurrentRef(), and used to later
+// check if we are on the same thread or not by using ==. These are safe
+// to copy between threads, but can't be copied to another process as they
+// have no meaning there. Also, the internal identifier can be re-used
+// after a thread dies, so a PlatformThreadRef cannot be reliably used
+// to distinguish a new thread from an old, dead thread.
+class PlatformThreadRef {
+ public:
+#if defined(OS_WIN)
+ typedef DWORD RefType;
+#else // OS_POSIX
+ typedef pthread_t RefType;
+#endif
+ constexpr PlatformThreadRef() : id_(0) {}
+
+ explicit constexpr PlatformThreadRef(RefType id) : id_(id) {}
+
+ bool operator==(PlatformThreadRef other) const {
+ return id_ == other.id_;
+ }
+
+ bool operator!=(PlatformThreadRef other) const { return id_ != other.id_; }
+
+ bool is_null() const {
+ return id_ == 0;
+ }
+ private:
+ RefType id_;
+};
+
+// Used to operate on threads.
+class PlatformThreadHandle {
+ public:
+#if defined(OS_WIN)
+ typedef void* Handle;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ typedef pthread_t Handle;
+#endif
+
+ constexpr PlatformThreadHandle() : handle_(0) {}
+
+ explicit constexpr PlatformThreadHandle(Handle handle) : handle_(handle) {}
+
+ bool is_equal(const PlatformThreadHandle& other) const {
+ return handle_ == other.handle_;
+ }
+
+ bool is_null() const {
+ return !handle_;
+ }
+
+ Handle platform_handle() const {
+ return handle_;
+ }
+
+ private:
+ Handle handle_;
+};
+
+const PlatformThreadId kInvalidThreadId(0);
+
+// Valid values for priority of Thread::Options and SimpleThread::Options, and
+// SetCurrentThreadPriority(), listed in increasing order of importance.
+enum class ThreadPriority : int {
+ // Suitable for threads that shouldn't disrupt high priority work.
+ BACKGROUND,
+ // Default priority level.
+ NORMAL,
+ // Suitable for threads which generate data for the display (at ~60Hz).
+ DISPLAY,
+ // Suitable for low-latency, glitch-resistant audio.
+ REALTIME_AUDIO,
+};
+
+// A namespace for low-level thread functions.
+class BASE_EXPORT PlatformThread {
+ public:
+ // Implement this interface to run code on a background thread. Your
+ // ThreadMain method will be called on the newly created thread.
+ class BASE_EXPORT Delegate {
+ public:
+ virtual void ThreadMain() = 0;
+
+ protected:
+ virtual ~Delegate() = default;
+ };
+
+ // Gets the current thread id, which may be useful for logging purposes.
+ static PlatformThreadId CurrentId();
+
+ // Gets the current thread reference, which can be used to check if
+ // we're on the right thread quickly.
+ static PlatformThreadRef CurrentRef();
+
+ // Get the handle representing the current thread. On Windows, this is a
+ // pseudo handle constant which will always represent the thread using it and
+ // hence should not be shared with other threads nor be used to differentiate
+ // the current thread from another.
+ static PlatformThreadHandle CurrentHandle();
+
+ // Yield the current thread so another thread can be scheduled.
+ static void YieldCurrentThread();
+
+ // Sleeps for the specified duration.
+ static void Sleep(base::TimeDelta duration);
+
+ // Sets the thread name visible to debuggers/tools. This will try to
+ // initialize the context for current thread unless it's a WorkerThread.
+ static void SetName(const std::string& name);
+
+ // Gets the thread name, if previously set by SetName.
+ static const char* GetName();
+
+ // Creates a new thread. The |stack_size| parameter can be 0 to indicate
+ // that the default stack size should be used. Upon success,
+ // |*thread_handle| will be assigned a handle to the newly created thread,
+ // and |delegate|'s ThreadMain method will be executed on the newly created
+ // thread.
+ // NOTE: When you are done with the thread handle, you must call Join to
+ // release system resources associated with the thread. You must ensure that
+ // the Delegate object outlives the thread.
+ static bool Create(size_t stack_size,
+ Delegate* delegate,
+ PlatformThreadHandle* thread_handle) {
+ return CreateWithPriority(stack_size, delegate, thread_handle,
+ ThreadPriority::NORMAL);
+ }
+
+ // CreateWithPriority() does the same thing as Create() except the priority of
+ // the thread is set based on |priority|.
+ static bool CreateWithPriority(size_t stack_size, Delegate* delegate,
+ PlatformThreadHandle* thread_handle,
+ ThreadPriority priority);
+
+ // CreateNonJoinable() does the same thing as Create() except the thread
+ // cannot be Join()'d. Therefore, it also does not output a
+ // PlatformThreadHandle.
+ static bool CreateNonJoinable(size_t stack_size, Delegate* delegate);
+
+ // CreateNonJoinableWithPriority() does the same thing as CreateNonJoinable()
+ // except the priority of the thread is set based on |priority|.
+ static bool CreateNonJoinableWithPriority(size_t stack_size,
+ Delegate* delegate,
+ ThreadPriority priority);
+
+ // Joins with a thread created via the Create function. This function blocks
+ // the caller until the designated thread exits. This will invalidate
+ // |thread_handle|.
+ static void Join(PlatformThreadHandle thread_handle);
+
+ // Detaches and releases the thread handle. The thread is no longer joinable
+ // and |thread_handle| is invalidated after this call.
+ static void Detach(PlatformThreadHandle thread_handle);
+
+ // Returns true if SetCurrentThreadPriority() can be used to increase the
+ // priority of the current thread.
+ static bool CanIncreaseCurrentThreadPriority();
+
+ // Toggles the current thread's priority at runtime.
+ //
+ // A thread may not be able to raise its priority back up after lowering it if
+ // the process does not have a proper permission, e.g. CAP_SYS_NICE on Linux.
+ // A thread may not be able to lower its priority back down after raising it
+ // to REALTIME_AUDIO.
+ //
+ // This function must not be called from the main thread on Mac. This is to
+ // avoid performance regressions (https://crbug.com/601270).
+ //
+ // Since changing other threads' priority is not permitted in favor of
+ // security, this interface is restricted to change only the current thread
+ // priority (https://crbug.com/399473).
+ static void SetCurrentThreadPriority(ThreadPriority priority);
+
+ static ThreadPriority GetCurrentThreadPriority();
+
+#if defined(OS_LINUX)
+ // Toggles a specific thread's priority at runtime. This can be used to
+ // change the priority of a thread in a different process and will fail
+ // if the calling process does not have proper permissions. The
+ // SetCurrentThreadPriority() function above is preferred in favor of
+ // security but on platforms where sandboxed processes are not allowed to
+ // change priority this function exists to allow a non-sandboxed process
+ // to change the priority of sandboxed threads for improved performance.
+ // Warning: Don't use this for a main thread because that will change the
+ // whole thread group's (i.e. process) priority.
+ static void SetThreadPriority(PlatformThreadId thread_id,
+ ThreadPriority priority);
+#endif
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_PLATFORM_THREAD_H_
diff --git a/base/threading/platform_thread_android.cc b/base/threading/platform_thread_android.cc
new file mode 100644
index 0000000..fd90d35
--- /dev/null
+++ b/base/threading/platform_thread_android.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/platform_thread.h"
+
+#include <errno.h>
+#include <stddef.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/android/jni_android.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/platform_thread_internal_posix.h"
+#include "base/threading/thread_id_name_manager.h"
+#include "jni/ThreadUtils_jni.h"
+
+namespace base {
+
+namespace internal {
+
+// - BACKGROUND corresponds to Android's PRIORITY_BACKGROUND = 10 value and can
+// result in heavy throttling and force the thread onto a little core on
+// big.LITTLE devices.
+// - DISPLAY corresponds to Android's PRIORITY_DISPLAY = -4 value.
+// - REALTIME_AUDIO corresponds to Android's PRIORITY_AUDIO = -16 value.
+const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4] = {
+ {ThreadPriority::BACKGROUND, 10},
+ {ThreadPriority::NORMAL, 0},
+ {ThreadPriority::DISPLAY, -4},
+ {ThreadPriority::REALTIME_AUDIO, -16},
+};
+
+bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority) {
+ // On Android, we set the Audio priority through JNI as Audio priority
+ // will also allow the process to run while it is backgrounded.
+ if (priority == ThreadPriority::REALTIME_AUDIO) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_ThreadUtils_setThreadPriorityAudio(env, PlatformThread::CurrentId());
+ return true;
+ }
+ return false;
+}
+
+bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority) {
+ DCHECK(priority);
+ *priority = ThreadPriority::NORMAL;
+ JNIEnv* env = base::android::AttachCurrentThread();
+ if (Java_ThreadUtils_isThreadPriorityAudio(
+ env, PlatformThread::CurrentId())) {
+ *priority = ThreadPriority::REALTIME_AUDIO;
+ return true;
+ }
+ return false;
+}
+
+} // namespace internal
+
+void PlatformThread::SetName(const std::string& name) {
+ ThreadIdNameManager::GetInstance()->SetName(name);
+
+ // Like linux, on android we can get the thread names to show up in the
+ // debugger by setting the process name for the LWP.
+ // We don't want to do this for the main thread because that would rename
+ // the process, causing tools like killall to stop working.
+ if (PlatformThread::CurrentId() == getpid())
+ return;
+
+ // Set the name for the LWP (which gets truncated to 15 characters).
+ int err = prctl(PR_SET_NAME, name.c_str());
+ if (err < 0 && errno != EPERM)
+ DPLOG(ERROR) << "prctl(PR_SET_NAME)";
+}
+
+
+void InitThreading() {
+}
+
+void TerminateOnThread() {
+ base::android::DetachFromVM();
+}
+
+size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {
+#if !defined(ADDRESS_SANITIZER)
+ return 0;
+#else
+ // AddressSanitizer bloats the stack approximately 2x. Default stack size of
+ // 1Mb is not enough for some tests (see http://crbug.com/263749 for example).
+ return 2 * (1 << 20); // 2Mb
+#endif
+}
+
+} // namespace base
diff --git a/base/threading/platform_thread_fuchsia.cc b/base/threading/platform_thread_fuchsia.cc
new file mode 100644
index 0000000..eb06795
--- /dev/null
+++ b/base/threading/platform_thread_fuchsia.cc
@@ -0,0 +1,50 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/platform_thread.h"
+
+#include <pthread.h>
+#include <sched.h>
+#include <zircon/syscalls.h>
+
+#include "base/threading/platform_thread_internal_posix.h"
+#include "base/threading/thread_id_name_manager.h"
+
+namespace base {
+
+void InitThreading() {}
+
+void TerminateOnThread() {}
+
+size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {
+ return 0;
+}
+
+// static
+void PlatformThread::SetName(const std::string& name) {
+ zx_status_t status = zx_object_set_property(CurrentId(), ZX_PROP_NAME,
+ name.data(), name.size());
+ DCHECK_EQ(status, ZX_OK);
+
+ ThreadIdNameManager::GetInstance()->SetName(name);
+}
+
+// static
+bool PlatformThread::CanIncreaseCurrentThreadPriority() {
+ return false;
+}
+
+// static
+void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) {
+ if (priority != ThreadPriority::NORMAL) {
+ NOTIMPLEMENTED() << "setting ThreadPriority " << static_cast<int>(priority);
+ }
+}
+
+// static
+ThreadPriority PlatformThread::GetCurrentThreadPriority() {
+ return ThreadPriority::NORMAL;
+}
+
+} // namespace base
diff --git a/base/threading/platform_thread_internal_posix.cc b/base/threading/platform_thread_internal_posix.cc
new file mode 100644
index 0000000..378a24d
--- /dev/null
+++ b/base/threading/platform_thread_internal_posix.cc
@@ -0,0 +1,39 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/platform_thread_internal_posix.h"
+
+#include "base/containers/adapters.h"
+#include "base/logging.h"
+
+namespace base {
+
+namespace internal {
+
+int ThreadPriorityToNiceValue(ThreadPriority priority) {
+ for (const auto& pair : kThreadPriorityToNiceValueMap) {
+ if (pair.priority == priority)
+ return pair.nice_value;
+ }
+ NOTREACHED() << "Unknown ThreadPriority";
+ return 0;
+}
+
+ThreadPriority NiceValueToThreadPriority(int nice_value) {
+ // Try to find a priority that best describes |nice_value|. If there isn't
+ // an exact match, this method returns the closest priority whose nice value
+ // is higher (lower priority) than |nice_value|.
+ for (const auto& pair : Reversed(kThreadPriorityToNiceValueMap)) {
+ if (pair.nice_value >= nice_value)
+ return pair.priority;
+ }
+
+ // Reaching here means |nice_value| is more than any of the defined
+ // priorities. The lowest priority is suitable in this case.
+ return ThreadPriority::BACKGROUND;
+}
+
+} // namespace internal
+
+} // namespace base
diff --git a/base/threading/platform_thread_internal_posix.h b/base/threading/platform_thread_internal_posix.h
new file mode 100644
index 0000000..5f4a215
--- /dev/null
+++ b/base/threading/platform_thread_internal_posix.h
@@ -0,0 +1,48 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_
+#define BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_
+
+#include "base/base_export.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+namespace internal {
+
+struct ThreadPriorityToNiceValuePair {
+ ThreadPriority priority;
+ int nice_value;
+};
+// The elements must be listed in the order of increasing priority (lowest
+// priority first), that is, in the order of decreasing nice values (highest
+// nice value first).
+BASE_EXPORT extern
+const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4];
+
+// Returns the nice value matching |priority| based on the platform-specific
+// implementation of kThreadPriorityToNiceValueMap.
+int ThreadPriorityToNiceValue(ThreadPriority priority);
+
+// Returns the ThreadPrioirty matching |nice_value| based on the platform-
+// specific implementation of kThreadPriorityToNiceValueMap.
+BASE_EXPORT ThreadPriority NiceValueToThreadPriority(int nice_value);
+
+// Allows platform specific tweaks to the generic POSIX solution for
+// SetCurrentThreadPriority. Returns true if the platform-specific
+// implementation handled this |priority| change, false if the generic
+// implementation should instead proceed.
+bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority);
+
+// Returns true if there is a platform-specific ThreadPriority set on the
+// current thread (and returns the actual ThreadPriority via |priority|).
+// Returns false otherwise, leaving |priority| untouched.
+bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority);
+
+} // namespace internal
+
+} // namespace base
+
+#endif // BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_
diff --git a/base/threading/platform_thread_linux.cc b/base/threading/platform_thread_linux.cc
new file mode 100644
index 0000000..190aced
--- /dev/null
+++ b/base/threading/platform_thread_linux.cc
@@ -0,0 +1,184 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/platform_thread.h"
+
+#include <errno.h>
+#include <sched.h>
+#include <stddef.h>
+
+#include "base/files/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/platform_thread_internal_posix.h"
+#include "base/threading/thread_id_name_manager.h"
+#include "build/build_config.h"
+
+#if !defined(OS_NACL) && !defined(OS_AIX)
+#include <pthread.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+namespace base {
+namespace {
+#if !defined(OS_NACL)
+const FilePath::CharType kCgroupDirectory[] =
+ FILE_PATH_LITERAL("/sys/fs/cgroup");
+
+FilePath ThreadPriorityToCgroupDirectory(const FilePath& cgroup_filepath,
+ ThreadPriority priority) {
+ switch (priority) {
+ case ThreadPriority::NORMAL:
+ return cgroup_filepath;
+ case ThreadPriority::BACKGROUND:
+ return cgroup_filepath.Append(FILE_PATH_LITERAL("non-urgent"));
+ case ThreadPriority::DISPLAY:
+ case ThreadPriority::REALTIME_AUDIO:
+ return cgroup_filepath.Append(FILE_PATH_LITERAL("urgent"));
+ }
+ NOTREACHED();
+ return FilePath();
+}
+
+void SetThreadCgroup(PlatformThreadId thread_id,
+ const FilePath& cgroup_directory) {
+ FilePath tasks_filepath = cgroup_directory.Append(FILE_PATH_LITERAL("tasks"));
+ std::string tid = IntToString(thread_id);
+ int bytes_written = WriteFile(tasks_filepath, tid.c_str(), tid.size());
+ if (bytes_written != static_cast<int>(tid.size())) {
+ DVLOG(1) << "Failed to add " << tid << " to " << tasks_filepath.value();
+ }
+}
+
+void SetThreadCgroupForThreadPriority(PlatformThreadId thread_id,
+ const FilePath& cgroup_filepath,
+ ThreadPriority priority) {
+ // Append "chrome" suffix.
+ FilePath cgroup_directory = ThreadPriorityToCgroupDirectory(
+ cgroup_filepath.Append(FILE_PATH_LITERAL("chrome")), priority);
+
+ // Silently ignore request if cgroup directory doesn't exist.
+ if (!DirectoryExists(cgroup_directory))
+ return;
+
+ SetThreadCgroup(thread_id, cgroup_directory);
+}
+
+void SetThreadCgroupsForThreadPriority(PlatformThreadId thread_id,
+ ThreadPriority priority) {
+ FilePath cgroup_filepath(kCgroupDirectory);
+ SetThreadCgroupForThreadPriority(
+ thread_id, cgroup_filepath.Append(FILE_PATH_LITERAL("cpuset")), priority);
+ SetThreadCgroupForThreadPriority(
+ thread_id, cgroup_filepath.Append(FILE_PATH_LITERAL("schedtune")),
+ priority);
+}
+#endif
+} // namespace
+
+namespace internal {
+
+namespace {
+#if !defined(OS_NACL)
+const struct sched_param kRealTimePrio = {8};
+#endif
+} // namespace
+
+const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4] = {
+ {ThreadPriority::BACKGROUND, 10},
+ {ThreadPriority::NORMAL, 0},
+ {ThreadPriority::DISPLAY, -8},
+ {ThreadPriority::REALTIME_AUDIO, -10},
+};
+
+bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority) {
+#if !defined(OS_NACL)
+ SetThreadCgroupsForThreadPriority(PlatformThread::CurrentId(), priority);
+ return priority == ThreadPriority::REALTIME_AUDIO &&
+ pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0;
+#else
+ return false;
+#endif
+}
+
+bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority) {
+#if !defined(OS_NACL)
+ int maybe_sched_rr = 0;
+ struct sched_param maybe_realtime_prio = {0};
+ if (pthread_getschedparam(pthread_self(), &maybe_sched_rr,
+ &maybe_realtime_prio) == 0 &&
+ maybe_sched_rr == SCHED_RR &&
+ maybe_realtime_prio.sched_priority == kRealTimePrio.sched_priority) {
+ *priority = ThreadPriority::REALTIME_AUDIO;
+ return true;
+ }
+#endif
+ return false;
+}
+
+} // namespace internal
+
+// static
+void PlatformThread::SetName(const std::string& name) {
+ ThreadIdNameManager::GetInstance()->SetName(name);
+
+#if !defined(OS_NACL) && !defined(OS_AIX)
+ // On linux we can get the thread names to show up in the debugger by setting
+ // the process name for the LWP. We don't want to do this for the main
+ // thread because that would rename the process, causing tools like killall
+ // to stop working.
+ if (PlatformThread::CurrentId() == getpid())
+ return;
+
+ // http://0pointer.de/blog/projects/name-your-threads.html
+ // Set the name for the LWP (which gets truncated to 15 characters).
+ // Note that glibc also has a 'pthread_setname_np' api, but it may not be
+ // available everywhere and it's only benefit over using prctl directly is
+ // that it can set the name of threads other than the current thread.
+ int err = prctl(PR_SET_NAME, name.c_str());
+ // We expect EPERM failures in sandboxed processes, just ignore those.
+ if (err < 0 && errno != EPERM)
+ DPLOG(ERROR) << "prctl(PR_SET_NAME)";
+#endif // !defined(OS_NACL) && !defined(OS_AIX)
+}
+
+#if !defined(OS_NACL) && !defined(OS_AIX)
+// static
+void PlatformThread::SetThreadPriority(PlatformThreadId thread_id,
+ ThreadPriority priority) {
+ // Changing current main threads' priority is not permitted in favor of
+ // security, this interface is restricted to change only non-main thread
+ // priority.
+ CHECK_NE(thread_id, getpid());
+
+ SetThreadCgroupsForThreadPriority(thread_id, priority);
+
+ const int nice_setting = internal::ThreadPriorityToNiceValue(priority);
+ if (setpriority(PRIO_PROCESS, thread_id, nice_setting)) {
+ DVPLOG(1) << "Failed to set nice value of thread (" << thread_id << ") to "
+ << nice_setting;
+ }
+}
+#endif // !defined(OS_NACL) && !defined(OS_AIX)
+
+void InitThreading() {}
+
+void TerminateOnThread() {}
+
+size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {
+#if !defined(THREAD_SANITIZER)
+ return 0;
+#else
+ // ThreadSanitizer bloats the stack heavily. Evidence has been that the
+ // default stack size isn't enough for some browser tests.
+ return 2 * (1 << 23); // 2 times 8192K (the default stack size on Linux).
+#endif
+}
+
+} // namespace base
diff --git a/base/threading/platform_thread_mac.mm b/base/threading/platform_thread_mac.mm
new file mode 100644
index 0000000..39d979d
--- /dev/null
+++ b/base/threading/platform_thread_mac.mm
@@ -0,0 +1,240 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/platform_thread.h"
+
+#import <Foundation/Foundation.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <mach/thread_policy.h>
+#include <stddef.h>
+#include <sys/resource.h>
+
+#include <algorithm>
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/mach_logging.h"
+#include "base/threading/thread_id_name_manager.h"
+#include "build/build_config.h"
+
+namespace base {
+
+namespace {
+NSString* const kThreadPriorityKey = @"CrThreadPriorityKey";
+} // namespace
+
+// If Cocoa is to be used on more than one thread, it must know that the
+// application is multithreaded. Since it's possible to enter Cocoa code
+// from threads created by pthread_thread_create, Cocoa won't necessarily
+// be aware that the application is multithreaded. Spawning an NSThread is
+// enough to get Cocoa to set up for multithreaded operation, so this is done
+// if necessary before pthread_thread_create spawns any threads.
+//
+// http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/chapter_4_section_4.html
+void InitThreading() {
+ static BOOL multithreaded = [NSThread isMultiThreaded];
+ if (!multithreaded) {
+ // +[NSObject class] is idempotent.
+ [NSThread detachNewThreadSelector:@selector(class)
+ toTarget:[NSObject class]
+ withObject:nil];
+ multithreaded = YES;
+
+ DCHECK([NSThread isMultiThreaded]);
+ }
+}
+
+// static
+void PlatformThread::SetName(const std::string& name) {
+ ThreadIdNameManager::GetInstance()->SetName(name);
+
+ // Mac OS X does not expose the length limit of the name, so
+ // hardcode it.
+ const int kMaxNameLength = 63;
+ std::string shortened_name = name.substr(0, kMaxNameLength);
+ // pthread_setname() fails (harmlessly) in the sandbox, ignore when it does.
+ // See http://crbug.com/47058
+ pthread_setname_np(shortened_name.c_str());
+}
+
+namespace {
+
+// Enables time-contraint policy and priority suitable for low-latency,
+// glitch-resistant audio.
+void SetPriorityRealtimeAudio() {
+ // Increase thread priority to real-time.
+
+ // Please note that the thread_policy_set() calls may fail in
+ // rare cases if the kernel decides the system is under heavy load
+ // and is unable to handle boosting the thread priority.
+ // In these cases we just return early and go on with life.
+
+ mach_port_t mach_thread_id =
+ pthread_mach_thread_np(PlatformThread::CurrentHandle().platform_handle());
+
+ // Make thread fixed priority.
+ thread_extended_policy_data_t policy;
+ policy.timeshare = 0; // Set to 1 for a non-fixed thread.
+ kern_return_t result =
+ thread_policy_set(mach_thread_id,
+ THREAD_EXTENDED_POLICY,
+ reinterpret_cast<thread_policy_t>(&policy),
+ THREAD_EXTENDED_POLICY_COUNT);
+ if (result != KERN_SUCCESS) {
+ MACH_DVLOG(1, result) << "thread_policy_set";
+ return;
+ }
+
+ // Set to relatively high priority.
+ thread_precedence_policy_data_t precedence;
+ precedence.importance = 63;
+ result = thread_policy_set(mach_thread_id,
+ THREAD_PRECEDENCE_POLICY,
+ reinterpret_cast<thread_policy_t>(&precedence),
+ THREAD_PRECEDENCE_POLICY_COUNT);
+ if (result != KERN_SUCCESS) {
+ MACH_DVLOG(1, result) << "thread_policy_set";
+ return;
+ }
+
+ // Most important, set real-time constraints.
+
+ // Define the guaranteed and max fraction of time for the audio thread.
+ // These "duty cycle" values can range from 0 to 1. A value of 0.5
+ // means the scheduler would give half the time to the thread.
+ // These values have empirically been found to yield good behavior.
+ // Good means that audio performance is high and other threads won't starve.
+ const double kGuaranteedAudioDutyCycle = 0.75;
+ const double kMaxAudioDutyCycle = 0.85;
+
+ // Define constants determining how much time the audio thread can
+ // use in a given time quantum. All times are in milliseconds.
+
+ // About 128 frames @44.1KHz
+ const double kTimeQuantum = 2.9;
+
+ // Time guaranteed each quantum.
+ const double kAudioTimeNeeded = kGuaranteedAudioDutyCycle * kTimeQuantum;
+
+ // Maximum time each quantum.
+ const double kMaxTimeAllowed = kMaxAudioDutyCycle * kTimeQuantum;
+
+ // Get the conversion factor from milliseconds to absolute time
+ // which is what the time-constraints call needs.
+ mach_timebase_info_data_t tb_info;
+ mach_timebase_info(&tb_info);
+ double ms_to_abs_time =
+ (static_cast<double>(tb_info.denom) / tb_info.numer) * 1000000;
+
+ thread_time_constraint_policy_data_t time_constraints;
+ time_constraints.period = kTimeQuantum * ms_to_abs_time;
+ time_constraints.computation = kAudioTimeNeeded * ms_to_abs_time;
+ time_constraints.constraint = kMaxTimeAllowed * ms_to_abs_time;
+ time_constraints.preemptible = 0;
+
+ result =
+ thread_policy_set(mach_thread_id,
+ THREAD_TIME_CONSTRAINT_POLICY,
+ reinterpret_cast<thread_policy_t>(&time_constraints),
+ THREAD_TIME_CONSTRAINT_POLICY_COUNT);
+ MACH_DVLOG_IF(1, result != KERN_SUCCESS, result) << "thread_policy_set";
+
+ return;
+}
+
+} // anonymous namespace
+
+// static
+bool PlatformThread::CanIncreaseCurrentThreadPriority() {
+ return true;
+}
+
+// static
+void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) {
+ // Changing the priority of the main thread causes performance regressions.
+ // https://crbug.com/601270
+ DCHECK(![[NSThread currentThread] isMainThread]);
+
+ switch (priority) {
+ case ThreadPriority::BACKGROUND:
+ [[NSThread currentThread] setThreadPriority:0];
+ break;
+ case ThreadPriority::NORMAL:
+ case ThreadPriority::DISPLAY:
+ [[NSThread currentThread] setThreadPriority:0.5];
+ break;
+ case ThreadPriority::REALTIME_AUDIO:
+ SetPriorityRealtimeAudio();
+ DCHECK_EQ([[NSThread currentThread] threadPriority], 1.0);
+ break;
+ }
+
+ [[[NSThread currentThread] threadDictionary]
+ setObject:@(static_cast<int>(priority))
+ forKey:kThreadPriorityKey];
+}
+
+// static
+ThreadPriority PlatformThread::GetCurrentThreadPriority() {
+ NSNumber* priority = base::mac::ObjCCast<NSNumber>([[[NSThread currentThread]
+ threadDictionary] objectForKey:kThreadPriorityKey]);
+
+ if (!priority)
+ return ThreadPriority::NORMAL;
+
+ ThreadPriority thread_priority =
+ static_cast<ThreadPriority>(priority.intValue);
+ switch (thread_priority) {
+ case ThreadPriority::BACKGROUND:
+ case ThreadPriority::NORMAL:
+ case ThreadPriority::DISPLAY:
+ case ThreadPriority::REALTIME_AUDIO:
+ return thread_priority;
+ default:
+ NOTREACHED() << "Unknown priority.";
+ return ThreadPriority::NORMAL;
+ }
+}
+
+size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {
+#if defined(OS_IOS)
+ return 0;
+#else
+ // The Mac OS X default for a pthread stack size is 512kB.
+ // Libc-594.1.4/pthreads/pthread.c's pthread_attr_init uses
+ // DEFAULT_STACK_SIZE for this purpose.
+ //
+ // 512kB isn't quite generous enough for some deeply recursive threads that
+ // otherwise request the default stack size by specifying 0. Here, adopt
+ // glibc's behavior as on Linux, which is to use the current stack size
+ // limit (ulimit -s) as the default stack size. See
+ // glibc-2.11.1/nptl/nptl-init.c's __pthread_initialize_minimal_internal. To
+ // avoid setting the limit below the Mac OS X default or the minimum usable
+ // stack size, these values are also considered. If any of these values
+ // can't be determined, or if stack size is unlimited (ulimit -s unlimited),
+ // stack_size is left at 0 to get the system default.
+ //
+ // Mac OS X normally only applies ulimit -s to the main thread stack. On
+ // contemporary OS X and Linux systems alike, this value is generally 8MB
+ // or in that neighborhood.
+ size_t default_stack_size = 0;
+ struct rlimit stack_rlimit;
+ if (pthread_attr_getstacksize(&attributes, &default_stack_size) == 0 &&
+ getrlimit(RLIMIT_STACK, &stack_rlimit) == 0 &&
+ stack_rlimit.rlim_cur != RLIM_INFINITY) {
+ default_stack_size =
+ std::max(std::max(default_stack_size,
+ static_cast<size_t>(PTHREAD_STACK_MIN)),
+ static_cast<size_t>(stack_rlimit.rlim_cur));
+ }
+ return default_stack_size;
+#endif
+}
+
+void TerminateOnThread() {
+}
+
+} // namespace base
diff --git a/base/threading/platform_thread_posix.cc b/base/threading/platform_thread_posix.cc
new file mode 100644
index 0000000..2466b78
--- /dev/null
+++ b/base/threading/platform_thread_posix.cc
@@ -0,0 +1,305 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/platform_thread.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include "base/debug/activity_tracker.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/platform_thread_internal_posix.h"
+#include "base/threading/thread_id_name_manager.h"
+#include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
+
+#if defined(OS_LINUX)
+#include <sys/syscall.h>
+#endif
+
+#if defined(OS_FUCHSIA)
+#include <zircon/process.h>
+#else
+#include <sys/resource.h>
+#endif
+
+namespace base {
+
+void InitThreading();
+void TerminateOnThread();
+size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes);
+
+namespace {
+
+struct ThreadParams {
+ ThreadParams()
+ : delegate(nullptr), joinable(false), priority(ThreadPriority::NORMAL) {}
+
+ PlatformThread::Delegate* delegate;
+ bool joinable;
+ ThreadPriority priority;
+};
+
+void* ThreadFunc(void* params) {
+ PlatformThread::Delegate* delegate = nullptr;
+
+ {
+ std::unique_ptr<ThreadParams> thread_params(
+ static_cast<ThreadParams*>(params));
+
+ delegate = thread_params->delegate;
+ if (!thread_params->joinable)
+ base::ThreadRestrictions::SetSingletonAllowed(false);
+
+#if !defined(OS_NACL)
+ // Threads on linux/android may inherit their priority from the thread
+ // where they were created. This explicitly sets the priority of all new
+ // threads.
+ PlatformThread::SetCurrentThreadPriority(thread_params->priority);
+#endif
+ }
+
+ ThreadIdNameManager::GetInstance()->RegisterThread(
+ PlatformThread::CurrentHandle().platform_handle(),
+ PlatformThread::CurrentId());
+
+ delegate->ThreadMain();
+
+ ThreadIdNameManager::GetInstance()->RemoveName(
+ PlatformThread::CurrentHandle().platform_handle(),
+ PlatformThread::CurrentId());
+
+ base::TerminateOnThread();
+ return nullptr;
+}
+
+bool CreateThread(size_t stack_size,
+ bool joinable,
+ PlatformThread::Delegate* delegate,
+ PlatformThreadHandle* thread_handle,
+ ThreadPriority priority) {
+ DCHECK(thread_handle);
+ base::InitThreading();
+
+ pthread_attr_t attributes;
+ pthread_attr_init(&attributes);
+
+ // Pthreads are joinable by default, so only specify the detached
+ // attribute if the thread should be non-joinable.
+ if (!joinable)
+ pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED);
+
+ // Get a better default if available.
+ if (stack_size == 0)
+ stack_size = base::GetDefaultThreadStackSize(attributes);
+
+ if (stack_size > 0)
+ pthread_attr_setstacksize(&attributes, stack_size);
+
+ std::unique_ptr<ThreadParams> params(new ThreadParams);
+ params->delegate = delegate;
+ params->joinable = joinable;
+ params->priority = priority;
+
+ pthread_t handle;
+ int err = pthread_create(&handle, &attributes, ThreadFunc, params.get());
+ bool success = !err;
+ if (success) {
+ // ThreadParams should be deleted on the created thread after used.
+ ignore_result(params.release());
+ } else {
+ // Value of |handle| is undefined if pthread_create fails.
+ handle = 0;
+ errno = err;
+ PLOG(ERROR) << "pthread_create";
+ }
+ *thread_handle = PlatformThreadHandle(handle);
+
+ pthread_attr_destroy(&attributes);
+
+ return success;
+}
+
+} // namespace
+
+// static
+PlatformThreadId PlatformThread::CurrentId() {
+ // Pthreads doesn't have the concept of a thread ID, so we have to reach down
+ // into the kernel.
+#if defined(OS_MACOSX)
+ return pthread_mach_thread_np(pthread_self());
+#elif defined(OS_LINUX)
+ return syscall(__NR_gettid);
+#elif defined(OS_ANDROID)
+ return gettid();
+#elif defined(OS_FUCHSIA)
+ return zx_thread_self();
+#elif defined(OS_SOLARIS) || defined(OS_QNX)
+ return pthread_self();
+#elif defined(OS_NACL) && defined(__GLIBC__)
+ return pthread_self();
+#elif defined(OS_NACL) && !defined(__GLIBC__)
+ // Pointers are 32-bits in NaCl.
+ return reinterpret_cast<int32_t>(pthread_self());
+#elif defined(OS_POSIX) && defined(OS_AIX)
+ return pthread_self();
+#elif defined(OS_POSIX) && !defined(OS_AIX)
+ return reinterpret_cast<int64_t>(pthread_self());
+#endif
+}
+
+// static
+PlatformThreadRef PlatformThread::CurrentRef() {
+ return PlatformThreadRef(pthread_self());
+}
+
+// static
+PlatformThreadHandle PlatformThread::CurrentHandle() {
+ return PlatformThreadHandle(pthread_self());
+}
+
+// static
+void PlatformThread::YieldCurrentThread() {
+ sched_yield();
+}
+
+// static
+void PlatformThread::Sleep(TimeDelta duration) {
+ struct timespec sleep_time, remaining;
+
+ // Break the duration into seconds and nanoseconds.
+ // NOTE: TimeDelta's microseconds are int64s while timespec's
+ // nanoseconds are longs, so this unpacking must prevent overflow.
+ sleep_time.tv_sec = duration.InSeconds();
+ duration -= TimeDelta::FromSeconds(sleep_time.tv_sec);
+ sleep_time.tv_nsec = duration.InMicroseconds() * 1000; // nanoseconds
+
+ while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR)
+ sleep_time = remaining;
+}
+
+// static
+const char* PlatformThread::GetName() {
+ return ThreadIdNameManager::GetInstance()->GetName(CurrentId());
+}
+
+// static
+bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate,
+ PlatformThreadHandle* thread_handle,
+ ThreadPriority priority) {
+ return CreateThread(stack_size, true /* joinable thread */, delegate,
+ thread_handle, priority);
+}
+
+// static
+bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) {
+ return CreateNonJoinableWithPriority(stack_size, delegate,
+ ThreadPriority::NORMAL);
+}
+
+// static
+bool PlatformThread::CreateNonJoinableWithPriority(size_t stack_size,
+ Delegate* delegate,
+ ThreadPriority priority) {
+ PlatformThreadHandle unused;
+
+ bool result = CreateThread(stack_size, false /* non-joinable thread */,
+ delegate, &unused, priority);
+ return result;
+}
+
+// static
+void PlatformThread::Join(PlatformThreadHandle thread_handle) {
+ // Record the event that this thread is blocking upon (for hang diagnosis).
+ base::debug::ScopedThreadJoinActivity thread_activity(&thread_handle);
+
+ // Joining another thread may block the current thread for a long time, since
+ // the thread referred to by |thread_handle| may still be running long-lived /
+ // blocking tasks.
+ AssertBlockingAllowed();
+ CHECK_EQ(0, pthread_join(thread_handle.platform_handle(), nullptr));
+}
+
+// static
+void PlatformThread::Detach(PlatformThreadHandle thread_handle) {
+ CHECK_EQ(0, pthread_detach(thread_handle.platform_handle()));
+}
+
+// Mac and Fuchsia have their own Set/GetCurrentThreadPriority()
+// implementations.
+#if !defined(OS_MACOSX) && !defined(OS_FUCHSIA)
+
+// static
+bool PlatformThread::CanIncreaseCurrentThreadPriority() {
+#if defined(OS_NACL)
+ return false;
+#else
+ // Only root can raise thread priority on POSIX environment. On Linux, users
+ // who have CAP_SYS_NICE permission also can raise the thread priority, but
+ // libcap.so would be needed to check the capability.
+ return geteuid() == 0;
+#endif // defined(OS_NACL)
+}
+
+// static
+void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) {
+#if defined(OS_NACL)
+ NOTIMPLEMENTED();
+#else
+ if (internal::SetCurrentThreadPriorityForPlatform(priority))
+ return;
+
+ // setpriority(2) should change the whole thread group's (i.e. process)
+ // priority. However, as stated in the bugs section of
+ // http://man7.org/linux/man-pages/man2/getpriority.2.html: "under the current
+ // Linux/NPTL implementation of POSIX threads, the nice value is a per-thread
+ // attribute". Also, 0 is prefered to the current thread id since it is
+ // equivalent but makes sandboxing easier (https://crbug.com/399473).
+ const int nice_setting = internal::ThreadPriorityToNiceValue(priority);
+ if (setpriority(PRIO_PROCESS, 0, nice_setting)) {
+ DVPLOG(1) << "Failed to set nice value of thread ("
+ << PlatformThread::CurrentId() << ") to " << nice_setting;
+ }
+#endif // defined(OS_NACL)
+}
+
+// static
+ThreadPriority PlatformThread::GetCurrentThreadPriority() {
+#if defined(OS_NACL)
+ NOTIMPLEMENTED();
+ return ThreadPriority::NORMAL;
+#else
+ // Mirrors SetCurrentThreadPriority()'s implementation.
+ ThreadPriority platform_specific_priority;
+ if (internal::GetCurrentThreadPriorityForPlatform(
+ &platform_specific_priority)) {
+ return platform_specific_priority;
+ }
+
+ // Need to clear errno before calling getpriority():
+ // http://man7.org/linux/man-pages/man2/getpriority.2.html
+ errno = 0;
+ int nice_value = getpriority(PRIO_PROCESS, 0);
+ if (errno != 0) {
+ DVPLOG(1) << "Failed to get nice value of thread ("
+ << PlatformThread::CurrentId() << ")";
+ return ThreadPriority::NORMAL;
+ }
+
+ return internal::NiceValueToThreadPriority(nice_value);
+#endif // !defined(OS_NACL)
+}
+
+#endif // !defined(OS_MACOSX) && !defined(OS_FUCHSIA)
+
+} // namespace base
diff --git a/base/threading/platform_thread_unittest.cc b/base/threading/platform_thread_unittest.cc
new file mode 100644
index 0000000..7eea22e
--- /dev/null
+++ b/base/threading/platform_thread_unittest.cc
@@ -0,0 +1,367 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_POSIX)
+#include "base/threading/platform_thread_internal_posix.h"
+#elif defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace base {
+
+// Trivial tests that thread runs and doesn't crash on create, join, or detach -
+
+namespace {
+
+class TrivialThread : public PlatformThread::Delegate {
+ public:
+ TrivialThread() : run_event_(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED) {}
+
+ void ThreadMain() override { run_event_.Signal(); }
+
+ WaitableEvent& run_event() { return run_event_; }
+
+ private:
+ WaitableEvent run_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(TrivialThread);
+};
+
+} // namespace
+
+TEST(PlatformThreadTest, TrivialJoin) {
+ TrivialThread thread;
+ PlatformThreadHandle handle;
+
+ ASSERT_FALSE(thread.run_event().IsSignaled());
+ ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
+ PlatformThread::Join(handle);
+ ASSERT_TRUE(thread.run_event().IsSignaled());
+}
+
+TEST(PlatformThreadTest, TrivialJoinTimesTen) {
+ TrivialThread thread[10];
+ PlatformThreadHandle handle[arraysize(thread)];
+
+ for (size_t n = 0; n < arraysize(thread); n++)
+ ASSERT_FALSE(thread[n].run_event().IsSignaled());
+ for (size_t n = 0; n < arraysize(thread); n++)
+ ASSERT_TRUE(PlatformThread::Create(0, &thread[n], &handle[n]));
+ for (size_t n = 0; n < arraysize(thread); n++)
+ PlatformThread::Join(handle[n]);
+ for (size_t n = 0; n < arraysize(thread); n++)
+ ASSERT_TRUE(thread[n].run_event().IsSignaled());
+}
+
+// The following detach tests are by nature racy. The run_event approximates the
+// end and termination of the thread, but threads could persist shortly after
+// the test completes.
+TEST(PlatformThreadTest, TrivialDetach) {
+ TrivialThread thread;
+ PlatformThreadHandle handle;
+
+ ASSERT_FALSE(thread.run_event().IsSignaled());
+ ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
+ PlatformThread::Detach(handle);
+ thread.run_event().Wait();
+}
+
+TEST(PlatformThreadTest, TrivialDetachTimesTen) {
+ TrivialThread thread[10];
+ PlatformThreadHandle handle[arraysize(thread)];
+
+ for (size_t n = 0; n < arraysize(thread); n++)
+ ASSERT_FALSE(thread[n].run_event().IsSignaled());
+ for (size_t n = 0; n < arraysize(thread); n++) {
+ ASSERT_TRUE(PlatformThread::Create(0, &thread[n], &handle[n]));
+ PlatformThread::Detach(handle[n]);
+ }
+ for (size_t n = 0; n < arraysize(thread); n++)
+ thread[n].run_event().Wait();
+}
+
+// Tests of basic thread functions ---------------------------------------------
+
+namespace {
+
+class FunctionTestThread : public PlatformThread::Delegate {
+ public:
+ FunctionTestThread()
+ : thread_id_(kInvalidThreadId),
+ termination_ready_(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED),
+ terminate_thread_(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED),
+ done_(false) {}
+ ~FunctionTestThread() override {
+ EXPECT_TRUE(terminate_thread_.IsSignaled())
+ << "Need to mark thread for termination and join the underlying thread "
+ << "before destroying a FunctionTestThread as it owns the "
+ << "WaitableEvent blocking the underlying thread's main.";
+ }
+
+ // Grabs |thread_id_|, runs an optional test on that thread, signals
+ // |termination_ready_|, and then waits for |terminate_thread_| to be
+ // signaled before exiting.
+ void ThreadMain() override {
+ thread_id_ = PlatformThread::CurrentId();
+ EXPECT_NE(thread_id_, kInvalidThreadId);
+
+ // Make sure that the thread ID is the same across calls.
+ EXPECT_EQ(thread_id_, PlatformThread::CurrentId());
+
+ // Run extra tests.
+ RunTest();
+
+ termination_ready_.Signal();
+ terminate_thread_.Wait();
+
+ done_ = true;
+ }
+
+ PlatformThreadId thread_id() const {
+ EXPECT_TRUE(termination_ready_.IsSignaled()) << "Thread ID still unknown";
+ return thread_id_;
+ }
+
+ bool IsRunning() const {
+ return termination_ready_.IsSignaled() && !done_;
+ }
+
+ // Blocks until this thread is started and ready to be terminated.
+ void WaitForTerminationReady() { termination_ready_.Wait(); }
+
+ // Marks this thread for termination (callers must then join this thread to be
+ // guaranteed of termination).
+ void MarkForTermination() { terminate_thread_.Signal(); }
+
+ private:
+ // Runs an optional test on the newly created thread.
+ virtual void RunTest() {}
+
+ PlatformThreadId thread_id_;
+
+ mutable WaitableEvent termination_ready_;
+ WaitableEvent terminate_thread_;
+ bool done_;
+
+ DISALLOW_COPY_AND_ASSIGN(FunctionTestThread);
+};
+
+} // namespace
+
+TEST(PlatformThreadTest, Function) {
+ PlatformThreadId main_thread_id = PlatformThread::CurrentId();
+
+ FunctionTestThread thread;
+ PlatformThreadHandle handle;
+
+ ASSERT_FALSE(thread.IsRunning());
+ ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
+ thread.WaitForTerminationReady();
+ ASSERT_TRUE(thread.IsRunning());
+ EXPECT_NE(thread.thread_id(), main_thread_id);
+
+ thread.MarkForTermination();
+ PlatformThread::Join(handle);
+ ASSERT_FALSE(thread.IsRunning());
+
+ // Make sure that the thread ID is the same across calls.
+ EXPECT_EQ(main_thread_id, PlatformThread::CurrentId());
+}
+
+TEST(PlatformThreadTest, FunctionTimesTen) {
+ PlatformThreadId main_thread_id = PlatformThread::CurrentId();
+
+ FunctionTestThread thread[10];
+ PlatformThreadHandle handle[arraysize(thread)];
+
+ for (size_t n = 0; n < arraysize(thread); n++)
+ ASSERT_FALSE(thread[n].IsRunning());
+
+ for (size_t n = 0; n < arraysize(thread); n++)
+ ASSERT_TRUE(PlatformThread::Create(0, &thread[n], &handle[n]));
+ for (size_t n = 0; n < arraysize(thread); n++)
+ thread[n].WaitForTerminationReady();
+
+ for (size_t n = 0; n < arraysize(thread); n++) {
+ ASSERT_TRUE(thread[n].IsRunning());
+ EXPECT_NE(thread[n].thread_id(), main_thread_id);
+
+ // Make sure no two threads get the same ID.
+ for (size_t i = 0; i < n; ++i) {
+ EXPECT_NE(thread[i].thread_id(), thread[n].thread_id());
+ }
+ }
+
+ for (size_t n = 0; n < arraysize(thread); n++)
+ thread[n].MarkForTermination();
+ for (size_t n = 0; n < arraysize(thread); n++)
+ PlatformThread::Join(handle[n]);
+ for (size_t n = 0; n < arraysize(thread); n++)
+ ASSERT_FALSE(thread[n].IsRunning());
+
+ // Make sure that the thread ID is the same across calls.
+ EXPECT_EQ(main_thread_id, PlatformThread::CurrentId());
+}
+
+namespace {
+
+const ThreadPriority kThreadPriorityTestValues[] = {
+// The order should be higher to lower to cover as much cases as possible on
+// Linux trybots running without CAP_SYS_NICE permission.
+#if !defined(OS_ANDROID)
+ // PlatformThread::GetCurrentThreadPriority() on Android does not support
+ // REALTIME_AUDIO case. See http://crbug.com/505474.
+ ThreadPriority::REALTIME_AUDIO,
+#endif
+ ThreadPriority::DISPLAY,
+ // This redundant BACKGROUND priority is to test backgrounding from other
+ // priorities, and unbackgrounding.
+ ThreadPriority::BACKGROUND,
+ ThreadPriority::NORMAL,
+ ThreadPriority::BACKGROUND};
+
+class ThreadPriorityTestThread : public FunctionTestThread {
+ public:
+ explicit ThreadPriorityTestThread(ThreadPriority priority)
+ : priority_(priority) {}
+ ~ThreadPriorityTestThread() override = default;
+
+ private:
+ void RunTest() override {
+ // Confirm that the current thread's priority is as expected.
+ EXPECT_EQ(ThreadPriority::NORMAL,
+ PlatformThread::GetCurrentThreadPriority());
+
+ // Alter and verify the current thread's priority.
+ PlatformThread::SetCurrentThreadPriority(priority_);
+ EXPECT_EQ(priority_, PlatformThread::GetCurrentThreadPriority());
+ }
+
+ const ThreadPriority priority_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadPriorityTestThread);
+};
+
+} // namespace
+
+// Test changing a created thread's priority (which has different semantics on
+// some platforms).
+TEST(PlatformThreadTest, ThreadPriorityCurrentThread) {
+ const bool increase_priority_allowed =
+ PlatformThread::CanIncreaseCurrentThreadPriority();
+
+// Bump the priority in order to verify that new threads are started with normal
+// priority. Skip this on Mac since this platform doesn't allow changing the
+// priority of the main thread. Also skip this on platforms that don't allow
+// increasing the priority of a thread.
+#if !defined(OS_MACOSX)
+ if (increase_priority_allowed)
+ PlatformThread::SetCurrentThreadPriority(ThreadPriority::DISPLAY);
+#endif
+
+ // Toggle each supported priority on the thread and confirm it affects it.
+ for (size_t i = 0; i < arraysize(kThreadPriorityTestValues); ++i) {
+ if (!increase_priority_allowed &&
+ kThreadPriorityTestValues[i] >
+ PlatformThread::GetCurrentThreadPriority()) {
+ continue;
+ }
+
+ ThreadPriorityTestThread thread(kThreadPriorityTestValues[i]);
+ PlatformThreadHandle handle;
+
+ ASSERT_FALSE(thread.IsRunning());
+ ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
+ thread.WaitForTerminationReady();
+ ASSERT_TRUE(thread.IsRunning());
+
+ thread.MarkForTermination();
+ PlatformThread::Join(handle);
+ ASSERT_FALSE(thread.IsRunning());
+ }
+}
+
+// This tests internal PlatformThread APIs used under some POSIX platforms,
+// with the exception of Mac OS X, iOS and Fuchsia.
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS) && \
+ !defined(OS_FUCHSIA)
+TEST(PlatformThreadTest, GetNiceValueToThreadPriority) {
+ using internal::NiceValueToThreadPriority;
+ using internal::kThreadPriorityToNiceValueMap;
+
+ EXPECT_EQ(ThreadPriority::BACKGROUND,
+ kThreadPriorityToNiceValueMap[0].priority);
+ EXPECT_EQ(ThreadPriority::NORMAL,
+ kThreadPriorityToNiceValueMap[1].priority);
+ EXPECT_EQ(ThreadPriority::DISPLAY,
+ kThreadPriorityToNiceValueMap[2].priority);
+ EXPECT_EQ(ThreadPriority::REALTIME_AUDIO,
+ kThreadPriorityToNiceValueMap[3].priority);
+
+ static const int kBackgroundNiceValue =
+ kThreadPriorityToNiceValueMap[0].nice_value;
+ static const int kNormalNiceValue =
+ kThreadPriorityToNiceValueMap[1].nice_value;
+ static const int kDisplayNiceValue =
+ kThreadPriorityToNiceValueMap[2].nice_value;
+ static const int kRealtimeAudioNiceValue =
+ kThreadPriorityToNiceValueMap[3].nice_value;
+
+ // The tests below assume the nice values specified in the map are within
+ // the range below (both ends exclusive).
+ static const int kHighestNiceValue = 19;
+ static const int kLowestNiceValue = -20;
+
+ EXPECT_GT(kHighestNiceValue, kBackgroundNiceValue);
+ EXPECT_GT(kBackgroundNiceValue, kNormalNiceValue);
+ EXPECT_GT(kNormalNiceValue, kDisplayNiceValue);
+ EXPECT_GT(kDisplayNiceValue, kRealtimeAudioNiceValue);
+ EXPECT_GT(kRealtimeAudioNiceValue, kLowestNiceValue);
+
+ EXPECT_EQ(ThreadPriority::BACKGROUND,
+ NiceValueToThreadPriority(kHighestNiceValue));
+ EXPECT_EQ(ThreadPriority::BACKGROUND,
+ NiceValueToThreadPriority(kBackgroundNiceValue + 1));
+ EXPECT_EQ(ThreadPriority::BACKGROUND,
+ NiceValueToThreadPriority(kBackgroundNiceValue));
+ EXPECT_EQ(ThreadPriority::BACKGROUND,
+ NiceValueToThreadPriority(kNormalNiceValue + 1));
+ EXPECT_EQ(ThreadPriority::NORMAL,
+ NiceValueToThreadPriority(kNormalNiceValue));
+ EXPECT_EQ(ThreadPriority::NORMAL,
+ NiceValueToThreadPriority(kDisplayNiceValue + 1));
+ EXPECT_EQ(ThreadPriority::DISPLAY,
+ NiceValueToThreadPriority(kDisplayNiceValue));
+ EXPECT_EQ(ThreadPriority::DISPLAY,
+ NiceValueToThreadPriority(kRealtimeAudioNiceValue + 1));
+ EXPECT_EQ(ThreadPriority::REALTIME_AUDIO,
+ NiceValueToThreadPriority(kRealtimeAudioNiceValue));
+ EXPECT_EQ(ThreadPriority::REALTIME_AUDIO,
+ NiceValueToThreadPriority(kLowestNiceValue));
+}
+#endif // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS) &&
+ // !defined(OS_FUCHSIA)
+
+TEST(PlatformThreadTest, SetHugeThreadName) {
+ // Construct an excessively long thread name.
+ std::string long_name(1024, 'a');
+
+ // SetName has no return code, so just verify that implementations
+ // don't [D]CHECK().
+ PlatformThread::SetName(long_name);
+}
+
+} // namespace base
diff --git a/base/threading/platform_thread_win.cc b/base/threading/platform_thread_win.cc
new file mode 100644
index 0000000..daccc0e
--- /dev/null
+++ b/base/threading/platform_thread_win.cc
@@ -0,0 +1,317 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/platform_thread.h"
+
+#include <stddef.h>
+
+#include "base/debug/activity_tracker.h"
+#include "base/debug/alias.h"
+#include "base/debug/profiler.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_id_name_manager.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/win/scoped_handle.h"
+
+#include <windows.h>
+
+namespace base {
+
+namespace {
+
+// The information on how to set the thread name comes from
+// a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx
+const DWORD kVCThreadNameException = 0x406D1388;
+
+typedef struct tagTHREADNAME_INFO {
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ DWORD dwThreadID; // Thread ID (-1=caller thread).
+ DWORD dwFlags; // Reserved for future use, must be zero.
+} THREADNAME_INFO;
+
+// The SetThreadDescription API was brought in version 1607 of Windows 10.
+typedef HRESULT(WINAPI* SetThreadDescription)(HANDLE hThread,
+ PCWSTR lpThreadDescription);
+
+// This function has try handling, so it is separated out of its caller.
+void SetNameInternal(PlatformThreadId thread_id, const char* name) {
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = name;
+ info.dwThreadID = thread_id;
+ info.dwFlags = 0;
+
+ __try {
+ RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD),
+ reinterpret_cast<DWORD_PTR*>(&info));
+ } __except(EXCEPTION_CONTINUE_EXECUTION) {
+ }
+}
+
+struct ThreadParams {
+ PlatformThread::Delegate* delegate;
+ bool joinable;
+ ThreadPriority priority;
+};
+
+DWORD __stdcall ThreadFunc(void* params) {
+ ThreadParams* thread_params = static_cast<ThreadParams*>(params);
+ PlatformThread::Delegate* delegate = thread_params->delegate;
+ if (!thread_params->joinable)
+ base::ThreadRestrictions::SetSingletonAllowed(false);
+
+ if (thread_params->priority != ThreadPriority::NORMAL)
+ PlatformThread::SetCurrentThreadPriority(thread_params->priority);
+
+ // Retrieve a copy of the thread handle to use as the key in the
+ // thread name mapping.
+ PlatformThreadHandle::Handle platform_handle;
+ BOOL did_dup = DuplicateHandle(GetCurrentProcess(),
+ GetCurrentThread(),
+ GetCurrentProcess(),
+ &platform_handle,
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS);
+
+ win::ScopedHandle scoped_platform_handle;
+
+ if (did_dup) {
+ scoped_platform_handle.Set(platform_handle);
+ ThreadIdNameManager::GetInstance()->RegisterThread(
+ scoped_platform_handle.Get(),
+ PlatformThread::CurrentId());
+ }
+
+ delete thread_params;
+ delegate->ThreadMain();
+
+ if (did_dup) {
+ ThreadIdNameManager::GetInstance()->RemoveName(
+ scoped_platform_handle.Get(),
+ PlatformThread::CurrentId());
+ }
+
+ return 0;
+}
+
+// CreateThreadInternal() matches PlatformThread::CreateWithPriority(), except
+// that |out_thread_handle| may be nullptr, in which case a non-joinable thread
+// is created.
+bool CreateThreadInternal(size_t stack_size,
+ PlatformThread::Delegate* delegate,
+ PlatformThreadHandle* out_thread_handle,
+ ThreadPriority priority) {
+ unsigned int flags = 0;
+ if (stack_size > 0) {
+ flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
+ }
+
+ ThreadParams* params = new ThreadParams;
+ params->delegate = delegate;
+ params->joinable = out_thread_handle != nullptr;
+ params->priority = priority;
+
+ void* thread_handle;
+ {
+ SCOPED_UMA_HISTOGRAM_TIMER("Windows.CreateThreadTime");
+
+ // Using CreateThread here vs _beginthreadex makes thread creation a bit
+ // faster and doesn't require the loader lock to be available. Our code
+ // will have to work running on CreateThread() threads anyway, since we run
+ // code on the Windows thread pool, etc. For some background on the
+ // difference:
+ // http://www.microsoft.com/msj/1099/win32/win321099.aspx
+ thread_handle =
+ ::CreateThread(nullptr, stack_size, ThreadFunc, params, flags, nullptr);
+ }
+
+ if (!thread_handle) {
+ delete params;
+ return false;
+ }
+
+ if (out_thread_handle)
+ *out_thread_handle = PlatformThreadHandle(thread_handle);
+ else
+ CloseHandle(thread_handle);
+ return true;
+}
+
+} // namespace
+
+// static
+PlatformThreadId PlatformThread::CurrentId() {
+ return ::GetCurrentThreadId();
+}
+
+// static
+PlatformThreadRef PlatformThread::CurrentRef() {
+ return PlatformThreadRef(::GetCurrentThreadId());
+}
+
+// static
+PlatformThreadHandle PlatformThread::CurrentHandle() {
+ return PlatformThreadHandle(::GetCurrentThread());
+}
+
+// static
+void PlatformThread::YieldCurrentThread() {
+ ::Sleep(0);
+}
+
+// static
+void PlatformThread::Sleep(TimeDelta duration) {
+ // When measured with a high resolution clock, Sleep() sometimes returns much
+ // too early. We may need to call it repeatedly to get the desired duration.
+ TimeTicks end = TimeTicks::Now() + duration;
+ for (TimeTicks now = TimeTicks::Now(); now < end; now = TimeTicks::Now())
+ ::Sleep(static_cast<DWORD>((end - now).InMillisecondsRoundedUp()));
+}
+
+// static
+void PlatformThread::SetName(const std::string& name) {
+ ThreadIdNameManager::GetInstance()->SetName(name);
+
+ // The SetThreadDescription API works even if no debugger is attached.
+ auto set_thread_description_func =
+ reinterpret_cast<SetThreadDescription>(::GetProcAddress(
+ ::GetModuleHandle(L"Kernel32.dll"), "SetThreadDescription"));
+ if (set_thread_description_func) {
+ set_thread_description_func(::GetCurrentThread(),
+ base::UTF8ToWide(name).c_str());
+ }
+
+ // The debugger needs to be around to catch the name in the exception. If
+ // there isn't a debugger, we are just needlessly throwing an exception.
+ if (!::IsDebuggerPresent())
+ return;
+
+ SetNameInternal(CurrentId(), name.c_str());
+}
+
+// static
+const char* PlatformThread::GetName() {
+ return ThreadIdNameManager::GetInstance()->GetName(CurrentId());
+}
+
+// static
+bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate,
+ PlatformThreadHandle* thread_handle,
+ ThreadPriority priority) {
+ DCHECK(thread_handle);
+ return CreateThreadInternal(stack_size, delegate, thread_handle, priority);
+}
+
+// static
+bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) {
+ return CreateNonJoinableWithPriority(stack_size, delegate,
+ ThreadPriority::NORMAL);
+}
+
+// static
+bool PlatformThread::CreateNonJoinableWithPriority(size_t stack_size,
+ Delegate* delegate,
+ ThreadPriority priority) {
+ return CreateThreadInternal(stack_size, delegate, nullptr /* non-joinable */,
+ priority);
+}
+
+// static
+void PlatformThread::Join(PlatformThreadHandle thread_handle) {
+ DCHECK(thread_handle.platform_handle());
+ // TODO(willchan): Enable this check once I can get it to work for Windows
+ // shutdown.
+ // Joining another thread may block the current thread for a long time, since
+ // the thread referred to by |thread_handle| may still be running long-lived /
+ // blocking tasks.
+ // AssertBlockingAllowed();
+
+ DWORD thread_id = 0;
+ thread_id = ::GetThreadId(thread_handle.platform_handle());
+ DWORD last_error = 0;
+ if (!thread_id)
+ last_error = ::GetLastError();
+
+ // Record information about the exiting thread in case joining hangs.
+ base::debug::Alias(&thread_id);
+ base::debug::Alias(&last_error);
+
+ // Record the event that this thread is blocking upon (for hang diagnosis).
+ base::debug::ScopedThreadJoinActivity thread_activity(&thread_handle);
+
+ // Wait for the thread to exit. It should already have terminated but make
+ // sure this assumption is valid.
+ CHECK_EQ(WAIT_OBJECT_0,
+ WaitForSingleObject(thread_handle.platform_handle(), INFINITE));
+ CloseHandle(thread_handle.platform_handle());
+}
+
+// static
+void PlatformThread::Detach(PlatformThreadHandle thread_handle) {
+ CloseHandle(thread_handle.platform_handle());
+}
+
+// static
+bool PlatformThread::CanIncreaseCurrentThreadPriority() {
+ return true;
+}
+
+// static
+void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) {
+ int desired_priority = THREAD_PRIORITY_ERROR_RETURN;
+ switch (priority) {
+ case ThreadPriority::BACKGROUND:
+ desired_priority = THREAD_PRIORITY_LOWEST;
+ break;
+ case ThreadPriority::NORMAL:
+ desired_priority = THREAD_PRIORITY_NORMAL;
+ break;
+ case ThreadPriority::DISPLAY:
+ desired_priority = THREAD_PRIORITY_ABOVE_NORMAL;
+ break;
+ case ThreadPriority::REALTIME_AUDIO:
+ desired_priority = THREAD_PRIORITY_TIME_CRITICAL;
+ break;
+ default:
+ NOTREACHED() << "Unknown priority.";
+ break;
+ }
+ DCHECK_NE(desired_priority, THREAD_PRIORITY_ERROR_RETURN);
+
+#if DCHECK_IS_ON()
+ const BOOL success =
+#endif
+ ::SetThreadPriority(PlatformThread::CurrentHandle().platform_handle(),
+ desired_priority);
+ DPLOG_IF(ERROR, !success) << "Failed to set thread priority to "
+ << desired_priority;
+}
+
+// static
+ThreadPriority PlatformThread::GetCurrentThreadPriority() {
+ int priority =
+ ::GetThreadPriority(PlatformThread::CurrentHandle().platform_handle());
+ switch (priority) {
+ case THREAD_PRIORITY_LOWEST:
+ return ThreadPriority::BACKGROUND;
+ case THREAD_PRIORITY_NORMAL:
+ return ThreadPriority::NORMAL;
+ case THREAD_PRIORITY_ABOVE_NORMAL:
+ return ThreadPriority::DISPLAY;
+ case THREAD_PRIORITY_TIME_CRITICAL:
+ return ThreadPriority::REALTIME_AUDIO;
+ case THREAD_PRIORITY_ERROR_RETURN:
+ DPCHECK(false) << "GetThreadPriority error";
+ FALLTHROUGH;
+ default:
+ NOTREACHED() << "Unexpected priority: " << priority;
+ return ThreadPriority::NORMAL;
+ }
+}
+
+} // namespace base
diff --git a/base/threading/post_task_and_reply_impl.cc b/base/threading/post_task_and_reply_impl.cc
new file mode 100644
index 0000000..5aacdad
--- /dev/null
+++ b/base/threading/post_task_and_reply_impl.cc
@@ -0,0 +1,127 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/post_task_and_reply_impl.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/debug/leak_annotations.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+
+namespace base {
+
+namespace {
+
+class PostTaskAndReplyRelay {
+ public:
+ PostTaskAndReplyRelay(const Location& from_here,
+ OnceClosure task,
+ OnceClosure reply)
+ : from_here_(from_here),
+ task_(std::move(task)),
+ reply_(std::move(reply)) {}
+ PostTaskAndReplyRelay(PostTaskAndReplyRelay&&) = default;
+
+ ~PostTaskAndReplyRelay() {
+ if (reply_) {
+ // This can run:
+ // 1) On origin sequence, when:
+ // 1a) Posting |task_| fails.
+ // 1b) |reply_| is cancelled before running.
+ // 1c) The DeleteSoon() below is scheduled.
+ // 2) On destination sequence, when:
+ // 2a) |task_| is cancelled before running.
+ // 2b) Posting |reply_| fails.
+
+ if (!reply_task_runner_->RunsTasksInCurrentSequence()) {
+ // Case 2a) or 2b).
+ //
+ // Destroy callbacks asynchronously on |reply_task_runner| since their
+ // destructors can rightfully be affine to it. As always, DeleteSoon()
+ // might leak its argument if the target execution environment is
+ // shutdown (e.g. MessageLoop deleted, TaskScheduler shutdown).
+ //
+ // Note: while it's obvious why |reply_| can be affine to
+ // |reply_task_runner|, the reason that |task_| can also be affine to it
+ // is that it if neither tasks ran, |task_| may still hold an object
+ // which was intended to be moved to |reply_| when |task_| ran (such an
+ // object's destruction can be affine to |reply_task_runner_| -- e.g.
+ // https://crbug.com/829122).
+ auto relay_to_delete =
+ std::make_unique<PostTaskAndReplyRelay>(std::move(*this));
+ ANNOTATE_LEAKING_OBJECT_PTR(relay_to_delete.get());
+ reply_task_runner_->DeleteSoon(from_here_, std::move(relay_to_delete));
+ }
+
+ // Case 1a), 1b), 1c).
+ //
+ // Callbacks will be destroyed synchronously at the end of this scope.
+ } else {
+ // This can run when both callbacks have run or have been moved to another
+ // PostTaskAndReplyRelay instance. If |reply_| is null, |task_| must be
+ // null too.
+ DCHECK(!task_);
+ }
+ }
+
+ // No assignment operator because of const members.
+ PostTaskAndReplyRelay& operator=(PostTaskAndReplyRelay&&) = delete;
+
+ // Static function is used because it is not possible to bind a method call to
+ // a non-pointer type.
+ static void RunTaskAndPostReply(PostTaskAndReplyRelay relay) {
+ DCHECK(relay.task_);
+ std::move(relay.task_).Run();
+
+ // Keep a reference to the reply TaskRunner for the PostTask() call before
+ // |relay| is moved into a callback.
+ scoped_refptr<SequencedTaskRunner> reply_task_runner =
+ relay.reply_task_runner_;
+
+ reply_task_runner->PostTask(
+ relay.from_here_,
+ BindOnce(&PostTaskAndReplyRelay::RunReply, std::move(relay)));
+ }
+
+ private:
+ // Static function is used because it is not possible to bind a method call to
+ // a non-pointer type.
+ static void RunReply(PostTaskAndReplyRelay relay) {
+ DCHECK(!relay.task_);
+ DCHECK(relay.reply_);
+ std::move(relay.reply_).Run();
+ }
+
+ const Location from_here_;
+ OnceClosure task_;
+ OnceClosure reply_;
+ const scoped_refptr<SequencedTaskRunner> reply_task_runner_ =
+ SequencedTaskRunnerHandle::Get();
+
+ DISALLOW_COPY_AND_ASSIGN(PostTaskAndReplyRelay);
+};
+
+} // namespace
+
+namespace internal {
+
+bool PostTaskAndReplyImpl::PostTaskAndReply(const Location& from_here,
+ OnceClosure task,
+ OnceClosure reply) {
+ DCHECK(task) << from_here.ToString();
+ DCHECK(reply) << from_here.ToString();
+
+ return PostTask(from_here,
+ BindOnce(&PostTaskAndReplyRelay::RunTaskAndPostReply,
+ PostTaskAndReplyRelay(from_here, std::move(task),
+ std::move(reply))));
+}
+
+} // namespace internal
+
+} // namespace base
diff --git a/base/threading/post_task_and_reply_impl.h b/base/threading/post_task_and_reply_impl.h
new file mode 100644
index 0000000..54038ce
--- /dev/null
+++ b/base/threading/post_task_and_reply_impl.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains the implementation for TaskRunner::PostTaskAndReply.
+
+#ifndef BASE_THREADING_POST_TASK_AND_REPLY_IMPL_H_
+#define BASE_THREADING_POST_TASK_AND_REPLY_IMPL_H_
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/location.h"
+
+namespace base {
+namespace internal {
+
+// Inherit from this in a class that implements PostTask to send a task to a
+// custom execution context.
+//
+// If you're looking for a concrete implementation of PostTaskAndReply, you
+// probably want base::TaskRunner or base/task_scheduler/post_task.h
+class BASE_EXPORT PostTaskAndReplyImpl {
+ public:
+ virtual ~PostTaskAndReplyImpl() = default;
+
+ // Posts |task| by calling PostTask(). On completion, posts |reply| to the
+ // origin sequence. Can only be called when
+ // SequencedTaskRunnerHandle::IsSet(). Each callback is deleted synchronously
+ // after running, or scheduled for asynchronous deletion on the origin
+ // sequence if it can't run (e.g. if a TaskRunner skips it on shutdown). See
+ // SequencedTaskRunner::DeleteSoon() for when objects scheduled for
+ // asynchronous deletion can be leaked. Note: All //base task posting APIs
+ // require callbacks to support deletion on the posting sequence if they can't
+ // be scheduled.
+ bool PostTaskAndReply(const Location& from_here,
+ OnceClosure task,
+ OnceClosure reply);
+
+ private:
+ virtual bool PostTask(const Location& from_here, OnceClosure task) = 0;
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_THREADING_POST_TASK_AND_REPLY_IMPL_H_
diff --git a/base/threading/post_task_and_reply_impl_unittest.cc b/base/threading/post_task_and_reply_impl_unittest.cc
new file mode 100644
index 0000000..319327d
--- /dev/null
+++ b/base/threading/post_task_and_reply_impl_unittest.cc
@@ -0,0 +1,198 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/post_task_and_reply_impl.h"
+
+#include <utility>
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+
+namespace base {
+namespace internal {
+
+namespace {
+
+class PostTaskAndReplyTaskRunner : public internal::PostTaskAndReplyImpl {
+ public:
+ explicit PostTaskAndReplyTaskRunner(TaskRunner* destination)
+ : destination_(destination) {}
+
+ private:
+ bool PostTask(const Location& from_here, OnceClosure task) override {
+ return destination_->PostTask(from_here, std::move(task));
+ }
+
+ // Non-owning.
+ TaskRunner* const destination_;
+};
+
+class ObjectToDelete : public RefCounted<ObjectToDelete> {
+ public:
+ // |delete_flag| is set to true when this object is deleted
+ ObjectToDelete(bool* delete_flag) : delete_flag_(delete_flag) {
+ EXPECT_FALSE(*delete_flag_);
+ }
+
+ private:
+ friend class RefCounted<ObjectToDelete>;
+ ~ObjectToDelete() { *delete_flag_ = true; }
+
+ bool* const delete_flag_;
+
+ DISALLOW_COPY_AND_ASSIGN(ObjectToDelete);
+};
+
+class MockObject {
+ public:
+ MockObject() = default;
+
+ MOCK_METHOD1(Task, void(scoped_refptr<ObjectToDelete>));
+ MOCK_METHOD1(Reply, void(scoped_refptr<ObjectToDelete>));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockObject);
+};
+
+class MockRunsTasksInCurrentSequenceTaskRunner : public TestMockTimeTaskRunner {
+ public:
+ MockRunsTasksInCurrentSequenceTaskRunner(
+ TestMockTimeTaskRunner::Type type =
+ TestMockTimeTaskRunner::Type::kStandalone)
+ : TestMockTimeTaskRunner(type) {}
+
+ void RunUntilIdleWithRunsTasksInCurrentSequence() {
+ AutoReset<bool> reset(&runs_tasks_in_current_sequence_, true);
+ RunUntilIdle();
+ }
+
+ void ClearPendingTasksWithRunsTasksInCurrentSequence() {
+ AutoReset<bool> reset(&runs_tasks_in_current_sequence_, true);
+ ClearPendingTasks();
+ }
+
+ // TestMockTimeTaskRunner:
+ bool RunsTasksInCurrentSequence() const override {
+ return runs_tasks_in_current_sequence_;
+ }
+
+ private:
+ ~MockRunsTasksInCurrentSequenceTaskRunner() override = default;
+
+ bool runs_tasks_in_current_sequence_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(MockRunsTasksInCurrentSequenceTaskRunner);
+};
+
+class PostTaskAndReplyImplTest : public testing::Test {
+ protected:
+ PostTaskAndReplyImplTest() = default;
+
+ void PostTaskAndReplyToMockObject() {
+ // Expect the post to succeed.
+ EXPECT_TRUE(
+ PostTaskAndReplyTaskRunner(post_runner_.get())
+ .PostTaskAndReply(
+ FROM_HERE,
+ BindOnce(&MockObject::Task, Unretained(&mock_object_),
+ MakeRefCounted<ObjectToDelete>(&delete_task_flag_)),
+ BindOnce(&MockObject::Reply, Unretained(&mock_object_),
+ MakeRefCounted<ObjectToDelete>(&delete_reply_flag_))));
+
+ // Expect the first task to be posted to |post_runner_|.
+ EXPECT_TRUE(post_runner_->HasPendingTask());
+ EXPECT_FALSE(reply_runner_->HasPendingTask());
+ EXPECT_FALSE(delete_task_flag_);
+ EXPECT_FALSE(delete_reply_flag_);
+ }
+
+ scoped_refptr<MockRunsTasksInCurrentSequenceTaskRunner> post_runner_ =
+ MakeRefCounted<MockRunsTasksInCurrentSequenceTaskRunner>();
+ scoped_refptr<MockRunsTasksInCurrentSequenceTaskRunner> reply_runner_ =
+ MakeRefCounted<MockRunsTasksInCurrentSequenceTaskRunner>(
+ TestMockTimeTaskRunner::Type::kBoundToThread);
+ testing::StrictMock<MockObject> mock_object_;
+ bool delete_task_flag_ = false;
+ bool delete_reply_flag_ = false;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PostTaskAndReplyImplTest);
+};
+
+} // namespace
+
+TEST_F(PostTaskAndReplyImplTest, PostTaskAndReply) {
+ PostTaskAndReplyToMockObject();
+
+ EXPECT_CALL(mock_object_, Task(_));
+ post_runner_->RunUntilIdleWithRunsTasksInCurrentSequence();
+ testing::Mock::VerifyAndClear(&mock_object_);
+ // The task should have been deleted right after being run.
+ EXPECT_TRUE(delete_task_flag_);
+ EXPECT_FALSE(delete_reply_flag_);
+
+ // Expect the reply to be posted to |reply_runner_|.
+ EXPECT_FALSE(post_runner_->HasPendingTask());
+ EXPECT_TRUE(reply_runner_->HasPendingTask());
+
+ EXPECT_CALL(mock_object_, Reply(_));
+ reply_runner_->RunUntilIdleWithRunsTasksInCurrentSequence();
+ testing::Mock::VerifyAndClear(&mock_object_);
+ EXPECT_TRUE(delete_task_flag_);
+ // The reply should have been deleted right after being run.
+ EXPECT_TRUE(delete_reply_flag_);
+
+ // Expect no pending task in |post_runner_| and |reply_runner_|.
+ EXPECT_FALSE(post_runner_->HasPendingTask());
+ EXPECT_FALSE(reply_runner_->HasPendingTask());
+}
+
+TEST_F(PostTaskAndReplyImplTest, TaskDoesNotRun) {
+ PostTaskAndReplyToMockObject();
+
+ // Clear the |post_runner_|. Both callbacks should be scheduled for deletion
+ // on the |reply_runner_|.
+ post_runner_->ClearPendingTasksWithRunsTasksInCurrentSequence();
+ EXPECT_FALSE(post_runner_->HasPendingTask());
+ EXPECT_TRUE(reply_runner_->HasPendingTask());
+ EXPECT_FALSE(delete_task_flag_);
+ EXPECT_FALSE(delete_reply_flag_);
+
+ // Run the |reply_runner_|. Both callbacks should be deleted.
+ reply_runner_->RunUntilIdleWithRunsTasksInCurrentSequence();
+ EXPECT_TRUE(delete_task_flag_);
+ EXPECT_TRUE(delete_reply_flag_);
+}
+
+TEST_F(PostTaskAndReplyImplTest, ReplyDoesNotRun) {
+ PostTaskAndReplyToMockObject();
+
+ EXPECT_CALL(mock_object_, Task(_));
+ post_runner_->RunUntilIdleWithRunsTasksInCurrentSequence();
+ testing::Mock::VerifyAndClear(&mock_object_);
+ // The task should have been deleted right after being run.
+ EXPECT_TRUE(delete_task_flag_);
+ EXPECT_FALSE(delete_reply_flag_);
+
+ // Expect the reply to be posted to |reply_runner_|.
+ EXPECT_FALSE(post_runner_->HasPendingTask());
+ EXPECT_TRUE(reply_runner_->HasPendingTask());
+
+ // Clear the |reply_runner_| queue without running tasks. The reply callback
+ // should be deleted.
+ reply_runner_->ClearPendingTasksWithRunsTasksInCurrentSequence();
+ EXPECT_TRUE(delete_task_flag_);
+ EXPECT_TRUE(delete_reply_flag_);
+}
+
+} // namespace internal
+} // namespace base
diff --git a/base/threading/scoped_blocking_call.cc b/base/threading/scoped_blocking_call.cc
new file mode 100644
index 0000000..1d2931c
--- /dev/null
+++ b/base/threading/scoped_blocking_call.cc
@@ -0,0 +1,72 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/scoped_blocking_call.h"
+
+#include "base/lazy_instance.h"
+#include "base/threading/thread_local.h"
+
+namespace base {
+
+namespace {
+
+LazyInstance<ThreadLocalPointer<internal::BlockingObserver>>::Leaky
+ tls_blocking_observer = LAZY_INSTANCE_INITIALIZER;
+
+// Last ScopedBlockingCall instantiated on this thread.
+LazyInstance<ThreadLocalPointer<ScopedBlockingCall>>::Leaky
+ tls_last_scoped_blocking_call = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+ScopedBlockingCall::ScopedBlockingCall(BlockingType blocking_type)
+ : blocking_observer_(tls_blocking_observer.Get().Get()),
+ previous_scoped_blocking_call_(tls_last_scoped_blocking_call.Get().Get()),
+ is_will_block_(blocking_type == BlockingType::WILL_BLOCK ||
+ (previous_scoped_blocking_call_ &&
+ previous_scoped_blocking_call_->is_will_block_)) {
+ tls_last_scoped_blocking_call.Get().Set(this);
+
+ if (blocking_observer_) {
+ if (!previous_scoped_blocking_call_) {
+ blocking_observer_->BlockingStarted(blocking_type);
+ } else if (blocking_type == BlockingType::WILL_BLOCK &&
+ !previous_scoped_blocking_call_->is_will_block_) {
+ blocking_observer_->BlockingTypeUpgraded();
+ }
+ }
+}
+
+ScopedBlockingCall::~ScopedBlockingCall() {
+ DCHECK_EQ(this, tls_last_scoped_blocking_call.Get().Get());
+ tls_last_scoped_blocking_call.Get().Set(previous_scoped_blocking_call_);
+ if (blocking_observer_ && !previous_scoped_blocking_call_)
+ blocking_observer_->BlockingEnded();
+}
+
+namespace internal {
+
+void SetBlockingObserverForCurrentThread(BlockingObserver* blocking_observer) {
+ DCHECK(!tls_blocking_observer.Get().Get());
+ tls_blocking_observer.Get().Set(blocking_observer);
+}
+
+void ClearBlockingObserverForTesting() {
+ tls_blocking_observer.Get().Set(nullptr);
+}
+
+ScopedClearBlockingObserverForTesting::ScopedClearBlockingObserverForTesting()
+ : blocking_observer_(tls_blocking_observer.Get().Get()) {
+ tls_blocking_observer.Get().Set(nullptr);
+}
+
+ScopedClearBlockingObserverForTesting::
+ ~ScopedClearBlockingObserverForTesting() {
+ DCHECK(!tls_blocking_observer.Get().Get());
+ tls_blocking_observer.Get().Set(blocking_observer_);
+}
+
+} // namespace internal
+
+} // namespace base
diff --git a/base/threading/scoped_blocking_call.h b/base/threading/scoped_blocking_call.h
new file mode 100644
index 0000000..e376c30
--- /dev/null
+++ b/base/threading/scoped_blocking_call.h
@@ -0,0 +1,140 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_SCOPED_BLOCKING_CALL_H
+#define BASE_THREADING_SCOPED_BLOCKING_CALL_H
+
+#include "base/base_export.h"
+#include "base/logging.h"
+
+namespace base {
+
+// BlockingType indicates the likelihood that a blocking call will actually
+// block.
+enum class BlockingType {
+ // The call might block (e.g. file I/O that might hit in memory cache).
+ MAY_BLOCK,
+ // The call will definitely block (e.g. cache already checked and now pinging
+ // server synchronously).
+ WILL_BLOCK
+};
+
+namespace internal {
+class BlockingObserver;
+}
+
+// This class must be instantiated in every scope where a blocking call is made.
+// CPU usage should be minimal within that scope. //base APIs that block
+// instantiate their own ScopedBlockingCall; it is not necessary to instantiate
+// another ScopedBlockingCall in the scope where these APIs are used.
+//
+// Good:
+// Data data;
+// {
+// ScopedBlockingCall scoped_blocking_call(BlockingType::WILL_BLOCK);
+// data = GetDataFromNetwork();
+// }
+// CPUIntensiveProcessing(data);
+//
+// Bad:
+// ScopedBlockingCall scoped_blocking_call(BlockingType::WILL_BLOCK);
+// Data data = GetDataFromNetwork();
+// CPUIntensiveProcessing(data); // CPU usage within a ScopedBlockingCall.
+//
+// Good:
+// Data a;
+// Data b;
+// {
+// ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
+// a = GetDataFromMemoryCacheOrNetwork();
+// b = GetDataFromMemoryCacheOrNetwork();
+// }
+// CPUIntensiveProcessing(a);
+// CPUIntensiveProcessing(b);
+//
+// Bad:
+// ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
+// Data a = GetDataFromMemoryCacheOrNetwork();
+// Data b = GetDataFromMemoryCacheOrNetwork();
+// CPUIntensiveProcessing(a); // CPU usage within a ScopedBlockingCall.
+// CPUIntensiveProcessing(b); // CPU usage within a ScopedBlockingCall.
+//
+// Good:
+// base::WaitableEvent waitable_event(...);
+// waitable_event.Wait();
+//
+// Bad:
+// base::WaitableEvent waitable_event(...);
+// ScopedBlockingCall scoped_blocking_call(BlockingType::WILL_BLOCK);
+// waitable_event.Wait(); // Wait() instantiates its own ScopedBlockingCall.
+//
+// When a ScopedBlockingCall is instantiated from a TaskScheduler parallel or
+// sequenced task, the thread pool size is incremented to compensate for the
+// blocked thread (more or less aggressively depending on BlockingType).
+class BASE_EXPORT ScopedBlockingCall {
+ public:
+ ScopedBlockingCall(BlockingType blocking_type);
+ ~ScopedBlockingCall();
+
+ private:
+ internal::BlockingObserver* const blocking_observer_;
+
+ // Previous ScopedBlockingCall instantiated on this thread.
+ ScopedBlockingCall* const previous_scoped_blocking_call_;
+
+ // Whether the BlockingType of the current thread was WILL_BLOCK after this
+ // ScopedBlockingCall was instantiated.
+ const bool is_will_block_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedBlockingCall);
+};
+
+namespace internal {
+
+// Interface for an observer to be informed when a thread enters or exits
+// the scope of ScopedBlockingCall objects.
+class BASE_EXPORT BlockingObserver {
+ public:
+ virtual ~BlockingObserver() = default;
+
+ // Invoked when a ScopedBlockingCall is instantiated on the observed thread
+ // where there wasn't an existing ScopedBlockingCall.
+ virtual void BlockingStarted(BlockingType blocking_type) = 0;
+
+ // Invoked when a WILL_BLOCK ScopedBlockingCall is instantiated on the
+ // observed thread where there was a MAY_BLOCK ScopedBlockingCall but not a
+ // WILL_BLOCK ScopedBlockingCall.
+ virtual void BlockingTypeUpgraded() = 0;
+
+ // Invoked when the last ScopedBlockingCall on the observed thread is
+ // destroyed.
+ virtual void BlockingEnded() = 0;
+};
+
+// Registers |blocking_observer| on the current thread. It is invalid to call
+// this on a thread where there is an active ScopedBlockingCall.
+BASE_EXPORT void SetBlockingObserverForCurrentThread(
+ BlockingObserver* blocking_observer);
+
+BASE_EXPORT void ClearBlockingObserverForTesting();
+
+// Unregisters the |blocking_observer| on the current thread within its scope.
+// Used in TaskScheduler tests to prevent calls to //base sync primitives from
+// affecting the thread pool capacity.
+class BASE_EXPORT ScopedClearBlockingObserverForTesting {
+ public:
+ ScopedClearBlockingObserverForTesting();
+ ~ScopedClearBlockingObserverForTesting();
+
+ private:
+ BlockingObserver* const blocking_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedClearBlockingObserverForTesting);
+};
+
+} // namespace internal
+
+} // namespace base
+
+#endif // BASE_THREADING_SCOPED_BLOCKING_CALL_H
diff --git a/base/threading/scoped_blocking_call_unittest.cc b/base/threading/scoped_blocking_call_unittest.cc
new file mode 100644
index 0000000..5e030f3
--- /dev/null
+++ b/base/threading/scoped_blocking_call_unittest.cc
@@ -0,0 +1,134 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/scoped_blocking_call.h"
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/test/gtest_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class MockBlockingObserver : public internal::BlockingObserver {
+ public:
+ MockBlockingObserver() = default;
+
+ MOCK_METHOD1(BlockingStarted, void(BlockingType));
+ MOCK_METHOD0(BlockingTypeUpgraded, void());
+ MOCK_METHOD0(BlockingEnded, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockBlockingObserver);
+};
+
+class ScopedBlockingCallTest : public testing::Test {
+ protected:
+ ScopedBlockingCallTest() {
+ internal::SetBlockingObserverForCurrentThread(&observer_);
+ }
+
+ ~ScopedBlockingCallTest() override {
+ internal::ClearBlockingObserverForTesting();
+ }
+
+ testing::StrictMock<MockBlockingObserver> observer_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedBlockingCallTest);
+};
+
+} // namespace
+
+TEST_F(ScopedBlockingCallTest, MayBlock) {
+ EXPECT_CALL(observer_, BlockingStarted(BlockingType::MAY_BLOCK));
+ ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
+ testing::Mock::VerifyAndClear(&observer_);
+ EXPECT_CALL(observer_, BlockingEnded());
+}
+
+TEST_F(ScopedBlockingCallTest, WillBlock) {
+ EXPECT_CALL(observer_, BlockingStarted(BlockingType::WILL_BLOCK));
+ ScopedBlockingCall scoped_blocking_call(BlockingType::WILL_BLOCK);
+ testing::Mock::VerifyAndClear(&observer_);
+ EXPECT_CALL(observer_, BlockingEnded());
+}
+
+TEST_F(ScopedBlockingCallTest, MayBlockWillBlock) {
+ EXPECT_CALL(observer_, BlockingStarted(BlockingType::MAY_BLOCK));
+ ScopedBlockingCall scoped_blocking_call_a(BlockingType::MAY_BLOCK);
+ testing::Mock::VerifyAndClear(&observer_);
+
+ {
+ EXPECT_CALL(observer_, BlockingTypeUpgraded());
+ ScopedBlockingCall scoped_blocking_call_b(BlockingType::WILL_BLOCK);
+ testing::Mock::VerifyAndClear(&observer_);
+ }
+
+ EXPECT_CALL(observer_, BlockingEnded());
+}
+
+TEST_F(ScopedBlockingCallTest, WillBlockMayBlock) {
+ EXPECT_CALL(observer_, BlockingStarted(BlockingType::WILL_BLOCK));
+ ScopedBlockingCall scoped_blocking_call_a(BlockingType::WILL_BLOCK);
+ testing::Mock::VerifyAndClear(&observer_);
+
+ { ScopedBlockingCall scoped_blocking_call_b(BlockingType::MAY_BLOCK); }
+
+ EXPECT_CALL(observer_, BlockingEnded());
+}
+
+TEST_F(ScopedBlockingCallTest, MayBlockMayBlock) {
+ EXPECT_CALL(observer_, BlockingStarted(BlockingType::MAY_BLOCK));
+ ScopedBlockingCall scoped_blocking_call_a(BlockingType::MAY_BLOCK);
+ testing::Mock::VerifyAndClear(&observer_);
+
+ { ScopedBlockingCall scoped_blocking_call_b(BlockingType::MAY_BLOCK); }
+
+ EXPECT_CALL(observer_, BlockingEnded());
+}
+
+TEST_F(ScopedBlockingCallTest, WillBlockWillBlock) {
+ EXPECT_CALL(observer_, BlockingStarted(BlockingType::WILL_BLOCK));
+ ScopedBlockingCall scoped_blocking_call_a(BlockingType::WILL_BLOCK);
+ testing::Mock::VerifyAndClear(&observer_);
+
+ { ScopedBlockingCall scoped_blocking_call_b(BlockingType::WILL_BLOCK); }
+
+ EXPECT_CALL(observer_, BlockingEnded());
+}
+
+TEST_F(ScopedBlockingCallTest, MayBlockWillBlockTwice) {
+ EXPECT_CALL(observer_, BlockingStarted(BlockingType::MAY_BLOCK));
+ ScopedBlockingCall scoped_blocking_call_a(BlockingType::MAY_BLOCK);
+ testing::Mock::VerifyAndClear(&observer_);
+
+ {
+ EXPECT_CALL(observer_, BlockingTypeUpgraded());
+ ScopedBlockingCall scoped_blocking_call_b(BlockingType::WILL_BLOCK);
+ testing::Mock::VerifyAndClear(&observer_);
+
+ {
+ ScopedBlockingCall scoped_blocking_call_c(BlockingType::MAY_BLOCK);
+ ScopedBlockingCall scoped_blocking_call_d(BlockingType::WILL_BLOCK);
+ }
+ }
+
+ EXPECT_CALL(observer_, BlockingEnded());
+}
+
+TEST(ScopedBlockingCallDestructionOrderTest, InvalidDestructionOrder) {
+ auto scoped_blocking_call_a =
+ std::make_unique<ScopedBlockingCall>(BlockingType::WILL_BLOCK);
+ auto scoped_blocking_call_b =
+ std::make_unique<ScopedBlockingCall>(BlockingType::WILL_BLOCK);
+
+ EXPECT_DCHECK_DEATH({ scoped_blocking_call_a.reset(); });
+}
+
+} // namespace base
diff --git a/base/threading/sequence_local_storage_map.cc b/base/threading/sequence_local_storage_map.cc
new file mode 100644
index 0000000..2837aa0
--- /dev/null
+++ b/base/threading/sequence_local_storage_map.cc
@@ -0,0 +1,105 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/sequence_local_storage_map.h"
+
+#include <utility>
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/thread_local.h"
+
+namespace base {
+namespace internal {
+
+namespace {
+LazyInstance<ThreadLocalPointer<SequenceLocalStorageMap>>::Leaky
+ tls_current_sequence_local_storage = LAZY_INSTANCE_INITIALIZER;
+} // namespace
+
+SequenceLocalStorageMap::SequenceLocalStorageMap() = default;
+
+SequenceLocalStorageMap::~SequenceLocalStorageMap() = default;
+
+ScopedSetSequenceLocalStorageMapForCurrentThread::
+ ScopedSetSequenceLocalStorageMapForCurrentThread(
+ SequenceLocalStorageMap* sequence_local_storage) {
+ DCHECK(!tls_current_sequence_local_storage.Get().Get());
+ tls_current_sequence_local_storage.Get().Set(sequence_local_storage);
+}
+
+ScopedSetSequenceLocalStorageMapForCurrentThread::
+ ~ScopedSetSequenceLocalStorageMapForCurrentThread() {
+ tls_current_sequence_local_storage.Get().Set(nullptr);
+}
+
+SequenceLocalStorageMap& SequenceLocalStorageMap::GetForCurrentThread() {
+ SequenceLocalStorageMap* current_sequence_local_storage =
+ tls_current_sequence_local_storage.Get().Get();
+
+ DCHECK(current_sequence_local_storage)
+ << "SequenceLocalStorageSlot cannot be used because no "
+ "SequenceLocalStorageMap was stored in TLS. Use "
+ "ScopedSetSequenceLocalStorageMapForCurrentThread to store a "
+ "SequenceLocalStorageMap object in TLS.";
+
+ return *current_sequence_local_storage;
+}
+
+void* SequenceLocalStorageMap::Get(int slot_id) {
+ const auto it = sls_map_.find(slot_id);
+ if (it == sls_map_.end())
+ return nullptr;
+ return it->second.value();
+}
+
+void SequenceLocalStorageMap::Set(
+ int slot_id,
+ SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair) {
+ auto it = sls_map_.find(slot_id);
+
+ if (it == sls_map_.end())
+ sls_map_.emplace(slot_id, std::move(value_destructor_pair));
+ else
+ it->second = std::move(value_destructor_pair);
+
+ // The maximum number of entries in the map is 256. This can be adjusted, but
+ // will require reviewing the choice of data structure for the map.
+ DCHECK_LE(sls_map_.size(), 256U);
+}
+
+SequenceLocalStorageMap::ValueDestructorPair::ValueDestructorPair(
+ void* value,
+ DestructorFunc* destructor)
+ : value_(value), destructor_(destructor) {}
+
+SequenceLocalStorageMap::ValueDestructorPair::~ValueDestructorPair() {
+ if (value_)
+ destructor_(value_);
+}
+
+SequenceLocalStorageMap::ValueDestructorPair::ValueDestructorPair(
+ ValueDestructorPair&& value_destructor_pair)
+ : value_(value_destructor_pair.value_),
+ destructor_(value_destructor_pair.destructor_) {
+ value_destructor_pair.value_ = nullptr;
+}
+
+SequenceLocalStorageMap::ValueDestructorPair&
+SequenceLocalStorageMap::ValueDestructorPair::operator=(
+ ValueDestructorPair&& value_destructor_pair) {
+ // Destroy |value_| before overwriting it with a new value.
+ if (value_)
+ destructor_(value_);
+
+ value_ = value_destructor_pair.value_;
+ destructor_ = value_destructor_pair.destructor_;
+
+ value_destructor_pair.value_ = nullptr;
+
+ return *this;
+}
+
+} // namespace internal
+} // namespace base
diff --git a/base/threading/sequence_local_storage_map.h b/base/threading/sequence_local_storage_map.h
new file mode 100644
index 0000000..8b9155c
--- /dev/null
+++ b/base/threading/sequence_local_storage_map.h
@@ -0,0 +1,90 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_SEQUENCE_LOCAL_STORAGE_MAP_H_
+#define BASE_THREADING_SEQUENCE_LOCAL_STORAGE_MAP_H_
+
+#include "base/base_export.h"
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+
+namespace base {
+namespace internal {
+
+// A SequenceLocalStorageMap holds (slot_id) -> (value, destructor) items for a
+// sequence. When a task runs, it is expected that a pointer to its sequence's
+// SequenceLocalStorageMap is set in TLS using
+// ScopedSetSequenceMapLocalStorageForCurrentThread. When a
+// SequenceLocalStorageMap is destroyed, it invokes the destructors associated
+// with values stored within it.
+// The Get() and Set() methods should not be accessed directly.
+// Use SequenceLocalStorageSlot to Get() and Set() values in the current
+// sequence's SequenceLocalStorageMap.
+class BASE_EXPORT SequenceLocalStorageMap {
+ public:
+ SequenceLocalStorageMap();
+ ~SequenceLocalStorageMap();
+
+ // Returns the SequenceLocalStorage bound to the current thread. It is invalid
+ // to call this outside the scope of a
+ // ScopedSetSequenceLocalStorageForCurrentThread.
+ static SequenceLocalStorageMap& GetForCurrentThread();
+
+ // Holds a pointer to a value alongside a destructor for this pointer.
+ // Calls the destructor on the value upon destruction.
+ class BASE_EXPORT ValueDestructorPair {
+ public:
+ using DestructorFunc = void(void*);
+
+ ValueDestructorPair(void* value, DestructorFunc* destructor);
+ ~ValueDestructorPair();
+
+ ValueDestructorPair(ValueDestructorPair&& value_destructor_pair);
+
+ ValueDestructorPair& operator=(ValueDestructorPair&& value_destructor_pair);
+
+ void* value() const { return value_; }
+
+ private:
+ void* value_;
+ DestructorFunc* destructor_;
+
+ DISALLOW_COPY_AND_ASSIGN(ValueDestructorPair);
+ };
+
+ // Returns the value stored in |slot_id| or nullptr if no value was stored.
+ void* Get(int slot_id);
+
+ // Stores |value_destructor_pair| in |slot_id|. Overwrites and destroys any
+ // previously stored value.
+ void Set(int slot_id, ValueDestructorPair value_destructor_pair);
+
+ private:
+ // Map from slot id to ValueDestructorPair.
+ // flat_map was chosen because there are expected to be relatively few entries
+ // in the map. For low number of entries, flat_map is known to perform better
+ // than other map implementations.
+ base::flat_map<int, ValueDestructorPair> sls_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(SequenceLocalStorageMap);
+};
+
+// Within the scope of this object,
+// SequenceLocalStorageMap::GetForCurrentThread() will return a reference to the
+// SequenceLocalStorageMap object passed to the constructor. There can be only
+// one ScopedSetSequenceLocalStorageMapForCurrentThread instance per scope.
+class BASE_EXPORT ScopedSetSequenceLocalStorageMapForCurrentThread {
+ public:
+ ScopedSetSequenceLocalStorageMapForCurrentThread(
+ SequenceLocalStorageMap* sequence_local_storage);
+
+ ~ScopedSetSequenceLocalStorageMapForCurrentThread();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedSetSequenceLocalStorageMapForCurrentThread);
+};
+} // namespace internal
+} // namespace base
+
+#endif // BASE_THREADING_SEQUENCE_LOCAL_STORAGE_MAP_H_
diff --git a/base/threading/sequence_local_storage_map_unittest.cc b/base/threading/sequence_local_storage_map_unittest.cc
new file mode 100644
index 0000000..a45bbc3
--- /dev/null
+++ b/base/threading/sequence_local_storage_map_unittest.cc
@@ -0,0 +1,117 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/sequence_local_storage_map.h"
+
+#include <memory>
+#include <utility>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace internal {
+
+namespace {
+
+constexpr int kSlotId = 1;
+
+class SetOnDestroy {
+ public:
+ SetOnDestroy(bool* was_destroyed_ptr)
+ : was_destroyed_ptr_(was_destroyed_ptr) {
+ DCHECK(was_destroyed_ptr_);
+ DCHECK(!(*was_destroyed_ptr_));
+ }
+ ~SetOnDestroy() {
+ DCHECK(!(*was_destroyed_ptr_));
+ *was_destroyed_ptr_ = true;
+ }
+
+ private:
+ bool* const was_destroyed_ptr_;
+
+ DISALLOW_COPY_AND_ASSIGN(SetOnDestroy);
+};
+
+template <typename T, typename... Args>
+SequenceLocalStorageMap::ValueDestructorPair CreateValueDestructorPair(
+ Args... args) {
+ T* value = new T(args...);
+ SequenceLocalStorageMap::ValueDestructorPair::DestructorFunc* destructor =
+ [](void* ptr) { std::default_delete<T>()(static_cast<T*>(ptr)); };
+
+ SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair{
+ value, destructor};
+
+ return value_destructor_pair;
+}
+
+} // namespace
+
+// Verify that setting a value in the SequenceLocalStorageMap, then getting
+// it will yield the same value.
+TEST(SequenceLocalStorageMapTest, SetGet) {
+ SequenceLocalStorageMap sequence_local_storage_map;
+ ScopedSetSequenceLocalStorageMapForCurrentThread
+ scoped_sequence_local_storage_map(&sequence_local_storage_map);
+
+ SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
+ CreateValueDestructorPair<int>(5);
+
+ sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));
+
+ EXPECT_EQ(*static_cast<int*>(sequence_local_storage_map.Get(kSlotId)), 5);
+}
+
+// Verify that the destructor is called on a value stored in the
+// SequenceLocalStorageMap when SequenceLocalStorageMap is destroyed.
+TEST(SequenceLocalStorageMapTest, Destructor) {
+ bool set_on_destruction = false;
+
+ {
+ SequenceLocalStorageMap sequence_local_storage_map;
+ ScopedSetSequenceLocalStorageMapForCurrentThread
+ scoped_sequence_local_storage_map(&sequence_local_storage_map);
+
+ SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
+ CreateValueDestructorPair<SetOnDestroy>(&set_on_destruction);
+
+ sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));
+ }
+
+ EXPECT_TRUE(set_on_destruction);
+}
+
+// Verify that overwriting a value already in the SequenceLocalStorageMap
+// calls value's destructor.
+TEST(SequenceLocalStorageMapTest, DestructorCalledOnSetOverwrite) {
+ bool set_on_destruction = false;
+ bool set_on_destruction2 = false;
+ {
+ SequenceLocalStorageMap sequence_local_storage_map;
+ ScopedSetSequenceLocalStorageMapForCurrentThread
+ scoped_sequence_local_storage_map(&sequence_local_storage_map);
+
+ SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
+ CreateValueDestructorPair<SetOnDestroy>(&set_on_destruction);
+ SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair2 =
+ CreateValueDestructorPair<SetOnDestroy>(&set_on_destruction2);
+
+ sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));
+
+ ASSERT_FALSE(set_on_destruction);
+
+ // Overwrites the old value in the slot.
+ sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair2));
+
+ // Destructor should've been called for the old value in the slot, and not
+ // yet called for the new value.
+ EXPECT_TRUE(set_on_destruction);
+ EXPECT_FALSE(set_on_destruction2);
+ }
+ EXPECT_TRUE(set_on_destruction2);
+}
+
+} // namespace internal
+} // namespace base
diff --git a/base/threading/sequence_local_storage_slot.cc b/base/threading/sequence_local_storage_slot.cc
new file mode 100644
index 0000000..b7db40b
--- /dev/null
+++ b/base/threading/sequence_local_storage_slot.cc
@@ -0,0 +1,26 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/sequence_local_storage_slot.h"
+
+#include <limits>
+
+#include "base/atomic_sequence_num.h"
+#include "base/logging.h"
+
+namespace base {
+namespace internal {
+
+namespace {
+AtomicSequenceNumber g_sequence_local_storage_slot_generator;
+} // namespace
+
+int GetNextSequenceLocalStorageSlotNumber() {
+ int slot_id = g_sequence_local_storage_slot_generator.GetNext();
+ DCHECK_LT(slot_id, std::numeric_limits<int>::max());
+ return slot_id;
+}
+
+} // namespace internal
+} // namespace base
diff --git a/base/threading/sequence_local_storage_slot.h b/base/threading/sequence_local_storage_slot.h
new file mode 100644
index 0000000..315df7d
--- /dev/null
+++ b/base/threading/sequence_local_storage_slot.h
@@ -0,0 +1,105 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_SEQUENCE_LOCAL_STORAGE_SLOT_H_
+#define BASE_THREADING_SEQUENCE_LOCAL_STORAGE_SLOT_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/base_export.h"
+#include "base/threading/sequence_local_storage_map.h"
+
+namespace base {
+
+namespace internal {
+BASE_EXPORT int GetNextSequenceLocalStorageSlotNumber();
+}
+
+// SequenceLocalStorageSlot allows arbitrary values to be stored and retrieved
+// from a sequence. Values are deleted when the sequence is deleted.
+//
+// Example usage:
+//
+// namespace {
+// base::LazyInstance<SequenceLocalStorageSlot<int>> sls_value;
+// }
+//
+// void Read() {
+// int value = sls_value.Get().Get();
+// ...
+// }
+//
+// void Write() {
+// sls_value.Get().Set(42);
+// }
+//
+// void PostTasks() {
+// // Since Read() runs on the same sequence as Write(), it
+// // will read the value "42". A Read() running on a different
+// // sequence would not see that value.
+// scoped_refptr<base::SequencedTaskRunner> task_runner = ...;
+// task_runner->PostTask(FROM_HERE, base::BindOnce(&Write));
+// task_runner->PostTask(FROM_HERE, base::BindOnce(&Read));
+// }
+//
+// SequenceLocalStorageSlot must be used within the scope of a
+// ScopedSetSequenceLocalStorageMapForCurrentThread object.
+// Note: this is true on all TaskScheduler workers and on threads bound to a
+// MessageLoop.
+template <typename T, typename Deleter = std::default_delete<T>>
+class SequenceLocalStorageSlot {
+ public:
+ SequenceLocalStorageSlot()
+ : slot_id_(internal::GetNextSequenceLocalStorageSlotNumber()) {}
+ ~SequenceLocalStorageSlot() = default;
+
+ // Get the sequence-local value stored in this slot. Returns a
+ // default-constructed value if no value was previously set.
+ T& Get() {
+ void* value =
+ internal::SequenceLocalStorageMap::GetForCurrentThread().Get(slot_id_);
+
+ // Sets and returns a default-constructed value if no value was previously
+ // set.
+ if (!value) {
+ Set(T());
+ return Get();
+ }
+ return *(static_cast<T*>(value));
+ }
+
+ // Set this slot's sequence-local value to |value|.
+ // Note that if T is expensive to copy, it may be more appropriate to instead
+ // store a std::unique_ptr<T>. This is enforced by the
+ // DISALLOW_COPY_AND_ASSIGN style rather than directly by this class however.
+ void Set(T value) {
+ // Allocates the |value| with new rather than std::make_unique.
+ // Since SequenceLocalStorageMap needs to store values of various types
+ // within the same map, the type of value_destructor_pair.value is void*
+ // (std::unique_ptr<void> is invalid). Memory is freed by calling
+ // |value_destructor_pair.destructor| in the destructor of
+ // ValueDestructorPair which is invoked when the value is overwritten by
+ // another call to SequenceLocalStorageMap::Set or when the
+ // SequenceLocalStorageMap is deleted.
+ T* value_ptr = new T(std::move(value));
+
+ internal::SequenceLocalStorageMap::ValueDestructorPair::DestructorFunc*
+ destructor = [](void* ptr) { Deleter()(static_cast<T*>(ptr)); };
+
+ internal::SequenceLocalStorageMap::ValueDestructorPair
+ value_destructor_pair(value_ptr, destructor);
+
+ internal::SequenceLocalStorageMap::GetForCurrentThread().Set(
+ slot_id_, std::move(value_destructor_pair));
+ }
+
+ private:
+ // |slot_id_| is used as a key in SequenceLocalStorageMap
+ const int slot_id_;
+ DISALLOW_COPY_AND_ASSIGN(SequenceLocalStorageSlot);
+};
+
+} // namespace base
+#endif // BASE_THREADING_SEQUENCE_LOCAL_STORAGE_SLOT_H_
diff --git a/base/threading/sequence_local_storage_slot_unittest.cc b/base/threading/sequence_local_storage_slot_unittest.cc
new file mode 100644
index 0000000..4a9f6a9
--- /dev/null
+++ b/base/threading/sequence_local_storage_slot_unittest.cc
@@ -0,0 +1,143 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/sequence_local_storage_slot.h"
+
+#include <utility>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/threading/sequence_local_storage_map.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class SequenceLocalStorageSlotTest : public testing::Test {
+ protected:
+ SequenceLocalStorageSlotTest()
+ : scoped_sequence_local_storage_(&sequence_local_storage_) {}
+
+ internal::SequenceLocalStorageMap sequence_local_storage_;
+ internal::ScopedSetSequenceLocalStorageMapForCurrentThread
+ scoped_sequence_local_storage_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SequenceLocalStorageSlotTest);
+};
+
+} // namespace
+
+// Verify that a value stored with Set() can be retrieved with Get().
+TEST_F(SequenceLocalStorageSlotTest, GetSet) {
+ SequenceLocalStorageSlot<int> slot;
+ slot.Set(5);
+ EXPECT_EQ(slot.Get(), 5);
+}
+
+// Verify that setting an object in a SequenceLocalStorageSlot creates a copy
+// of that object independent of the original one.
+TEST_F(SequenceLocalStorageSlotTest, SetObjectIsIndependent) {
+ bool should_be_false = false;
+
+ SequenceLocalStorageSlot<bool> slot;
+
+ slot.Set(should_be_false);
+
+ EXPECT_FALSE(slot.Get());
+ slot.Get() = true;
+ EXPECT_TRUE(slot.Get());
+
+ EXPECT_NE(should_be_false, slot.Get());
+}
+
+// Verify that multiple slots work and that calling Get after overwriting
+// a value in a slot yields the new value.
+TEST_F(SequenceLocalStorageSlotTest, GetSetMultipleSlots) {
+ SequenceLocalStorageSlot<int> slot1;
+ SequenceLocalStorageSlot<int> slot2;
+ SequenceLocalStorageSlot<int> slot3;
+
+ slot1.Set(1);
+ slot2.Set(2);
+ slot3.Set(3);
+
+ EXPECT_EQ(slot1.Get(), 1);
+ EXPECT_EQ(slot2.Get(), 2);
+ EXPECT_EQ(slot3.Get(), 3);
+
+ slot3.Set(4);
+ slot2.Set(5);
+ slot1.Set(6);
+
+ EXPECT_EQ(slot3.Get(), 4);
+ EXPECT_EQ(slot2.Get(), 5);
+ EXPECT_EQ(slot1.Get(), 6);
+}
+
+// Verify that changing the the value returned by Get() changes the value
+// in sequence local storage.
+TEST_F(SequenceLocalStorageSlotTest, GetReferenceModifiable) {
+ SequenceLocalStorageSlot<bool> slot;
+ slot.Set(false);
+ slot.Get() = true;
+ EXPECT_TRUE(slot.Get());
+}
+
+// Verify that a move-only type can be stored in sequence local storage.
+TEST_F(SequenceLocalStorageSlotTest, SetGetWithMoveOnlyType) {
+ std::unique_ptr<int> int_unique_ptr = std::make_unique<int>(5);
+
+ SequenceLocalStorageSlot<std::unique_ptr<int>> slot;
+ slot.Set(std::move(int_unique_ptr));
+
+ EXPECT_EQ(*slot.Get(), 5);
+}
+
+// Verify that a Get() without a previous Set() on a slot returns a
+// default-constructed value.
+TEST_F(SequenceLocalStorageSlotTest, GetWithoutSetDefaultConstructs) {
+ struct DefaultConstructable {
+ int x = 0x12345678;
+ };
+
+ SequenceLocalStorageSlot<DefaultConstructable> slot;
+
+ EXPECT_EQ(slot.Get().x, 0x12345678);
+}
+
+// Verify that a Get() without a previous Set() on a slot with a POD-type
+// returns a default-constructed value.
+// Note: this test could be flaky and give a false pass. If it's flaky, the test
+// might've "passed" because the memory for the slot happened to be zeroed.
+TEST_F(SequenceLocalStorageSlotTest, GetWithoutSetDefaultConstructsPOD) {
+ SequenceLocalStorageSlot<void*> slot;
+
+ EXPECT_EQ(slot.Get(), nullptr);
+}
+
+// Verify that the value of a slot is specific to a SequenceLocalStorageMap
+TEST(SequenceLocalStorageSlotMultipleMapTest, SetGetMultipleMapsOneSlot) {
+ SequenceLocalStorageSlot<unsigned int> slot;
+ internal::SequenceLocalStorageMap sequence_local_storage_maps[5];
+
+ // Set the value of the slot to be the index of the current
+ // SequenceLocalStorageMaps in the vector
+ for (unsigned int i = 0; i < arraysize(sequence_local_storage_maps); ++i) {
+ internal::ScopedSetSequenceLocalStorageMapForCurrentThread
+ scoped_sequence_local_storage(&sequence_local_storage_maps[i]);
+
+ slot.Set(i);
+ }
+
+ for (unsigned int i = 0; i < arraysize(sequence_local_storage_maps); ++i) {
+ internal::ScopedSetSequenceLocalStorageMapForCurrentThread
+ scoped_sequence_local_storage(&sequence_local_storage_maps[i]);
+
+ EXPECT_EQ(slot.Get(), i);
+ }
+}
+
+} // namespace base
diff --git a/base/threading/sequenced_task_runner_handle.cc b/base/threading/sequenced_task_runner_handle.cc
new file mode 100644
index 0000000..e6920f5
--- /dev/null
+++ b/base/threading/sequenced_task_runner_handle.cc
@@ -0,0 +1,59 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/sequenced_task_runner_handle.h"
+
+#include <utility>
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/thread_local.h"
+#include "base/threading/thread_task_runner_handle.h"
+
+namespace base {
+
+namespace {
+
+LazyInstance<ThreadLocalPointer<SequencedTaskRunnerHandle>>::Leaky
+ sequenced_task_runner_tls = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+// static
+scoped_refptr<SequencedTaskRunner> SequencedTaskRunnerHandle::Get() {
+ // Return the registered SequencedTaskRunner, if any.
+ const SequencedTaskRunnerHandle* handle =
+ sequenced_task_runner_tls.Pointer()->Get();
+ if (handle)
+ return handle->task_runner_;
+
+ // Note if you hit this: the problem is the lack of a sequenced context. The
+ // ThreadTaskRunnerHandle is just the last attempt at finding such a context.
+ CHECK(ThreadTaskRunnerHandle::IsSet())
+ << "Error: This caller requires a sequenced context (i.e. the "
+ "current task needs to run from a SequencedTaskRunner).";
+ return ThreadTaskRunnerHandle::Get();
+}
+
+// static
+bool SequencedTaskRunnerHandle::IsSet() {
+ return sequenced_task_runner_tls.Pointer()->Get() ||
+ ThreadTaskRunnerHandle::IsSet();
+}
+
+SequencedTaskRunnerHandle::SequencedTaskRunnerHandle(
+ scoped_refptr<SequencedTaskRunner> task_runner)
+ : task_runner_(std::move(task_runner)) {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+ DCHECK(!SequencedTaskRunnerHandle::IsSet());
+ sequenced_task_runner_tls.Pointer()->Set(this);
+}
+
+SequencedTaskRunnerHandle::~SequencedTaskRunnerHandle() {
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+ DCHECK_EQ(sequenced_task_runner_tls.Pointer()->Get(), this);
+ sequenced_task_runner_tls.Pointer()->Set(nullptr);
+}
+
+} // namespace base
diff --git a/base/threading/sequenced_task_runner_handle.h b/base/threading/sequenced_task_runner_handle.h
new file mode 100644
index 0000000..f55cee5
--- /dev/null
+++ b/base/threading/sequenced_task_runner_handle.h
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_SEQUENCED_TASK_RUNNER_HANDLE_H_
+#define BASE_THREADING_SEQUENCED_TASK_RUNNER_HANDLE_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+
+namespace base {
+
+class BASE_EXPORT SequencedTaskRunnerHandle {
+ public:
+ // Returns a SequencedTaskRunner which guarantees that posted tasks will only
+ // run after the current task is finished and will satisfy a SequenceChecker.
+ // It should only be called if IsSet() returns true (see the comment there for
+ // the requirements).
+ static scoped_refptr<SequencedTaskRunner> Get();
+
+ // Returns true if one of the following conditions is fulfilled:
+ // a) A SequencedTaskRunner has been assigned to the current thread by
+ // instantiating a SequencedTaskRunnerHandle.
+ // b) The current thread has a ThreadTaskRunnerHandle (which includes any
+ // thread that has a MessageLoop associated with it).
+ static bool IsSet();
+
+ // Binds |task_runner| to the current thread.
+ explicit SequencedTaskRunnerHandle(
+ scoped_refptr<SequencedTaskRunner> task_runner);
+ ~SequencedTaskRunnerHandle();
+
+ private:
+ scoped_refptr<SequencedTaskRunner> task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(SequencedTaskRunnerHandle);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_SEQUENCED_TASK_RUNNER_HANDLE_H_
diff --git a/base/threading/sequenced_task_runner_handle_unittest.cc b/base/threading/sequenced_task_runner_handle_unittest.cc
new file mode 100644
index 0000000..48394da
--- /dev/null
+++ b/base/threading/sequenced_task_runner_handle_unittest.cc
@@ -0,0 +1,90 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/sequenced_task_runner_handle.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "base/sequence_checker_impl.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+class SequencedTaskRunnerHandleTest : public ::testing::Test {
+ protected:
+ // Verifies that the context it runs on has a SequencedTaskRunnerHandle
+ // and that posting to it results in the posted task running in that same
+ // context (sequence).
+ static void VerifyCurrentSequencedTaskRunner() {
+ ASSERT_TRUE(SequencedTaskRunnerHandle::IsSet());
+ scoped_refptr<SequencedTaskRunner> task_runner =
+ SequencedTaskRunnerHandle::Get();
+ ASSERT_TRUE(task_runner);
+
+ // Use SequenceCheckerImpl to make sure it's not a no-op in Release builds.
+ std::unique_ptr<SequenceCheckerImpl> sequence_checker(
+ new SequenceCheckerImpl);
+ task_runner->PostTask(
+ FROM_HERE,
+ base::BindOnce(&SequencedTaskRunnerHandleTest::CheckValidSequence,
+ std::move(sequence_checker)));
+ }
+
+ static void CheckValidSequence(
+ std::unique_ptr<SequenceCheckerImpl> sequence_checker) {
+ EXPECT_TRUE(sequence_checker->CalledOnValidSequence());
+ }
+
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+};
+
+TEST_F(SequencedTaskRunnerHandleTest, FromMessageLoop) {
+ VerifyCurrentSequencedTaskRunner();
+ RunLoop().RunUntilIdle();
+}
+
+TEST_F(SequencedTaskRunnerHandleTest, FromTaskSchedulerSequencedTask) {
+ base::CreateSequencedTaskRunnerWithTraits({})->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &SequencedTaskRunnerHandleTest::VerifyCurrentSequencedTaskRunner));
+ scoped_task_environment_.RunUntilIdle();
+}
+
+TEST_F(SequencedTaskRunnerHandleTest, NoHandleFromUnsequencedTask) {
+ base::PostTask(FROM_HERE, base::BindOnce([]() {
+ EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
+ }));
+ scoped_task_environment_.RunUntilIdle();
+}
+
+TEST(SequencedTaskRunnerHandleTestWithoutMessageLoop, FromHandleInScope) {
+ scoped_refptr<SequencedTaskRunner> test_task_runner(new TestSimpleTaskRunner);
+ EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
+ EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+ {
+ SequencedTaskRunnerHandle handle(test_task_runner);
+ EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet());
+ EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+ EXPECT_EQ(test_task_runner, SequencedTaskRunnerHandle::Get());
+ }
+ EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
+ EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+}
+
+} // namespace
+} // namespace base
diff --git a/base/threading/simple_thread.cc b/base/threading/simple_thread.cc
new file mode 100644
index 0000000..04a5285
--- /dev/null
+++ b/base/threading/simple_thread.cc
@@ -0,0 +1,182 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/simple_thread.h"
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace base {
+
+SimpleThread::SimpleThread(const std::string& name_prefix)
+ : SimpleThread(name_prefix, Options()) {}
+
+SimpleThread::SimpleThread(const std::string& name_prefix,
+ const Options& options)
+ : name_prefix_(name_prefix),
+ options_(options),
+ event_(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED) {}
+
+SimpleThread::~SimpleThread() {
+ DCHECK(HasBeenStarted()) << "SimpleThread was never started.";
+ DCHECK(!options_.joinable || HasBeenJoined())
+ << "Joinable SimpleThread destroyed without being Join()ed.";
+}
+
+void SimpleThread::Start() {
+ StartAsync();
+ ThreadRestrictions::ScopedAllowWait allow_wait;
+ event_.Wait(); // Wait for the thread to complete initialization.
+}
+
+void SimpleThread::Join() {
+ DCHECK(options_.joinable) << "A non-joinable thread can't be joined.";
+ DCHECK(HasStartBeenAttempted()) << "Tried to Join a never-started thread.";
+ DCHECK(!HasBeenJoined()) << "Tried to Join a thread multiple times.";
+ BeforeJoin();
+ PlatformThread::Join(thread_);
+ thread_ = PlatformThreadHandle();
+ joined_ = true;
+}
+
+void SimpleThread::StartAsync() {
+ DCHECK(!HasStartBeenAttempted()) << "Tried to Start a thread multiple times.";
+ start_called_ = true;
+ BeforeStart();
+ bool success =
+ options_.joinable
+ ? PlatformThread::CreateWithPriority(options_.stack_size, this,
+ &thread_, options_.priority)
+ : PlatformThread::CreateNonJoinableWithPriority(
+ options_.stack_size, this, options_.priority);
+ DCHECK(success);
+}
+
+PlatformThreadId SimpleThread::tid() {
+ DCHECK(HasBeenStarted());
+ return tid_;
+}
+
+bool SimpleThread::HasBeenStarted() {
+ ThreadRestrictions::ScopedAllowWait allow_wait;
+ return event_.IsSignaled();
+}
+
+void SimpleThread::ThreadMain() {
+ tid_ = PlatformThread::CurrentId();
+ // Construct our full name of the form "name_prefix_/TID".
+ std::string name(name_prefix_);
+ name.push_back('/');
+ name.append(IntToString(tid_));
+ PlatformThread::SetName(name);
+
+ // We've initialized our new thread, signal that we're done to Start().
+ event_.Signal();
+
+ BeforeRun();
+ Run();
+}
+
+DelegateSimpleThread::DelegateSimpleThread(Delegate* delegate,
+ const std::string& name_prefix)
+ : DelegateSimpleThread(delegate, name_prefix, Options()) {}
+
+DelegateSimpleThread::DelegateSimpleThread(Delegate* delegate,
+ const std::string& name_prefix,
+ const Options& options)
+ : SimpleThread(name_prefix, options),
+ delegate_(delegate) {
+ DCHECK(delegate_);
+}
+
+DelegateSimpleThread::~DelegateSimpleThread() = default;
+
+void DelegateSimpleThread::Run() {
+ DCHECK(delegate_) << "Tried to call Run without a delegate (called twice?)";
+
+ // Non-joinable DelegateSimpleThreads are allowed to be deleted during Run().
+ // Member state must not be accessed after invoking Run().
+ Delegate* delegate = delegate_;
+ delegate_ = nullptr;
+ delegate->Run();
+}
+
+DelegateSimpleThreadPool::DelegateSimpleThreadPool(
+ const std::string& name_prefix,
+ int num_threads)
+ : name_prefix_(name_prefix),
+ num_threads_(num_threads),
+ dry_(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED) {}
+
+DelegateSimpleThreadPool::~DelegateSimpleThreadPool() {
+ DCHECK(threads_.empty());
+ DCHECK(delegates_.empty());
+ DCHECK(!dry_.IsSignaled());
+}
+
+void DelegateSimpleThreadPool::Start() {
+ DCHECK(threads_.empty()) << "Start() called with outstanding threads.";
+ for (int i = 0; i < num_threads_; ++i) {
+ DelegateSimpleThread* thread = new DelegateSimpleThread(this, name_prefix_);
+ thread->Start();
+ threads_.push_back(thread);
+ }
+}
+
+void DelegateSimpleThreadPool::JoinAll() {
+ DCHECK(!threads_.empty()) << "JoinAll() called with no outstanding threads.";
+
+ // Tell all our threads to quit their worker loop.
+ AddWork(nullptr, num_threads_);
+
+ // Join and destroy all the worker threads.
+ for (int i = 0; i < num_threads_; ++i) {
+ threads_[i]->Join();
+ delete threads_[i];
+ }
+ threads_.clear();
+ DCHECK(delegates_.empty());
+}
+
+void DelegateSimpleThreadPool::AddWork(Delegate* delegate, int repeat_count) {
+ AutoLock locked(lock_);
+ for (int i = 0; i < repeat_count; ++i)
+ delegates_.push(delegate);
+ // If we were empty, signal that we have work now.
+ if (!dry_.IsSignaled())
+ dry_.Signal();
+}
+
+void DelegateSimpleThreadPool::Run() {
+ Delegate* work = nullptr;
+
+ while (true) {
+ dry_.Wait();
+ {
+ AutoLock locked(lock_);
+ if (!dry_.IsSignaled())
+ continue;
+
+ DCHECK(!delegates_.empty());
+ work = delegates_.front();
+ delegates_.pop();
+
+ // Signal to any other threads that we're currently out of work.
+ if (delegates_.empty())
+ dry_.Reset();
+ }
+
+ // A NULL delegate pointer signals us to quit.
+ if (!work)
+ break;
+
+ work->Run();
+ }
+}
+
+} // namespace base
diff --git a/base/threading/simple_thread.h b/base/threading/simple_thread.h
new file mode 100644
index 0000000..976f557
--- /dev/null
+++ b/base/threading/simple_thread.h
@@ -0,0 +1,232 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// WARNING: You should probably be using Thread (thread.h) instead. Thread is
+// Chrome's message-loop based Thread abstraction, and if you are a
+// thread running in the browser, there will likely be assumptions
+// that your thread will have an associated message loop.
+//
+// This is a simple thread interface that backs to a native operating system
+// thread. You should use this only when you want a thread that does not have
+// an associated MessageLoop. Unittesting is the best example of this.
+//
+// The simplest interface to use is DelegateSimpleThread, which will create
+// a new thread, and execute the Delegate's virtual Run() in this new thread
+// until it has completed, exiting the thread.
+//
+// NOTE: You *MUST* call Join on the thread to clean up the underlying thread
+// resources. You are also responsible for destructing the SimpleThread object.
+// It is invalid to destroy a SimpleThread while it is running, or without
+// Start() having been called (and a thread never created). The Delegate
+// object should live as long as a DelegateSimpleThread.
+//
+// Thread Safety: A SimpleThread is not completely thread safe. It is safe to
+// access it from the creating thread or from the newly created thread. This
+// implies that the creator thread should be the thread that calls Join.
+//
+// Example:
+// class MyThreadRunner : public DelegateSimpleThread::Delegate { ... };
+// MyThreadRunner runner;
+// DelegateSimpleThread thread(&runner, "good_name_here");
+// thread.Start();
+// // Start will return after the Thread has been successfully started and
+// // initialized. The newly created thread will invoke runner->Run(), and
+// // run until it returns.
+// thread.Join(); // Wait until the thread has exited. You *MUST* Join!
+// // The SimpleThread object is still valid, however you may not call Join
+// // or Start again.
+
+#ifndef BASE_THREADING_SIMPLE_THREAD_H_
+#define BASE_THREADING_SIMPLE_THREAD_H_
+
+#include <stddef.h>
+
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/containers/queue.h"
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+// This is the base SimpleThread. You can derive from it and implement the
+// virtual Run method, or you can use the DelegateSimpleThread interface.
+class BASE_EXPORT SimpleThread : public PlatformThread::Delegate {
+ public:
+ struct BASE_EXPORT Options {
+ public:
+ Options() = default;
+ explicit Options(ThreadPriority priority_in) : priority(priority_in) {}
+ ~Options() = default;
+
+ // Allow copies.
+ Options(const Options& other) = default;
+ Options& operator=(const Options& other) = default;
+
+ // A custom stack size, or 0 for the system default.
+ size_t stack_size = 0;
+
+ ThreadPriority priority = ThreadPriority::NORMAL;
+
+ // If false, the underlying thread's PlatformThreadHandle will not be kept
+ // around and as such the SimpleThread instance will not be Join()able and
+ // must not be deleted before Run() is invoked. After that, it's up to
+ // the subclass to determine when it is safe to delete itself.
+ bool joinable = true;
+ };
+
+ // Create a SimpleThread. |options| should be used to manage any specific
+ // configuration involving the thread creation and management.
+ // Every thread has a name, in the form of |name_prefix|/TID, for example
+ // "my_thread/321". The thread will not be created until Start() is called.
+ explicit SimpleThread(const std::string& name_prefix);
+ SimpleThread(const std::string& name_prefix, const Options& options);
+
+ ~SimpleThread() override;
+
+ // Starts the thread and returns only after the thread has started and
+ // initialized (i.e. ThreadMain() has been called).
+ void Start();
+
+ // Joins the thread. If StartAsync() was used to start the thread, then this
+ // first waits for the thread to start cleanly, then it joins.
+ void Join();
+
+ // Starts the thread, but returns immediately, without waiting for the thread
+ // to have initialized first (i.e. this does not wait for ThreadMain() to have
+ // been run first).
+ void StartAsync();
+
+ // Subclasses should override the Run method.
+ virtual void Run() = 0;
+
+ // Returns the thread id, only valid after the thread has started. If the
+ // thread was started using Start(), then this will be valid after the call to
+ // Start(). If StartAsync() was used to start the thread, then this must not
+ // be called before HasBeenStarted() returns True.
+ PlatformThreadId tid();
+
+ // Returns True if the thread has been started and initialized (i.e. if
+ // ThreadMain() has run). If the thread was started with StartAsync(), but it
+ // hasn't been initialized yet (i.e. ThreadMain() has not run), then this will
+ // return False.
+ bool HasBeenStarted();
+
+ // Returns True if Join() has ever been called.
+ bool HasBeenJoined() { return joined_; }
+
+ // Returns true if Start() or StartAsync() has been called.
+ bool HasStartBeenAttempted() { return start_called_; }
+
+ // Overridden from PlatformThread::Delegate:
+ void ThreadMain() override;
+
+ private:
+ // This is called just before the thread is started. This is called regardless
+ // of whether Start() or StartAsync() is used to start the thread.
+ virtual void BeforeStart() {}
+
+ // This is called just after the thread has been initialized and just before
+ // Run() is called. This is called on the newly started thread.
+ virtual void BeforeRun() {}
+
+ // This is called just before the thread is joined. The thread is started and
+ // has been initialized before this is called.
+ virtual void BeforeJoin() {}
+
+ const std::string name_prefix_;
+ std::string name_;
+ const Options options_;
+ PlatformThreadHandle thread_; // PlatformThread handle, reset after Join.
+ WaitableEvent event_; // Signaled if Start() was ever called.
+ PlatformThreadId tid_ = kInvalidThreadId; // The backing thread's id.
+ bool joined_ = false; // True if Join has been called.
+ // Set to true when the platform-thread creation has started.
+ bool start_called_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(SimpleThread);
+};
+
+// A SimpleThread which delegates Run() to its Delegate. Non-joinable
+// DelegateSimpleThread are safe to delete after Run() was invoked, their
+// Delegates are also safe to delete after that point from this class' point of
+// view (although implementations must of course make sure that Run() will not
+// use their Delegate's member state after its deletion).
+class BASE_EXPORT DelegateSimpleThread : public SimpleThread {
+ public:
+ class BASE_EXPORT Delegate {
+ public:
+ virtual ~Delegate() = default;
+ virtual void Run() = 0;
+ };
+
+ DelegateSimpleThread(Delegate* delegate,
+ const std::string& name_prefix);
+ DelegateSimpleThread(Delegate* delegate,
+ const std::string& name_prefix,
+ const Options& options);
+
+ ~DelegateSimpleThread() override;
+ void Run() override;
+
+ private:
+ Delegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(DelegateSimpleThread);
+};
+
+// DelegateSimpleThreadPool allows you to start up a fixed number of threads,
+// and then add jobs which will be dispatched to the threads. This is
+// convenient when you have a lot of small work that you want done
+// multi-threaded, but don't want to spawn a thread for each small bit of work.
+//
+// You just call AddWork() to add a delegate to the list of work to be done.
+// JoinAll() will make sure that all outstanding work is processed, and wait
+// for everything to finish. You can reuse a pool, so you can call Start()
+// again after you've called JoinAll().
+class BASE_EXPORT DelegateSimpleThreadPool
+ : public DelegateSimpleThread::Delegate {
+ public:
+ typedef DelegateSimpleThread::Delegate Delegate;
+
+ DelegateSimpleThreadPool(const std::string& name_prefix, int num_threads);
+ ~DelegateSimpleThreadPool() override;
+
+ // Start up all of the underlying threads, and start processing work if we
+ // have any.
+ void Start();
+
+ // Make sure all outstanding work is finished, and wait for and destroy all
+ // of the underlying threads in the pool.
+ void JoinAll();
+
+ // It is safe to AddWork() any time, before or after Start().
+ // Delegate* should always be a valid pointer, NULL is reserved internally.
+ void AddWork(Delegate* work, int repeat_count);
+ void AddWork(Delegate* work) {
+ AddWork(work, 1);
+ }
+
+ // We implement the Delegate interface, for running our internal threads.
+ void Run() override;
+
+ private:
+ const std::string name_prefix_;
+ int num_threads_;
+ std::vector<DelegateSimpleThread*> threads_;
+ base::queue<Delegate*> delegates_;
+ base::Lock lock_; // Locks delegates_
+ WaitableEvent dry_; // Not signaled when there is no work to do.
+
+ DISALLOW_COPY_AND_ASSIGN(DelegateSimpleThreadPool);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_SIMPLE_THREAD_H_
diff --git a/base/threading/simple_thread_unittest.cc b/base/threading/simple_thread_unittest.cc
new file mode 100644
index 0000000..4e618f9
--- /dev/null
+++ b/base/threading/simple_thread_unittest.cc
@@ -0,0 +1,234 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "base/atomic_sequence_num.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/gtest_util.h"
+#include "base/threading/simple_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class SetIntRunner : public DelegateSimpleThread::Delegate {
+ public:
+ SetIntRunner(int* ptr, int val) : ptr_(ptr), val_(val) { }
+ ~SetIntRunner() override = default;
+
+ private:
+ void Run() override { *ptr_ = val_; }
+
+ int* ptr_;
+ int val_;
+
+ DISALLOW_COPY_AND_ASSIGN(SetIntRunner);
+};
+
+// Signals |started_| when Run() is invoked and waits until |released_| is
+// signaled to return, signaling |done_| before doing so. Useful for tests that
+// care to control Run()'s flow.
+class ControlledRunner : public DelegateSimpleThread::Delegate {
+ public:
+ ControlledRunner()
+ : started_(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED),
+ released_(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED),
+ done_(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED) {}
+
+ ~ControlledRunner() override { ReleaseAndWaitUntilDone(); }
+
+ void WaitUntilStarted() { started_.Wait(); }
+
+ void ReleaseAndWaitUntilDone() {
+ released_.Signal();
+ done_.Wait();
+ }
+
+ private:
+ void Run() override {
+ started_.Signal();
+ released_.Wait();
+ done_.Signal();
+ }
+
+ WaitableEvent started_;
+ WaitableEvent released_;
+ WaitableEvent done_;
+
+ DISALLOW_COPY_AND_ASSIGN(ControlledRunner);
+};
+
+class WaitEventRunner : public DelegateSimpleThread::Delegate {
+ public:
+ explicit WaitEventRunner(WaitableEvent* event) : event_(event) { }
+ ~WaitEventRunner() override = default;
+
+ private:
+ void Run() override {
+ EXPECT_FALSE(event_->IsSignaled());
+ event_->Signal();
+ EXPECT_TRUE(event_->IsSignaled());
+ }
+
+ WaitableEvent* event_;
+
+ DISALLOW_COPY_AND_ASSIGN(WaitEventRunner);
+};
+
+class SeqRunner : public DelegateSimpleThread::Delegate {
+ public:
+ explicit SeqRunner(AtomicSequenceNumber* seq) : seq_(seq) { }
+
+ private:
+ void Run() override { seq_->GetNext(); }
+
+ AtomicSequenceNumber* seq_;
+
+ DISALLOW_COPY_AND_ASSIGN(SeqRunner);
+};
+
+// We count up on a sequence number, firing on the event when we've hit our
+// expected amount, otherwise we wait on the event. This will ensure that we
+// have all threads outstanding until we hit our expected thread pool size.
+class VerifyPoolRunner : public DelegateSimpleThread::Delegate {
+ public:
+ VerifyPoolRunner(AtomicSequenceNumber* seq,
+ int total, WaitableEvent* event)
+ : seq_(seq), total_(total), event_(event) { }
+
+ private:
+ void Run() override {
+ if (seq_->GetNext() == total_) {
+ event_->Signal();
+ } else {
+ event_->Wait();
+ }
+ }
+
+ AtomicSequenceNumber* seq_;
+ int total_;
+ WaitableEvent* event_;
+
+ DISALLOW_COPY_AND_ASSIGN(VerifyPoolRunner);
+};
+
+} // namespace
+
+TEST(SimpleThreadTest, CreateAndJoin) {
+ int stack_int = 0;
+
+ SetIntRunner runner(&stack_int, 7);
+ EXPECT_EQ(0, stack_int);
+
+ DelegateSimpleThread thread(&runner, "int_setter");
+ EXPECT_FALSE(thread.HasBeenStarted());
+ EXPECT_FALSE(thread.HasBeenJoined());
+ EXPECT_EQ(0, stack_int);
+
+ thread.Start();
+ EXPECT_TRUE(thread.HasBeenStarted());
+ EXPECT_FALSE(thread.HasBeenJoined());
+
+ thread.Join();
+ EXPECT_TRUE(thread.HasBeenStarted());
+ EXPECT_TRUE(thread.HasBeenJoined());
+ EXPECT_EQ(7, stack_int);
+}
+
+TEST(SimpleThreadTest, WaitForEvent) {
+ // Create a thread, and wait for it to signal us.
+ WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED);
+
+ WaitEventRunner runner(&event);
+ DelegateSimpleThread thread(&runner, "event_waiter");
+
+ EXPECT_FALSE(event.IsSignaled());
+ thread.Start();
+ event.Wait();
+ EXPECT_TRUE(event.IsSignaled());
+ thread.Join();
+}
+
+TEST(SimpleThreadTest, NonJoinableStartAndDieOnJoin) {
+ ControlledRunner runner;
+
+ SimpleThread::Options options;
+ options.joinable = false;
+ DelegateSimpleThread thread(&runner, "non_joinable", options);
+
+ EXPECT_FALSE(thread.HasBeenStarted());
+ thread.Start();
+ EXPECT_TRUE(thread.HasBeenStarted());
+
+ // Note: this is not quite the same as |thread.HasBeenStarted()| which
+ // represents ThreadMain() getting ready to invoke Run() whereas
+ // |runner.WaitUntilStarted()| ensures Run() was actually invoked.
+ runner.WaitUntilStarted();
+
+ EXPECT_FALSE(thread.HasBeenJoined());
+ EXPECT_DCHECK_DEATH({ thread.Join(); });
+}
+
+TEST(SimpleThreadTest, NonJoinableInactiveDelegateDestructionIsOkay) {
+ std::unique_ptr<ControlledRunner> runner(new ControlledRunner);
+
+ SimpleThread::Options options;
+ options.joinable = false;
+ std::unique_ptr<DelegateSimpleThread> thread(
+ new DelegateSimpleThread(runner.get(), "non_joinable", options));
+
+ thread->Start();
+ runner->WaitUntilStarted();
+
+ // Deleting a non-joinable SimpleThread after Run() was invoked is okay.
+ thread.reset();
+
+ runner->WaitUntilStarted();
+ runner->ReleaseAndWaitUntilDone();
+ // It should be safe to destroy a Delegate after its Run() method completed.
+ runner.reset();
+}
+
+TEST(SimpleThreadTest, ThreadPool) {
+ AtomicSequenceNumber seq;
+ SeqRunner runner(&seq);
+ DelegateSimpleThreadPool pool("seq_runner", 10);
+
+ // Add work before we're running.
+ pool.AddWork(&runner, 300);
+
+ EXPECT_EQ(seq.GetNext(), 0);
+ pool.Start();
+
+ // Add work while we're running.
+ pool.AddWork(&runner, 300);
+
+ pool.JoinAll();
+
+ EXPECT_EQ(seq.GetNext(), 601);
+
+ // We can reuse our pool. Verify that all 10 threads can actually run in
+ // parallel, so this test will only pass if there are actually 10 threads.
+ AtomicSequenceNumber seq2;
+ WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED);
+ // Changing 9 to 10, for example, would cause us JoinAll() to never return.
+ VerifyPoolRunner verifier(&seq2, 9, &event);
+ pool.Start();
+
+ pool.AddWork(&verifier, 10);
+
+ pool.JoinAll();
+ EXPECT_EQ(seq2.GetNext(), 10);
+}
+
+} // namespace base
diff --git a/base/threading/thread.cc b/base/threading/thread.cc
new file mode 100644
index 0000000..97e160f
--- /dev/null
+++ b/base/threading/thread.cc
@@ -0,0 +1,370 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/lazy_instance.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/thread_id_name_manager.h"
+#include "base/threading/thread_local.h"
+#include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
+
+#if defined(OS_POSIX) && !defined(OS_NACL)
+#include "base/files/file_descriptor_watcher_posix.h"
+#endif
+
+#if defined(OS_WIN)
+#include "base/win/scoped_com_initializer.h"
+#endif
+
+namespace base {
+
+namespace {
+
+// We use this thread-local variable to record whether or not a thread exited
+// because its Stop method was called. This allows us to catch cases where
+// MessageLoop::QuitWhenIdle() is called directly, which is unexpected when
+// using a Thread to setup and run a MessageLoop.
+base::LazyInstance<base::ThreadLocalBoolean>::Leaky lazy_tls_bool =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+Thread::Options::Options() = default;
+
+Thread::Options::Options(MessageLoop::Type type, size_t size)
+ : message_loop_type(type), stack_size(size) {}
+
+Thread::Options::Options(const Options& other) = default;
+
+Thread::Options::~Options() = default;
+
+Thread::Thread(const std::string& name)
+ : id_event_(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED),
+ name_(name),
+ start_event_(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED) {
+ // Only bind the sequence on Start(): the state is constant between
+ // construction and Start() and it's thus valid for Start() to be called on
+ // another sequence as long as every other operation is then performed on that
+ // sequence.
+ owning_sequence_checker_.DetachFromSequence();
+}
+
+Thread::~Thread() {
+ Stop();
+}
+
+bool Thread::Start() {
+ DCHECK(owning_sequence_checker_.CalledOnValidSequence());
+
+ Options options;
+#if defined(OS_WIN)
+ if (com_status_ == STA)
+ options.message_loop_type = MessageLoop::TYPE_UI;
+#endif
+ return StartWithOptions(options);
+}
+
+bool Thread::StartWithOptions(const Options& options) {
+ DCHECK(owning_sequence_checker_.CalledOnValidSequence());
+ DCHECK(!message_loop_);
+ DCHECK(!IsRunning());
+ DCHECK(!stopping_) << "Starting a non-joinable thread a second time? That's "
+ << "not allowed!";
+#if defined(OS_WIN)
+ DCHECK((com_status_ != STA) ||
+ (options.message_loop_type == MessageLoop::TYPE_UI));
+#endif
+
+ // Reset |id_| here to support restarting the thread.
+ id_event_.Reset();
+ id_ = kInvalidThreadId;
+
+ SetThreadWasQuitProperly(false);
+
+ MessageLoop::Type type = options.message_loop_type;
+ if (!options.message_pump_factory.is_null())
+ type = MessageLoop::TYPE_CUSTOM;
+
+ message_loop_timer_slack_ = options.timer_slack;
+ std::unique_ptr<MessageLoop> message_loop_owned =
+ MessageLoop::CreateUnbound(type, options.message_pump_factory);
+ message_loop_ = message_loop_owned.get();
+ start_event_.Reset();
+
+ // Hold |thread_lock_| while starting the new thread to synchronize with
+ // Stop() while it's not guaranteed to be sequenced (until crbug/629139 is
+ // fixed).
+ {
+ AutoLock lock(thread_lock_);
+ bool success =
+ options.joinable
+ ? PlatformThread::CreateWithPriority(options.stack_size, this,
+ &thread_, options.priority)
+ : PlatformThread::CreateNonJoinableWithPriority(
+ options.stack_size, this, options.priority);
+ if (!success) {
+ DLOG(ERROR) << "failed to create thread";
+ message_loop_ = nullptr;
+ return false;
+ }
+ }
+
+ joinable_ = options.joinable;
+
+ // The ownership of |message_loop_| is managed by the newly created thread
+ // within the ThreadMain.
+ ignore_result(message_loop_owned.release());
+
+ DCHECK(message_loop_);
+ return true;
+}
+
+bool Thread::StartAndWaitForTesting() {
+ DCHECK(owning_sequence_checker_.CalledOnValidSequence());
+ bool result = Start();
+ if (!result)
+ return false;
+ WaitUntilThreadStarted();
+ return true;
+}
+
+bool Thread::WaitUntilThreadStarted() const {
+ DCHECK(owning_sequence_checker_.CalledOnValidSequence());
+ if (!message_loop_)
+ return false;
+ base::ThreadRestrictions::ScopedAllowWait allow_wait;
+ start_event_.Wait();
+ return true;
+}
+
+void Thread::FlushForTesting() {
+ DCHECK(owning_sequence_checker_.CalledOnValidSequence());
+ if (!message_loop_)
+ return;
+
+ WaitableEvent done(WaitableEvent::ResetPolicy::AUTOMATIC,
+ WaitableEvent::InitialState::NOT_SIGNALED);
+ task_runner()->PostTask(FROM_HERE,
+ BindOnce(&WaitableEvent::Signal, Unretained(&done)));
+ done.Wait();
+}
+
+void Thread::Stop() {
+ DCHECK(joinable_);
+
+ // TODO(gab): Fix improper usage of this API (http://crbug.com/629139) and
+ // enable this check, until then synchronization with Start() via
+ // |thread_lock_| is required...
+ // DCHECK(owning_sequence_checker_.CalledOnValidSequence());
+ AutoLock lock(thread_lock_);
+
+ StopSoon();
+
+ // Can't join if the |thread_| is either already gone or is non-joinable.
+ if (thread_.is_null())
+ return;
+
+ // Wait for the thread to exit.
+ //
+ // TODO(darin): Unfortunately, we need to keep |message_loop_| around until
+ // the thread exits. Some consumers are abusing the API. Make them stop.
+ //
+ PlatformThread::Join(thread_);
+ thread_ = base::PlatformThreadHandle();
+
+ // The thread should nullify |message_loop_| on exit (note: Join() adds an
+ // implicit memory barrier and no lock is thus required for this check).
+ DCHECK(!message_loop_);
+
+ stopping_ = false;
+}
+
+void Thread::StopSoon() {
+ // TODO(gab): Fix improper usage of this API (http://crbug.com/629139) and
+ // enable this check.
+ // DCHECK(owning_sequence_checker_.CalledOnValidSequence());
+
+ if (stopping_ || !message_loop_)
+ return;
+
+ stopping_ = true;
+
+ if (using_external_message_loop_) {
+ // Setting |stopping_| to true above should have been sufficient for this
+ // thread to be considered "stopped" per it having never set its |running_|
+ // bit by lack of its own ThreadMain.
+ DCHECK(!IsRunning());
+ message_loop_ = nullptr;
+ return;
+ }
+
+ task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&Thread::ThreadQuitHelper, Unretained(this)));
+}
+
+void Thread::DetachFromSequence() {
+ DCHECK(owning_sequence_checker_.CalledOnValidSequence());
+ owning_sequence_checker_.DetachFromSequence();
+}
+
+PlatformThreadId Thread::GetThreadId() const {
+ // If the thread is created but not started yet, wait for |id_| being ready.
+ base::ThreadRestrictions::ScopedAllowWait allow_wait;
+ id_event_.Wait();
+ return id_;
+}
+
+PlatformThreadHandle Thread::GetThreadHandle() const {
+ AutoLock lock(thread_lock_);
+ return thread_;
+}
+
+bool Thread::IsRunning() const {
+ // TODO(gab): Fix improper usage of this API (http://crbug.com/629139) and
+ // enable this check.
+ // DCHECK(owning_sequence_checker_.CalledOnValidSequence());
+
+ // If the thread's already started (i.e. |message_loop_| is non-null) and not
+ // yet requested to stop (i.e. |stopping_| is false) we can just return true.
+ // (Note that |stopping_| is touched only on the same sequence that starts /
+ // started the new thread so we need no locking here.)
+ if (message_loop_ && !stopping_)
+ return true;
+ // Otherwise check the |running_| flag, which is set to true by the new thread
+ // only while it is inside Run().
+ AutoLock lock(running_lock_);
+ return running_;
+}
+
+void Thread::Run(RunLoop* run_loop) {
+ // Overridable protected method to be called from our |thread_| only.
+ DCHECK(id_event_.IsSignaled());
+ DCHECK_EQ(id_, PlatformThread::CurrentId());
+
+ run_loop->Run();
+}
+
+// static
+void Thread::SetThreadWasQuitProperly(bool flag) {
+ lazy_tls_bool.Pointer()->Set(flag);
+}
+
+// static
+bool Thread::GetThreadWasQuitProperly() {
+ bool quit_properly = true;
+#ifndef NDEBUG
+ quit_properly = lazy_tls_bool.Pointer()->Get();
+#endif
+ return quit_properly;
+}
+
+void Thread::SetMessageLoop(MessageLoop* message_loop) {
+ DCHECK(owning_sequence_checker_.CalledOnValidSequence());
+ DCHECK(message_loop);
+
+ // Setting |message_loop_| should suffice for this thread to be considered
+ // as "running", until Stop() is invoked.
+ DCHECK(!IsRunning());
+ message_loop_ = message_loop;
+ DCHECK(IsRunning());
+
+ using_external_message_loop_ = true;
+}
+
+void Thread::ThreadMain() {
+ // First, make GetThreadId() available to avoid deadlocks. It could be called
+ // any place in the following thread initialization code.
+ DCHECK(!id_event_.IsSignaled());
+ // Note: this read of |id_| while |id_event_| isn't signaled is exceptionally
+ // okay because ThreadMain has a happens-after relationship with the other
+ // write in StartWithOptions().
+ DCHECK_EQ(kInvalidThreadId, id_);
+ id_ = PlatformThread::CurrentId();
+ DCHECK_NE(kInvalidThreadId, id_);
+ id_event_.Signal();
+
+ // Complete the initialization of our Thread object.
+ PlatformThread::SetName(name_.c_str());
+ ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector.
+
+ // Lazily initialize the |message_loop| so that it can run on this thread.
+ DCHECK(message_loop_);
+ std::unique_ptr<MessageLoop> message_loop(message_loop_);
+ message_loop_->BindToCurrentThread();
+ message_loop_->SetTimerSlack(message_loop_timer_slack_);
+
+#if defined(OS_POSIX) && !defined(OS_NACL)
+ // Allow threads running a MessageLoopForIO to use FileDescriptorWatcher API.
+ std::unique_ptr<FileDescriptorWatcher> file_descriptor_watcher;
+ if (MessageLoopForIO::IsCurrent()) {
+ file_descriptor_watcher.reset(new FileDescriptorWatcher(
+ static_cast<MessageLoopForIO*>(message_loop_)));
+ }
+#endif
+
+#if defined(OS_WIN)
+ std::unique_ptr<win::ScopedCOMInitializer> com_initializer;
+ if (com_status_ != NONE) {
+ com_initializer.reset((com_status_ == STA) ?
+ new win::ScopedCOMInitializer() :
+ new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA));
+ }
+#endif
+
+ // Let the thread do extra initialization.
+ Init();
+
+ {
+ AutoLock lock(running_lock_);
+ running_ = true;
+ }
+
+ start_event_.Signal();
+
+ RunLoop run_loop;
+ run_loop_ = &run_loop;
+ Run(run_loop_);
+
+ {
+ AutoLock lock(running_lock_);
+ running_ = false;
+ }
+
+ // Let the thread do extra cleanup.
+ CleanUp();
+
+#if defined(OS_WIN)
+ com_initializer.reset();
+#endif
+
+ if (message_loop->type() != MessageLoop::TYPE_CUSTOM) {
+ // Assert that RunLoop::QuitWhenIdle was called by ThreadQuitHelper. Don't
+ // check for custom message pumps, because their shutdown might not allow
+ // this.
+ DCHECK(GetThreadWasQuitProperly());
+ }
+
+ // We can't receive messages anymore.
+ // (The message loop is destructed at the end of this block)
+ message_loop_ = nullptr;
+ run_loop_ = nullptr;
+}
+
+void Thread::ThreadQuitHelper() {
+ DCHECK(run_loop_);
+ run_loop_->QuitWhenIdle();
+ SetThreadWasQuitProperly(true);
+}
+
+} // namespace base
diff --git a/base/threading/thread.h b/base/threading/thread.h
new file mode 100644
index 0000000..9fbdcb8
--- /dev/null
+++ b/base/threading/thread.h
@@ -0,0 +1,356 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_THREAD_H_
+#define BASE_THREADING_THREAD_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/timer_slack.h"
+#include "base/sequence_checker.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/atomic_flag.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "build/build_config.h"
+
+namespace base {
+
+class MessagePump;
+class RunLoop;
+
+// IMPORTANT: Instead of creating a base::Thread, consider using
+// base::Create(Sequenced|SingleThread)TaskRunnerWithTraits().
+//
+// A simple thread abstraction that establishes a MessageLoop on a new thread.
+// The consumer uses the MessageLoop of the thread to cause code to execute on
+// the thread. When this object is destroyed the thread is terminated. All
+// pending tasks queued on the thread's message loop will run to completion
+// before the thread is terminated.
+//
+// WARNING! SUBCLASSES MUST CALL Stop() IN THEIR DESTRUCTORS! See ~Thread().
+//
+// After the thread is stopped, the destruction sequence is:
+//
+// (1) Thread::CleanUp()
+// (2) MessageLoop::~MessageLoop
+// (3.b) MessageLoopCurrent::DestructionObserver::WillDestroyCurrentMessageLoop
+//
+// This API is not thread-safe: unless indicated otherwise its methods are only
+// valid from the owning sequence (which is the one from which Start() is
+// invoked -- should it differ from the one on which it was constructed).
+//
+// Sometimes it's useful to kick things off on the initial sequence (e.g.
+// construction, Start(), task_runner()), but to then hand the Thread over to a
+// pool of users for the last one of them to destroy it when done. For that use
+// case, Thread::DetachFromSequence() allows the owning sequence to give up
+// ownership. The caller is then responsible to ensure a happens-after
+// relationship between the DetachFromSequence() call and the next use of that
+// Thread object (including ~Thread()).
+class BASE_EXPORT Thread : PlatformThread::Delegate {
+ public:
+ struct BASE_EXPORT Options {
+ typedef Callback<std::unique_ptr<MessagePump>()> MessagePumpFactory;
+
+ Options();
+ Options(MessageLoop::Type type, size_t size);
+ Options(const Options& other);
+ ~Options();
+
+ // Specifies the type of message loop that will be allocated on the thread.
+ // This is ignored if message_pump_factory.is_null() is false.
+ MessageLoop::Type message_loop_type = MessageLoop::TYPE_DEFAULT;
+
+ // Specifies timer slack for thread message loop.
+ TimerSlack timer_slack = TIMER_SLACK_NONE;
+
+ // Used to create the MessagePump for the MessageLoop. The callback is Run()
+ // on the thread. If message_pump_factory.is_null(), then a MessagePump
+ // appropriate for |message_loop_type| is created. Setting this forces the
+ // MessageLoop::Type to TYPE_CUSTOM.
+ MessagePumpFactory message_pump_factory;
+
+ // Specifies the maximum stack size that the thread is allowed to use.
+ // This does not necessarily correspond to the thread's initial stack size.
+ // A value of 0 indicates that the default maximum should be used.
+ size_t stack_size = 0;
+
+ // Specifies the initial thread priority.
+ ThreadPriority priority = ThreadPriority::NORMAL;
+
+ // If false, the thread will not be joined on destruction. This is intended
+ // for threads that want TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN
+ // semantics. Non-joinable threads can't be joined (must be leaked and
+ // can't be destroyed or Stop()'ed).
+ // TODO(gab): allow non-joinable instances to be deleted without causing
+ // user-after-frees (proposal @ https://crbug.com/629139#c14)
+ bool joinable = true;
+ };
+
+ // Constructor.
+ // name is a display string to identify the thread.
+ explicit Thread(const std::string& name);
+
+ // Destroys the thread, stopping it if necessary.
+ //
+ // NOTE: ALL SUBCLASSES OF Thread MUST CALL Stop() IN THEIR DESTRUCTORS (or
+ // guarantee Stop() is explicitly called before the subclass is destroyed).
+ // This is required to avoid a data race between the destructor modifying the
+ // vtable, and the thread's ThreadMain calling the virtual method Run(). It
+ // also ensures that the CleanUp() virtual method is called on the subclass
+ // before it is destructed.
+ ~Thread() override;
+
+#if defined(OS_WIN)
+ // Causes the thread to initialize COM. This must be called before calling
+ // Start() or StartWithOptions(). If |use_mta| is false, the thread is also
+ // started with a TYPE_UI message loop. It is an error to call
+ // init_com_with_mta(false) and then StartWithOptions() with any message loop
+ // type other than TYPE_UI.
+ void init_com_with_mta(bool use_mta) {
+ DCHECK(!message_loop_);
+ com_status_ = use_mta ? MTA : STA;
+ }
+#endif
+
+ // Starts the thread. Returns true if the thread was successfully started;
+ // otherwise, returns false. Upon successful return, the message_loop()
+ // getter will return non-null.
+ //
+ // Note: This function can't be called on Windows with the loader lock held;
+ // i.e. during a DllMain, global object construction or destruction, atexit()
+ // callback.
+ bool Start();
+
+ // Starts the thread. Behaves exactly like Start in addition to allow to
+ // override the default options.
+ //
+ // Note: This function can't be called on Windows with the loader lock held;
+ // i.e. during a DllMain, global object construction or destruction, atexit()
+ // callback.
+ bool StartWithOptions(const Options& options);
+
+ // Starts the thread and wait for the thread to start and run initialization
+ // before returning. It's same as calling Start() and then
+ // WaitUntilThreadStarted().
+ // Note that using this (instead of Start() or StartWithOptions() causes
+ // jank on the calling thread, should be used only in testing code.
+ bool StartAndWaitForTesting();
+
+ // Blocks until the thread starts running. Called within StartAndWait().
+ // Note that calling this causes jank on the calling thread, must be used
+ // carefully for production code.
+ bool WaitUntilThreadStarted() const;
+
+ // Blocks until all tasks previously posted to this thread have been executed.
+ void FlushForTesting();
+
+ // Signals the thread to exit and returns once the thread has exited. The
+ // Thread object is completely reset and may be used as if it were newly
+ // constructed (i.e., Start may be called again). Can only be called if
+ // |joinable_|.
+ //
+ // Stop may be called multiple times and is simply ignored if the thread is
+ // already stopped or currently stopping.
+ //
+ // Start/Stop are not thread-safe and callers that desire to invoke them from
+ // different threads must ensure mutual exclusion.
+ //
+ // NOTE: If you are a consumer of Thread, it is not necessary to call this
+ // before deleting your Thread objects, as the destructor will do it.
+ // IF YOU ARE A SUBCLASS OF Thread, YOU MUST CALL THIS IN YOUR DESTRUCTOR.
+ void Stop();
+
+ // Signals the thread to exit in the near future.
+ //
+ // WARNING: This function is not meant to be commonly used. Use at your own
+ // risk. Calling this function will cause message_loop() to become invalid in
+ // the near future. This function was created to workaround a specific
+ // deadlock on Windows with printer worker thread. In any other case, Stop()
+ // should be used.
+ //
+ // Call Stop() to reset the thread object once it is known that the thread has
+ // quit.
+ void StopSoon();
+
+ // Detaches the owning sequence, indicating that the next call to this API
+ // (including ~Thread()) can happen from a different sequence (to which it
+ // will be rebound). This call itself must happen on the current owning
+ // sequence and the caller must ensure the next API call has a happens-after
+ // relationship with this one.
+ void DetachFromSequence();
+
+ // Returns the message loop for this thread. Use the MessageLoop's
+ // PostTask methods to execute code on the thread. This only returns
+ // non-null after a successful call to Start. After Stop has been called,
+ // this will return nullptr.
+ //
+ // NOTE: You must not call this MessageLoop's Quit method directly. Use
+ // the Thread's Stop method instead.
+ //
+ // In addition to this Thread's owning sequence, this can also safely be
+ // called from the underlying thread itself.
+ MessageLoop* message_loop() const {
+ // This class doesn't provide synchronization around |message_loop_| and as
+ // such only the owner should access it (and the underlying thread which
+ // never sees it before it's set). In practice, many callers are coming from
+ // unrelated threads but provide their own implicit (e.g. memory barriers
+ // from task posting) or explicit (e.g. locks) synchronization making the
+ // access of |message_loop_| safe... Changing all of those callers is
+ // unfeasible; instead verify that they can reliably see
+ // |message_loop_ != nullptr| without synchronization as a proof that their
+ // external synchronization catches the unsynchronized effects of Start().
+ // TODO(gab): Despite all of the above this test has to be disabled for now
+ // per crbug.com/629139#c6.
+ // DCHECK(owning_sequence_checker_.CalledOnValidSequence() ||
+ // (id_event_.IsSignaled() && id_ == PlatformThread::CurrentId()) ||
+ // message_loop_);
+ return message_loop_;
+ }
+
+ // Returns a TaskRunner for this thread. Use the TaskRunner's PostTask
+ // methods to execute code on the thread. Returns nullptr if the thread is not
+ // running (e.g. before Start or after Stop have been called). Callers can
+ // hold on to this even after the thread is gone; in this situation, attempts
+ // to PostTask() will fail.
+ //
+ // In addition to this Thread's owning sequence, this can also safely be
+ // called from the underlying thread itself.
+ scoped_refptr<SingleThreadTaskRunner> task_runner() const {
+ // Refer to the DCHECK and comment inside |message_loop()|.
+ DCHECK(owning_sequence_checker_.CalledOnValidSequence() ||
+ (id_event_.IsSignaled() && id_ == PlatformThread::CurrentId()) ||
+ message_loop_);
+ return message_loop_ ? message_loop_->task_runner() : nullptr;
+ }
+
+ // Returns the name of this thread (for display in debugger too).
+ const std::string& thread_name() const { return name_; }
+
+ // Returns the thread ID. Should not be called before the first Start*()
+ // call. Keeps on returning the same ID even after a Stop() call. The next
+ // Start*() call renews the ID.
+ //
+ // WARNING: This function will block if the thread hasn't started yet.
+ //
+ // This method is thread-safe.
+ PlatformThreadId GetThreadId() const;
+
+ // Returns the current thread handle. If called before Start*() returns or
+ // after Stop() returns, an empty thread handle will be returned.
+ //
+ // This method is thread-safe.
+ //
+ // TODO(robliao): Remove this when it no longer needs to be temporarily
+ // exposed for http://crbug.com/717380.
+ PlatformThreadHandle GetThreadHandle() const;
+
+ // Returns true if the thread has been started, and not yet stopped.
+ bool IsRunning() const;
+
+ protected:
+ // Called just prior to starting the message loop
+ virtual void Init() {}
+
+ // Called to start the run loop
+ virtual void Run(RunLoop* run_loop);
+
+ // Called just after the message loop ends
+ virtual void CleanUp() {}
+
+ static void SetThreadWasQuitProperly(bool flag);
+ static bool GetThreadWasQuitProperly();
+
+ // Bind this Thread to an existing MessageLoop instead of starting a new one.
+ // TODO(gab): Remove this after ios/ has undergone the same surgery as
+ // BrowserThreadImpl (ref.
+ // https://chromium-review.googlesource.com/c/chromium/src/+/969104).
+ void SetMessageLoop(MessageLoop* message_loop);
+
+ bool using_external_message_loop() const {
+ return using_external_message_loop_;
+ }
+
+ private:
+#if defined(OS_WIN)
+ enum ComStatus {
+ NONE,
+ STA,
+ MTA,
+ };
+#endif
+
+ // PlatformThread::Delegate methods:
+ void ThreadMain() override;
+
+ void ThreadQuitHelper();
+
+#if defined(OS_WIN)
+ // Whether this thread needs to initialize COM, and if so, in what mode.
+ ComStatus com_status_ = NONE;
+#endif
+
+ // Mirrors the Options::joinable field used to start this thread. Verified
+ // on Stop() -- non-joinable threads can't be joined (must be leaked).
+ bool joinable_ = true;
+
+ // If true, we're in the middle of stopping, and shouldn't access
+ // |message_loop_|. It may non-nullptr and invalid.
+ // Should be written on the thread that created this thread. Also read data
+ // could be wrong on other threads.
+ bool stopping_ = false;
+
+ // True while inside of Run().
+ bool running_ = false;
+ mutable base::Lock running_lock_; // Protects |running_|.
+
+ // The thread's handle.
+ PlatformThreadHandle thread_;
+ mutable base::Lock thread_lock_; // Protects |thread_|.
+
+ // The thread's id once it has started.
+ PlatformThreadId id_ = kInvalidThreadId;
+ // Protects |id_| which must only be read while it's signaled.
+ mutable WaitableEvent id_event_;
+
+ // The thread's MessageLoop and RunLoop. Valid only while the thread is alive.
+ // Set by the created thread.
+ MessageLoop* message_loop_ = nullptr;
+ RunLoop* run_loop_ = nullptr;
+
+ // True only if |message_loop_| was externally provided by |SetMessageLoop()|
+ // in which case this Thread has no underlying |thread_| and should merely
+ // drop |message_loop_| on Stop(). In that event, this remains true after
+ // Stop() was invoked so that subclasses can use this state to build their own
+ // cleanup logic as required.
+ bool using_external_message_loop_ = false;
+
+ // Stores Options::timer_slack_ until the message loop has been bound to
+ // a thread.
+ TimerSlack message_loop_timer_slack_ = TIMER_SLACK_NONE;
+
+ // The name of the thread. Used for debugging purposes.
+ const std::string name_;
+
+ // Signaled when the created thread gets ready to use the message loop.
+ mutable WaitableEvent start_event_;
+
+ // This class is not thread-safe, use this to verify access from the owning
+ // sequence of the Thread.
+ SequenceChecker owning_sequence_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(Thread);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_H_
diff --git a/base/threading/thread_checker.h b/base/threading/thread_checker.h
new file mode 100644
index 0000000..6799e25
--- /dev/null
+++ b/base/threading/thread_checker.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_THREAD_CHECKER_H_
+#define BASE_THREADING_THREAD_CHECKER_H_
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/threading/thread_checker_impl.h"
+
+// ThreadChecker is a helper class used to help verify that some methods of a
+// class are called from the same thread (for thread-affinity).
+//
+// Use the macros below instead of the ThreadChecker directly so that the unused
+// member doesn't result in an extra byte (four when padded) per instance in
+// production.
+//
+// Usage of this class should be *rare* as most classes require thread-safety
+// but not thread-affinity. Prefer base::SequenceChecker to verify thread-safe
+// access.
+//
+// Thread-affinity checks should only be required in classes that use thread-
+// local-storage or a third-party API that does.
+//
+// Prefer to encode the minimum requirements of each class instead of the
+// environment it happens to run in today. e.g. if a class requires thread-
+// safety but not thread-affinity, use a SequenceChecker even if it happens to
+// run on a SingleThreadTaskRunner today. That makes it easier to understand
+// what would need to change to turn that SingleThreadTaskRunner into a
+// SequencedTaskRunner for ease of scheduling as well as minimizes side-effects
+// if that change is made.
+//
+// Usage:
+// class MyClass {
+// public:
+// MyClass() {
+// // It's sometimes useful to detach on construction for objects that are
+// // constructed in one place and forever after used from another
+// // thread.
+// DETACH_FROM_THREAD(my_thread_checker_);
+// }
+//
+// ~MyClass() {
+// // ThreadChecker doesn't automatically check it's destroyed on origin
+// // thread for the same reason it's sometimes detached in the
+// // constructor. It's okay to destroy off thread if the owner otherwise
+// // knows usage on the associated thread is done. If you're not
+// // detaching in the constructor, you probably want to explicitly check
+// // in the destructor.
+// DCHECK_CALLED_ON_VALID_THREAD(my_thread_checker_);
+// }
+//
+// void MyMethod() {
+// DCHECK_CALLED_ON_VALID_THREAD(my_thread_checker_);
+// ... (do stuff) ...
+// }
+//
+// private:
+// THREAD_CHECKER(my_thread_checker_);
+// }
+
+#if DCHECK_IS_ON()
+#define THREAD_CHECKER(name) base::ThreadChecker name
+#define DCHECK_CALLED_ON_VALID_THREAD(name) DCHECK((name).CalledOnValidThread())
+#define DETACH_FROM_THREAD(name) (name).DetachFromThread()
+#else // DCHECK_IS_ON()
+#define THREAD_CHECKER(name)
+#define DCHECK_CALLED_ON_VALID_THREAD(name) EAT_STREAM_PARAMETERS
+#define DETACH_FROM_THREAD(name)
+#endif // DCHECK_IS_ON()
+
+namespace base {
+
+// Do nothing implementation, for use in release mode.
+//
+// Note: You should almost always use the ThreadChecker class (through the above
+// macros) to get the right version for your build configuration.
+class ThreadCheckerDoNothing {
+ public:
+ ThreadCheckerDoNothing() = default;
+ bool CalledOnValidThread() const WARN_UNUSED_RESULT { return true; }
+ void DetachFromThread() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ThreadCheckerDoNothing);
+};
+
+// Note that ThreadCheckerImpl::CalledOnValidThread() returns false when called
+// from tasks posted to SingleThreadTaskRunners bound to different sequences,
+// even if the tasks happen to run on the same thread (e.g. two independent
+// SingleThreadTaskRunners on the TaskScheduler that happen to share a thread).
+#if DCHECK_IS_ON()
+class ThreadChecker : public ThreadCheckerImpl {
+};
+#else
+class ThreadChecker : public ThreadCheckerDoNothing {
+};
+#endif // DCHECK_IS_ON()
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_CHECKER_H_
diff --git a/base/threading/thread_checker_impl.cc b/base/threading/thread_checker_impl.cc
new file mode 100644
index 0000000..d5ccbdb
--- /dev/null
+++ b/base/threading/thread_checker_impl.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_checker_impl.h"
+
+#include "base/threading/thread_task_runner_handle.h"
+
+namespace base {
+
+ThreadCheckerImpl::ThreadCheckerImpl() {
+ AutoLock auto_lock(lock_);
+ EnsureAssigned();
+}
+
+ThreadCheckerImpl::~ThreadCheckerImpl() = default;
+
+bool ThreadCheckerImpl::CalledOnValidThread() const {
+ AutoLock auto_lock(lock_);
+ EnsureAssigned();
+
+ // Always return true when called from the task from which this
+ // ThreadCheckerImpl was assigned to a thread.
+ if (task_token_ == TaskToken::GetForCurrentThread())
+ return true;
+
+ // If this ThreadCheckerImpl is bound to a valid SequenceToken, it must be
+ // equal to the current SequenceToken and there must be a registered
+ // ThreadTaskRunnerHandle. Otherwise, the fact that the current task runs on
+ // the thread to which this ThreadCheckerImpl is bound is fortuitous.
+ if (sequence_token_.IsValid() &&
+ (sequence_token_ != SequenceToken::GetForCurrentThread() ||
+ !ThreadTaskRunnerHandle::IsSet())) {
+ return false;
+ }
+
+ return thread_id_ == PlatformThread::CurrentRef();
+}
+
+void ThreadCheckerImpl::DetachFromThread() {
+ AutoLock auto_lock(lock_);
+ thread_id_ = PlatformThreadRef();
+ task_token_ = TaskToken();
+ sequence_token_ = SequenceToken();
+}
+
+void ThreadCheckerImpl::EnsureAssigned() const {
+ lock_.AssertAcquired();
+ if (!thread_id_.is_null())
+ return;
+
+ thread_id_ = PlatformThread::CurrentRef();
+ task_token_ = TaskToken::GetForCurrentThread();
+ sequence_token_ = SequenceToken::GetForCurrentThread();
+}
+
+} // namespace base
diff --git a/base/threading/thread_checker_impl.h b/base/threading/thread_checker_impl.h
new file mode 100644
index 0000000..103dfe7
--- /dev/null
+++ b/base/threading/thread_checker_impl.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_THREAD_CHECKER_IMPL_H_
+#define BASE_THREADING_THREAD_CHECKER_IMPL_H_
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/sequence_token.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+// Real implementation of ThreadChecker, for use in debug mode, or for temporary
+// use in release mode (e.g. to CHECK on a threading issue seen only in the
+// wild).
+//
+// Note: You should almost always use the ThreadChecker class to get the right
+// version for your build configuration.
+class BASE_EXPORT ThreadCheckerImpl {
+ public:
+ ThreadCheckerImpl();
+ ~ThreadCheckerImpl();
+
+ bool CalledOnValidThread() const WARN_UNUSED_RESULT;
+
+ // Changes the thread that is checked for in CalledOnValidThread. This may
+ // be useful when an object may be created on one thread and then used
+ // exclusively on another thread.
+ void DetachFromThread();
+
+ private:
+ void EnsureAssigned() const;
+
+ // Members are mutable so that CalledOnValidThread() can set them.
+
+ // Synchronizes access to all members.
+ mutable base::Lock lock_;
+
+ // Thread on which CalledOnValidThread() may return true.
+ mutable PlatformThreadRef thread_id_;
+
+ // TaskToken for which CalledOnValidThread() always returns true. This allows
+ // CalledOnValidThread() to return true when called multiple times from the
+ // same task, even if it's not running in a single-threaded context itself
+ // (allowing usage of ThreadChecker objects on the stack in the scope of one-
+ // off tasks). Note: CalledOnValidThread() may return true even if the current
+ // TaskToken is not equal to this.
+ mutable TaskToken task_token_;
+
+ // SequenceToken for which CalledOnValidThread() may return true. Used to
+ // ensure that CalledOnValidThread() doesn't return true for TaskScheduler
+ // tasks that happen to run on the same thread but weren't posted to the same
+ // SingleThreadTaskRunner.
+ mutable SequenceToken sequence_token_;
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_CHECKER_IMPL_H_
diff --git a/base/threading/thread_checker_unittest.cc b/base/threading/thread_checker_unittest.cc
new file mode 100644
index 0000000..5fbbc52
--- /dev/null
+++ b/base/threading/thread_checker_unittest.cc
@@ -0,0 +1,245 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_checker.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequence_token.h"
+#include "base/test/gtest_util.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/simple_thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+// A thread that runs a callback.
+class RunCallbackThread : public SimpleThread {
+ public:
+ explicit RunCallbackThread(const Closure& callback)
+ : SimpleThread("RunCallbackThread"), callback_(callback) {}
+
+ private:
+ // SimpleThread:
+ void Run() override { callback_.Run(); }
+
+ const Closure callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(RunCallbackThread);
+};
+
+// Runs a callback on a new thread synchronously.
+void RunCallbackOnNewThreadSynchronously(const Closure& callback) {
+ RunCallbackThread run_callback_thread(callback);
+ run_callback_thread.Start();
+ run_callback_thread.Join();
+}
+
+void ExpectCalledOnValidThread(ThreadCheckerImpl* thread_checker) {
+ ASSERT_TRUE(thread_checker);
+
+ // This should bind |thread_checker| to the current thread if it wasn't
+ // already bound to a thread.
+ EXPECT_TRUE(thread_checker->CalledOnValidThread());
+
+ // Since |thread_checker| is now bound to the current thread, another call to
+ // CalledOnValidThread() should return true.
+ EXPECT_TRUE(thread_checker->CalledOnValidThread());
+}
+
+void ExpectNotCalledOnValidThread(ThreadCheckerImpl* thread_checker) {
+ ASSERT_TRUE(thread_checker);
+ EXPECT_FALSE(thread_checker->CalledOnValidThread());
+}
+
+void ExpectNotCalledOnValidThreadWithSequenceTokenAndThreadTaskRunnerHandle(
+ ThreadCheckerImpl* thread_checker,
+ SequenceToken sequence_token) {
+ ThreadTaskRunnerHandle thread_task_runner_handle(
+ MakeRefCounted<TestSimpleTaskRunner>());
+ ScopedSetSequenceTokenForCurrentThread
+ scoped_set_sequence_token_for_current_thread(sequence_token);
+ ExpectNotCalledOnValidThread(thread_checker);
+}
+
+} // namespace
+
+TEST(ThreadCheckerTest, AllowedSameThreadNoSequenceToken) {
+ ThreadCheckerImpl thread_checker;
+ EXPECT_TRUE(thread_checker.CalledOnValidThread());
+}
+
+TEST(ThreadCheckerTest,
+ AllowedSameThreadAndSequenceDifferentTasksWithThreadTaskRunnerHandle) {
+ ThreadTaskRunnerHandle thread_task_runner_handle(
+ MakeRefCounted<TestSimpleTaskRunner>());
+
+ std::unique_ptr<ThreadCheckerImpl> thread_checker;
+ const SequenceToken sequence_token = SequenceToken::Create();
+
+ {
+ ScopedSetSequenceTokenForCurrentThread
+ scoped_set_sequence_token_for_current_thread(sequence_token);
+ thread_checker.reset(new ThreadCheckerImpl);
+ }
+
+ {
+ ScopedSetSequenceTokenForCurrentThread
+ scoped_set_sequence_token_for_current_thread(sequence_token);
+ EXPECT_TRUE(thread_checker->CalledOnValidThread());
+ }
+}
+
+TEST(ThreadCheckerTest,
+ AllowedSameThreadSequenceAndTaskNoThreadTaskRunnerHandle) {
+ ScopedSetSequenceTokenForCurrentThread
+ scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
+ ThreadCheckerImpl thread_checker;
+ EXPECT_TRUE(thread_checker.CalledOnValidThread());
+}
+
+TEST(ThreadCheckerTest,
+ DisallowedSameThreadAndSequenceDifferentTasksNoThreadTaskRunnerHandle) {
+ std::unique_ptr<ThreadCheckerImpl> thread_checker;
+
+ {
+ ScopedSetSequenceTokenForCurrentThread
+ scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
+ thread_checker.reset(new ThreadCheckerImpl);
+ }
+
+ {
+ ScopedSetSequenceTokenForCurrentThread
+ scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
+ EXPECT_FALSE(thread_checker->CalledOnValidThread());
+ }
+}
+
+TEST(ThreadCheckerTest, DisallowedDifferentThreadsNoSequenceToken) {
+ ThreadCheckerImpl thread_checker;
+ RunCallbackOnNewThreadSynchronously(
+ Bind(&ExpectNotCalledOnValidThread, Unretained(&thread_checker)));
+}
+
+TEST(ThreadCheckerTest, DisallowedDifferentThreadsSameSequence) {
+ ThreadTaskRunnerHandle thread_task_runner_handle(
+ MakeRefCounted<TestSimpleTaskRunner>());
+ const SequenceToken sequence_token(SequenceToken::Create());
+
+ ScopedSetSequenceTokenForCurrentThread
+ scoped_set_sequence_token_for_current_thread(sequence_token);
+ ThreadCheckerImpl thread_checker;
+ EXPECT_TRUE(thread_checker.CalledOnValidThread());
+
+ RunCallbackOnNewThreadSynchronously(Bind(
+ &ExpectNotCalledOnValidThreadWithSequenceTokenAndThreadTaskRunnerHandle,
+ Unretained(&thread_checker), sequence_token));
+}
+
+TEST(ThreadCheckerTest, DisallowedSameThreadDifferentSequence) {
+ std::unique_ptr<ThreadCheckerImpl> thread_checker;
+
+ ThreadTaskRunnerHandle thread_task_runner_handle(
+ MakeRefCounted<TestSimpleTaskRunner>());
+
+ {
+ ScopedSetSequenceTokenForCurrentThread
+ scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
+ thread_checker.reset(new ThreadCheckerImpl);
+ }
+
+ {
+ // Different SequenceToken.
+ ScopedSetSequenceTokenForCurrentThread
+ scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
+ EXPECT_FALSE(thread_checker->CalledOnValidThread());
+ }
+
+ // No SequenceToken.
+ EXPECT_FALSE(thread_checker->CalledOnValidThread());
+}
+
+TEST(ThreadCheckerTest, DetachFromThread) {
+ ThreadCheckerImpl thread_checker;
+ thread_checker.DetachFromThread();
+
+ // Verify that CalledOnValidThread() returns true when called on a different
+ // thread after a call to DetachFromThread().
+ RunCallbackOnNewThreadSynchronously(
+ Bind(&ExpectCalledOnValidThread, Unretained(&thread_checker)));
+
+ EXPECT_FALSE(thread_checker.CalledOnValidThread());
+}
+
+TEST(ThreadCheckerTest, DetachFromThreadWithSequenceToken) {
+ ThreadTaskRunnerHandle thread_task_runner_handle(
+ MakeRefCounted<TestSimpleTaskRunner>());
+ ScopedSetSequenceTokenForCurrentThread
+ scoped_set_sequence_token_for_current_thread(SequenceToken::Create());
+ ThreadCheckerImpl thread_checker;
+ thread_checker.DetachFromThread();
+
+ // Verify that CalledOnValidThread() returns true when called on a different
+ // thread after a call to DetachFromThread().
+ RunCallbackOnNewThreadSynchronously(
+ Bind(&ExpectCalledOnValidThread, Unretained(&thread_checker)));
+
+ EXPECT_FALSE(thread_checker.CalledOnValidThread());
+}
+
+namespace {
+
+// This fixture is a helper for unit testing the thread checker macros as it is
+// not possible to inline ExpectDeathOnOtherThread() and
+// ExpectNoDeathOnOtherThreadAfterDetach() as lambdas since binding
+// |Unretained(&my_sequence_checker)| wouldn't compile on non-dcheck builds
+// where it won't be defined.
+class ThreadCheckerMacroTest : public testing::Test {
+ public:
+ ThreadCheckerMacroTest() = default;
+
+ void ExpectDeathOnOtherThread() {
+#if DCHECK_IS_ON()
+ EXPECT_DCHECK_DEATH({ DCHECK_CALLED_ON_VALID_THREAD(my_thread_checker_); });
+#else
+ // Happily no-ops on non-dcheck builds.
+ DCHECK_CALLED_ON_VALID_THREAD(my_thread_checker_);
+#endif
+ }
+
+ void ExpectNoDeathOnOtherThreadAfterDetach() {
+ DCHECK_CALLED_ON_VALID_THREAD(my_thread_checker_);
+ DCHECK_CALLED_ON_VALID_THREAD(my_thread_checker_)
+ << "Make sure it compiles when DCHECK is off";
+ }
+
+ protected:
+ THREAD_CHECKER(my_thread_checker_);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ThreadCheckerMacroTest);
+};
+
+} // namespace
+
+TEST_F(ThreadCheckerMacroTest, Macros) {
+ THREAD_CHECKER(my_thread_checker);
+
+ RunCallbackOnNewThreadSynchronously(Bind(
+ &ThreadCheckerMacroTest::ExpectDeathOnOtherThread, Unretained(this)));
+
+ DETACH_FROM_THREAD(my_thread_checker_);
+
+ RunCallbackOnNewThreadSynchronously(
+ Bind(&ThreadCheckerMacroTest::ExpectNoDeathOnOtherThreadAfterDetach,
+ Unretained(this)));
+}
+
+} // namespace base
diff --git a/base/threading/thread_collision_warner.cc b/base/threading/thread_collision_warner.cc
new file mode 100644
index 0000000..547e11c
--- /dev/null
+++ b/base/threading/thread_collision_warner.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_collision_warner.h"
+
+#include "base/logging.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+void DCheckAsserter::warn() {
+ NOTREACHED() << "Thread Collision";
+}
+
+static subtle::Atomic32 CurrentThread() {
+ const PlatformThreadId current_thread_id = PlatformThread::CurrentId();
+ // We need to get the thread id into an atomic data type. This might be a
+ // truncating conversion, but any loss-of-information just increases the
+ // chance of a fault negative, not a false positive.
+ const subtle::Atomic32 atomic_thread_id =
+ static_cast<subtle::Atomic32>(current_thread_id);
+
+ return atomic_thread_id;
+}
+
+void ThreadCollisionWarner::EnterSelf() {
+ // If the active thread is 0 then I'll write the current thread ID
+ // if two or more threads arrive here only one will succeed to
+ // write on valid_thread_id_ the current thread ID.
+ subtle::Atomic32 current_thread_id = CurrentThread();
+
+ int previous_value = subtle::NoBarrier_CompareAndSwap(&valid_thread_id_,
+ 0,
+ current_thread_id);
+ if (previous_value != 0 && previous_value != current_thread_id) {
+ // gotcha! a thread is trying to use the same class and that is
+ // not current thread.
+ asserter_->warn();
+ }
+
+ subtle::NoBarrier_AtomicIncrement(&counter_, 1);
+}
+
+void ThreadCollisionWarner::Enter() {
+ subtle::Atomic32 current_thread_id = CurrentThread();
+
+ if (subtle::NoBarrier_CompareAndSwap(&valid_thread_id_,
+ 0,
+ current_thread_id) != 0) {
+ // gotcha! another thread is trying to use the same class.
+ asserter_->warn();
+ }
+
+ subtle::NoBarrier_AtomicIncrement(&counter_, 1);
+}
+
+void ThreadCollisionWarner::Leave() {
+ if (subtle::Barrier_AtomicIncrement(&counter_, -1) == 0) {
+ subtle::NoBarrier_Store(&valid_thread_id_, 0);
+ }
+}
+
+} // namespace base
diff --git a/base/threading/thread_collision_warner.h b/base/threading/thread_collision_warner.h
new file mode 100644
index 0000000..b6993f6
--- /dev/null
+++ b/base/threading/thread_collision_warner.h
@@ -0,0 +1,245 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_THREAD_COLLISION_WARNER_H_
+#define BASE_THREADING_THREAD_COLLISION_WARNER_H_
+
+#include <memory>
+
+#include "base/atomicops.h"
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+
+// A helper class alongside macros to be used to verify assumptions about thread
+// safety of a class.
+//
+// Example: Queue implementation non thread-safe but still usable if clients
+// are synchronized somehow.
+//
+// In this case the macro DFAKE_SCOPED_LOCK has to be
+// used, it checks that if a thread is inside the push/pop then
+// noone else is still inside the pop/push
+//
+// class NonThreadSafeQueue {
+// public:
+// ...
+// void push(int) { DFAKE_SCOPED_LOCK(push_pop_); ... }
+// int pop() { DFAKE_SCOPED_LOCK(push_pop_); ... }
+// ...
+// private:
+// DFAKE_MUTEX(push_pop_);
+// };
+//
+//
+// Example: Queue implementation non thread-safe but still usable if clients
+// are synchronized somehow, it calls a method to "protect" from
+// a "protected" method
+//
+// In this case the macro DFAKE_SCOPED_RECURSIVE_LOCK
+// has to be used, it checks that if a thread is inside the push/pop
+// then noone else is still inside the pop/push
+//
+// class NonThreadSafeQueue {
+// public:
+// void push(int) {
+// DFAKE_SCOPED_LOCK(push_pop_);
+// ...
+// }
+// int pop() {
+// DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
+// bar();
+// ...
+// }
+// void bar() { DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); ... }
+// ...
+// private:
+// DFAKE_MUTEX(push_pop_);
+// };
+//
+//
+// Example: Queue implementation not usable even if clients are synchronized,
+// so only one thread in the class life cycle can use the two members
+// push/pop.
+//
+// In this case the macro DFAKE_SCOPED_LOCK_THREAD_LOCKED pins the
+// specified
+// critical section the first time a thread enters push or pop, from
+// that time on only that thread is allowed to execute push or pop.
+//
+// class NonThreadSafeQueue {
+// public:
+// ...
+// void push(int) { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... }
+// int pop() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... }
+// ...
+// private:
+// DFAKE_MUTEX(push_pop_);
+// };
+//
+//
+// Example: Class that has to be contructed/destroyed on same thread, it has
+// a "shareable" method (with external synchronization) and a not
+// shareable method (even with external synchronization).
+//
+// In this case 3 Critical sections have to be defined
+//
+// class ExoticClass {
+// public:
+// ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... }
+// ~ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... }
+//
+// void Shareable() { DFAKE_SCOPED_LOCK(shareable_section_); ... }
+// void NotShareable() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... }
+// ...
+// private:
+// DFAKE_MUTEX(ctor_dtor_);
+// DFAKE_MUTEX(shareable_section_);
+// };
+
+
+#if !defined(NDEBUG)
+
+// Defines a class member that acts like a mutex. It is used only as a
+// verification tool.
+#define DFAKE_MUTEX(obj) \
+ mutable base::ThreadCollisionWarner obj
+// Asserts the call is never called simultaneously in two threads. Used at
+// member function scope.
+#define DFAKE_SCOPED_LOCK(obj) \
+ base::ThreadCollisionWarner::ScopedCheck s_check_##obj(&obj)
+// Asserts the call is never called simultaneously in two threads. Used at
+// member function scope. Same as DFAKE_SCOPED_LOCK but allows recursive locks.
+#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) \
+ base::ThreadCollisionWarner::ScopedRecursiveCheck sr_check_##obj(&obj)
+// Asserts the code is always executed in the same thread.
+#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) \
+ base::ThreadCollisionWarner::Check check_##obj(&obj)
+
+#else
+
+#define DFAKE_MUTEX(obj) typedef void InternalFakeMutexType##obj
+#define DFAKE_SCOPED_LOCK(obj) ((void)0)
+#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) ((void)0)
+#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) ((void)0)
+
+#endif
+
+namespace base {
+
+// The class ThreadCollisionWarner uses an Asserter to notify the collision
+// AsserterBase is the interfaces and DCheckAsserter is the default asserter
+// used. During the unit tests is used another class that doesn't "DCHECK"
+// in case of collision (check thread_collision_warner_unittests.cc)
+struct BASE_EXPORT AsserterBase {
+ virtual ~AsserterBase() = default;
+ virtual void warn() = 0;
+};
+
+struct BASE_EXPORT DCheckAsserter : public AsserterBase {
+ ~DCheckAsserter() override = default;
+ void warn() override;
+};
+
+class BASE_EXPORT ThreadCollisionWarner {
+ public:
+ // The parameter asserter is there only for test purpose
+ explicit ThreadCollisionWarner(AsserterBase* asserter = new DCheckAsserter())
+ : valid_thread_id_(0),
+ counter_(0),
+ asserter_(asserter) {}
+
+ ~ThreadCollisionWarner() {
+ delete asserter_;
+ }
+
+ // This class is meant to be used through the macro
+ // DFAKE_SCOPED_LOCK_THREAD_LOCKED
+ // it doesn't leave the critical section, as opposed to ScopedCheck,
+ // because the critical section being pinned is allowed to be used only
+ // from one thread
+ class BASE_EXPORT Check {
+ public:
+ explicit Check(ThreadCollisionWarner* warner)
+ : warner_(warner) {
+ warner_->EnterSelf();
+ }
+
+ ~Check() = default;
+
+ private:
+ ThreadCollisionWarner* warner_;
+
+ DISALLOW_COPY_AND_ASSIGN(Check);
+ };
+
+ // This class is meant to be used through the macro
+ // DFAKE_SCOPED_LOCK
+ class BASE_EXPORT ScopedCheck {
+ public:
+ explicit ScopedCheck(ThreadCollisionWarner* warner)
+ : warner_(warner) {
+ warner_->Enter();
+ }
+
+ ~ScopedCheck() {
+ warner_->Leave();
+ }
+
+ private:
+ ThreadCollisionWarner* warner_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedCheck);
+ };
+
+ // This class is meant to be used through the macro
+ // DFAKE_SCOPED_RECURSIVE_LOCK
+ class BASE_EXPORT ScopedRecursiveCheck {
+ public:
+ explicit ScopedRecursiveCheck(ThreadCollisionWarner* warner)
+ : warner_(warner) {
+ warner_->EnterSelf();
+ }
+
+ ~ScopedRecursiveCheck() {
+ warner_->Leave();
+ }
+
+ private:
+ ThreadCollisionWarner* warner_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedRecursiveCheck);
+ };
+
+ private:
+ // This method stores the current thread identifier and does a DCHECK
+ // if a another thread has already done it, it is safe if same thread
+ // calls this multiple time (recursion allowed).
+ void EnterSelf();
+
+ // Same as EnterSelf but recursion is not allowed.
+ void Enter();
+
+ // Removes the thread_id stored in order to allow other threads to
+ // call EnterSelf or Enter.
+ void Leave();
+
+ // This stores the thread id that is inside the critical section, if the
+ // value is 0 then no thread is inside.
+ volatile subtle::Atomic32 valid_thread_id_;
+
+ // Counter to trace how many time a critical section was "pinned"
+ // (when allowed) in order to unpin it when counter_ reaches 0.
+ volatile subtle::Atomic32 counter_;
+
+ // Here only for class unit tests purpose, during the test I need to not
+ // DCHECK but notify the collision with something else.
+ AsserterBase* asserter_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadCollisionWarner);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_COLLISION_WARNER_H_
diff --git a/base/threading/thread_collision_warner_unittest.cc b/base/threading/thread_collision_warner_unittest.cc
new file mode 100644
index 0000000..cd56768
--- /dev/null
+++ b/base/threading/thread_collision_warner_unittest.cc
@@ -0,0 +1,382 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_collision_warner.h"
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/simple_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// '' : local class member function does not have a body
+MSVC_PUSH_DISABLE_WARNING(4822)
+
+
+#if defined(NDEBUG)
+
+// Would cause a memory leak otherwise.
+#undef DFAKE_MUTEX
+#define DFAKE_MUTEX(obj) std::unique_ptr<base::AsserterBase> obj
+
+// In Release, we expect the AsserterBase::warn() to not happen.
+#define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_FALSE
+
+#else
+
+// In Debug, we expect the AsserterBase::warn() to happen.
+#define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_TRUE
+
+#endif
+
+
+namespace {
+
+// This is the asserter used with ThreadCollisionWarner instead of the default
+// DCheckAsserter. The method fail_state is used to know if a collision took
+// place.
+class AssertReporter : public base::AsserterBase {
+ public:
+ AssertReporter()
+ : failed_(false) {}
+
+ void warn() override { failed_ = true; }
+
+ ~AssertReporter() override = default;
+
+ bool fail_state() const { return failed_; }
+ void reset() { failed_ = false; }
+
+ private:
+ bool failed_;
+};
+
+} // namespace
+
+TEST(ThreadCollisionTest, BookCriticalSection) {
+ AssertReporter* local_reporter = new AssertReporter();
+
+ base::ThreadCollisionWarner warner(local_reporter);
+ EXPECT_FALSE(local_reporter->fail_state());
+
+ { // Pin section.
+ DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner);
+ EXPECT_FALSE(local_reporter->fail_state());
+ { // Pin section.
+ DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner);
+ EXPECT_FALSE(local_reporter->fail_state());
+ }
+ }
+}
+
+TEST(ThreadCollisionTest, ScopedRecursiveBookCriticalSection) {
+ AssertReporter* local_reporter = new AssertReporter();
+
+ base::ThreadCollisionWarner warner(local_reporter);
+ EXPECT_FALSE(local_reporter->fail_state());
+
+ { // Pin section.
+ DFAKE_SCOPED_RECURSIVE_LOCK(warner);
+ EXPECT_FALSE(local_reporter->fail_state());
+ { // Pin section again (allowed by DFAKE_SCOPED_RECURSIVE_LOCK)
+ DFAKE_SCOPED_RECURSIVE_LOCK(warner);
+ EXPECT_FALSE(local_reporter->fail_state());
+ } // Unpin section.
+ } // Unpin section.
+
+ // Check that section is not pinned
+ { // Pin section.
+ DFAKE_SCOPED_LOCK(warner);
+ EXPECT_FALSE(local_reporter->fail_state());
+ } // Unpin section.
+}
+
+TEST(ThreadCollisionTest, ScopedBookCriticalSection) {
+ AssertReporter* local_reporter = new AssertReporter();
+
+ base::ThreadCollisionWarner warner(local_reporter);
+ EXPECT_FALSE(local_reporter->fail_state());
+
+ { // Pin section.
+ DFAKE_SCOPED_LOCK(warner);
+ EXPECT_FALSE(local_reporter->fail_state());
+ } // Unpin section.
+
+ { // Pin section.
+ DFAKE_SCOPED_LOCK(warner);
+ EXPECT_FALSE(local_reporter->fail_state());
+ {
+ // Pin section again (not allowed by DFAKE_SCOPED_LOCK)
+ DFAKE_SCOPED_LOCK(warner);
+ EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state());
+ // Reset the status of warner for further tests.
+ local_reporter->reset();
+ } // Unpin section.
+ } // Unpin section.
+
+ {
+ // Pin section.
+ DFAKE_SCOPED_LOCK(warner);
+ EXPECT_FALSE(local_reporter->fail_state());
+ } // Unpin section.
+}
+
+TEST(ThreadCollisionTest, MTBookCriticalSectionTest) {
+ class NonThreadSafeQueue {
+ public:
+ explicit NonThreadSafeQueue(base::AsserterBase* asserter)
+ : push_pop_(asserter) {
+ }
+
+ void push(int value) {
+ DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_);
+ }
+
+ int pop() {
+ DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_);
+ return 0;
+ }
+
+ private:
+ DFAKE_MUTEX(push_pop_);
+
+ DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
+ };
+
+ class QueueUser : public base::DelegateSimpleThread::Delegate {
+ public:
+ explicit QueueUser(NonThreadSafeQueue* queue) : queue_(queue) {}
+
+ void Run() override {
+ queue_->push(0);
+ queue_->pop();
+ }
+
+ private:
+ NonThreadSafeQueue* queue_;
+ };
+
+ AssertReporter* local_reporter = new AssertReporter();
+
+ NonThreadSafeQueue queue(local_reporter);
+
+ QueueUser queue_user_a(&queue);
+ QueueUser queue_user_b(&queue);
+
+ base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
+ base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
+
+ thread_a.Start();
+ thread_b.Start();
+
+ thread_a.Join();
+ thread_b.Join();
+
+ EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state());
+}
+
+TEST(ThreadCollisionTest, MTScopedBookCriticalSectionTest) {
+ // Queue with a 5 seconds push execution time, hopefuly the two used threads
+ // in the test will enter the push at same time.
+ class NonThreadSafeQueue {
+ public:
+ explicit NonThreadSafeQueue(base::AsserterBase* asserter)
+ : push_pop_(asserter) {
+ }
+
+ void push(int value) {
+ DFAKE_SCOPED_LOCK(push_pop_);
+ base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(5));
+ }
+
+ int pop() {
+ DFAKE_SCOPED_LOCK(push_pop_);
+ return 0;
+ }
+
+ private:
+ DFAKE_MUTEX(push_pop_);
+
+ DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
+ };
+
+ class QueueUser : public base::DelegateSimpleThread::Delegate {
+ public:
+ explicit QueueUser(NonThreadSafeQueue* queue) : queue_(queue) {}
+
+ void Run() override {
+ queue_->push(0);
+ queue_->pop();
+ }
+
+ private:
+ NonThreadSafeQueue* queue_;
+ };
+
+ AssertReporter* local_reporter = new AssertReporter();
+
+ NonThreadSafeQueue queue(local_reporter);
+
+ QueueUser queue_user_a(&queue);
+ QueueUser queue_user_b(&queue);
+
+ base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
+ base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
+
+ thread_a.Start();
+ thread_b.Start();
+
+ thread_a.Join();
+ thread_b.Join();
+
+ EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state());
+}
+
+TEST(ThreadCollisionTest, MTSynchedScopedBookCriticalSectionTest) {
+ // Queue with a 2 seconds push execution time, hopefuly the two used threads
+ // in the test will enter the push at same time.
+ class NonThreadSafeQueue {
+ public:
+ explicit NonThreadSafeQueue(base::AsserterBase* asserter)
+ : push_pop_(asserter) {
+ }
+
+ void push(int value) {
+ DFAKE_SCOPED_LOCK(push_pop_);
+ base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(2));
+ }
+
+ int pop() {
+ DFAKE_SCOPED_LOCK(push_pop_);
+ return 0;
+ }
+
+ private:
+ DFAKE_MUTEX(push_pop_);
+
+ DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
+ };
+
+ // This time the QueueUser class protects the non thread safe queue with
+ // a lock.
+ class QueueUser : public base::DelegateSimpleThread::Delegate {
+ public:
+ QueueUser(NonThreadSafeQueue* queue, base::Lock* lock)
+ : queue_(queue), lock_(lock) {}
+
+ void Run() override {
+ {
+ base::AutoLock auto_lock(*lock_);
+ queue_->push(0);
+ }
+ {
+ base::AutoLock auto_lock(*lock_);
+ queue_->pop();
+ }
+ }
+ private:
+ NonThreadSafeQueue* queue_;
+ base::Lock* lock_;
+ };
+
+ AssertReporter* local_reporter = new AssertReporter();
+
+ NonThreadSafeQueue queue(local_reporter);
+
+ base::Lock lock;
+
+ QueueUser queue_user_a(&queue, &lock);
+ QueueUser queue_user_b(&queue, &lock);
+
+ base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
+ base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
+
+ thread_a.Start();
+ thread_b.Start();
+
+ thread_a.Join();
+ thread_b.Join();
+
+ EXPECT_FALSE(local_reporter->fail_state());
+}
+
+TEST(ThreadCollisionTest, MTSynchedScopedRecursiveBookCriticalSectionTest) {
+ // Queue with a 2 seconds push execution time, hopefuly the two used threads
+ // in the test will enter the push at same time.
+ class NonThreadSafeQueue {
+ public:
+ explicit NonThreadSafeQueue(base::AsserterBase* asserter)
+ : push_pop_(asserter) {
+ }
+
+ void push(int) {
+ DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
+ bar();
+ base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(2));
+ }
+
+ int pop() {
+ DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
+ return 0;
+ }
+
+ void bar() {
+ DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
+ }
+
+ private:
+ DFAKE_MUTEX(push_pop_);
+
+ DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
+ };
+
+ // This time the QueueUser class protects the non thread safe queue with
+ // a lock.
+ class QueueUser : public base::DelegateSimpleThread::Delegate {
+ public:
+ QueueUser(NonThreadSafeQueue* queue, base::Lock* lock)
+ : queue_(queue), lock_(lock) {}
+
+ void Run() override {
+ {
+ base::AutoLock auto_lock(*lock_);
+ queue_->push(0);
+ }
+ {
+ base::AutoLock auto_lock(*lock_);
+ queue_->bar();
+ }
+ {
+ base::AutoLock auto_lock(*lock_);
+ queue_->pop();
+ }
+ }
+ private:
+ NonThreadSafeQueue* queue_;
+ base::Lock* lock_;
+ };
+
+ AssertReporter* local_reporter = new AssertReporter();
+
+ NonThreadSafeQueue queue(local_reporter);
+
+ base::Lock lock;
+
+ QueueUser queue_user_a(&queue, &lock);
+ QueueUser queue_user_b(&queue, &lock);
+
+ base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
+ base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
+
+ thread_a.Start();
+ thread_b.Start();
+
+ thread_a.Join();
+ thread_b.Join();
+
+ EXPECT_FALSE(local_reporter->fail_state());
+}
diff --git a/base/threading/thread_id_name_manager.cc b/base/threading/thread_id_name_manager.cc
new file mode 100644
index 0000000..ca1979d
--- /dev/null
+++ b/base/threading/thread_id_name_manager.cc
@@ -0,0 +1,142 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_id_name_manager.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
+#include "base/strings/string_util.h"
+#include "base/threading/thread_local.h"
+#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
+
+namespace base {
+namespace {
+
+static const char kDefaultName[] = "";
+static std::string* g_default_name;
+
+ThreadLocalStorage::Slot& GetThreadNameTLS() {
+ static base::NoDestructor<base::ThreadLocalStorage::Slot> thread_name_tls;
+ return *thread_name_tls;
+}
+}
+
+ThreadIdNameManager::ThreadIdNameManager()
+ : main_process_name_(nullptr), main_process_id_(kInvalidThreadId) {
+ g_default_name = new std::string(kDefaultName);
+
+ AutoLock locked(lock_);
+ name_to_interned_name_[kDefaultName] = g_default_name;
+}
+
+ThreadIdNameManager::~ThreadIdNameManager() = default;
+
+ThreadIdNameManager* ThreadIdNameManager::GetInstance() {
+ return Singleton<ThreadIdNameManager,
+ LeakySingletonTraits<ThreadIdNameManager> >::get();
+}
+
+const char* ThreadIdNameManager::GetDefaultInternedString() {
+ return g_default_name->c_str();
+}
+
+void ThreadIdNameManager::RegisterThread(PlatformThreadHandle::Handle handle,
+ PlatformThreadId id) {
+ AutoLock locked(lock_);
+ thread_id_to_handle_[id] = handle;
+ thread_handle_to_interned_name_[handle] =
+ name_to_interned_name_[kDefaultName];
+}
+
+void ThreadIdNameManager::InstallSetNameCallback(SetNameCallback callback) {
+ AutoLock locked(lock_);
+ set_name_callback_ = std::move(callback);
+}
+
+void ThreadIdNameManager::SetName(const std::string& name) {
+ PlatformThreadId id = PlatformThread::CurrentId();
+ std::string* leaked_str = nullptr;
+ {
+ AutoLock locked(lock_);
+ NameToInternedNameMap::iterator iter = name_to_interned_name_.find(name);
+ if (iter != name_to_interned_name_.end()) {
+ leaked_str = iter->second;
+ } else {
+ leaked_str = new std::string(name);
+ name_to_interned_name_[name] = leaked_str;
+ }
+
+ ThreadIdToHandleMap::iterator id_to_handle_iter =
+ thread_id_to_handle_.find(id);
+
+ GetThreadNameTLS().Set(const_cast<char*>(leaked_str->c_str()));
+ if (set_name_callback_) {
+ set_name_callback_.Run(leaked_str->c_str());
+ }
+
+ // The main thread of a process will not be created as a Thread object which
+ // means there is no PlatformThreadHandler registered.
+ if (id_to_handle_iter == thread_id_to_handle_.end()) {
+ main_process_name_ = leaked_str;
+ main_process_id_ = id;
+ return;
+ }
+ thread_handle_to_interned_name_[id_to_handle_iter->second] = leaked_str;
+ }
+
+ // Add the leaked thread name to heap profiler context tracker. The name added
+ // is valid for the lifetime of the process. AllocationContextTracker cannot
+ // call GetName(which holds a lock) during the first allocation because it can
+ // cause a deadlock when the first allocation happens in the
+ // ThreadIdNameManager itself when holding the lock.
+ trace_event::AllocationContextTracker::SetCurrentThreadName(
+ leaked_str->c_str());
+}
+
+const char* ThreadIdNameManager::GetName(PlatformThreadId id) {
+ AutoLock locked(lock_);
+
+ if (id == main_process_id_)
+ return main_process_name_->c_str();
+
+ ThreadIdToHandleMap::iterator id_to_handle_iter =
+ thread_id_to_handle_.find(id);
+ if (id_to_handle_iter == thread_id_to_handle_.end())
+ return name_to_interned_name_[kDefaultName]->c_str();
+
+ ThreadHandleToInternedNameMap::iterator handle_to_name_iter =
+ thread_handle_to_interned_name_.find(id_to_handle_iter->second);
+ return handle_to_name_iter->second->c_str();
+}
+
+const char* ThreadIdNameManager::GetNameForCurrentThread() {
+ const char* name = reinterpret_cast<const char*>(GetThreadNameTLS().Get());
+ return name ? name : kDefaultName;
+}
+
+void ThreadIdNameManager::RemoveName(PlatformThreadHandle::Handle handle,
+ PlatformThreadId id) {
+ AutoLock locked(lock_);
+ ThreadHandleToInternedNameMap::iterator handle_to_name_iter =
+ thread_handle_to_interned_name_.find(handle);
+
+ DCHECK(handle_to_name_iter != thread_handle_to_interned_name_.end());
+ thread_handle_to_interned_name_.erase(handle_to_name_iter);
+
+ ThreadIdToHandleMap::iterator id_to_handle_iter =
+ thread_id_to_handle_.find(id);
+ DCHECK((id_to_handle_iter!= thread_id_to_handle_.end()));
+ // The given |id| may have been re-used by the system. Make sure the
+ // mapping points to the provided |handle| before removal.
+ if (id_to_handle_iter->second != handle)
+ return;
+
+ thread_id_to_handle_.erase(id_to_handle_iter);
+}
+
+} // namespace base
diff --git a/base/threading/thread_id_name_manager.h b/base/threading/thread_id_name_manager.h
new file mode 100644
index 0000000..f17dc1a
--- /dev/null
+++ b/base/threading/thread_id_name_manager.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_THREAD_ID_NAME_MANAGER_H_
+#define BASE_THREADING_THREAD_ID_NAME_MANAGER_H_
+
+#include <map>
+#include <string>
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+template <typename T>
+struct DefaultSingletonTraits;
+
+class BASE_EXPORT ThreadIdNameManager {
+ public:
+ static ThreadIdNameManager* GetInstance();
+
+ static const char* GetDefaultInternedString();
+
+ // Register the mapping between a thread |id| and |handle|.
+ void RegisterThread(PlatformThreadHandle::Handle handle, PlatformThreadId id);
+
+ // The callback is called on the thread, immediately after the name is set.
+ // |name| is a pointer to a C string that is guaranteed to remain valid for
+ // the duration of the process.
+ using SetNameCallback = base::RepeatingCallback<void(const char* name)>;
+ void InstallSetNameCallback(SetNameCallback callback);
+
+ // Set the name for the current thread.
+ void SetName(const std::string& name);
+
+ // Get the name for the given id.
+ const char* GetName(PlatformThreadId id);
+
+ // Unlike |GetName|, this method using TLS and avoids touching |lock_|.
+ const char* GetNameForCurrentThread();
+
+ // Remove the name for the given id.
+ void RemoveName(PlatformThreadHandle::Handle handle, PlatformThreadId id);
+
+ private:
+ friend struct DefaultSingletonTraits<ThreadIdNameManager>;
+
+ typedef std::map<PlatformThreadId, PlatformThreadHandle::Handle>
+ ThreadIdToHandleMap;
+ typedef std::map<PlatformThreadHandle::Handle, std::string*>
+ ThreadHandleToInternedNameMap;
+ typedef std::map<std::string, std::string*> NameToInternedNameMap;
+
+ ThreadIdNameManager();
+ ~ThreadIdNameManager();
+
+ // lock_ protects the name_to_interned_name_, thread_id_to_handle_ and
+ // thread_handle_to_interned_name_ maps.
+ Lock lock_;
+
+ NameToInternedNameMap name_to_interned_name_;
+ ThreadIdToHandleMap thread_id_to_handle_;
+ ThreadHandleToInternedNameMap thread_handle_to_interned_name_;
+
+ // Treat the main process specially as there is no PlatformThreadHandle.
+ std::string* main_process_name_;
+ PlatformThreadId main_process_id_;
+
+ SetNameCallback set_name_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadIdNameManager);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_ID_NAME_MANAGER_H_
diff --git a/base/threading/thread_id_name_manager_unittest.cc b/base/threading/thread_id_name_manager_unittest.cc
new file mode 100644
index 0000000..350dc0f
--- /dev/null
+++ b/base/threading/thread_id_name_manager_unittest.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_id_name_manager.h"
+
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+typedef PlatformTest ThreadIdNameManagerTest;
+
+namespace {
+
+const char kAThread[] = "a thread";
+const char kBThread[] = "b thread";
+
+TEST_F(ThreadIdNameManagerTest, AddThreads) {
+ base::ThreadIdNameManager* manager = base::ThreadIdNameManager::GetInstance();
+ base::Thread thread_a(kAThread);
+ base::Thread thread_b(kBThread);
+
+ thread_a.StartAndWaitForTesting();
+ thread_b.StartAndWaitForTesting();
+
+ EXPECT_STREQ(kAThread, manager->GetName(thread_a.GetThreadId()));
+ EXPECT_STREQ(kBThread, manager->GetName(thread_b.GetThreadId()));
+
+ thread_b.Stop();
+ thread_a.Stop();
+}
+
+TEST_F(ThreadIdNameManagerTest, RemoveThreads) {
+ base::ThreadIdNameManager* manager = base::ThreadIdNameManager::GetInstance();
+ base::Thread thread_a(kAThread);
+
+ thread_a.StartAndWaitForTesting();
+ {
+ base::Thread thread_b(kBThread);
+ thread_b.StartAndWaitForTesting();
+ thread_b.Stop();
+ }
+ EXPECT_STREQ(kAThread, manager->GetName(thread_a.GetThreadId()));
+
+ thread_a.Stop();
+ EXPECT_STREQ("", manager->GetName(thread_a.GetThreadId()));
+}
+
+TEST_F(ThreadIdNameManagerTest, RestartThread) {
+ base::ThreadIdNameManager* manager = base::ThreadIdNameManager::GetInstance();
+ base::Thread thread_a(kAThread);
+
+ thread_a.StartAndWaitForTesting();
+ base::PlatformThreadId a_id = thread_a.GetThreadId();
+ EXPECT_STREQ(kAThread, manager->GetName(a_id));
+ thread_a.Stop();
+
+ thread_a.StartAndWaitForTesting();
+ EXPECT_STREQ("", manager->GetName(a_id));
+ EXPECT_STREQ(kAThread, manager->GetName(thread_a.GetThreadId()));
+ thread_a.Stop();
+}
+
+TEST_F(ThreadIdNameManagerTest, ThreadNameInterning) {
+ base::ThreadIdNameManager* manager = base::ThreadIdNameManager::GetInstance();
+
+ base::PlatformThreadId a_id = base::PlatformThread::CurrentId();
+ base::PlatformThread::SetName("First Name");
+ std::string version = manager->GetName(a_id);
+
+ base::PlatformThread::SetName("New name");
+ EXPECT_NE(version, manager->GetName(a_id));
+ base::PlatformThread::SetName("");
+}
+
+TEST_F(ThreadIdNameManagerTest, ResettingNameKeepsCorrectInternedValue) {
+ base::ThreadIdNameManager* manager = base::ThreadIdNameManager::GetInstance();
+
+ base::PlatformThreadId a_id = base::PlatformThread::CurrentId();
+ base::PlatformThread::SetName("Test Name");
+ std::string version = manager->GetName(a_id);
+
+ base::PlatformThread::SetName("New name");
+ EXPECT_NE(version, manager->GetName(a_id));
+
+ base::PlatformThread::SetName("Test Name");
+ EXPECT_EQ(version, manager->GetName(a_id));
+
+ base::PlatformThread::SetName("");
+}
+
+} // namespace
diff --git a/base/threading/thread_local.h b/base/threading/thread_local.h
new file mode 100644
index 0000000..cad9add
--- /dev/null
+++ b/base/threading/thread_local.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// WARNING: Thread local storage is a bit tricky to get right. Please make sure
+// that this is really the proper solution for what you're trying to achieve.
+// Don't prematurely optimize, most likely you can just use a Lock.
+//
+// These classes implement a wrapper around ThreadLocalStorage::Slot. On
+// construction, they will allocate a TLS slot, and free the TLS slot on
+// destruction. No memory management (creation or destruction) is handled. This
+// means for uses of ThreadLocalPointer, you must correctly manage the memory
+// yourself, these classes will not destroy the pointer for you. There are no
+// at-thread-exit actions taken by these classes.
+//
+// ThreadLocalPointer<Type> wraps a Type*. It performs no creation or
+// destruction, so memory management must be handled elsewhere. The first call
+// to Get() on a thread will return NULL. You can update the pointer with a call
+// to Set().
+//
+// ThreadLocalBoolean wraps a bool. It will default to false if it has never
+// been set otherwise with Set().
+//
+// Thread Safety: An instance of ThreadLocalStorage is completely thread safe
+// once it has been created. If you want to dynamically create an instance, you
+// must of course properly deal with safety and race conditions. This means a
+// function-level static initializer is generally inappropiate.
+//
+// In Android, the system TLS is limited.
+//
+// Example usage:
+// // My class is logically attached to a single thread. We cache a pointer
+// // on the thread it was created on, so we can implement current().
+// MyClass::MyClass() {
+// DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() == NULL);
+// Singleton<ThreadLocalPointer<MyClass> >::get()->Set(this);
+// }
+//
+// MyClass::~MyClass() {
+// DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() != NULL);
+// Singleton<ThreadLocalPointer<MyClass> >::get()->Set(NULL);
+// }
+//
+// // Return the current MyClass associated with the calling thread, can be
+// // NULL if there isn't a MyClass associated.
+// MyClass* MyClass::current() {
+// return Singleton<ThreadLocalPointer<MyClass> >::get()->Get();
+// }
+
+#ifndef BASE_THREADING_THREAD_LOCAL_H_
+#define BASE_THREADING_THREAD_LOCAL_H_
+
+#include "base/macros.h"
+#include "base/threading/thread_local_storage.h"
+
+namespace base {
+
+template <typename Type>
+class ThreadLocalPointer {
+ public:
+ ThreadLocalPointer() = default;
+ ~ThreadLocalPointer() = default;
+
+ Type* Get() {
+ return static_cast<Type*>(slot_.Get());
+ }
+
+ void Set(Type* ptr) {
+ slot_.Set(const_cast<void*>(static_cast<const void*>(ptr)));
+ }
+
+ private:
+ ThreadLocalStorage::Slot slot_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer<Type>);
+};
+
+class ThreadLocalBoolean {
+ public:
+ ThreadLocalBoolean() = default;
+ ~ThreadLocalBoolean() = default;
+
+ bool Get() {
+ return tlp_.Get() != nullptr;
+ }
+
+ void Set(bool val) {
+ tlp_.Set(val ? this : nullptr);
+ }
+
+ private:
+ ThreadLocalPointer<void> tlp_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadLocalBoolean);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_LOCAL_H_
diff --git a/base/threading/thread_local_storage.cc b/base/threading/thread_local_storage.cc
new file mode 100644
index 0000000..21fd323
--- /dev/null
+++ b/base/threading/thread_local_storage.cc
@@ -0,0 +1,397 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_local_storage.h"
+
+#include "base/atomicops.h"
+#include "base/logging.h"
+#include "base/synchronization/lock.h"
+#include "build/build_config.h"
+
+using base::internal::PlatformThreadLocalStorage;
+
+// Chrome Thread Local Storage (TLS)
+//
+// This TLS system allows Chrome to use a single OS level TLS slot process-wide,
+// and allows us to control the slot limits instead of being at the mercy of the
+// platform. To do this, Chrome TLS replicates an array commonly found in the OS
+// thread metadata.
+//
+// Overview:
+//
+// OS TLS Slots Per-Thread Per-Process Global
+// ...
+// [] Chrome TLS Array Chrome TLS Metadata
+// [] ----------> [][][][][ ][][][][] [][][][][ ][][][][]
+// [] | |
+// ... V V
+// Metadata Version Slot Information
+// Your Data!
+//
+// Using a single OS TLS slot, Chrome TLS allocates an array on demand for the
+// lifetime of each thread that requests Chrome TLS data. Each per-thread TLS
+// array matches the length of the per-process global metadata array.
+//
+// A per-process global TLS metadata array tracks information about each item in
+// the per-thread array:
+// * Status: Tracks if the slot is allocated or free to assign.
+// * Destructor: An optional destructor to call on thread destruction for that
+// specific slot.
+// * Version: Tracks the current version of the TLS slot. Each TLS slot
+// allocation is associated with a unique version number.
+//
+// Most OS TLS APIs guarantee that a newly allocated TLS slot is
+// initialized to 0 for all threads. The Chrome TLS system provides
+// this guarantee by tracking the version for each TLS slot here
+// on each per-thread Chrome TLS array entry. Threads that access
+// a slot with a mismatched version will receive 0 as their value.
+// The metadata version is incremented when the client frees a
+// slot. The per-thread metadata version is updated when a client
+// writes to the slot. This scheme allows for constant time
+// invalidation and avoids the need to iterate through each Chrome
+// TLS array to mark the slot as zero.
+//
+// Just like an OS TLS API, clients of the Chrome TLS are responsible for
+// managing any necessary lifetime of the data in their slots. The only
+// convenience provided is automatic destruction when a thread ends. If a client
+// frees a slot, that client is responsible for destroying the data in the slot.
+
+namespace {
+// In order to make TLS destructors work, we need to keep around a function
+// pointer to the destructor for each slot. We keep this array of pointers in a
+// global (static) array.
+// We use the single OS-level TLS slot (giving us one pointer per thread) to
+// hold a pointer to a per-thread array (table) of slots that we allocate to
+// Chromium consumers.
+
+// g_native_tls_key is the one native TLS that we use. It stores our table.
+base::subtle::Atomic32 g_native_tls_key =
+ PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES;
+
+// The OS TLS slot has three states:
+// * kUninitialized: Any call to Slot::Get()/Set() will create the base
+// per-thread TLS state. On POSIX, kUninitialized must be 0.
+// * [Memory Address]: Raw pointer to the base per-thread TLS state.
+// * kDestroyed: The base per-thread TLS state has been freed.
+//
+// Final States:
+// * Windows: kDestroyed. Windows does not iterate through the OS TLS to clean
+// up the values.
+// * POSIX: kUninitialized. POSIX iterates through TLS until all slots contain
+// nullptr.
+//
+// More details on this design:
+// We need some type of thread-local state to indicate that the TLS system has
+// been destroyed. To do so, we leverage the multi-pass nature of destruction
+// of pthread_key.
+//
+// a) After destruction of TLS system, we set the pthread_key to a sentinel
+// kDestroyed.
+// b) All calls to Slot::Get() DCHECK that the state is not kDestroyed, and
+// any system which might potentially invoke Slot::Get() after destruction
+// of TLS must check ThreadLocalStorage::ThreadIsBeingDestroyed().
+// c) After a full pass of the pthread_keys, on the next invocation of
+// ConstructTlsVector(), we'll then set the key to nullptr.
+// d) At this stage, the TLS system is back in its uninitialized state.
+// e) If in the second pass of destruction of pthread_keys something were to
+// re-initialize TLS [this should never happen! Since the only code which
+// uses Chrome TLS is Chrome controlled, we should really be striving for
+// single-pass destruction], then TLS will be re-initialized and then go
+// through the 2-pass destruction system again. Everything should just
+// work (TM).
+
+// The consumers of kUninitialized and kDestroyed expect void*, since that's
+// what the API exposes on both POSIX and Windows.
+void* const kUninitialized = nullptr;
+
+// A sentinel value to indicate that the TLS system has been destroyed.
+void* const kDestroyed = reinterpret_cast<void*>(1);
+
+// The maximum number of slots in our thread local storage stack.
+constexpr int kThreadLocalStorageSize = 256;
+
+enum TlsStatus {
+ FREE,
+ IN_USE,
+};
+
+struct TlsMetadata {
+ TlsStatus status;
+ base::ThreadLocalStorage::TLSDestructorFunc destructor;
+ uint32_t version;
+};
+
+struct TlsVectorEntry {
+ void* data;
+ uint32_t version;
+};
+
+// This lock isn't needed until after we've constructed the per-thread TLS
+// vector, so it's safe to use.
+base::Lock* GetTLSMetadataLock() {
+ static auto* lock = new base::Lock();
+ return lock;
+}
+TlsMetadata g_tls_metadata[kThreadLocalStorageSize];
+size_t g_last_assigned_slot = 0;
+
+// The maximum number of times to try to clear slots by calling destructors.
+// Use pthread naming convention for clarity.
+constexpr int kMaxDestructorIterations = kThreadLocalStorageSize;
+
+// This function is called to initialize our entire Chromium TLS system.
+// It may be called very early, and we need to complete most all of the setup
+// (initialization) before calling *any* memory allocator functions, which may
+// recursively depend on this initialization.
+// As a result, we use Atomics, and avoid anything (like a singleton) that might
+// require memory allocations.
+TlsVectorEntry* ConstructTlsVector() {
+ PlatformThreadLocalStorage::TLSKey key =
+ base::subtle::NoBarrier_Load(&g_native_tls_key);
+ if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) {
+ CHECK(PlatformThreadLocalStorage::AllocTLS(&key));
+
+ // The TLS_KEY_OUT_OF_INDEXES is used to find out whether the key is set or
+ // not in NoBarrier_CompareAndSwap, but Posix doesn't have invalid key, we
+ // define an almost impossible value be it.
+ // If we really get TLS_KEY_OUT_OF_INDEXES as value of key, just alloc
+ // another TLS slot.
+ if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) {
+ PlatformThreadLocalStorage::TLSKey tmp = key;
+ CHECK(PlatformThreadLocalStorage::AllocTLS(&key) &&
+ key != PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES);
+ PlatformThreadLocalStorage::FreeTLS(tmp);
+ }
+ // Atomically test-and-set the tls_key. If the key is
+ // TLS_KEY_OUT_OF_INDEXES, go ahead and set it. Otherwise, do nothing, as
+ // another thread already did our dirty work.
+ if (PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES !=
+ static_cast<PlatformThreadLocalStorage::TLSKey>(
+ base::subtle::NoBarrier_CompareAndSwap(
+ &g_native_tls_key,
+ PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key))) {
+ // We've been shortcut. Another thread replaced g_native_tls_key first so
+ // we need to destroy our index and use the one the other thread got
+ // first.
+ PlatformThreadLocalStorage::FreeTLS(key);
+ key = base::subtle::NoBarrier_Load(&g_native_tls_key);
+ }
+ }
+ CHECK_EQ(PlatformThreadLocalStorage::GetTLSValue(key), kUninitialized);
+
+ // Some allocators, such as TCMalloc, make use of thread local storage. As a
+ // result, any attempt to call new (or malloc) will lazily cause such a system
+ // to initialize, which will include registering for a TLS key. If we are not
+ // careful here, then that request to create a key will call new back, and
+ // we'll have an infinite loop. We avoid that as follows: Use a stack
+ // allocated vector, so that we don't have dependence on our allocator until
+ // our service is in place. (i.e., don't even call new until after we're
+ // setup)
+ TlsVectorEntry stack_allocated_tls_data[kThreadLocalStorageSize];
+ memset(stack_allocated_tls_data, 0, sizeof(stack_allocated_tls_data));
+ // Ensure that any rentrant calls change the temp version.
+ PlatformThreadLocalStorage::SetTLSValue(key, stack_allocated_tls_data);
+
+ // Allocate an array to store our data.
+ TlsVectorEntry* tls_data = new TlsVectorEntry[kThreadLocalStorageSize];
+ memcpy(tls_data, stack_allocated_tls_data, sizeof(stack_allocated_tls_data));
+ PlatformThreadLocalStorage::SetTLSValue(key, tls_data);
+ return tls_data;
+}
+
+void OnThreadExitInternal(TlsVectorEntry* tls_data) {
+ // This branch is for POSIX, where this function is called twice. The first
+ // pass calls dtors and sets state to kDestroyed. The second pass sets
+ // kDestroyed to kUninitialized.
+ if (tls_data == kDestroyed) {
+ PlatformThreadLocalStorage::TLSKey key =
+ base::subtle::NoBarrier_Load(&g_native_tls_key);
+ PlatformThreadLocalStorage::SetTLSValue(key, kUninitialized);
+ return;
+ }
+
+ DCHECK(tls_data);
+ // Some allocators, such as TCMalloc, use TLS. As a result, when a thread
+ // terminates, one of the destructor calls we make may be to shut down an
+ // allocator. We have to be careful that after we've shutdown all of the known
+ // destructors (perchance including an allocator), that we don't call the
+ // allocator and cause it to resurrect itself (with no possibly destructor
+ // call to follow). We handle this problem as follows: Switch to using a stack
+ // allocated vector, so that we don't have dependence on our allocator after
+ // we have called all g_tls_metadata destructors. (i.e., don't even call
+ // delete[] after we're done with destructors.)
+ TlsVectorEntry stack_allocated_tls_data[kThreadLocalStorageSize];
+ memcpy(stack_allocated_tls_data, tls_data, sizeof(stack_allocated_tls_data));
+ // Ensure that any re-entrant calls change the temp version.
+ PlatformThreadLocalStorage::TLSKey key =
+ base::subtle::NoBarrier_Load(&g_native_tls_key);
+ PlatformThreadLocalStorage::SetTLSValue(key, stack_allocated_tls_data);
+ delete[] tls_data; // Our last dependence on an allocator.
+
+ // Snapshot the TLS Metadata so we don't have to lock on every access.
+ TlsMetadata tls_metadata[kThreadLocalStorageSize];
+ {
+ base::AutoLock auto_lock(*GetTLSMetadataLock());
+ memcpy(tls_metadata, g_tls_metadata, sizeof(g_tls_metadata));
+ }
+
+ int remaining_attempts = kMaxDestructorIterations;
+ bool need_to_scan_destructors = true;
+ while (need_to_scan_destructors) {
+ need_to_scan_destructors = false;
+ // Try to destroy the first-created-slot (which is slot 1) in our last
+ // destructor call. That user was able to function, and define a slot with
+ // no other services running, so perhaps it is a basic service (like an
+ // allocator) and should also be destroyed last. If we get the order wrong,
+ // then we'll iterate several more times, so it is really not that critical
+ // (but it might help).
+ for (int slot = 0; slot < kThreadLocalStorageSize ; ++slot) {
+ void* tls_value = stack_allocated_tls_data[slot].data;
+ if (!tls_value || tls_metadata[slot].status == TlsStatus::FREE ||
+ stack_allocated_tls_data[slot].version != tls_metadata[slot].version)
+ continue;
+
+ base::ThreadLocalStorage::TLSDestructorFunc destructor =
+ tls_metadata[slot].destructor;
+ if (!destructor)
+ continue;
+ stack_allocated_tls_data[slot].data = nullptr; // pre-clear the slot.
+ destructor(tls_value);
+ // Any destructor might have called a different service, which then set a
+ // different slot to a non-null value. Hence we need to check the whole
+ // vector again. This is a pthread standard.
+ need_to_scan_destructors = true;
+ }
+ if (--remaining_attempts <= 0) {
+ NOTREACHED(); // Destructors might not have been called.
+ break;
+ }
+ }
+
+ // Remove our stack allocated vector.
+ PlatformThreadLocalStorage::SetTLSValue(key, kDestroyed);
+}
+
+} // namespace
+
+namespace base {
+
+namespace internal {
+
+#if defined(OS_WIN)
+void PlatformThreadLocalStorage::OnThreadExit() {
+ PlatformThreadLocalStorage::TLSKey key =
+ base::subtle::NoBarrier_Load(&g_native_tls_key);
+ if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES)
+ return;
+ void *tls_data = GetTLSValue(key);
+
+ // On Windows, thread destruction callbacks are only invoked once per module,
+ // so there should be no way that this could be invoked twice.
+ DCHECK_NE(tls_data, kDestroyed);
+
+ // Maybe we have never initialized TLS for this thread.
+ if (tls_data == kUninitialized)
+ return;
+ OnThreadExitInternal(static_cast<TlsVectorEntry*>(tls_data));
+}
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+void PlatformThreadLocalStorage::OnThreadExit(void* value) {
+ OnThreadExitInternal(static_cast<TlsVectorEntry*>(value));
+}
+#endif // defined(OS_WIN)
+
+} // namespace internal
+
+bool ThreadLocalStorage::HasBeenDestroyed() {
+ PlatformThreadLocalStorage::TLSKey key =
+ base::subtle::NoBarrier_Load(&g_native_tls_key);
+ if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES)
+ return false;
+ return PlatformThreadLocalStorage::GetTLSValue(key) == kDestroyed;
+}
+
+void ThreadLocalStorage::Slot::Initialize(TLSDestructorFunc destructor) {
+ PlatformThreadLocalStorage::TLSKey key =
+ base::subtle::NoBarrier_Load(&g_native_tls_key);
+ if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES ||
+ PlatformThreadLocalStorage::GetTLSValue(key) == kUninitialized) {
+ ConstructTlsVector();
+ }
+
+ // Grab a new slot.
+ {
+ base::AutoLock auto_lock(*GetTLSMetadataLock());
+ for (int i = 0; i < kThreadLocalStorageSize; ++i) {
+ // Tracking the last assigned slot is an attempt to find the next
+ // available slot within one iteration. Under normal usage, slots remain
+ // in use for the lifetime of the process (otherwise before we reclaimed
+ // slots, we would have run out of slots). This makes it highly likely the
+ // next slot is going to be a free slot.
+ size_t slot_candidate =
+ (g_last_assigned_slot + 1 + i) % kThreadLocalStorageSize;
+ if (g_tls_metadata[slot_candidate].status == TlsStatus::FREE) {
+ g_tls_metadata[slot_candidate].status = TlsStatus::IN_USE;
+ g_tls_metadata[slot_candidate].destructor = destructor;
+ g_last_assigned_slot = slot_candidate;
+ DCHECK_EQ(kInvalidSlotValue, slot_);
+ slot_ = slot_candidate;
+ version_ = g_tls_metadata[slot_candidate].version;
+ break;
+ }
+ }
+ }
+ CHECK_NE(slot_, kInvalidSlotValue);
+ CHECK_LT(slot_, kThreadLocalStorageSize);
+}
+
+void ThreadLocalStorage::Slot::Free() {
+ DCHECK_NE(slot_, kInvalidSlotValue);
+ DCHECK_LT(slot_, kThreadLocalStorageSize);
+ {
+ base::AutoLock auto_lock(*GetTLSMetadataLock());
+ g_tls_metadata[slot_].status = TlsStatus::FREE;
+ g_tls_metadata[slot_].destructor = nullptr;
+ ++(g_tls_metadata[slot_].version);
+ }
+ slot_ = kInvalidSlotValue;
+}
+
+void* ThreadLocalStorage::Slot::Get() const {
+ TlsVectorEntry* tls_data = static_cast<TlsVectorEntry*>(
+ PlatformThreadLocalStorage::GetTLSValue(
+ base::subtle::NoBarrier_Load(&g_native_tls_key)));
+ DCHECK_NE(tls_data, kDestroyed);
+ if (!tls_data)
+ return nullptr;
+ DCHECK_NE(slot_, kInvalidSlotValue);
+ DCHECK_LT(slot_, kThreadLocalStorageSize);
+ // Version mismatches means this slot was previously freed.
+ if (tls_data[slot_].version != version_)
+ return nullptr;
+ return tls_data[slot_].data;
+}
+
+void ThreadLocalStorage::Slot::Set(void* value) {
+ TlsVectorEntry* tls_data = static_cast<TlsVectorEntry*>(
+ PlatformThreadLocalStorage::GetTLSValue(
+ base::subtle::NoBarrier_Load(&g_native_tls_key)));
+ DCHECK_NE(tls_data, kDestroyed);
+ if (!tls_data)
+ tls_data = ConstructTlsVector();
+ DCHECK_NE(slot_, kInvalidSlotValue);
+ DCHECK_LT(slot_, kThreadLocalStorageSize);
+ tls_data[slot_].data = value;
+ tls_data[slot_].version = version_;
+}
+
+ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) {
+ Initialize(destructor);
+}
+
+ThreadLocalStorage::Slot::~Slot() {
+ Free();
+}
+
+} // namespace base
diff --git a/base/threading/thread_local_storage.h b/base/threading/thread_local_storage.h
new file mode 100644
index 0000000..f84ac33
--- /dev/null
+++ b/base/threading/thread_local_storage.h
@@ -0,0 +1,167 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_THREAD_LOCAL_STORAGE_H_
+#define BASE_THREADING_THREAD_LOCAL_STORAGE_H_
+
+#include <stdint.h>
+
+#include "base/atomicops.h"
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_types.h"
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <pthread.h>
+#endif
+
+namespace heap_profiling {
+class ScopedAllowLogging;
+} // namespace heap_profiling
+
+namespace base {
+
+class SamplingHeapProfiler;
+
+namespace trace_event {
+class MallocDumpProvider;
+} // namespace trace_event
+
+namespace internal {
+
+class ThreadLocalStorageTestInternal;
+
+// WARNING: You should *NOT* use this class directly.
+// PlatformThreadLocalStorage is a low-level abstraction of the OS's TLS
+// interface. Instead, you should use one of the following:
+// * ThreadLocalBoolean (from thread_local.h) for booleans.
+// * ThreadLocalPointer (from thread_local.h) for pointers.
+// * ThreadLocalStorage::StaticSlot/Slot for more direct control of the slot.
+class BASE_EXPORT PlatformThreadLocalStorage {
+ public:
+
+#if defined(OS_WIN)
+ typedef unsigned long TLSKey;
+ enum : unsigned { TLS_KEY_OUT_OF_INDEXES = TLS_OUT_OF_INDEXES };
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ typedef pthread_key_t TLSKey;
+ // The following is a "reserved key" which is used in our generic Chromium
+ // ThreadLocalStorage implementation. We expect that an OS will not return
+ // such a key, but if it is returned (i.e., the OS tries to allocate it) we
+ // will just request another key.
+ enum { TLS_KEY_OUT_OF_INDEXES = 0x7FFFFFFF };
+#endif
+
+ // The following methods need to be supported on each OS platform, so that
+ // the Chromium ThreadLocalStore functionality can be constructed.
+ // Chromium will use these methods to acquire a single OS slot, and then use
+ // that to support a much larger number of Chromium slots (independent of the
+ // OS restrictions).
+ // The following returns true if it successfully is able to return an OS
+ // key in |key|.
+ static bool AllocTLS(TLSKey* key);
+ // Note: FreeTLS() doesn't have to be called, it is fine with this leak, OS
+ // might not reuse released slot, you might just reset the TLS value with
+ // SetTLSValue().
+ static void FreeTLS(TLSKey key);
+ static void SetTLSValue(TLSKey key, void* value);
+ static void* GetTLSValue(TLSKey key) {
+#if defined(OS_WIN)
+ return TlsGetValue(key);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ return pthread_getspecific(key);
+#endif
+ }
+
+ // Each platform (OS implementation) is required to call this method on each
+ // terminating thread when the thread is about to terminate. This method
+ // will then call all registered destructors for slots in Chromium
+ // ThreadLocalStorage, until there are no slot values remaining as having
+ // been set on this thread.
+ // Destructors may end up being called multiple times on a terminating
+ // thread, as other destructors may re-set slots that were previously
+ // destroyed.
+#if defined(OS_WIN)
+ // Since Windows which doesn't support TLS destructor, the implementation
+ // should use GetTLSValue() to retrieve the value of TLS slot.
+ static void OnThreadExit();
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ // |Value| is the data stored in TLS slot, The implementation can't use
+ // GetTLSValue() to retrieve the value of slot as it has already been reset
+ // in Posix.
+ static void OnThreadExit(void* value);
+#endif
+};
+
+} // namespace internal
+
+// Wrapper for thread local storage. This class doesn't do much except provide
+// an API for portability.
+class BASE_EXPORT ThreadLocalStorage {
+ public:
+ // Prototype for the TLS destructor function, which can be optionally used to
+ // cleanup thread local storage on thread exit. 'value' is the data that is
+ // stored in thread local storage.
+ typedef void (*TLSDestructorFunc)(void* value);
+
+ // A key representing one value stored in TLS. Use as a class member or a
+ // local variable. If you need a static storage duration variable, use the
+ // following pattern with a NoDestructor<Slot>:
+ // void MyDestructorFunc(void* value);
+ // ThreadLocalStorage::Slot& ImportantContentTLS() {
+ // static NoDestructor<ThreadLocalStorage::Slot> important_content_tls(
+ // &MyDestructorFunc);
+ // return *important_content_tls;
+ // }
+ class BASE_EXPORT Slot final {
+ public:
+ // |destructor| is a pointer to a function to perform per-thread cleanup of
+ // this object. If set to nullptr, no cleanup is done for this TLS slot.
+ explicit Slot(TLSDestructorFunc destructor = nullptr);
+ // If a destructor was set for this slot, removes the destructor so that
+ // remaining threads exiting will not free data.
+ ~Slot();
+
+ // Get the thread-local value stored in slot 'slot'.
+ // Values are guaranteed to initially be zero.
+ void* Get() const;
+
+ // Set the thread-local value stored in slot 'slot' to
+ // value 'value'.
+ void Set(void* value);
+
+ private:
+ void Initialize(TLSDestructorFunc destructor);
+ void Free();
+
+ static constexpr int kInvalidSlotValue = -1;
+ int slot_ = kInvalidSlotValue;
+ uint32_t version_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(Slot);
+ };
+
+ private:
+ // In most cases, most callers should not need access to HasBeenDestroyed().
+ // If you are working in code that runs during thread destruction, contact the
+ // base OWNERs for advice and then make a friend request.
+ //
+ // Returns |true| if Chrome's implementation of TLS has been destroyed during
+ // thread destruction. Attempting to call Slot::Get() during destruction is
+ // disallowed and will hit a DCHECK. Any code that relies on TLS during thread
+ // destruction must first check this method before calling Slot::Get().
+ friend class base::SamplingHeapProfiler;
+ friend class base::internal::ThreadLocalStorageTestInternal;
+ friend class base::trace_event::MallocDumpProvider;
+ friend class heap_profiling::ScopedAllowLogging;
+ static bool HasBeenDestroyed();
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorage);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_LOCAL_STORAGE_H_
diff --git a/base/threading/thread_local_storage_posix.cc b/base/threading/thread_local_storage_posix.cc
new file mode 100644
index 0000000..89edeee
--- /dev/null
+++ b/base/threading/thread_local_storage_posix.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_local_storage.h"
+
+#include "base/logging.h"
+
+namespace base {
+
+namespace internal {
+
+bool PlatformThreadLocalStorage::AllocTLS(TLSKey* key) {
+ return !pthread_key_create(key,
+ base::internal::PlatformThreadLocalStorage::OnThreadExit);
+}
+
+void PlatformThreadLocalStorage::FreeTLS(TLSKey key) {
+ int ret = pthread_key_delete(key);
+ DCHECK_EQ(ret, 0);
+}
+
+void PlatformThreadLocalStorage::SetTLSValue(TLSKey key, void* value) {
+ int ret = pthread_setspecific(key, value);
+ DCHECK_EQ(ret, 0);
+}
+
+} // namespace internal
+
+} // namespace base
diff --git a/base/threading/thread_local_storage_unittest.cc b/base/threading/thread_local_storage_unittest.cc
new file mode 100644
index 0000000..9062ff0
--- /dev/null
+++ b/base/threading/thread_local_storage_unittest.cc
@@ -0,0 +1,278 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_local_storage.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include <process.h>
+#endif
+
+#include "base/macros.h"
+#include "base/no_destructor.h"
+#include "base/threading/simple_thread.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+// Ignore warnings about ptr->int conversions that we use when
+// storing ints into ThreadLocalStorage.
+#pragma warning(disable : 4311 4312)
+#endif
+
+namespace base {
+
+#if defined(OS_POSIX)
+
+namespace internal {
+
+// This class is friended by ThreadLocalStorage.
+class ThreadLocalStorageTestInternal {
+ public:
+ static bool HasBeenDestroyed() {
+ return ThreadLocalStorage::HasBeenDestroyed();
+ }
+};
+
+} // namespace internal
+
+#endif // defined(OS_POSIX)
+
+namespace {
+
+const int kInitialTlsValue = 0x5555;
+const int kFinalTlsValue = 0x7777;
+// How many times must a destructor be called before we really are done.
+const int kNumberDestructorCallRepetitions = 3;
+
+void ThreadLocalStorageCleanup(void* value);
+
+ThreadLocalStorage::Slot& TLSSlot() {
+ static NoDestructor<ThreadLocalStorage::Slot> slot(
+ &ThreadLocalStorageCleanup);
+ return *slot;
+}
+
+class ThreadLocalStorageRunner : public DelegateSimpleThread::Delegate {
+ public:
+ explicit ThreadLocalStorageRunner(int* tls_value_ptr)
+ : tls_value_ptr_(tls_value_ptr) {}
+
+ ~ThreadLocalStorageRunner() override = default;
+
+ void Run() override {
+ *tls_value_ptr_ = kInitialTlsValue;
+ TLSSlot().Set(tls_value_ptr_);
+
+ int* ptr = static_cast<int*>(TLSSlot().Get());
+ EXPECT_EQ(ptr, tls_value_ptr_);
+ EXPECT_EQ(*ptr, kInitialTlsValue);
+ *tls_value_ptr_ = 0;
+
+ ptr = static_cast<int*>(TLSSlot().Get());
+ EXPECT_EQ(ptr, tls_value_ptr_);
+ EXPECT_EQ(*ptr, 0);
+
+ *ptr = kFinalTlsValue + kNumberDestructorCallRepetitions;
+ }
+
+ private:
+ int* tls_value_ptr_;
+ DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorageRunner);
+};
+
+
+void ThreadLocalStorageCleanup(void *value) {
+ int *ptr = reinterpret_cast<int*>(value);
+ // Destructors should never be called with a NULL.
+ ASSERT_NE(reinterpret_cast<int*>(NULL), ptr);
+ if (*ptr == kFinalTlsValue)
+ return; // We've been called enough times.
+ ASSERT_LT(kFinalTlsValue, *ptr);
+ ASSERT_GE(kFinalTlsValue + kNumberDestructorCallRepetitions, *ptr);
+ --*ptr; // Move closer to our target.
+ // Tell tls that we're not done with this thread, and still need destruction.
+ TLSSlot().Set(value);
+}
+
+#if defined(OS_POSIX)
+constexpr intptr_t kDummyValue = 0xABCD;
+constexpr size_t kKeyCount = 20;
+
+// The order in which pthread keys are destructed is not specified by the POSIX
+// specification. Hopefully, of the 20 keys we create, some of them should be
+// destroyed after the TLS key is destroyed.
+class UseTLSDuringDestructionRunner {
+ public:
+ UseTLSDuringDestructionRunner() = default;
+
+ // The order in which pthread_key destructors are called is not well defined.
+ // Hopefully, by creating 10 both before and after initializing TLS on the
+ // thread, at least 1 will be called after TLS destruction.
+ void Run() {
+ ASSERT_FALSE(internal::ThreadLocalStorageTestInternal::HasBeenDestroyed());
+
+ // Create 10 pthread keys before initializing TLS on the thread.
+ size_t slot_index = 0;
+ for (; slot_index < 10; ++slot_index) {
+ CreateTlsKeyWithDestructor(slot_index);
+ }
+
+ // Initialize the Chrome TLS system. It's possible that base::Thread has
+ // already initialized Chrome TLS, but we don't rely on that.
+ slot_.Set(reinterpret_cast<void*>(kDummyValue));
+
+ // Create 10 pthread keys after initializing TLS on the thread.
+ for (; slot_index < kKeyCount; ++slot_index) {
+ CreateTlsKeyWithDestructor(slot_index);
+ }
+ }
+
+ bool teardown_works_correctly() { return teardown_works_correctly_; }
+
+ private:
+ struct TLSState {
+ pthread_key_t key;
+ bool* teardown_works_correctly;
+ };
+
+ // The POSIX TLS destruction API takes as input a single C-function, which is
+ // called with the current |value| of a (key, value) pair. We need this
+ // function to do two things: set the |value| to nullptr, which requires
+ // knowing the associated |key|, and update the |teardown_works_correctly_|
+ // state.
+ //
+ // To accomplish this, we set the value to an instance of TLSState, which
+ // contains |key| as well as a pointer to |teardown_works_correctly|.
+ static void ThreadLocalDestructor(void* value) {
+ TLSState* state = static_cast<TLSState*>(value);
+ int result = pthread_setspecific(state->key, nullptr);
+ ASSERT_EQ(result, 0);
+
+ // If this path is hit, then the thread local destructor was called after
+ // the Chrome-TLS destructor and the internal state was updated correctly.
+ // No further checks are necessary.
+ if (internal::ThreadLocalStorageTestInternal::HasBeenDestroyed()) {
+ *(state->teardown_works_correctly) = true;
+ return;
+ }
+
+ // If this path is hit, then the thread local destructor was called before
+ // the Chrome-TLS destructor is hit. The ThreadLocalStorage::Slot should
+ // still function correctly.
+ ASSERT_EQ(reinterpret_cast<intptr_t>(slot_.Get()), kDummyValue);
+ }
+
+ void CreateTlsKeyWithDestructor(size_t index) {
+ ASSERT_LT(index, kKeyCount);
+
+ tls_states_[index].teardown_works_correctly = &teardown_works_correctly_;
+ int result = pthread_key_create(
+ &(tls_states_[index].key),
+ UseTLSDuringDestructionRunner::ThreadLocalDestructor);
+ ASSERT_EQ(result, 0);
+
+ result = pthread_setspecific(tls_states_[index].key, &tls_states_[index]);
+ ASSERT_EQ(result, 0);
+ }
+
+ static base::ThreadLocalStorage::Slot slot_;
+ bool teardown_works_correctly_ = false;
+ TLSState tls_states_[kKeyCount];
+
+ DISALLOW_COPY_AND_ASSIGN(UseTLSDuringDestructionRunner);
+};
+
+base::ThreadLocalStorage::Slot UseTLSDuringDestructionRunner::slot_;
+
+void* UseTLSTestThreadRun(void* input) {
+ UseTLSDuringDestructionRunner* runner =
+ static_cast<UseTLSDuringDestructionRunner*>(input);
+ runner->Run();
+ return nullptr;
+}
+
+#endif // defined(OS_POSIX)
+
+} // namespace
+
+TEST(ThreadLocalStorageTest, Basics) {
+ ThreadLocalStorage::Slot slot;
+ slot.Set(reinterpret_cast<void*>(123));
+ int value = reinterpret_cast<intptr_t>(slot.Get());
+ EXPECT_EQ(value, 123);
+}
+
+#if defined(THREAD_SANITIZER) || \
+ (defined(OS_WIN) && defined(ARCH_CPU_X86_64) && !defined(NDEBUG))
+// Do not run the test under ThreadSanitizer. Because this test iterates its
+// own TSD destructor for the maximum possible number of times, TSan can't jump
+// in after the last destructor invocation, therefore the destructor remains
+// unsynchronized with the following users of the same TSD slot. This results
+// in race reports between the destructor and functions in other tests.
+//
+// It is disabled on Win x64 with incremental linking (i.e. "Debug") pending
+// resolution of http://crbug.com/251251.
+#define MAYBE_TLSDestructors DISABLED_TLSDestructors
+#else
+#define MAYBE_TLSDestructors TLSDestructors
+#endif
+TEST(ThreadLocalStorageTest, MAYBE_TLSDestructors) {
+ // Create a TLS index with a destructor. Create a set of
+ // threads that set the TLS, while the destructor cleans it up.
+ // After the threads finish, verify that the value is cleaned up.
+ const int kNumThreads = 5;
+ int values[kNumThreads];
+ ThreadLocalStorageRunner* thread_delegates[kNumThreads];
+ DelegateSimpleThread* threads[kNumThreads];
+
+ // Spawn the threads.
+ for (int index = 0; index < kNumThreads; index++) {
+ values[index] = kInitialTlsValue;
+ thread_delegates[index] = new ThreadLocalStorageRunner(&values[index]);
+ threads[index] = new DelegateSimpleThread(thread_delegates[index],
+ "tls thread");
+ threads[index]->Start();
+ }
+
+ // Wait for the threads to finish.
+ for (int index = 0; index < kNumThreads; index++) {
+ threads[index]->Join();
+ delete threads[index];
+ delete thread_delegates[index];
+
+ // Verify that the destructor was called and that we reset.
+ EXPECT_EQ(values[index], kFinalTlsValue);
+ }
+}
+
+TEST(ThreadLocalStorageTest, TLSReclaim) {
+ // Creates and destroys many TLS slots and ensures they all zero-inited.
+ for (int i = 0; i < 1000; ++i) {
+ ThreadLocalStorage::Slot slot(nullptr);
+ EXPECT_EQ(nullptr, slot.Get());
+ slot.Set(reinterpret_cast<void*>(0xBAADF00D));
+ EXPECT_EQ(reinterpret_cast<void*>(0xBAADF00D), slot.Get());
+ }
+}
+
+#if defined(OS_POSIX)
+// Unlike POSIX, Windows does not iterate through the OS TLS to cleanup any
+// values there. Instead a per-module thread destruction function is called.
+// However, it is not possible to perform a check after this point (as the code
+// is detached from the thread), so this check remains POSIX only.
+TEST(ThreadLocalStorageTest, UseTLSDuringDestruction) {
+ UseTLSDuringDestructionRunner runner;
+ pthread_t thread;
+ int result = pthread_create(&thread, nullptr, UseTLSTestThreadRun, &runner);
+ ASSERT_EQ(result, 0);
+
+ result = pthread_join(thread, nullptr);
+ ASSERT_EQ(result, 0);
+
+ EXPECT_TRUE(runner.teardown_works_correctly());
+}
+#endif // defined(OS_POSIX)
+
+} // namespace base
diff --git a/base/threading/thread_local_storage_win.cc b/base/threading/thread_local_storage_win.cc
new file mode 100644
index 0000000..a9aec31
--- /dev/null
+++ b/base/threading/thread_local_storage_win.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_local_storage.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+
+namespace base {
+
+namespace internal {
+
+bool PlatformThreadLocalStorage::AllocTLS(TLSKey* key) {
+ TLSKey value = TlsAlloc();
+ if (value != TLS_OUT_OF_INDEXES) {
+ *key = value;
+ return true;
+ }
+ return false;
+}
+
+void PlatformThreadLocalStorage::FreeTLS(TLSKey key) {
+ BOOL ret = TlsFree(key);
+ DCHECK(ret);
+}
+
+void PlatformThreadLocalStorage::SetTLSValue(TLSKey key, void* value) {
+ BOOL ret = TlsSetValue(key, value);
+ DCHECK(ret);
+}
+
+} // namespace internal
+
+} // namespace base
+
+// Thread Termination Callbacks.
+// Windows doesn't support a per-thread destructor with its
+// TLS primitives. So, we build it manually by inserting a
+// function to be called on each thread's exit.
+// This magic is from http://www.codeproject.com/threads/tls.asp
+// and it works for VC++ 7.0 and later.
+
+// Force a reference to _tls_used to make the linker create the TLS directory
+// if it's not already there. (e.g. if __declspec(thread) is not used).
+// Force a reference to p_thread_callback_base to prevent whole program
+// optimization from discarding the variable.
+#ifdef _WIN64
+
+#pragma comment(linker, "/INCLUDE:_tls_used")
+#pragma comment(linker, "/INCLUDE:p_thread_callback_base")
+
+#else // _WIN64
+
+#pragma comment(linker, "/INCLUDE:__tls_used")
+#pragma comment(linker, "/INCLUDE:_p_thread_callback_base")
+
+#endif // _WIN64
+
+// Static callback function to call with each thread termination.
+void NTAPI OnThreadExit(PVOID module, DWORD reason, PVOID reserved) {
+ // On XP SP0 & SP1, the DLL_PROCESS_ATTACH is never seen. It is sent on SP2+
+ // and on W2K and W2K3. So don't assume it is sent.
+ if (DLL_THREAD_DETACH == reason || DLL_PROCESS_DETACH == reason)
+ base::internal::PlatformThreadLocalStorage::OnThreadExit();
+}
+
+// .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are
+// called automatically by the OS loader code (not the CRT) when the module is
+// loaded and on thread creation. They are NOT called if the module has been
+// loaded by a LoadLibrary() call. It must have implicitly been loaded at
+// process startup.
+// By implicitly loaded, I mean that it is directly referenced by the main EXE
+// or by one of its dependent DLLs. Delay-loaded DLL doesn't count as being
+// implicitly loaded.
+//
+// See VC\crt\src\tlssup.c for reference.
+
+// extern "C" suppresses C++ name mangling so we know the symbol name for the
+// linker /INCLUDE:symbol pragma above.
+extern "C" {
+// The linker must not discard p_thread_callback_base. (We force a reference
+// to this variable with a linker /INCLUDE:symbol pragma to ensure that.) If
+// this variable is discarded, the OnThreadExit function will never be called.
+#ifdef _WIN64
+
+// .CRT section is merged with .rdata on x64 so it must be constant data.
+#pragma const_seg(".CRT$XLB")
+// When defining a const variable, it must have external linkage to be sure the
+// linker doesn't discard it.
+extern const PIMAGE_TLS_CALLBACK p_thread_callback_base;
+const PIMAGE_TLS_CALLBACK p_thread_callback_base = OnThreadExit;
+
+// Reset the default section.
+#pragma const_seg()
+
+#else // _WIN64
+
+#pragma data_seg(".CRT$XLB")
+PIMAGE_TLS_CALLBACK p_thread_callback_base = OnThreadExit;
+
+// Reset the default section.
+#pragma data_seg()
+
+#endif // _WIN64
+} // extern "C"
diff --git a/base/threading/thread_local_unittest.cc b/base/threading/thread_local_unittest.cc
new file mode 100644
index 0000000..54f2ad2
--- /dev/null
+++ b/base/threading/thread_local_unittest.cc
@@ -0,0 +1,164 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "base/threading/simple_thread.h"
+#include "base/threading/thread_local.h"
+#include "base/synchronization/waitable_event.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class ThreadLocalTesterBase : public base::DelegateSimpleThreadPool::Delegate {
+ public:
+ typedef base::ThreadLocalPointer<char> TLPType;
+
+ ThreadLocalTesterBase(TLPType* tlp, base::WaitableEvent* done)
+ : tlp_(tlp),
+ done_(done) {
+ }
+ ~ThreadLocalTesterBase() override = default;
+
+ protected:
+ TLPType* tlp_;
+ base::WaitableEvent* done_;
+};
+
+class SetThreadLocal : public ThreadLocalTesterBase {
+ public:
+ SetThreadLocal(TLPType* tlp, base::WaitableEvent* done)
+ : ThreadLocalTesterBase(tlp, done), val_(nullptr) {}
+ ~SetThreadLocal() override = default;
+
+ void set_value(char* val) { val_ = val; }
+
+ void Run() override {
+ DCHECK(!done_->IsSignaled());
+ tlp_->Set(val_);
+ done_->Signal();
+ }
+
+ private:
+ char* val_;
+};
+
+class GetThreadLocal : public ThreadLocalTesterBase {
+ public:
+ GetThreadLocal(TLPType* tlp, base::WaitableEvent* done)
+ : ThreadLocalTesterBase(tlp, done), ptr_(nullptr) {}
+ ~GetThreadLocal() override = default;
+
+ void set_ptr(char** ptr) { ptr_ = ptr; }
+
+ void Run() override {
+ DCHECK(!done_->IsSignaled());
+ *ptr_ = tlp_->Get();
+ done_->Signal();
+ }
+
+ private:
+ char** ptr_;
+};
+
+} // namespace
+
+// In this test, we start 2 threads which will access a ThreadLocalPointer. We
+// make sure the default is NULL, and the pointers are unique to the threads.
+TEST(ThreadLocalTest, Pointer) {
+ base::DelegateSimpleThreadPool tp1("ThreadLocalTest tp1", 1);
+ base::DelegateSimpleThreadPool tp2("ThreadLocalTest tp1", 1);
+ tp1.Start();
+ tp2.Start();
+
+ base::ThreadLocalPointer<char> tlp;
+
+ static char* const kBogusPointer = reinterpret_cast<char*>(0x1234);
+
+ char* tls_val;
+ base::WaitableEvent done(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED);
+
+ GetThreadLocal getter(&tlp, &done);
+ getter.set_ptr(&tls_val);
+
+ // Check that both threads defaulted to NULL.
+ tls_val = kBogusPointer;
+ done.Reset();
+ tp1.AddWork(&getter);
+ done.Wait();
+ EXPECT_EQ(static_cast<char*>(nullptr), tls_val);
+
+ tls_val = kBogusPointer;
+ done.Reset();
+ tp2.AddWork(&getter);
+ done.Wait();
+ EXPECT_EQ(static_cast<char*>(nullptr), tls_val);
+
+ SetThreadLocal setter(&tlp, &done);
+ setter.set_value(kBogusPointer);
+
+ // Have thread 1 set their pointer value to kBogusPointer.
+ done.Reset();
+ tp1.AddWork(&setter);
+ done.Wait();
+
+ tls_val = nullptr;
+ done.Reset();
+ tp1.AddWork(&getter);
+ done.Wait();
+ EXPECT_EQ(kBogusPointer, tls_val);
+
+ // Make sure thread 2 is still NULL
+ tls_val = kBogusPointer;
+ done.Reset();
+ tp2.AddWork(&getter);
+ done.Wait();
+ EXPECT_EQ(static_cast<char*>(nullptr), tls_val);
+
+ // Set thread 2 to kBogusPointer + 1.
+ setter.set_value(kBogusPointer + 1);
+
+ done.Reset();
+ tp2.AddWork(&setter);
+ done.Wait();
+
+ tls_val = nullptr;
+ done.Reset();
+ tp2.AddWork(&getter);
+ done.Wait();
+ EXPECT_EQ(kBogusPointer + 1, tls_val);
+
+ // Make sure thread 1 is still kBogusPointer.
+ tls_val = nullptr;
+ done.Reset();
+ tp1.AddWork(&getter);
+ done.Wait();
+ EXPECT_EQ(kBogusPointer, tls_val);
+
+ tp1.JoinAll();
+ tp2.JoinAll();
+}
+
+TEST(ThreadLocalTest, Boolean) {
+ {
+ base::ThreadLocalBoolean tlb;
+ EXPECT_FALSE(tlb.Get());
+
+ tlb.Set(false);
+ EXPECT_FALSE(tlb.Get());
+
+ tlb.Set(true);
+ EXPECT_TRUE(tlb.Get());
+ }
+
+ // Our slot should have been freed, we're all reset.
+ {
+ base::ThreadLocalBoolean tlb;
+ EXPECT_FALSE(tlb.Get());
+ }
+}
+
+} // namespace base
diff --git a/base/threading/thread_perftest.cc b/base/threading/thread_perftest.cc
new file mode 100644
index 0000000..bf89049
--- /dev/null
+++ b/base/threading/thread_perftest.cc
@@ -0,0 +1,318 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
+
+#if defined(OS_POSIX)
+#include <pthread.h>
+#endif
+
+namespace base {
+
+namespace {
+
+const int kNumRuns = 100000;
+
+// Base class for a threading perf-test. This sets up some threads for the
+// test and measures the clock-time in addition to time spent on each thread.
+class ThreadPerfTest : public testing::Test {
+ public:
+ ThreadPerfTest()
+ : done_(WaitableEvent::ResetPolicy::AUTOMATIC,
+ WaitableEvent::InitialState::NOT_SIGNALED) {}
+
+ // To be implemented by each test. Subclass must uses threads_ such that
+ // their cpu-time can be measured. Test must return from PingPong() _and_
+ // call FinishMeasurement from any thread to complete the test.
+ virtual void Init() {
+ if (ThreadTicks::IsSupported())
+ ThreadTicks::WaitUntilInitialized();
+ }
+ virtual void PingPong(int hops) = 0;
+ virtual void Reset() {}
+
+ void TimeOnThread(base::ThreadTicks* ticks, base::WaitableEvent* done) {
+ *ticks = base::ThreadTicks::Now();
+ done->Signal();
+ }
+
+ base::ThreadTicks ThreadNow(const base::Thread& thread) {
+ base::WaitableEvent done(WaitableEvent::ResetPolicy::AUTOMATIC,
+ WaitableEvent::InitialState::NOT_SIGNALED);
+ base::ThreadTicks ticks;
+ thread.task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&ThreadPerfTest::TimeOnThread,
+ base::Unretained(this), &ticks, &done));
+ done.Wait();
+ return ticks;
+ }
+
+ void RunPingPongTest(const std::string& name, unsigned num_threads) {
+ // Create threads and collect starting cpu-time for each thread.
+ std::vector<base::ThreadTicks> thread_starts;
+ while (threads_.size() < num_threads) {
+ threads_.push_back(std::make_unique<base::Thread>("PingPonger"));
+ threads_.back()->Start();
+ if (base::ThreadTicks::IsSupported())
+ thread_starts.push_back(ThreadNow(*threads_.back()));
+ }
+
+ Init();
+
+ base::TimeTicks start = base::TimeTicks::Now();
+ PingPong(kNumRuns);
+ done_.Wait();
+ base::TimeTicks end = base::TimeTicks::Now();
+
+ // Gather the cpu-time spent on each thread. This does one extra tasks,
+ // but that should be in the noise given enough runs.
+ base::TimeDelta thread_time;
+ while (threads_.size()) {
+ if (base::ThreadTicks::IsSupported()) {
+ thread_time += ThreadNow(*threads_.back()) - thread_starts.back();
+ thread_starts.pop_back();
+ }
+ threads_.pop_back();
+ }
+
+ Reset();
+
+ double num_runs = static_cast<double>(kNumRuns);
+ double us_per_task_clock = (end - start).InMicroseconds() / num_runs;
+ double us_per_task_cpu = thread_time.InMicroseconds() / num_runs;
+
+ // Clock time per task.
+ perf_test::PrintResult(
+ "task", "", name + "_time ", us_per_task_clock, "us/hop", true);
+
+ // Total utilization across threads if available (likely higher).
+ if (base::ThreadTicks::IsSupported()) {
+ perf_test::PrintResult(
+ "task", "", name + "_cpu ", us_per_task_cpu, "us/hop", true);
+ }
+ }
+
+ protected:
+ void FinishMeasurement() { done_.Signal(); }
+ std::vector<std::unique_ptr<base::Thread>> threads_;
+
+ private:
+ base::WaitableEvent done_;
+};
+
+// Class to test task performance by posting empty tasks back and forth.
+class TaskPerfTest : public ThreadPerfTest {
+ base::Thread* NextThread(int count) {
+ return threads_[count % threads_.size()].get();
+ }
+
+ void PingPong(int hops) override {
+ if (!hops) {
+ FinishMeasurement();
+ return;
+ }
+ NextThread(hops)->task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&ThreadPerfTest::PingPong,
+ base::Unretained(this), hops - 1));
+ }
+};
+
+// This tries to test the 'best-case' as well as the 'worst-case' task posting
+// performance. The best-case keeps one thread alive such that it never yeilds,
+// while the worse-case forces a context switch for every task. Four threads are
+// used to ensure the threads do yeild (with just two it might be possible for
+// both threads to stay awake if they can signal each other fast enough).
+TEST_F(TaskPerfTest, TaskPingPong) {
+ RunPingPongTest("1_Task_Threads", 1);
+ RunPingPongTest("4_Task_Threads", 4);
+}
+
+
+// Same as above, but add observers to test their perf impact.
+class MessageLoopObserver : public base::MessageLoop::TaskObserver {
+ public:
+ void WillProcessTask(const base::PendingTask& pending_task) override {}
+ void DidProcessTask(const base::PendingTask& pending_task) override {}
+};
+MessageLoopObserver message_loop_observer;
+
+class TaskObserverPerfTest : public TaskPerfTest {
+ public:
+ void Init() override {
+ TaskPerfTest::Init();
+ for (size_t i = 0; i < threads_.size(); i++) {
+ threads_[i]->message_loop()->AddTaskObserver(&message_loop_observer);
+ }
+ }
+};
+
+TEST_F(TaskObserverPerfTest, TaskPingPong) {
+ RunPingPongTest("1_Task_Threads_With_Observer", 1);
+ RunPingPongTest("4_Task_Threads_With_Observer", 4);
+}
+
+// Class to test our WaitableEvent performance by signaling back and fort.
+// WaitableEvent is templated so we can also compare with other versions.
+template <typename WaitableEventType>
+class EventPerfTest : public ThreadPerfTest {
+ public:
+ void Init() override {
+ for (size_t i = 0; i < threads_.size(); i++) {
+ events_.push_back(std::make_unique<WaitableEventType>(
+ WaitableEvent::ResetPolicy::AUTOMATIC,
+ WaitableEvent::InitialState::NOT_SIGNALED));
+ }
+ }
+
+ void Reset() override { events_.clear(); }
+
+ void WaitAndSignalOnThread(size_t event) {
+ size_t next_event = (event + 1) % events_.size();
+ int my_hops = 0;
+ do {
+ events_[event]->Wait();
+ my_hops = --remaining_hops_; // We own 'hops' between Wait and Signal.
+ events_[next_event]->Signal();
+ } while (my_hops > 0);
+ // Once we are done, all threads will signal as hops passes zero.
+ // We only signal completion once, on the thread that reaches zero.
+ if (!my_hops)
+ FinishMeasurement();
+ }
+
+ void PingPong(int hops) override {
+ remaining_hops_ = hops;
+ for (size_t i = 0; i < threads_.size(); i++) {
+ threads_[i]->task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&EventPerfTest::WaitAndSignalOnThread,
+ base::Unretained(this), i));
+ }
+
+ // Kick off the Signal ping-ponging.
+ events_.front()->Signal();
+ }
+
+ int remaining_hops_;
+ std::vector<std::unique_ptr<WaitableEventType>> events_;
+};
+
+// Similar to the task posting test, this just tests similar functionality
+// using WaitableEvents. We only test four threads (worst-case), but we
+// might want to craft a way to test the best-case (where the thread doesn't
+// end up blocking because the event is already signalled).
+typedef EventPerfTest<base::WaitableEvent> WaitableEventThreadPerfTest;
+TEST_F(WaitableEventThreadPerfTest, EventPingPong) {
+ RunPingPongTest("4_WaitableEvent_Threads", 4);
+}
+
+// Build a minimal event using ConditionVariable.
+class ConditionVariableEvent {
+ public:
+ ConditionVariableEvent(WaitableEvent::ResetPolicy reset_policy,
+ WaitableEvent::InitialState initial_state)
+ : cond_(&lock_), signaled_(false) {
+ DCHECK_EQ(WaitableEvent::ResetPolicy::AUTOMATIC, reset_policy);
+ DCHECK_EQ(WaitableEvent::InitialState::NOT_SIGNALED, initial_state);
+ }
+
+ void Signal() {
+ {
+ base::AutoLock scoped_lock(lock_);
+ signaled_ = true;
+ }
+ cond_.Signal();
+ }
+
+ void Wait() {
+ base::AutoLock scoped_lock(lock_);
+ while (!signaled_)
+ cond_.Wait();
+ signaled_ = false;
+ }
+
+ private:
+ base::Lock lock_;
+ base::ConditionVariable cond_;
+ bool signaled_;
+};
+
+// This is meant to test the absolute minimal context switching time
+// using our own base synchronization code.
+typedef EventPerfTest<ConditionVariableEvent> ConditionVariablePerfTest;
+TEST_F(ConditionVariablePerfTest, EventPingPong) {
+ RunPingPongTest("4_ConditionVariable_Threads", 4);
+}
+#if defined(OS_POSIX)
+
+// Absolutely 100% minimal posix waitable event. If there is a better/faster
+// way to force a context switch, we should use that instead.
+class PthreadEvent {
+ public:
+ PthreadEvent(WaitableEvent::ResetPolicy reset_policy,
+ WaitableEvent::InitialState initial_state) {
+ DCHECK_EQ(WaitableEvent::ResetPolicy::AUTOMATIC, reset_policy);
+ DCHECK_EQ(WaitableEvent::InitialState::NOT_SIGNALED, initial_state);
+ pthread_mutex_init(&mutex_, nullptr);
+ pthread_cond_init(&cond_, nullptr);
+ signaled_ = false;
+ }
+
+ ~PthreadEvent() {
+ pthread_cond_destroy(&cond_);
+ pthread_mutex_destroy(&mutex_);
+ }
+
+ void Signal() {
+ pthread_mutex_lock(&mutex_);
+ signaled_ = true;
+ pthread_mutex_unlock(&mutex_);
+ pthread_cond_signal(&cond_);
+ }
+
+ void Wait() {
+ pthread_mutex_lock(&mutex_);
+ while (!signaled_)
+ pthread_cond_wait(&cond_, &mutex_);
+ signaled_ = false;
+ pthread_mutex_unlock(&mutex_);
+ }
+
+ private:
+ bool signaled_;
+ pthread_mutex_t mutex_;
+ pthread_cond_t cond_;
+};
+
+// This is meant to test the absolute minimal context switching time.
+// If there is any faster way to do this we should substitute it in.
+typedef EventPerfTest<PthreadEvent> PthreadEventPerfTest;
+TEST_F(PthreadEventPerfTest, EventPingPong) {
+ RunPingPongTest("4_PthreadCondVar_Threads", 4);
+}
+
+#endif
+
+} // namespace
+
+} // namespace base
diff --git a/base/threading/thread_restrictions.cc b/base/threading/thread_restrictions.cc
new file mode 100644
index 0000000..633bcb2
--- /dev/null
+++ b/base/threading/thread_restrictions.cc
@@ -0,0 +1,176 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_restrictions.h"
+
+#if DCHECK_IS_ON()
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/thread_local.h"
+
+namespace base {
+
+namespace {
+
+LazyInstance<ThreadLocalBoolean>::Leaky g_blocking_disallowed =
+ LAZY_INSTANCE_INITIALIZER;
+
+LazyInstance<ThreadLocalBoolean>::Leaky
+ g_singleton_disallowed = LAZY_INSTANCE_INITIALIZER;
+
+LazyInstance<ThreadLocalBoolean>::Leaky g_base_sync_primitives_disallowed =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+void AssertBlockingAllowed() {
+ DCHECK(!g_blocking_disallowed.Get().Get())
+ << "Function marked as blocking was called from a scope that disallows "
+ "blocking! If this task is running inside the TaskScheduler, it needs "
+ "to have MayBlock() in its TaskTraits. Otherwise, consider making "
+ "this blocking work asynchronous or, as a last resort, you may use "
+ "ScopedAllowBlocking in a narrow scope.";
+}
+
+void DisallowBlocking() {
+ g_blocking_disallowed.Get().Set(true);
+}
+
+ScopedDisallowBlocking::ScopedDisallowBlocking()
+ : was_disallowed_(g_blocking_disallowed.Get().Get()) {
+ g_blocking_disallowed.Get().Set(true);
+}
+
+ScopedDisallowBlocking::~ScopedDisallowBlocking() {
+ DCHECK(g_blocking_disallowed.Get().Get());
+ g_blocking_disallowed.Get().Set(was_disallowed_);
+}
+
+ScopedAllowBlocking::ScopedAllowBlocking()
+ : was_disallowed_(g_blocking_disallowed.Get().Get()) {
+ g_blocking_disallowed.Get().Set(false);
+}
+
+ScopedAllowBlocking::~ScopedAllowBlocking() {
+ DCHECK(!g_blocking_disallowed.Get().Get());
+ g_blocking_disallowed.Get().Set(was_disallowed_);
+}
+
+void DisallowBaseSyncPrimitives() {
+ g_base_sync_primitives_disallowed.Get().Set(true);
+}
+
+ScopedAllowBaseSyncPrimitives::ScopedAllowBaseSyncPrimitives()
+ : was_disallowed_(g_base_sync_primitives_disallowed.Get().Get()) {
+ DCHECK(!g_blocking_disallowed.Get().Get())
+ << "To allow //base sync primitives in a scope where blocking is "
+ "disallowed use ScopedAllowBaseSyncPrimitivesOutsideBlockingScope.";
+ g_base_sync_primitives_disallowed.Get().Set(false);
+}
+
+ScopedAllowBaseSyncPrimitives::~ScopedAllowBaseSyncPrimitives() {
+ DCHECK(!g_base_sync_primitives_disallowed.Get().Get());
+ g_base_sync_primitives_disallowed.Get().Set(was_disallowed_);
+}
+
+ScopedAllowBaseSyncPrimitivesOutsideBlockingScope::
+ ScopedAllowBaseSyncPrimitivesOutsideBlockingScope()
+ : was_disallowed_(g_base_sync_primitives_disallowed.Get().Get()) {
+ g_base_sync_primitives_disallowed.Get().Set(false);
+}
+
+ScopedAllowBaseSyncPrimitivesOutsideBlockingScope::
+ ~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() {
+ DCHECK(!g_base_sync_primitives_disallowed.Get().Get());
+ g_base_sync_primitives_disallowed.Get().Set(was_disallowed_);
+}
+
+ScopedAllowBaseSyncPrimitivesForTesting::
+ ScopedAllowBaseSyncPrimitivesForTesting()
+ : was_disallowed_(g_base_sync_primitives_disallowed.Get().Get()) {
+ g_base_sync_primitives_disallowed.Get().Set(false);
+}
+
+ScopedAllowBaseSyncPrimitivesForTesting::
+ ~ScopedAllowBaseSyncPrimitivesForTesting() {
+ DCHECK(!g_base_sync_primitives_disallowed.Get().Get());
+ g_base_sync_primitives_disallowed.Get().Set(was_disallowed_);
+}
+
+namespace internal {
+
+void AssertBaseSyncPrimitivesAllowed() {
+ DCHECK(!g_base_sync_primitives_disallowed.Get().Get())
+ << "Waiting on a //base sync primitive is not allowed on this thread to "
+ "prevent jank and deadlock. If waiting on a //base sync primitive is "
+ "unavoidable, do it within the scope of a "
+ "ScopedAllowBaseSyncPrimitives. If in a test, "
+ "use ScopedAllowBaseSyncPrimitivesForTesting.";
+}
+
+void ResetThreadRestrictionsForTesting() {
+ g_blocking_disallowed.Get().Set(false);
+ g_singleton_disallowed.Get().Set(false);
+ g_base_sync_primitives_disallowed.Get().Set(false);
+}
+
+} // namespace internal
+
+ThreadRestrictions::ScopedAllowIO::ScopedAllowIO()
+ : was_allowed_(SetIOAllowed(true)) {}
+
+ThreadRestrictions::ScopedAllowIO::~ScopedAllowIO() {
+ SetIOAllowed(was_allowed_);
+}
+
+// static
+bool ThreadRestrictions::SetIOAllowed(bool allowed) {
+ bool previous_disallowed = g_blocking_disallowed.Get().Get();
+ g_blocking_disallowed.Get().Set(!allowed);
+ return !previous_disallowed;
+}
+
+// static
+bool ThreadRestrictions::SetSingletonAllowed(bool allowed) {
+ bool previous_disallowed = g_singleton_disallowed.Get().Get();
+ g_singleton_disallowed.Get().Set(!allowed);
+ return !previous_disallowed;
+}
+
+// static
+void ThreadRestrictions::AssertSingletonAllowed() {
+ if (g_singleton_disallowed.Get().Get()) {
+ NOTREACHED() << "LazyInstance/Singleton is not allowed to be used on this "
+ << "thread. Most likely it's because this thread is not "
+ << "joinable (or the current task is running with "
+ << "TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN semantics), so "
+ << "AtExitManager may have deleted the object on shutdown, "
+ << "leading to a potential shutdown crash. If you need to use "
+ << "the object from this context, it'll have to be updated to "
+ << "use Leaky traits.";
+ }
+}
+
+// static
+void ThreadRestrictions::DisallowWaiting() {
+ DisallowBaseSyncPrimitives();
+}
+
+bool ThreadRestrictions::SetWaitAllowed(bool allowed) {
+ bool previous_disallowed = g_base_sync_primitives_disallowed.Get().Get();
+ g_base_sync_primitives_disallowed.Get().Set(!allowed);
+ return !previous_disallowed;
+}
+
+ThreadRestrictions::ScopedAllowWait::ScopedAllowWait()
+ : was_allowed_(SetWaitAllowed(true)) {}
+
+ThreadRestrictions::ScopedAllowWait::~ScopedAllowWait() {
+ SetWaitAllowed(was_allowed_);
+}
+
+} // namespace base
+
+#endif // DCHECK_IS_ON()
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
new file mode 100644
index 0000000..57f2f21
--- /dev/null
+++ b/base/threading/thread_restrictions.h
@@ -0,0 +1,506 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_THREAD_RESTRICTIONS_H_
+#define BASE_THREADING_THREAD_RESTRICTIONS_H_
+
+#include "base/base_export.h"
+#include "base/gtest_prod_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+
+class BrowserProcessImpl;
+class HistogramSynchronizer;
+class NativeBackendKWallet;
+class KeyStorageLinux;
+
+namespace android_webview {
+class AwFormDatabaseService;
+class CookieManager;
+class ScopedAllowInitGLBindings;
+}
+
+namespace cc {
+class CompletionEvent;
+class SingleThreadTaskGraphRunner;
+}
+namespace chromeos {
+class BlockingMethodCaller;
+namespace system {
+class StatisticsProviderImpl;
+}
+}
+namespace chrome_browser_net {
+class Predictor;
+}
+namespace content {
+class BrowserGpuChannelHostFactory;
+class BrowserGpuMemoryBufferManager;
+class BrowserMainLoop;
+class BrowserProcessSubThread;
+class BrowserShutdownProfileDumper;
+class BrowserSurfaceViewManager;
+class BrowserTestBase;
+class CategorizedWorkerPool;
+class NestedMessagePumpAndroid;
+class ScopedAllowWaitForAndroidLayoutTests;
+class ScopedAllowWaitForDebugURL;
+class SessionStorageDatabase;
+class SoftwareOutputDeviceMus;
+class SynchronousCompositor;
+class SynchronousCompositorHost;
+class SynchronousCompositorSyncCallBridge;
+class TextInputClientMac;
+} // namespace content
+namespace cronet {
+class CronetPrefsManager;
+class CronetURLRequestContext;
+} // namespace cronet
+namespace dbus {
+class Bus;
+}
+namespace disk_cache {
+class BackendImpl;
+class InFlightIO;
+}
+namespace functions {
+class ExecScriptScopedAllowBaseSyncPrimitives;
+}
+namespace gpu {
+class GpuChannelHost;
+}
+namespace leveldb {
+class LevelDBMojoProxy;
+}
+namespace media {
+class AudioInputDevice;
+class BlockingUrlProtocol;
+}
+namespace midi {
+class TaskService; // https://crbug.com/796830
+}
+namespace mojo {
+class CoreLibraryInitializer;
+class SyncCallRestrictions;
+namespace edk {
+class ScopedIPCSupport;
+}
+}
+namespace rlz_lib {
+class FinancialPing;
+}
+namespace ui {
+class CommandBufferClientImpl;
+class CommandBufferLocal;
+class GpuState;
+class MaterialDesignController;
+}
+namespace net {
+class MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives;
+class NetworkChangeNotifierMac;
+namespace internal {
+class AddressTrackerLinux;
+}
+}
+
+namespace remoting {
+class AutoThread;
+}
+
+namespace resource_coordinator {
+class TabManagerDelegate;
+}
+
+namespace service_manager {
+class ServiceProcessLauncher;
+}
+
+namespace shell_integration {
+class LaunchXdgUtilityScopedAllowBaseSyncPrimitives;
+}
+
+namespace ui {
+class WindowResizeHelperMac;
+}
+
+namespace views {
+class ScreenMus;
+}
+
+namespace viz {
+class ServerGpuMemoryBufferManager;
+}
+
+namespace webrtc {
+class DesktopConfigurationMonitor;
+}
+
+namespace base {
+
+namespace android {
+class JavaHandlerThread;
+}
+
+namespace internal {
+class TaskTracker;
+}
+
+class GetAppOutputScopedAllowBaseSyncPrimitives;
+class SimpleThread;
+class StackSamplingProfiler;
+class Thread;
+class ThreadTestHelper;
+
+#if DCHECK_IS_ON()
+#define INLINE_IF_DCHECK_IS_OFF BASE_EXPORT
+#define EMPTY_BODY_IF_DCHECK_IS_OFF
+#else
+#define INLINE_IF_DCHECK_IS_OFF inline
+#define EMPTY_BODY_IF_DCHECK_IS_OFF \
+ {}
+#endif
+
+// A "blocking call" refers to any call that causes the calling thread to wait
+// off-CPU. It includes but is not limited to calls that wait on synchronous
+// file I/O operations: read or write a file from disk, interact with a pipe or
+// a socket, rename or delete a file, enumerate files in a directory, etc.
+// Acquiring a low contention lock is not considered a blocking call.
+
+// Asserts that blocking calls are allowed in the current scope.
+//
+// Style tip: It's best if you put AssertBlockingAllowed() checks as close to
+// the blocking call as possible. For example:
+//
+// void ReadFile() {
+// PreWork();
+//
+// base::AssertBlockingAllowed();
+// fopen(...);
+//
+// PostWork();
+// }
+//
+// void Bar() {
+// ReadFile();
+// }
+//
+// void Foo() {
+// Bar();
+// }
+INLINE_IF_DCHECK_IS_OFF void AssertBlockingAllowed()
+ EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+// Disallows blocking on the current thread.
+INLINE_IF_DCHECK_IS_OFF void DisallowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+// Disallows blocking calls within its scope.
+class BASE_EXPORT ScopedDisallowBlocking {
+ public:
+ ScopedDisallowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF;
+ ~ScopedDisallowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+ private:
+#if DCHECK_IS_ON()
+ const bool was_disallowed_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedDisallowBlocking);
+};
+
+// ScopedAllowBlocking(ForTesting) allow blocking calls within a scope where
+// they are normally disallowed.
+//
+// Avoid using this. Prefer making blocking calls from tasks posted to
+// base::TaskScheduler with base::MayBlock().
+class BASE_EXPORT ScopedAllowBlocking {
+ private:
+ // This can only be instantiated by friends. Use ScopedAllowBlockingForTesting
+ // in unit tests to avoid the friend requirement.
+ FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest, ScopedAllowBlocking);
+ friend class android_webview::ScopedAllowInitGLBindings;
+ friend class content::BrowserProcessSubThread;
+ friend class cronet::CronetPrefsManager;
+ friend class cronet::CronetURLRequestContext;
+ friend class media::AudioInputDevice;
+ friend class mojo::CoreLibraryInitializer;
+ friend class resource_coordinator::TabManagerDelegate; // crbug.com/778703
+ friend class ui::MaterialDesignController;
+ friend class ScopedAllowBlockingForTesting;
+ friend class StackSamplingProfiler;
+
+ ScopedAllowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF;
+ ~ScopedAllowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+#if DCHECK_IS_ON()
+ const bool was_disallowed_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowBlocking);
+};
+
+class ScopedAllowBlockingForTesting {
+ public:
+ ScopedAllowBlockingForTesting() {}
+ ~ScopedAllowBlockingForTesting() {}
+
+ private:
+#if DCHECK_IS_ON()
+ ScopedAllowBlocking scoped_allow_blocking_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowBlockingForTesting);
+};
+
+// "Waiting on a //base sync primitive" refers to calling one of these methods:
+// - base::WaitableEvent::*Wait*
+// - base::ConditionVariable::*Wait*
+// - base::Process::WaitForExit*
+
+// Disallows waiting on a //base sync primitive on the current thread.
+INLINE_IF_DCHECK_IS_OFF void DisallowBaseSyncPrimitives()
+ EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+// ScopedAllowBaseSyncPrimitives(ForTesting)(OutsideBlockingScope) allow waiting
+// on a //base sync primitive within a scope where this is normally disallowed.
+//
+// Avoid using this.
+//
+// Instead of waiting on a WaitableEvent or a ConditionVariable, put the work
+// that should happen after the wait in a callback and post that callback from
+// where the WaitableEvent or ConditionVariable would have been signaled. If
+// something needs to be scheduled after many tasks have executed, use
+// base::BarrierClosure.
+//
+// On Windows, join processes asynchronously using base::win::ObjectWatcher.
+
+// This can only be used in a scope where blocking is allowed.
+class BASE_EXPORT ScopedAllowBaseSyncPrimitives {
+ private:
+ // This can only be instantiated by friends. Use
+ // ScopedAllowBaseSyncPrimitivesForTesting in unit tests to avoid the friend
+ // requirement.
+ FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest,
+ ScopedAllowBaseSyncPrimitives);
+ FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest,
+ ScopedAllowBaseSyncPrimitivesResetsState);
+ FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest,
+ ScopedAllowBaseSyncPrimitivesWithBlockingDisallowed);
+ friend class base::GetAppOutputScopedAllowBaseSyncPrimitives;
+ friend class content::BrowserProcessSubThread;
+ friend class content::SessionStorageDatabase;
+ friend class functions::ExecScriptScopedAllowBaseSyncPrimitives;
+ friend class leveldb::LevelDBMojoProxy;
+ friend class media::BlockingUrlProtocol;
+ friend class net::MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives;
+ friend class rlz_lib::FinancialPing;
+ friend class shell_integration::LaunchXdgUtilityScopedAllowBaseSyncPrimitives;
+ friend class webrtc::DesktopConfigurationMonitor;
+
+ ScopedAllowBaseSyncPrimitives() EMPTY_BODY_IF_DCHECK_IS_OFF;
+ ~ScopedAllowBaseSyncPrimitives() EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+#if DCHECK_IS_ON()
+ const bool was_disallowed_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowBaseSyncPrimitives);
+};
+
+// This can be used in a scope where blocking is disallowed.
+class BASE_EXPORT ScopedAllowBaseSyncPrimitivesOutsideBlockingScope {
+ private:
+ // This can only be instantiated by friends. Use
+ // ScopedAllowBaseSyncPrimitivesForTesting in unit tests to avoid the friend
+ // requirement.
+ FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest,
+ ScopedAllowBaseSyncPrimitivesOutsideBlockingScope);
+ FRIEND_TEST_ALL_PREFIXES(
+ ThreadRestrictionsTest,
+ ScopedAllowBaseSyncPrimitivesOutsideBlockingScopeResetsState);
+ friend class ::KeyStorageLinux;
+ friend class content::SynchronousCompositor;
+ friend class content::SynchronousCompositorHost;
+ friend class content::SynchronousCompositorSyncCallBridge;
+ friend class midi::TaskService; // https://crbug.com/796830
+ // Not used in production yet, https://crbug.com/844078.
+ friend class service_manager::ServiceProcessLauncher;
+
+ ScopedAllowBaseSyncPrimitivesOutsideBlockingScope()
+ EMPTY_BODY_IF_DCHECK_IS_OFF;
+ ~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope()
+ EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+#if DCHECK_IS_ON()
+ const bool was_disallowed_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowBaseSyncPrimitivesOutsideBlockingScope);
+};
+
+// This can be used in tests without being a friend of
+// ScopedAllowBaseSyncPrimitives(OutsideBlockingScope).
+class BASE_EXPORT ScopedAllowBaseSyncPrimitivesForTesting {
+ public:
+ ScopedAllowBaseSyncPrimitivesForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF;
+ ~ScopedAllowBaseSyncPrimitivesForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+ private:
+#if DCHECK_IS_ON()
+ const bool was_disallowed_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowBaseSyncPrimitivesForTesting);
+};
+
+namespace internal {
+
+// Asserts that waiting on a //base sync primitive is allowed in the current
+// scope.
+INLINE_IF_DCHECK_IS_OFF void AssertBaseSyncPrimitivesAllowed()
+ EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+// Resets all thread restrictions on the current thread.
+INLINE_IF_DCHECK_IS_OFF void ResetThreadRestrictionsForTesting()
+ EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+} // namespace internal
+
+class BASE_EXPORT ThreadRestrictions {
+ public:
+ // Constructing a ScopedAllowIO temporarily allows IO for the current
+ // thread. Doing this is almost certainly always incorrect.
+ //
+ // DEPRECATED. Use ScopedAllowBlocking(ForTesting).
+ class BASE_EXPORT ScopedAllowIO {
+ public:
+ ScopedAllowIO() EMPTY_BODY_IF_DCHECK_IS_OFF;
+ ~ScopedAllowIO() EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+ private:
+#if DCHECK_IS_ON()
+ const bool was_allowed_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowIO);
+ };
+
+#if DCHECK_IS_ON()
+ // Set whether the current thread to make IO calls.
+ // Threads start out in the *allowed* state.
+ // Returns the previous value.
+ //
+ // DEPRECATED. Use ScopedAllowBlocking(ForTesting) or ScopedDisallowBlocking.
+ static bool SetIOAllowed(bool allowed);
+
+ // Set whether the current thread can use singletons. Returns the previous
+ // value.
+ static bool SetSingletonAllowed(bool allowed);
+
+ // Check whether the current thread is allowed to use singletons (Singleton /
+ // LazyInstance). DCHECKs if not.
+ static void AssertSingletonAllowed();
+
+ // Disable waiting on the current thread. Threads start out in the *allowed*
+ // state. Returns the previous value.
+ //
+ // DEPRECATED. Use DisallowBaseSyncPrimitives.
+ static void DisallowWaiting();
+#else
+ // Inline the empty definitions of these functions so that they can be
+ // compiled out.
+ static bool SetIOAllowed(bool allowed) { return true; }
+ static bool SetSingletonAllowed(bool allowed) { return true; }
+ static void AssertSingletonAllowed() {}
+ static void DisallowWaiting() {}
+#endif
+
+ private:
+ // DO NOT ADD ANY OTHER FRIEND STATEMENTS, talk to jam or brettw first.
+ // BEGIN ALLOWED USAGE.
+ friend class android_webview::AwFormDatabaseService;
+ friend class android_webview::CookieManager;
+ friend class base::StackSamplingProfiler;
+ friend class content::BrowserMainLoop;
+ friend class content::BrowserShutdownProfileDumper;
+ friend class content::BrowserSurfaceViewManager;
+ friend class content::BrowserTestBase;
+ friend class content::NestedMessagePumpAndroid;
+ friend class content::ScopedAllowWaitForAndroidLayoutTests;
+ friend class content::ScopedAllowWaitForDebugURL;
+ friend class ::HistogramSynchronizer;
+ friend class internal::TaskTracker;
+ friend class cc::CompletionEvent;
+ friend class cc::SingleThreadTaskGraphRunner;
+ friend class content::CategorizedWorkerPool;
+ friend class remoting::AutoThread;
+ friend class ui::WindowResizeHelperMac;
+ friend class MessagePumpDefault;
+ friend class SimpleThread;
+ friend class Thread;
+ friend class ThreadTestHelper;
+ friend class PlatformThread;
+ friend class android::JavaHandlerThread;
+ friend class mojo::SyncCallRestrictions;
+ friend class mojo::edk::ScopedIPCSupport;
+ friend class ui::CommandBufferClientImpl;
+ friend class ui::CommandBufferLocal;
+ friend class ui::GpuState;
+
+ // END ALLOWED USAGE.
+ // BEGIN USAGE THAT NEEDS TO BE FIXED.
+ friend class ::chromeos::BlockingMethodCaller; // http://crbug.com/125360
+ friend class ::chromeos::system::StatisticsProviderImpl; // http://crbug.com/125385
+ friend class chrome_browser_net::Predictor; // http://crbug.com/78451
+ friend class
+ content::BrowserGpuChannelHostFactory; // http://crbug.com/125248
+ friend class
+ content::BrowserGpuMemoryBufferManager; // http://crbug.com/420368
+ friend class content::TextInputClientMac; // http://crbug.com/121917
+ friend class dbus::Bus; // http://crbug.com/125222
+ friend class disk_cache::BackendImpl; // http://crbug.com/74623
+ friend class disk_cache::InFlightIO; // http://crbug.com/74623
+ friend class gpu::GpuChannelHost; // http://crbug.com/125264
+ friend class net::internal::AddressTrackerLinux; // http://crbug.com/125097
+ friend class net::NetworkChangeNotifierMac; // http://crbug.com/125097
+ friend class ::BrowserProcessImpl; // http://crbug.com/125207
+ friend class ::NativeBackendKWallet; // http://crbug.com/125331
+#if !defined(OFFICIAL_BUILD)
+ friend class content::SoftwareOutputDeviceMus; // Interim non-production code
+#endif
+ friend class views::ScreenMus;
+ friend class viz::ServerGpuMemoryBufferManager;
+// END USAGE THAT NEEDS TO BE FIXED.
+
+#if DCHECK_IS_ON()
+ // DEPRECATED. Use ScopedAllowBaseSyncPrimitives.
+ static bool SetWaitAllowed(bool allowed);
+#else
+ static bool SetWaitAllowed(bool allowed) { return true; }
+#endif
+
+ // Constructing a ScopedAllowWait temporarily allows waiting on the current
+ // thread. Doing this is almost always incorrect, which is why we limit who
+ // can use this through friend. If you find yourself needing to use this, find
+ // another way. Talk to jam or brettw.
+ //
+ // DEPRECATED. Use ScopedAllowBaseSyncPrimitives.
+ class BASE_EXPORT ScopedAllowWait {
+ public:
+ ScopedAllowWait() EMPTY_BODY_IF_DCHECK_IS_OFF;
+ ~ScopedAllowWait() EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+ private:
+#if DCHECK_IS_ON()
+ const bool was_allowed_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowWait);
+ };
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ThreadRestrictions);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_RESTRICTIONS_H_
diff --git a/base/threading/thread_restrictions_unittest.cc b/base/threading/thread_restrictions_unittest.cc
new file mode 100644
index 0000000..a957a9a
--- /dev/null
+++ b/base/threading/thread_restrictions_unittest.cc
@@ -0,0 +1,137 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_restrictions.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/test/gtest_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class ThreadRestrictionsTest : public testing::Test {
+ public:
+ ThreadRestrictionsTest() = default;
+ ~ThreadRestrictionsTest() override {
+ internal::ResetThreadRestrictionsForTesting();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ThreadRestrictionsTest);
+};
+
+} // namespace
+
+TEST_F(ThreadRestrictionsTest, BlockingAllowedByDefault) {
+ AssertBlockingAllowed();
+}
+
+TEST_F(ThreadRestrictionsTest, ScopedDisallowBlocking) {
+ {
+ ScopedDisallowBlocking scoped_disallow_blocking;
+ EXPECT_DCHECK_DEATH({ AssertBlockingAllowed(); });
+ }
+ AssertBlockingAllowed();
+}
+
+TEST_F(ThreadRestrictionsTest, ScopedAllowBlocking) {
+ ScopedDisallowBlocking scoped_disallow_blocking;
+ {
+ ScopedAllowBlocking scoped_allow_blocking;
+ AssertBlockingAllowed();
+ }
+ EXPECT_DCHECK_DEATH({ AssertBlockingAllowed(); });
+}
+
+TEST_F(ThreadRestrictionsTest, ScopedAllowBlockingForTesting) {
+ ScopedDisallowBlocking scoped_disallow_blocking;
+ {
+ ScopedAllowBlockingForTesting scoped_allow_blocking_for_testing;
+ AssertBlockingAllowed();
+ }
+ EXPECT_DCHECK_DEATH({ AssertBlockingAllowed(); });
+}
+
+TEST_F(ThreadRestrictionsTest, BaseSyncPrimitivesAllowedByDefault) {}
+
+TEST_F(ThreadRestrictionsTest, DisallowBaseSyncPrimitives) {
+ DisallowBaseSyncPrimitives();
+ EXPECT_DCHECK_DEATH({ internal::AssertBaseSyncPrimitivesAllowed(); });
+}
+
+TEST_F(ThreadRestrictionsTest, ScopedAllowBaseSyncPrimitives) {
+ DisallowBaseSyncPrimitives();
+ ScopedAllowBaseSyncPrimitives scoped_allow_base_sync_primitives;
+ internal::AssertBaseSyncPrimitivesAllowed();
+}
+
+TEST_F(ThreadRestrictionsTest, ScopedAllowBaseSyncPrimitivesResetsState) {
+ DisallowBaseSyncPrimitives();
+ { ScopedAllowBaseSyncPrimitives scoped_allow_base_sync_primitives; }
+ EXPECT_DCHECK_DEATH({ internal::AssertBaseSyncPrimitivesAllowed(); });
+}
+
+TEST_F(ThreadRestrictionsTest,
+ ScopedAllowBaseSyncPrimitivesWithBlockingDisallowed) {
+ ScopedDisallowBlocking scoped_disallow_blocking;
+ DisallowBaseSyncPrimitives();
+
+ // This should DCHECK because blocking is not allowed in this scope
+ // and OutsideBlockingScope is not passed to the constructor.
+ EXPECT_DCHECK_DEATH(
+ { ScopedAllowBaseSyncPrimitives scoped_allow_base_sync_primitives; });
+}
+
+TEST_F(ThreadRestrictionsTest,
+ ScopedAllowBaseSyncPrimitivesOutsideBlockingScope) {
+ ScopedDisallowBlocking scoped_disallow_blocking;
+ DisallowBaseSyncPrimitives();
+ ScopedAllowBaseSyncPrimitivesOutsideBlockingScope
+ scoped_allow_base_sync_primitives;
+ internal::AssertBaseSyncPrimitivesAllowed();
+}
+
+TEST_F(ThreadRestrictionsTest,
+ ScopedAllowBaseSyncPrimitivesOutsideBlockingScopeResetsState) {
+ DisallowBaseSyncPrimitives();
+ {
+ ScopedAllowBaseSyncPrimitivesOutsideBlockingScope
+ scoped_allow_base_sync_primitives;
+ }
+ EXPECT_DCHECK_DEATH({ internal::AssertBaseSyncPrimitivesAllowed(); });
+}
+
+TEST_F(ThreadRestrictionsTest, ScopedAllowBaseSyncPrimitivesForTesting) {
+ DisallowBaseSyncPrimitives();
+ ScopedAllowBaseSyncPrimitivesForTesting
+ scoped_allow_base_sync_primitives_for_testing;
+ internal::AssertBaseSyncPrimitivesAllowed();
+}
+
+TEST_F(ThreadRestrictionsTest,
+ ScopedAllowBaseSyncPrimitivesForTestingResetsState) {
+ DisallowBaseSyncPrimitives();
+ {
+ ScopedAllowBaseSyncPrimitivesForTesting
+ scoped_allow_base_sync_primitives_for_testing;
+ }
+ EXPECT_DCHECK_DEATH({ internal::AssertBaseSyncPrimitivesAllowed(); });
+}
+
+TEST_F(ThreadRestrictionsTest,
+ ScopedAllowBaseSyncPrimitivesForTestingWithBlockingDisallowed) {
+ ScopedDisallowBlocking scoped_disallow_blocking;
+ DisallowBaseSyncPrimitives();
+ // This should not DCHECK.
+ ScopedAllowBaseSyncPrimitivesForTesting
+ scoped_allow_base_sync_primitives_for_testing;
+}
+
+} // namespace base
diff --git a/base/threading/thread_task_runner_handle.cc b/base/threading/thread_task_runner_handle.cc
new file mode 100644
index 0000000..314b303
--- /dev/null
+++ b/base/threading/thread_task_runner_handle.cc
@@ -0,0 +1,106 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_task_runner_handle.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_local.h"
+
+namespace base {
+
+namespace {
+
+base::LazyInstance<base::ThreadLocalPointer<ThreadTaskRunnerHandle>>::Leaky
+ thread_task_runner_tls = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+// static
+scoped_refptr<SingleThreadTaskRunner> ThreadTaskRunnerHandle::Get() {
+ ThreadTaskRunnerHandle* current = thread_task_runner_tls.Pointer()->Get();
+ CHECK(current) << "Error: This caller requires a single-threaded context "
+ "(i.e. the current task needs to run from a "
+ "SingleThreadTaskRunner).";
+ return current->task_runner_;
+}
+
+// static
+bool ThreadTaskRunnerHandle::IsSet() {
+ return !!thread_task_runner_tls.Pointer()->Get();
+}
+
+// static
+ScopedClosureRunner ThreadTaskRunnerHandle::OverrideForTesting(
+ scoped_refptr<SingleThreadTaskRunner> overriding_task_runner) {
+ // OverrideForTesting() is not compatible with a SequencedTaskRunnerHandle
+ // being set (but SequencedTaskRunnerHandle::IsSet() includes
+ // ThreadTaskRunnerHandle::IsSet() so that's discounted as the only valid
+ // excuse for it to be true). Sadly this means that tests that merely need a
+ // SequencedTaskRunnerHandle on their main thread can be forced to use a
+ // ThreadTaskRunnerHandle if they're also using test task runners (that
+ // OverrideForTesting() when running their tasks from said main thread). To
+ // solve this: sequence_task_runner_handle.cc and thread_task_runner_handle.cc
+ // would have to be merged into a single impl file and share TLS state. This
+ // was deemed unecessary for now as most tests should use higher level
+ // constructs and not have to instantiate task runner handles on their own.
+ DCHECK(!SequencedTaskRunnerHandle::IsSet() || IsSet());
+
+ if (!IsSet()) {
+ auto top_level_ttrh = std::make_unique<ThreadTaskRunnerHandle>(
+ std::move(overriding_task_runner));
+ return ScopedClosureRunner(base::BindOnce(
+ [](std::unique_ptr<ThreadTaskRunnerHandle> ttrh_to_release) {},
+ std::move(top_level_ttrh)));
+ }
+
+ ThreadTaskRunnerHandle* ttrh = thread_task_runner_tls.Pointer()->Get();
+ // Swap the two (and below bind |overriding_task_runner|, which is now the
+ // previous one, as the |task_runner_to_restore|).
+ ttrh->task_runner_.swap(overriding_task_runner);
+
+ auto no_running_during_override =
+ std::make_unique<RunLoop::ScopedDisallowRunningForTesting>();
+
+ return ScopedClosureRunner(base::BindOnce(
+ [](scoped_refptr<SingleThreadTaskRunner> task_runner_to_restore,
+ SingleThreadTaskRunner* expected_task_runner_before_restore,
+ std::unique_ptr<RunLoop::ScopedDisallowRunningForTesting>
+ no_running_during_override) {
+ ThreadTaskRunnerHandle* ttrh = thread_task_runner_tls.Pointer()->Get();
+
+ DCHECK_EQ(expected_task_runner_before_restore, ttrh->task_runner_.get())
+ << "Nested overrides must expire their ScopedClosureRunners "
+ "in LIFO order.";
+
+ ttrh->task_runner_.swap(task_runner_to_restore);
+ },
+ std::move(overriding_task_runner),
+ base::Unretained(ttrh->task_runner_.get()),
+ std::move(no_running_during_override)));
+}
+
+ThreadTaskRunnerHandle::ThreadTaskRunnerHandle(
+ scoped_refptr<SingleThreadTaskRunner> task_runner)
+ : task_runner_(std::move(task_runner)) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ // No SequencedTaskRunnerHandle (which includes ThreadTaskRunnerHandles)
+ // should already be set for this thread.
+ DCHECK(!SequencedTaskRunnerHandle::IsSet());
+ thread_task_runner_tls.Pointer()->Set(this);
+}
+
+ThreadTaskRunnerHandle::~ThreadTaskRunnerHandle() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ DCHECK_EQ(thread_task_runner_tls.Pointer()->Get(), this);
+ thread_task_runner_tls.Pointer()->Set(nullptr);
+}
+
+} // namespace base
diff --git a/base/threading/thread_task_runner_handle.h b/base/threading/thread_task_runner_handle.h
new file mode 100644
index 0000000..f6b71d7
--- /dev/null
+++ b/base/threading/thread_task_runner_handle.h
@@ -0,0 +1,57 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_THREAD_TASK_RUNNER_HANDLE_H_
+#define BASE_THREADING_THREAD_TASK_RUNNER_HANDLE_H_
+
+#include "base/base_export.h"
+#include "base/callback_helpers.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+
+namespace base {
+
+// ThreadTaskRunnerHandle stores a reference to a thread's TaskRunner
+// in thread-local storage. Callers can then retrieve the TaskRunner
+// for the current thread by calling ThreadTaskRunnerHandle::Get().
+// At most one TaskRunner may be bound to each thread at a time.
+// Prefer SequencedTaskRunnerHandle to this unless thread affinity is required.
+class BASE_EXPORT ThreadTaskRunnerHandle {
+ public:
+ // Gets the SingleThreadTaskRunner for the current thread.
+ static scoped_refptr<SingleThreadTaskRunner> Get();
+
+ // Returns true if the SingleThreadTaskRunner is already created for
+ // the current thread.
+ static bool IsSet();
+
+ // Overrides ThreadTaskRunnerHandle::Get()'s |task_runner_| to point at
+ // |overriding_task_runner| until the returned ScopedClosureRunner goes out of
+ // scope (instantiates a ThreadTaskRunnerHandle for that scope if |!IsSet()|).
+ // Nested overrides are allowed but callers must ensure the
+ // ScopedClosureRunners expire in LIFO (stack) order. Note: nesting
+ // ThreadTaskRunnerHandles isn't generally desired but it's useful in unit
+ // tests where multiple task runners can share the main thread for simplicity
+ // and determinism.
+ static ScopedClosureRunner OverrideForTesting(
+ scoped_refptr<SingleThreadTaskRunner> overriding_task_runner)
+ WARN_UNUSED_RESULT;
+
+ // Binds |task_runner| to the current thread. |task_runner| must belong
+ // to the current thread for this to succeed.
+ explicit ThreadTaskRunnerHandle(
+ scoped_refptr<SingleThreadTaskRunner> task_runner);
+ ~ThreadTaskRunnerHandle();
+
+ private:
+ scoped_refptr<SingleThreadTaskRunner> task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadTaskRunnerHandle);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_TASK_RUNNER_HANDLE_H_
diff --git a/base/threading/thread_task_runner_handle_unittest.cc b/base/threading/thread_task_runner_handle_unittest.cc
new file mode 100644
index 0000000..1aa02d1
--- /dev/null
+++ b/base/threading/thread_task_runner_handle_unittest.cc
@@ -0,0 +1,122 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_task_runner_handle.h"
+
+#include "base/memory/ref_counted.h"
+#include "base/test/gtest_util.h"
+#include "base/test/test_simple_task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(ThreadTaskRunnerHandleTest, Basic) {
+ scoped_refptr<SingleThreadTaskRunner> task_runner(new TestSimpleTaskRunner);
+
+ EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+ {
+ ThreadTaskRunnerHandle ttrh1(task_runner);
+ EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+ EXPECT_EQ(task_runner, ThreadTaskRunnerHandle::Get());
+ }
+ EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+}
+
+TEST(ThreadTaskRunnerHandleTest, DeathOnImplicitOverride) {
+ scoped_refptr<SingleThreadTaskRunner> task_runner(new TestSimpleTaskRunner);
+ scoped_refptr<SingleThreadTaskRunner> overidding_task_runner(
+ new TestSimpleTaskRunner);
+
+ ThreadTaskRunnerHandle ttrh(task_runner);
+ EXPECT_DCHECK_DEATH(
+ { ThreadTaskRunnerHandle overriding_ttrh(overidding_task_runner); });
+}
+
+TEST(ThreadTaskRunnerHandleTest, OverrideForTestingExistingTTRH) {
+ scoped_refptr<SingleThreadTaskRunner> task_runner_1(new TestSimpleTaskRunner);
+ scoped_refptr<SingleThreadTaskRunner> task_runner_2(new TestSimpleTaskRunner);
+ scoped_refptr<SingleThreadTaskRunner> task_runner_3(new TestSimpleTaskRunner);
+ scoped_refptr<SingleThreadTaskRunner> task_runner_4(new TestSimpleTaskRunner);
+
+ EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+ {
+ // TTRH in place prior to override.
+ ThreadTaskRunnerHandle ttrh1(task_runner_1);
+ EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+ EXPECT_EQ(task_runner_1, ThreadTaskRunnerHandle::Get());
+
+ {
+ // Override.
+ ScopedClosureRunner undo_override_2 =
+ ThreadTaskRunnerHandle::OverrideForTesting(task_runner_2);
+ EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+ EXPECT_EQ(task_runner_2, ThreadTaskRunnerHandle::Get());
+
+ {
+ // Nested override.
+ ScopedClosureRunner undo_override_3 =
+ ThreadTaskRunnerHandle::OverrideForTesting(task_runner_3);
+ EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+ EXPECT_EQ(task_runner_3, ThreadTaskRunnerHandle::Get());
+ }
+
+ // Back to single override.
+ EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+ EXPECT_EQ(task_runner_2, ThreadTaskRunnerHandle::Get());
+
+ {
+ // Backup to double override with another TTRH.
+ ScopedClosureRunner undo_override_4 =
+ ThreadTaskRunnerHandle::OverrideForTesting(task_runner_4);
+ EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+ EXPECT_EQ(task_runner_4, ThreadTaskRunnerHandle::Get());
+ }
+ }
+
+ // Back to simple TTRH.
+ EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+ EXPECT_EQ(task_runner_1, ThreadTaskRunnerHandle::Get());
+ }
+ EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+}
+
+TEST(ThreadTaskRunnerHandleTest, OverrideForTestingNoExistingTTRH) {
+ scoped_refptr<SingleThreadTaskRunner> task_runner_1(new TestSimpleTaskRunner);
+ scoped_refptr<SingleThreadTaskRunner> task_runner_2(new TestSimpleTaskRunner);
+
+ EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+ {
+ // Override with no TTRH in place.
+ ScopedClosureRunner undo_override_1 =
+ ThreadTaskRunnerHandle::OverrideForTesting(task_runner_1);
+ EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+ EXPECT_EQ(task_runner_1, ThreadTaskRunnerHandle::Get());
+
+ {
+ // Nested override works the same.
+ ScopedClosureRunner undo_override_2 =
+ ThreadTaskRunnerHandle::OverrideForTesting(task_runner_2);
+ EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+ EXPECT_EQ(task_runner_2, ThreadTaskRunnerHandle::Get());
+ }
+
+ // Back to single override.
+ EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet());
+ EXPECT_EQ(task_runner_1, ThreadTaskRunnerHandle::Get());
+ }
+ EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet());
+}
+
+TEST(ThreadTaskRunnerHandleTest, DeathOnTTRHOverOverride) {
+ scoped_refptr<SingleThreadTaskRunner> task_runner(new TestSimpleTaskRunner);
+ scoped_refptr<SingleThreadTaskRunner> overidding_task_runner(
+ new TestSimpleTaskRunner);
+
+ ScopedClosureRunner undo_override =
+ ThreadTaskRunnerHandle::OverrideForTesting(task_runner);
+ EXPECT_DCHECK_DEATH(
+ { ThreadTaskRunnerHandle overriding_ttrh(overidding_task_runner); });
+}
+
+} // namespace base
diff --git a/base/threading/thread_unittest.cc b/base/threading/thread_unittest.cc
new file mode 100644
index 0000000..d90b1f9
--- /dev/null
+++ b/base/threading/thread_unittest.cc
@@ -0,0 +1,579 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/debug/leak_annotations.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_current.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/gtest_util.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+using base::Thread;
+
+typedef PlatformTest ThreadTest;
+
+namespace {
+
+void ToggleValue(bool* value) {
+ ANNOTATE_BENIGN_RACE(value, "Test-only data race on boolean "
+ "in base/thread_unittest");
+ *value = !*value;
+}
+
+class SleepInsideInitThread : public Thread {
+ public:
+ SleepInsideInitThread() : Thread("none") {
+ init_called_ = false;
+ ANNOTATE_BENIGN_RACE(
+ this, "Benign test-only data race on vptr - http://crbug.com/98219");
+ }
+ ~SleepInsideInitThread() override { Stop(); }
+
+ void Init() override {
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(500));
+ init_called_ = true;
+ }
+ bool InitCalled() { return init_called_; }
+
+ private:
+ bool init_called_;
+
+ DISALLOW_COPY_AND_ASSIGN(SleepInsideInitThread);
+};
+
+enum ThreadEvent {
+ // Thread::Init() was called.
+ THREAD_EVENT_INIT = 0,
+
+ // The MessageLoop for the thread was deleted.
+ THREAD_EVENT_MESSAGE_LOOP_DESTROYED,
+
+ // Thread::CleanUp() was called.
+ THREAD_EVENT_CLEANUP,
+
+ // Keep at end of list.
+ THREAD_NUM_EVENTS
+};
+
+typedef std::vector<ThreadEvent> EventList;
+
+class CaptureToEventList : public Thread {
+ public:
+ // This Thread pushes events into the vector |event_list| to show
+ // the order they occured in. |event_list| must remain valid for the
+ // lifetime of this thread.
+ explicit CaptureToEventList(EventList* event_list)
+ : Thread("none"),
+ event_list_(event_list) {
+ }
+
+ ~CaptureToEventList() override { Stop(); }
+
+ void Init() override { event_list_->push_back(THREAD_EVENT_INIT); }
+
+ void CleanUp() override { event_list_->push_back(THREAD_EVENT_CLEANUP); }
+
+ private:
+ EventList* event_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(CaptureToEventList);
+};
+
+// Observer that writes a value into |event_list| when a message loop has been
+// destroyed.
+class CapturingDestructionObserver
+ : public base::MessageLoopCurrent::DestructionObserver {
+ public:
+ // |event_list| must remain valid throughout the observer's lifetime.
+ explicit CapturingDestructionObserver(EventList* event_list)
+ : event_list_(event_list) {
+ }
+
+ // DestructionObserver implementation:
+ void WillDestroyCurrentMessageLoop() override {
+ event_list_->push_back(THREAD_EVENT_MESSAGE_LOOP_DESTROYED);
+ event_list_ = nullptr;
+ }
+
+ private:
+ EventList* event_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(CapturingDestructionObserver);
+};
+
+// Task that adds a destruction observer to the current message loop.
+void RegisterDestructionObserver(
+ base::MessageLoopCurrent::DestructionObserver* observer) {
+ base::MessageLoopCurrent::Get()->AddDestructionObserver(observer);
+}
+
+// Task that calls GetThreadId() of |thread|, stores the result into |id|, then
+// signal |event|.
+void ReturnThreadId(base::Thread* thread,
+ base::PlatformThreadId* id,
+ base::WaitableEvent* event) {
+ *id = thread->GetThreadId();
+ event->Signal();
+}
+
+} // namespace
+
+TEST_F(ThreadTest, StartWithOptions_StackSize) {
+ Thread a("StartWithStackSize");
+ // Ensure that the thread can work with only 12 kb and still process a
+ // message. At the same time, we should scale with the bitness of the system
+ // where 12 kb is definitely not enough.
+ // 12 kb = 3072 Slots on a 32-bit system, so we'll scale based off of that.
+ Thread::Options options;
+#if defined(ADDRESS_SANITIZER) || !defined(NDEBUG)
+ // ASan bloats the stack variables and overflows the 3072 slot stack. Some
+ // debug builds also grow the stack too much.
+ options.stack_size = 2 * 3072 * sizeof(uintptr_t);
+#else
+ options.stack_size = 3072 * sizeof(uintptr_t);
+#endif
+ EXPECT_TRUE(a.StartWithOptions(options));
+ EXPECT_TRUE(a.message_loop());
+ EXPECT_TRUE(a.IsRunning());
+
+ base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ a.task_runner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(&event)));
+ event.Wait();
+}
+
+// Intentional test-only race for otherwise untestable code, won't fix.
+// https://crbug.com/634383
+#if !defined(THREAD_SANITIZER)
+TEST_F(ThreadTest, StartWithOptions_NonJoinable) {
+ Thread* a = new Thread("StartNonJoinable");
+ // Non-joinable threads have to be leaked for now (see
+ // Thread::Options::joinable for details).
+ ANNOTATE_LEAKING_OBJECT_PTR(a);
+
+ Thread::Options options;
+ options.joinable = false;
+ EXPECT_TRUE(a->StartWithOptions(options));
+ EXPECT_TRUE(a->message_loop());
+ EXPECT_TRUE(a->IsRunning());
+
+ // Without this call this test is racy. The above IsRunning() succeeds because
+ // of an early-return condition while between Start() and StopSoon(), after
+ // invoking StopSoon() below this early-return condition is no longer
+ // satisfied and the real |is_running_| bit has to be checked. It could still
+ // be false if the message loop hasn't started for real in practice. This is
+ // only a requirement for this test because the non-joinable property forces
+ // it to use StopSoon() and not wait for a complete Stop().
+ EXPECT_TRUE(a->WaitUntilThreadStarted());
+
+ // Make the thread block until |block_event| is signaled.
+ base::WaitableEvent block_event(
+ base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ a->task_runner()->PostTask(FROM_HERE,
+ base::BindOnce(&base::WaitableEvent::Wait,
+ base::Unretained(&block_event)));
+
+ a->StopSoon();
+ EXPECT_TRUE(a->IsRunning());
+
+ // Unblock the task and give a bit of extra time to unwind QuitWhenIdle().
+ block_event.Signal();
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
+
+ // The thread should now have stopped on its own.
+ EXPECT_FALSE(a->IsRunning());
+}
+#endif
+
+TEST_F(ThreadTest, TwoTasksOnJoinableThread) {
+ bool was_invoked = false;
+ {
+ Thread a("TwoTasksOnJoinableThread");
+ EXPECT_TRUE(a.Start());
+ EXPECT_TRUE(a.message_loop());
+
+ // Test that all events are dispatched before the Thread object is
+ // destroyed. We do this by dispatching a sleep event before the
+ // event that will toggle our sentinel value.
+ a.task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(static_cast<void (*)(base::TimeDelta)>(
+ &base::PlatformThread::Sleep),
+ base::TimeDelta::FromMilliseconds(20)));
+ a.task_runner()->PostTask(FROM_HERE,
+ base::BindOnce(&ToggleValue, &was_invoked));
+ }
+ EXPECT_TRUE(was_invoked);
+}
+
+TEST_F(ThreadTest, DestroyWhileRunningIsSafe) {
+ Thread a("DestroyWhileRunningIsSafe");
+ EXPECT_TRUE(a.Start());
+ EXPECT_TRUE(a.WaitUntilThreadStarted());
+}
+
+// TODO(gab): Enable this test when destroying a non-joinable Thread instance
+// is supported (proposal @ https://crbug.com/629139#c14).
+TEST_F(ThreadTest, DISABLED_DestroyWhileRunningNonJoinableIsSafe) {
+ {
+ Thread a("DestroyWhileRunningNonJoinableIsSafe");
+ Thread::Options options;
+ options.joinable = false;
+ EXPECT_TRUE(a.StartWithOptions(options));
+ EXPECT_TRUE(a.WaitUntilThreadStarted());
+ }
+
+ // Attempt to catch use-after-frees from the non-joinable thread in the
+ // scope of this test if any.
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
+}
+
+TEST_F(ThreadTest, StopSoon) {
+ Thread a("StopSoon");
+ EXPECT_TRUE(a.Start());
+ EXPECT_TRUE(a.message_loop());
+ EXPECT_TRUE(a.IsRunning());
+ a.StopSoon();
+ a.Stop();
+ EXPECT_FALSE(a.message_loop());
+ EXPECT_FALSE(a.IsRunning());
+}
+
+TEST_F(ThreadTest, StopTwiceNop) {
+ Thread a("StopTwiceNop");
+ EXPECT_TRUE(a.Start());
+ EXPECT_TRUE(a.message_loop());
+ EXPECT_TRUE(a.IsRunning());
+ a.StopSoon();
+ // Calling StopSoon() a second time should be a nop.
+ a.StopSoon();
+ a.Stop();
+ // Same with Stop().
+ a.Stop();
+ EXPECT_FALSE(a.message_loop());
+ EXPECT_FALSE(a.IsRunning());
+ // Calling them when not running should also nop.
+ a.StopSoon();
+ a.Stop();
+}
+
+// TODO(gab): Enable this test in conjunction with re-enabling the sequence
+// check in Thread::Stop() as part of http://crbug.com/629139.
+TEST_F(ThreadTest, DISABLED_StopOnNonOwningThreadIsDeath) {
+ Thread a("StopOnNonOwningThreadDeath");
+ EXPECT_TRUE(a.StartAndWaitForTesting());
+
+ Thread b("NonOwningThread");
+ b.Start();
+ EXPECT_DCHECK_DEATH({
+ // Stopping |a| on |b| isn't allowed.
+ b.task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&Thread::Stop, base::Unretained(&a)));
+ // Block here so the DCHECK on |b| always happens in this scope.
+ base::PlatformThread::Sleep(base::TimeDelta::Max());
+ });
+}
+
+TEST_F(ThreadTest, TransferOwnershipAndStop) {
+ std::unique_ptr<Thread> a =
+ std::make_unique<Thread>("TransferOwnershipAndStop");
+ EXPECT_TRUE(a->StartAndWaitForTesting());
+ EXPECT_TRUE(a->IsRunning());
+
+ Thread b("TakingOwnershipThread");
+ b.Start();
+
+ base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+ // a->DetachFromSequence() should allow |b| to use |a|'s Thread API.
+ a->DetachFromSequence();
+ b.task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(
+ [](std::unique_ptr<Thread> thread_to_stop,
+ base::WaitableEvent* event_to_signal) -> void {
+ thread_to_stop->Stop();
+ event_to_signal->Signal();
+ },
+ std::move(a), base::Unretained(&event)));
+
+ event.Wait();
+}
+
+TEST_F(ThreadTest, StartTwice) {
+ Thread a("StartTwice");
+
+ EXPECT_FALSE(a.message_loop());
+ EXPECT_FALSE(a.IsRunning());
+
+ EXPECT_TRUE(a.Start());
+ EXPECT_TRUE(a.message_loop());
+ EXPECT_TRUE(a.IsRunning());
+
+ a.Stop();
+ EXPECT_FALSE(a.message_loop());
+ EXPECT_FALSE(a.IsRunning());
+
+ EXPECT_TRUE(a.Start());
+ EXPECT_TRUE(a.message_loop());
+ EXPECT_TRUE(a.IsRunning());
+
+ a.Stop();
+ EXPECT_FALSE(a.message_loop());
+ EXPECT_FALSE(a.IsRunning());
+}
+
+// Intentional test-only race for otherwise untestable code, won't fix.
+// https://crbug.com/634383
+#if !defined(THREAD_SANITIZER)
+TEST_F(ThreadTest, StartTwiceNonJoinableNotAllowed) {
+ LOG(ERROR) << __FUNCTION__;
+ Thread* a = new Thread("StartTwiceNonJoinable");
+ // Non-joinable threads have to be leaked for now (see
+ // Thread::Options::joinable for details).
+ ANNOTATE_LEAKING_OBJECT_PTR(a);
+
+ Thread::Options options;
+ options.joinable = false;
+ EXPECT_TRUE(a->StartWithOptions(options));
+ EXPECT_TRUE(a->message_loop());
+ EXPECT_TRUE(a->IsRunning());
+
+ // Signaled when last task on |a| is processed.
+ base::WaitableEvent last_task_event(
+ base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ a->task_runner()->PostTask(
+ FROM_HERE, base::BindOnce(&base::WaitableEvent::Signal,
+ base::Unretained(&last_task_event)));
+
+ // StopSoon() is non-blocking, Yield() to |a|, wait for last task to be
+ // processed and a little more for QuitWhenIdle() to unwind before considering
+ // the thread "stopped".
+ a->StopSoon();
+ base::PlatformThread::YieldCurrentThread();
+ last_task_event.Wait();
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
+
+ // This test assumes that the above was sufficient to let the thread fully
+ // stop.
+ ASSERT_FALSE(a->IsRunning());
+
+ // Restarting it should not be allowed.
+ EXPECT_DCHECK_DEATH(a->Start());
+}
+#endif
+
+TEST_F(ThreadTest, ThreadName) {
+ Thread a("ThreadName");
+ EXPECT_TRUE(a.Start());
+ EXPECT_EQ("ThreadName", a.thread_name());
+}
+
+TEST_F(ThreadTest, ThreadId) {
+ Thread a("ThreadId0");
+ Thread b("ThreadId1");
+ a.Start();
+ b.Start();
+
+ // Post a task that calls GetThreadId() on the created thread.
+ base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ base::PlatformThreadId id_from_new_thread;
+ a.task_runner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(ReturnThreadId, &a, &id_from_new_thread, &event));
+
+ // Call GetThreadId() on the current thread before calling event.Wait() so
+ // that this test can find a race issue with TSAN.
+ base::PlatformThreadId id_from_current_thread = a.GetThreadId();
+
+ // Check if GetThreadId() returns consistent value in both threads.
+ event.Wait();
+ EXPECT_EQ(id_from_current_thread, id_from_new_thread);
+
+ // A started thread should have a valid ID.
+ EXPECT_NE(base::kInvalidThreadId, a.GetThreadId());
+ EXPECT_NE(base::kInvalidThreadId, b.GetThreadId());
+
+ // Each thread should have a different thread ID.
+ EXPECT_NE(a.GetThreadId(), b.GetThreadId());
+}
+
+TEST_F(ThreadTest, ThreadIdWithRestart) {
+ Thread a("ThreadIdWithRestart");
+ base::PlatformThreadId previous_id = base::kInvalidThreadId;
+
+ for (size_t i = 0; i < 16; ++i) {
+ EXPECT_TRUE(a.Start());
+ base::PlatformThreadId current_id = a.GetThreadId();
+ EXPECT_NE(previous_id, current_id);
+ previous_id = current_id;
+ a.Stop();
+ }
+}
+
+// Make sure Init() is called after Start() and before
+// WaitUntilThreadInitialized() returns.
+TEST_F(ThreadTest, SleepInsideInit) {
+ SleepInsideInitThread t;
+ EXPECT_FALSE(t.InitCalled());
+ t.StartAndWaitForTesting();
+ EXPECT_TRUE(t.InitCalled());
+}
+
+// Make sure that the destruction sequence is:
+//
+// (1) Thread::CleanUp()
+// (2) MessageLoop::~MessageLoop()
+// MessageLoopCurrent::DestructionObservers called.
+TEST_F(ThreadTest, CleanUp) {
+ EventList captured_events;
+ CapturingDestructionObserver loop_destruction_observer(&captured_events);
+
+ {
+ // Start a thread which writes its event into |captured_events|.
+ CaptureToEventList t(&captured_events);
+ EXPECT_TRUE(t.Start());
+ EXPECT_TRUE(t.message_loop());
+ EXPECT_TRUE(t.IsRunning());
+
+ // Register an observer that writes into |captured_events| once the
+ // thread's message loop is destroyed.
+ t.task_runner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&RegisterDestructionObserver,
+ base::Unretained(&loop_destruction_observer)));
+
+ // Upon leaving this scope, the thread is deleted.
+ }
+
+ // Check the order of events during shutdown.
+ ASSERT_EQ(static_cast<size_t>(THREAD_NUM_EVENTS), captured_events.size());
+ EXPECT_EQ(THREAD_EVENT_INIT, captured_events[0]);
+ EXPECT_EQ(THREAD_EVENT_CLEANUP, captured_events[1]);
+ EXPECT_EQ(THREAD_EVENT_MESSAGE_LOOP_DESTROYED, captured_events[2]);
+}
+
+TEST_F(ThreadTest, ThreadNotStarted) {
+ Thread a("Inert");
+ EXPECT_FALSE(a.task_runner());
+}
+
+TEST_F(ThreadTest, MultipleWaitUntilThreadStarted) {
+ Thread a("MultipleWaitUntilThreadStarted");
+ EXPECT_TRUE(a.Start());
+ // It's OK to call WaitUntilThreadStarted() multiple times.
+ EXPECT_TRUE(a.WaitUntilThreadStarted());
+ EXPECT_TRUE(a.WaitUntilThreadStarted());
+}
+
+TEST_F(ThreadTest, FlushForTesting) {
+ Thread a("FlushForTesting");
+
+ // Flushing a non-running thread should be a no-op.
+ a.FlushForTesting();
+
+ ASSERT_TRUE(a.Start());
+
+ // Flushing a thread with no tasks shouldn't block.
+ a.FlushForTesting();
+
+ constexpr base::TimeDelta kSleepPerTestTask =
+ base::TimeDelta::FromMilliseconds(50);
+ constexpr size_t kNumSleepTasks = 5;
+
+ const base::TimeTicks ticks_before_post = base::TimeTicks::Now();
+
+ for (size_t i = 0; i < kNumSleepTasks; ++i) {
+ a.task_runner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&base::PlatformThread::Sleep, kSleepPerTestTask));
+ }
+
+ // All tasks should have executed, as reflected by the elapsed time.
+ a.FlushForTesting();
+ EXPECT_GE(base::TimeTicks::Now() - ticks_before_post,
+ kNumSleepTasks * kSleepPerTestTask);
+
+ a.Stop();
+
+ // Flushing a stopped thread should be a no-op.
+ a.FlushForTesting();
+}
+
+namespace {
+
+// A Thread which uses a MessageLoop on the stack. It won't start a real
+// underlying thread (instead its messages can be processed by a RunLoop on the
+// stack).
+class ExternalMessageLoopThread : public Thread {
+ public:
+ ExternalMessageLoopThread() : Thread("ExternalMessageLoopThread") {}
+
+ ~ExternalMessageLoopThread() override { Stop(); }
+
+ void InstallMessageLoop() { SetMessageLoop(&external_message_loop_); }
+
+ void VerifyUsingExternalMessageLoop(
+ bool expected_using_external_message_loop) {
+ EXPECT_EQ(expected_using_external_message_loop,
+ using_external_message_loop());
+ }
+
+ private:
+ base::MessageLoop external_message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExternalMessageLoopThread);
+};
+
+} // namespace
+
+TEST_F(ThreadTest, ExternalMessageLoop) {
+ ExternalMessageLoopThread a;
+ EXPECT_FALSE(a.message_loop());
+ EXPECT_FALSE(a.IsRunning());
+ a.VerifyUsingExternalMessageLoop(false);
+
+ a.InstallMessageLoop();
+ EXPECT_TRUE(a.message_loop());
+ EXPECT_TRUE(a.IsRunning());
+ a.VerifyUsingExternalMessageLoop(true);
+
+ bool ran = false;
+ a.task_runner()->PostTask(
+ FROM_HERE, base::BindOnce([](bool* toggled) { *toggled = true; }, &ran));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(ran);
+
+ a.Stop();
+ EXPECT_FALSE(a.message_loop());
+ EXPECT_FALSE(a.IsRunning());
+ a.VerifyUsingExternalMessageLoop(true);
+
+ // Confirm that running any remaining tasks posted from Stop() goes smoothly
+ // (e.g. https://codereview.chromium.org/2135413003/#ps300001 crashed if
+ // StopSoon() posted Thread::ThreadQuitHelper() while |run_loop_| was null).
+ base::RunLoop().RunUntilIdle();
+}
diff --git a/base/threading/watchdog.cc b/base/threading/watchdog.cc
new file mode 100644
index 0000000..6c384b1
--- /dev/null
+++ b/base/threading/watchdog.cc
@@ -0,0 +1,187 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/watchdog.h"
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+namespace {
+
+// When the debugger breaks (when we alarm), all the other alarms that are
+// armed will expire (also alarm). To diminish this effect, we track any
+// delay due to debugger breaks, and we *try* to adjust the effective start
+// time of other alarms to step past the debugging break.
+// Without this safety net, any alarm will typically trigger a host of follow
+// on alarms from callers that specify old times.
+
+struct StaticData {
+ // Lock for access of static data...
+ Lock lock;
+
+ // When did we last alarm and get stuck (for a while) in a debugger?
+ TimeTicks last_debugged_alarm_time;
+
+ // How long did we sit on a break in the debugger?
+ TimeDelta last_debugged_alarm_delay;
+};
+
+StaticData* GetStaticData() {
+ static auto* static_data = new StaticData();
+ return static_data;
+}
+
+} // namespace
+
+// Start thread running in a Disarmed state.
+Watchdog::Watchdog(const TimeDelta& duration,
+ const std::string& thread_watched_name,
+ bool enabled)
+ : enabled_(enabled),
+ lock_(),
+ condition_variable_(&lock_),
+ state_(DISARMED),
+ duration_(duration),
+ thread_watched_name_(thread_watched_name),
+ delegate_(this) {
+ if (!enabled_)
+ return; // Don't start thread, or doing anything really.
+ enabled_ = PlatformThread::Create(0, // Default stack size.
+ &delegate_,
+ &handle_);
+ DCHECK(enabled_);
+}
+
+// Notify watchdog thread, and wait for it to finish up.
+Watchdog::~Watchdog() {
+ if (!enabled_)
+ return;
+ if (!IsJoinable())
+ Cleanup();
+ condition_variable_.Signal();
+ PlatformThread::Join(handle_);
+}
+
+void Watchdog::Cleanup() {
+ if (!enabled_)
+ return;
+ {
+ AutoLock lock(lock_);
+ state_ = SHUTDOWN;
+ }
+ condition_variable_.Signal();
+}
+
+bool Watchdog::IsJoinable() {
+ if (!enabled_)
+ return true;
+ AutoLock lock(lock_);
+ return (state_ == JOINABLE);
+}
+
+void Watchdog::Arm() {
+ ArmAtStartTime(TimeTicks::Now());
+}
+
+void Watchdog::ArmSomeTimeDeltaAgo(const TimeDelta& time_delta) {
+ ArmAtStartTime(TimeTicks::Now() - time_delta);
+}
+
+// Start clock for watchdog.
+void Watchdog::ArmAtStartTime(const TimeTicks start_time) {
+ {
+ AutoLock lock(lock_);
+ start_time_ = start_time;
+ state_ = ARMED;
+ }
+ // Force watchdog to wake up, and go to sleep with the timer ticking with the
+ // proper duration.
+ condition_variable_.Signal();
+}
+
+// Disable watchdog so that it won't do anything when time expires.
+void Watchdog::Disarm() {
+ AutoLock lock(lock_);
+ state_ = DISARMED;
+ // We don't need to signal, as the watchdog will eventually wake up, and it
+ // will check its state and time, and act accordingly.
+}
+
+void Watchdog::Alarm() {
+ DVLOG(1) << "Watchdog alarmed for " << thread_watched_name_;
+}
+
+//------------------------------------------------------------------------------
+// Internal private methods that the watchdog thread uses.
+
+void Watchdog::ThreadDelegate::ThreadMain() {
+ SetThreadName();
+ TimeDelta remaining_duration;
+ StaticData* static_data = GetStaticData();
+ while (1) {
+ AutoLock lock(watchdog_->lock_);
+ while (DISARMED == watchdog_->state_)
+ watchdog_->condition_variable_.Wait();
+ if (SHUTDOWN == watchdog_->state_) {
+ watchdog_->state_ = JOINABLE;
+ return;
+ }
+ DCHECK(ARMED == watchdog_->state_);
+ remaining_duration = watchdog_->duration_ -
+ (TimeTicks::Now() - watchdog_->start_time_);
+ if (remaining_duration.InMilliseconds() > 0) {
+ // Spurios wake? Timer drifts? Go back to sleep for remaining time.
+ watchdog_->condition_variable_.TimedWait(remaining_duration);
+ continue;
+ }
+ // We overslept, so this seems like a real alarm.
+ // Watch out for a user that stopped the debugger on a different alarm!
+ {
+ AutoLock static_lock(static_data->lock);
+ if (static_data->last_debugged_alarm_time > watchdog_->start_time_) {
+ // False alarm: we started our clock before the debugger break (last
+ // alarm time).
+ watchdog_->start_time_ += static_data->last_debugged_alarm_delay;
+ if (static_data->last_debugged_alarm_time > watchdog_->start_time_)
+ // Too many alarms must have taken place.
+ watchdog_->state_ = DISARMED;
+ continue;
+ }
+ }
+ watchdog_->state_ = DISARMED; // Only alarm at most once.
+ TimeTicks last_alarm_time = TimeTicks::Now();
+ {
+ AutoUnlock unlock(watchdog_->lock_);
+ watchdog_->Alarm(); // Set a break point here to debug on alarms.
+ }
+ TimeDelta last_alarm_delay = TimeTicks::Now() - last_alarm_time;
+ if (last_alarm_delay <= TimeDelta::FromMilliseconds(2))
+ continue;
+ // Ignore race of two alarms/breaks going off at roughly the same time.
+ AutoLock static_lock(static_data->lock);
+ // This was a real debugger break.
+ static_data->last_debugged_alarm_time = last_alarm_time;
+ static_data->last_debugged_alarm_delay = last_alarm_delay;
+ }
+}
+
+void Watchdog::ThreadDelegate::SetThreadName() const {
+ std::string name = watchdog_->thread_watched_name_ + " Watchdog";
+ PlatformThread::SetName(name);
+ DVLOG(1) << "Watchdog active: " << name;
+}
+
+// static
+void Watchdog::ResetStaticData() {
+ StaticData* static_data = GetStaticData();
+ AutoLock lock(static_data->lock);
+ // See https://crbug.com/734232 for why this cannot be zero-initialized.
+ static_data->last_debugged_alarm_time = TimeTicks::Min();
+ static_data->last_debugged_alarm_delay = TimeDelta();
+}
+
+} // namespace base
diff --git a/base/threading/watchdog.h b/base/threading/watchdog.h
new file mode 100644
index 0000000..f806984
--- /dev/null
+++ b/base/threading/watchdog.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The Watchdog class creates a second thread that can Alarm if a specific
+// duration of time passes without proper attention. The duration of time is
+// specified at construction time. The Watchdog may be used many times by
+// simply calling Arm() (to start timing) and Disarm() (to reset the timer).
+// The Watchdog is typically used under a debugger, where the stack traces on
+// other threads can be examined if/when the Watchdog alarms.
+
+// Some watchdogs will be enabled or disabled via command line switches. To
+// facilitate such code, an "enabled" argument for the constuctor can be used
+// to permanently disable the watchdog. Disabled watchdogs don't even spawn
+// a second thread, and their methods call (Arm() and Disarm()) return very
+// quickly.
+
+#ifndef BASE_THREADING_WATCHDOG_H_
+#define BASE_THREADING_WATCHDOG_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+
+namespace base {
+
+class BASE_EXPORT Watchdog {
+ public:
+ // Constructor specifies how long the Watchdog will wait before alarming.
+ Watchdog(const TimeDelta& duration,
+ const std::string& thread_watched_name,
+ bool enabled);
+ virtual ~Watchdog();
+
+ // Notify watchdog thread to finish up. Sets the state_ to SHUTDOWN.
+ void Cleanup();
+
+ // Returns true if we state_ is JOINABLE (which indicates that Watchdog has
+ // exited).
+ bool IsJoinable();
+
+ // Start timing, and alarm when time expires (unless we're disarm()ed.)
+ void Arm(); // Arm starting now.
+ void ArmSomeTimeDeltaAgo(const TimeDelta& time_delta);
+ void ArmAtStartTime(const TimeTicks start_time);
+
+ // Reset time, and do not set off the alarm.
+ void Disarm();
+
+ // Alarm is called if the time expires after an Arm() without someone calling
+ // Disarm(). This method can be overridden to create testable classes.
+ virtual void Alarm();
+
+ // Reset static data to initial state. Useful for tests, to ensure
+ // they are independent.
+ static void ResetStaticData();
+
+ private:
+ class ThreadDelegate : public PlatformThread::Delegate {
+ public:
+ explicit ThreadDelegate(Watchdog* watchdog) : watchdog_(watchdog) {
+ }
+ void ThreadMain() override;
+
+ private:
+ void SetThreadName() const;
+
+ Watchdog* watchdog_;
+ };
+
+ enum State {ARMED, DISARMED, SHUTDOWN, JOINABLE };
+
+ bool enabled_;
+
+ Lock lock_; // Mutex for state_.
+ ConditionVariable condition_variable_;
+ State state_;
+ const TimeDelta duration_; // How long after start_time_ do we alarm?
+ const std::string thread_watched_name_;
+ PlatformThreadHandle handle_;
+ ThreadDelegate delegate_; // Store it, because it must outlive the thread.
+
+ TimeTicks start_time_; // Start of epoch, and alarm after duration_.
+
+ DISALLOW_COPY_AND_ASSIGN(Watchdog);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_WATCHDOG_H_
diff --git a/base/threading/watchdog_unittest.cc b/base/threading/watchdog_unittest.cc
new file mode 100644
index 0000000..f534a86
--- /dev/null
+++ b/base/threading/watchdog_unittest.cc
@@ -0,0 +1,141 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/watchdog.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/synchronization/spin_wait.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+//------------------------------------------------------------------------------
+// Provide a derived class to facilitate testing.
+
+class WatchdogCounter : public Watchdog {
+ public:
+ WatchdogCounter(const TimeDelta& duration,
+ const std::string& thread_watched_name,
+ bool enabled)
+ : Watchdog(duration, thread_watched_name, enabled),
+ alarm_counter_(0) {
+ }
+
+ ~WatchdogCounter() override = default;
+
+ void Alarm() override {
+ alarm_counter_++;
+ Watchdog::Alarm();
+ }
+
+ int alarm_counter() { return alarm_counter_; }
+
+ private:
+ int alarm_counter_;
+
+ DISALLOW_COPY_AND_ASSIGN(WatchdogCounter);
+};
+
+class WatchdogTest : public testing::Test {
+ public:
+ void SetUp() override { Watchdog::ResetStaticData(); }
+};
+
+} // namespace
+
+//------------------------------------------------------------------------------
+// Actual tests
+
+// Minimal constructor/destructor test.
+TEST_F(WatchdogTest, StartupShutdownTest) {
+ Watchdog watchdog1(TimeDelta::FromMilliseconds(300), "Disabled", false);
+ Watchdog watchdog2(TimeDelta::FromMilliseconds(300), "Enabled", true);
+}
+
+// Test ability to call Arm and Disarm repeatedly.
+TEST_F(WatchdogTest, ArmDisarmTest) {
+ Watchdog watchdog1(TimeDelta::FromMilliseconds(300), "Disabled", false);
+ watchdog1.Arm();
+ watchdog1.Disarm();
+ watchdog1.Arm();
+ watchdog1.Disarm();
+
+ Watchdog watchdog2(TimeDelta::FromMilliseconds(300), "Enabled", true);
+ watchdog2.Arm();
+ watchdog2.Disarm();
+ watchdog2.Arm();
+ watchdog2.Disarm();
+}
+
+// Make sure a basic alarm fires when the time has expired.
+TEST_F(WatchdogTest, AlarmTest) {
+ WatchdogCounter watchdog(TimeDelta::FromMilliseconds(10), "Enabled", true);
+ watchdog.Arm();
+ SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5),
+ watchdog.alarm_counter() > 0);
+ EXPECT_EQ(1, watchdog.alarm_counter());
+}
+
+// Make sure a basic alarm fires when the time has expired.
+TEST_F(WatchdogTest, AlarmPriorTimeTest) {
+ WatchdogCounter watchdog(TimeDelta(), "Enabled2", true);
+ // Set a time in the past.
+ watchdog.ArmSomeTimeDeltaAgo(TimeDelta::FromSeconds(2));
+ // It should instantly go off, but certainly in less than 5 minutes.
+ SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5),
+ watchdog.alarm_counter() > 0);
+
+ EXPECT_EQ(1, watchdog.alarm_counter());
+}
+
+// Make sure a disable alarm does nothing, even if we arm it.
+TEST_F(WatchdogTest, ConstructorDisabledTest) {
+ WatchdogCounter watchdog(TimeDelta::FromMilliseconds(10), "Disabled", false);
+ watchdog.Arm();
+ // Alarm should not fire, as it was disabled.
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(500));
+ EXPECT_EQ(0, watchdog.alarm_counter());
+}
+
+// Make sure Disarming will prevent firing, even after Arming.
+TEST_F(WatchdogTest, DisarmTest) {
+ WatchdogCounter watchdog(TimeDelta::FromSeconds(1), "Enabled3", true);
+
+ TimeTicks start = TimeTicks::Now();
+ watchdog.Arm();
+ // Sleep a bit, but not past the alarm point.
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+ watchdog.Disarm();
+ TimeTicks end = TimeTicks::Now();
+
+ if (end - start > TimeDelta::FromMilliseconds(500)) {
+ LOG(WARNING) << "100ms sleep took over 500ms, making the results of this "
+ << "timing-sensitive test suspicious. Aborting now.";
+ return;
+ }
+
+ // Alarm should not have fired before it was disarmed.
+ EXPECT_EQ(0, watchdog.alarm_counter());
+
+ // Sleep past the point where it would have fired if it wasn't disarmed,
+ // and verify that it didn't fire.
+ PlatformThread::Sleep(TimeDelta::FromSeconds(1));
+ EXPECT_EQ(0, watchdog.alarm_counter());
+
+ // ...but even after disarming, we can still use the alarm...
+ // Set a time greater than the timeout into the past.
+ watchdog.ArmSomeTimeDeltaAgo(TimeDelta::FromSeconds(10));
+ // It should almost instantly go off, but certainly in less than 5 minutes.
+ SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5),
+ watchdog.alarm_counter() > 0);
+
+ EXPECT_EQ(1, watchdog.alarm_counter());
+}
+
+} // namespace base