Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1,335 @@
// Copyright 2020 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/hang_watcher.h"
#include <algorithm>
#include <atomic>
#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/debug/dump_without_crashing.h"
#include "base/feature_list.h"
#include "base/no_destructor.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_checker.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
namespace base {
// static
const base::Feature HangWatcher::kEnableHangWatcher{
"EnableHangWatcher", base::FEATURE_DISABLED_BY_DEFAULT};
const base::TimeDelta HangWatchScope::kDefaultHangWatchTime =
base::TimeDelta::FromSeconds(10);
namespace {
HangWatcher* g_instance = nullptr;
}
constexpr const char* kThreadName = "HangWatcher";
// The time that the HangWatcher thread will sleep for between calls to
// Monitor(). Increasing or decreasing this does not modify the type of hangs
// that can be detected. It instead increases the probability that a call to
// Monitor() will happen at the right time to catch a hang. This has to be
// balanced with power/cpu use concerns as busy looping would catch amost all
// hangs but present unacceptable overhead.
const base::TimeDelta kMonitoringPeriod = base::TimeDelta::FromSeconds(10);
HangWatchScope::HangWatchScope(TimeDelta timeout) {
internal::HangWatchState* current_hang_watch_state =
internal::HangWatchState::GetHangWatchStateForCurrentThread()->Get();
// TODO(crbug.com/1034046): Remove when all threads using HangWatchScope are
// monitored. Thread is not monitored, noop.
if (!current_hang_watch_state) {
return;
}
DCHECK(current_hang_watch_state)
<< "A scope can only be used on a thread that "
"registered for hang watching with HangWatcher::RegisterThread.";
#if DCHECK_IS_ON()
previous_scope_ = current_hang_watch_state->GetCurrentHangWatchScope();
current_hang_watch_state->SetCurrentHangWatchScope(this);
#endif
// TODO(crbug.com/1034046): Check whether we are over deadline already for the
// previous scope here by issuing only one TimeTicks::Now() and resuing the
// value.
previous_deadline_ = current_hang_watch_state->GetDeadline();
TimeTicks deadline = TimeTicks::Now() + timeout;
current_hang_watch_state->SetDeadline(deadline);
}
HangWatchScope::~HangWatchScope() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
internal::HangWatchState* current_hang_watch_state =
internal::HangWatchState::GetHangWatchStateForCurrentThread()->Get();
// TODO(crbug.com/1034046): Remove when all threads using HangWatchScope are
// monitored. Thread is not monitored, noop.
if (!current_hang_watch_state) {
return;
}
// If a hang is currently being captured we should block here so execution
// stops and the relevant stack frames are recorded.
base::HangWatcher::GetInstance()->BlockIfCaptureInProgress();
#if DCHECK_IS_ON()
// Verify that no Scope was destructed out of order.
DCHECK_EQ(this, current_hang_watch_state->GetCurrentHangWatchScope());
current_hang_watch_state->SetCurrentHangWatchScope(previous_scope_);
#endif
// Reset the deadline to the value it had before entering this scope.
current_hang_watch_state->SetDeadline(previous_deadline_);
// TODO(crbug.com/1034046): Log when a HangWatchScope exits after its deadline
// and that went undetected by the HangWatcher.
}
HangWatcher::HangWatcher(RepeatingClosure on_hang_closure)
: monitor_period_(kMonitoringPeriod),
should_monitor_(WaitableEvent::ResetPolicy::AUTOMATIC),
on_hang_closure_(std::move(on_hang_closure)),
thread_(this, kThreadName) {
// |thread_checker_| should not be bound to the constructing thread.
DETACH_FROM_THREAD(thread_checker_);
should_monitor_.declare_only_used_while_idle();
DCHECK(!g_instance);
g_instance = this;
Start();
}
HangWatcher::~HangWatcher() {
DCHECK_EQ(g_instance, this);
DCHECK(watch_states_.empty());
g_instance = nullptr;
Stop();
}
void HangWatcher::Start() {
thread_.Start();
}
void HangWatcher::Stop() {
keep_monitoring_.store(false, std::memory_order_relaxed);
should_monitor_.Signal();
thread_.Join();
}
bool HangWatcher::IsWatchListEmpty() {
AutoLock auto_lock(watch_state_lock_);
return watch_states_.empty();
}
void HangWatcher::Run() {
// Monitor() should only run on |thread_|. Bind |thread_checker_| here to make
// sure of that.
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
while (keep_monitoring_.load(std::memory_order_relaxed)) {
// If there is nothing to watch sleep until there is.
if (IsWatchListEmpty()) {
should_monitor_.Wait();
} else {
Monitor();
}
if (keep_monitoring_.load(std::memory_order_relaxed)) {
// Sleep until next scheduled monitoring.
should_monitor_.TimedWait(monitor_period_);
}
}
}
// static
HangWatcher* HangWatcher::GetInstance() {
return g_instance;
}
// static
void HangWatcher::RecordHang() {
base::debug::DumpWithoutCrashing();
// Defining |inhibit_tail_call_optimization| *after* calling
// DumpWithoutCrashing() prevents tail call optimization from omitting this
// function's address on the stack.
volatile int inhibit_tail_call_optimization = __LINE__;
ALLOW_UNUSED_LOCAL(inhibit_tail_call_optimization);
}
ScopedClosureRunner HangWatcher::RegisterThread() {
AutoLock auto_lock(watch_state_lock_);
watch_states_.push_back(
internal::HangWatchState::CreateHangWatchStateForCurrentThread());
// Now that there is a thread to monitor we wake the HangWatcher thread.
if (watch_states_.size() == 1) {
should_monitor_.Signal();
}
return ScopedClosureRunner(BindOnce(&HangWatcher::UnregisterThread,
Unretained(HangWatcher::GetInstance())));
}
void HangWatcher::Monitor() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
bool must_invoke_hang_closure = false;
{
AutoLock auto_lock(watch_state_lock_);
for (const auto& watch_state : watch_states_) {
if (watch_state->IsOverDeadline()) {
must_invoke_hang_closure = true;
break;
}
}
}
if (must_invoke_hang_closure) {
capture_in_progress.store(true, std::memory_order_relaxed);
base::AutoLock scope_lock(capture_lock_);
// Invoke the closure outside the scope of |watch_state_lock_|
// to prevent lock reentrancy.
on_hang_closure_.Run();
capture_in_progress.store(false, std::memory_order_relaxed);
}
if (after_monitor_closure_for_testing_) {
after_monitor_closure_for_testing_.Run();
}
}
void HangWatcher::SetAfterMonitorClosureForTesting(
base::RepeatingClosure closure) {
after_monitor_closure_for_testing_ = std::move(closure);
}
void HangWatcher::SetMonitoringPeriodForTesting(base::TimeDelta period) {
monitor_period_ = period;
}
void HangWatcher::SignalMonitorEventForTesting() {
should_monitor_.Signal();
}
void HangWatcher::BlockIfCaptureInProgress() {
// Makes a best-effort attempt to block execution if a hang is currently being
// captured.Only block on |capture_lock| if |capture_in_progress| hints that
// it's already held to avoid serializing all threads on this function when no
// hang capture is in-progress.
if (capture_in_progress.load(std::memory_order_relaxed)) {
base::AutoLock hang_lock(capture_lock_);
}
}
void HangWatcher::UnregisterThread() {
AutoLock auto_lock(watch_state_lock_);
internal::HangWatchState* current_hang_watch_state =
internal::HangWatchState::GetHangWatchStateForCurrentThread()->Get();
auto it =
std::find_if(watch_states_.cbegin(), watch_states_.cend(),
[current_hang_watch_state](
const std::unique_ptr<internal::HangWatchState>& state) {
return state.get() == current_hang_watch_state;
});
// Thread should be registered to get unregistered.
DCHECK(it != watch_states_.end());
watch_states_.erase(it);
}
namespace internal {
// |deadline_| starts at Max() to avoid validation problems
// when setting the first legitimate value.
HangWatchState::HangWatchState() {
// There should not exist a state object for this thread already.
DCHECK(!GetHangWatchStateForCurrentThread()->Get());
// Bind the new instance to this thread.
GetHangWatchStateForCurrentThread()->Set(this);
}
HangWatchState::~HangWatchState() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK_EQ(GetHangWatchStateForCurrentThread()->Get(), this);
GetHangWatchStateForCurrentThread()->Set(nullptr);
#if DCHECK_IS_ON()
// Destroying the HangWatchState should not be done if there are live
// HangWatchScopes.
DCHECK(!current_hang_watch_scope_);
#endif
}
// static
std::unique_ptr<HangWatchState>
HangWatchState::CreateHangWatchStateForCurrentThread() {
// Allocate a watch state object for this thread.
std::unique_ptr<HangWatchState> hang_state =
std::make_unique<HangWatchState>();
// Setting the thread local worked.
DCHECK_EQ(GetHangWatchStateForCurrentThread()->Get(), hang_state.get());
// Transfer ownership to caller.
return hang_state;
}
TimeTicks HangWatchState::GetDeadline() const {
return deadline_.load(std::memory_order_relaxed);
}
void HangWatchState::SetDeadline(TimeTicks deadline) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
deadline_.store(deadline, std::memory_order_relaxed);
}
bool HangWatchState::IsOverDeadline() const {
return TimeTicks::Now() > deadline_.load(std::memory_order_relaxed);
}
#if DCHECK_IS_ON()
void HangWatchState::SetCurrentHangWatchScope(HangWatchScope* scope) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
current_hang_watch_scope_ = scope;
}
HangWatchScope* HangWatchState::GetCurrentHangWatchScope() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return current_hang_watch_scope_;
}
#endif
// static
ThreadLocalPointer<HangWatchState>*
HangWatchState::GetHangWatchStateForCurrentThread() {
static NoDestructor<ThreadLocalPointer<HangWatchState>> hang_watch_state;
return hang_watch_state.get();
}
} // namespace internal
} // namespace base

View file

@ -0,0 +1,249 @@
// Copyright 2020 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_HANG_WATCHER_H_
#define BASE_THREADING_HANG_WATCHER_H_
#include <atomic>
#include <memory>
#include <vector>
#include "base/atomicops.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/feature_list.h"
#include "base/synchronization/lock.h"
#include "base/threading/simple_thread.h"
#include "base/threading/thread_checker.h"
#include "base/threading/thread_local.h"
#include "base/time/time.h"
namespace base {
class HangWatchScope;
namespace internal {
class HangWatchState;
} // namespace internal
} // namespace base
namespace base {
// Instantiate a HangWatchScope in a scope to register to be
// watched for hangs of more than |timeout| by the HangWatcher.
//
// Example usage:
//
// void FooBar(){
// HangWatchScope scope(base::TimeDelta::FromSeconds(5));
// DoSomeWork();
// }
//
// If DoSomeWork() takes more than 5s to run and the HangWatcher
// inspects the thread state before Foobar returns a hang will be
// reported.
//
// HangWatchScopes are typically meant to live on the stack. In some cases it's
// necessary to keep a HangWatchScope instance as a class member but special
// care is required when doing so as a HangWatchScope that stays alive longer
// than intended will generate non-actionable hang reports.
class BASE_EXPORT HangWatchScope {
public:
// A good default value needs to be large enough to represent a significant
// hang and avoid noise while being small enough to not exclude too many
// hangs. The nature of the work that gets executed on the thread is also
// important. We can be much stricter when monitoring a UI thread compared tp
// a ThreadPool thread for example.
static const base::TimeDelta kDefaultHangWatchTime;
// Constructing/destructing thread must be the same thread.
explicit HangWatchScope(TimeDelta timeout);
~HangWatchScope();
HangWatchScope(const HangWatchScope&) = delete;
HangWatchScope& operator=(const HangWatchScope&) = delete;
private:
// This object should always be constructed and destructed on the same thread.
THREAD_CHECKER(thread_checker_);
// The deadline set by the previous HangWatchScope created on this thread.
// Stored so it can be restored when this HangWatchScope is destroyed.
TimeTicks previous_deadline_;
#if DCHECK_IS_ON()
// The previous HangWatchScope created on this thread.
HangWatchScope* previous_scope_;
#endif
};
// Monitors registered threads for hangs by inspecting their associated
// HangWatchStates for deadline overruns. This happens at a regular interval on
// a separate thread. Only one instance of HangWatcher can exist at a time
// within a single process. This instance must outlive all monitored threads.
class BASE_EXPORT HangWatcher : public DelegateSimpleThread::Delegate {
public:
static const base::Feature kEnableHangWatcher;
// The first invocation of the constructor will set the global instance
// accessible through GetInstance(). This means that only one instance can
// exist at a time.
explicit HangWatcher(RepeatingClosure on_hang_closure);
// Clears the global instance for the class.
~HangWatcher() override;
HangWatcher(const HangWatcher&) = delete;
HangWatcher& operator=(const HangWatcher&) = delete;
// Returns a non-owning pointer to the global HangWatcher instance.
static HangWatcher* GetInstance();
// Invoke base::debug::DumpWithoutCrashing() insuring that the stack frame
// right under it in the trace belongs to HangWatcher for easier attribution.
NOINLINE static void RecordHang();
// Sets up the calling thread to be monitored for threads. Returns a
// ScopedClosureRunner that unregisters the thread. This closure has to be
// called from the registered thread before it's joined.
ScopedClosureRunner RegisterThread()
LOCKS_EXCLUDED(watch_state_lock_) WARN_UNUSED_RESULT;
// Choose a closure to be run at the end of each call to Monitor(). Use only
// for testing.
void SetAfterMonitorClosureForTesting(base::RepeatingClosure closure);
// Set a monitoring period other than the default. Use only for
// testing.
void SetMonitoringPeriodForTesting(base::TimeDelta period);
// Force the monitoring loop to resume and evaluate whether to continue.
// This can trigger a call to Monitor() or not depending on why the
// HangWatcher thread is sleeping. Use only for testing.
void SignalMonitorEventForTesting();
// Use to block until the hang is recorded. Allows the caller to halt
// execution so it does not overshoot the hang watch target and result in a
// non-actionable stack trace in the crash recorded.
void BlockIfCaptureInProgress();
private:
THREAD_CHECKER(thread_checker_);
// Inspects the state of all registered threads to check if they are hung and
// invokes the appropriate closure if so.
void Monitor();
// Call Run() on the HangWatcher thread.
void Start();
// Stop all monitoring and join the HangWatcher thread.
void Stop();
// Run the loop that periodically monitors the registered thread at a
// set time interval.
void Run() override;
base::TimeDelta monitor_period_;
// Indicates whether Run() should return after the next monitoring.
std::atomic<bool> keep_monitoring_{true};
// Use to make the HangWatcher thread wake or sleep to schedule the
// appropriate monitoring frequency.
WaitableEvent should_monitor_;
bool IsWatchListEmpty() LOCKS_EXCLUDED(watch_state_lock_);
// Stops hang watching on the calling thread by removing the entry from the
// watch list.
void UnregisterThread() LOCKS_EXCLUDED(watch_state_lock_);
const RepeatingClosure on_hang_closure_;
Lock watch_state_lock_;
std::vector<std::unique_ptr<internal::HangWatchState>> watch_states_
GUARDED_BY(watch_state_lock_);
base::DelegateSimpleThread thread_;
base::RepeatingClosure after_monitor_closure_for_testing_;
base::Lock capture_lock_;
std::atomic<bool> capture_in_progress{false};
FRIEND_TEST_ALL_PREFIXES(HangWatcherTest, NestedScopes);
};
// Classes here are exposed in the header only for testing. They are not
// intended to be used outside of base.
namespace internal {
// Contains the information necessary for hang watching a specific
// thread. Instances of this class are accessed concurrently by the associated
// thread and the HangWatcher. The HangWatcher owns instances of this
// class and outside of it they are accessed through
// GetHangWatchStateForCurrentThread().
class BASE_EXPORT HangWatchState {
public:
HangWatchState();
~HangWatchState();
HangWatchState(const HangWatchState&) = delete;
HangWatchState& operator=(const HangWatchState&) = delete;
// Allocates a new state object bound to the calling thread and returns an
// owning pointer to it.
static std::unique_ptr<HangWatchState> CreateHangWatchStateForCurrentThread();
// Retrieves the hang watch state associated with the calling thread.
// Returns nullptr if no HangWatchState exists for the current thread (see
// CreateHangWatchStateForCurrentThread()).
static ThreadLocalPointer<HangWatchState>*
GetHangWatchStateForCurrentThread();
// Returns the value of the current deadline. Use this function if you need to
// store the value. To test if the deadline has expired use IsOverDeadline().
TimeTicks GetDeadline() const;
// Atomically sets the deadline to a new value and return the previous value.
void SetDeadline(TimeTicks deadline);
// Tests whether the associated thread's execution has gone over the deadline.
bool IsOverDeadline() const;
#if DCHECK_IS_ON()
// Saves the supplied HangWatchScope as the currently active scope.
void SetCurrentHangWatchScope(HangWatchScope* scope);
// Retrieve the currently active scope.
HangWatchScope* GetCurrentHangWatchScope();
#endif
private:
// The thread that creates the instance should be the class that updates
// the deadline.
THREAD_CHECKER(thread_checker_);
// If the deadline fails to be updated before TimeTicks::Now() ever
// reaches the value contained in it this constistutes a hang.
std::atomic<TimeTicks> deadline_{base::TimeTicks::Max()};
#if DCHECK_IS_ON()
// Used to keep track of the current HangWatchScope and detect improper usage.
// Scopes should always be destructed in reverse order from the one they were
// constructed in. Example of improper use:
//
// {
// std::unique_ptr<Scope> scope = std::make_unique<Scope>(...);
// Scope other_scope;
// |scope| gets deallocated first, violating reverse destruction order.
// scope.reset();
// }
HangWatchScope* current_hang_watch_scope_{nullptr};
#endif
};
} // namespace internal
} // namespace base
#endif // BASE_THREADING_HANG_WATCHER_H_

View file

@ -0,0 +1,50 @@
// Copyright 2018 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 <memory>
#include "base/feature_list.h"
namespace base {
namespace {
// Whether thread priorities should be used. When disabled,
// PlatformThread::SetCurrentThreadPriority() no-ops.
const Feature kThreadPrioritiesFeature{"ThreadPriorities",
FEATURE_ENABLED_BY_DEFAULT};
// Whether thread priorities should be used.
//
// PlatformThread::SetCurrentThreadPriority() doesn't query the state of the
// feature directly because FeatureList initialization is not always
// synchronized with PlatformThread::SetCurrentThreadPriority().
std::atomic<bool> g_use_thread_priorities(true);
} // namespace
// static
void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) {
if (g_use_thread_priorities.load())
SetCurrentThreadPriorityImpl(priority);
}
namespace internal {
void InitializeThreadPrioritiesFeature() {
// A DCHECK is triggered on FeatureList initialization if the state of a
// feature has been checked before. To avoid triggering this DCHECK in unit
// tests that call this before initializing the FeatureList, only check the
// state of the feature if the FeatureList is initialized.
if (FeatureList::GetInstance() &&
!FeatureList::IsEnabled(kThreadPrioritiesFeature)) {
g_use_thread_priorities.store(false);
}
}
} // namespace internal
} // namespace base

View file

@ -0,0 +1,259 @@
// 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 (real-time; ignores time overrides).
// Note: The sleep duration may be in base::Time or base::TimeTicks, depending
// on platform. If you're looking to use this in unit tests testing delayed
// tasks, this will be unreliable - instead, use
// base::test::TaskEnvironment with MOCK_TIME mode.
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() should be able to increase the
// priority of a thread to |priority|.
static bool CanIncreaseThreadPriority(ThreadPriority priority);
// 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
// Returns the default thread stack size set by chrome. If we do not
// explicitly set default size then returns 0.
static size_t GetDefaultThreadStackSize();
private:
static void SetCurrentThreadPriorityImpl(ThreadPriority priority);
DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread);
};
namespace internal {
// Initializes the "ThreadPriorities" feature. The feature state is only taken
// into account after this initialization. This initialization must be
// synchronized with calls to PlatformThread::SetCurrentThreadPriority().
void InitializeThreadPrioritiesFeature();
} // namespace internal
} // namespace base
#endif // BASE_THREADING_PLATFORM_THREAD_H_

View file

@ -0,0 +1,100 @@
// 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/base_jni_headers/ThreadUtils_jni.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"
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},
};
Optional<bool> CanIncreaseCurrentThreadPriorityForPlatform(
ThreadPriority priority) {
if (priority == ThreadPriority::REALTIME_AUDIO)
return base::make_optional(true);
return base::nullopt;
}
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;
}
Optional<ThreadPriority> GetCurrentThreadPriorityForPlatform() {
JNIEnv* env = base::android::AttachCurrentThread();
if (Java_ThreadUtils_isThreadPriorityAudio(
env, PlatformThread::CurrentId())) {
return base::make_optional(ThreadPriority::REALTIME_AUDIO);
}
return base::nullopt;
}
} // 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

View file

@ -0,0 +1,48 @@
// 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::CanIncreaseThreadPriority(ThreadPriority priority) {
return false;
}
// static
void PlatformThread::SetCurrentThreadPriorityImpl(ThreadPriority priority) {
// TODO(https://crbug.com/926583): Fuchsia does not currently support this.
}
// static
ThreadPriority PlatformThread::GetCurrentThreadPriority() {
return ThreadPriority::NORMAL;
}
} // namespace base

View file

@ -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

View file

@ -0,0 +1,62 @@
// 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/optional.h"
#include "base/threading/platform_thread.h"
#include "build/build_config.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);
// If non-nullopt, this return value will be used as the platform-specific
// result of CanIncreaseThreadPriority().
Optional<bool> CanIncreaseCurrentThreadPriorityForPlatform(
ThreadPriority priority);
// 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);
// If non-null, this return value will be used as the platform-specific result
// of CanIncreaseThreadPriority().
Optional<ThreadPriority> GetCurrentThreadPriorityForPlatform();
#if defined(OS_LINUX)
// Current thread id is cached in thread local storage for performance reasons.
// In some rare cases it's important to clear that cache explicitly (e.g. after
// going through clone() syscall which does not call pthread_atfork()
// handlers).
BASE_EXPORT void ClearTidCache();
#endif // defined(OS_LINUX)
} // namespace internal
} // namespace base
#endif // BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_

View file

@ -0,0 +1,197 @@
// 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 = NumberToString(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},
};
Optional<bool> CanIncreaseCurrentThreadPriorityForPlatform(
ThreadPriority priority) {
#if !defined(OS_NACL)
// A non-zero soft-limit on RLIMIT_RTPRIO is required to be allowed to invoke
// pthread_setschedparam in SetCurrentThreadPriorityForPlatform().
struct rlimit rlim;
if (priority == ThreadPriority::REALTIME_AUDIO &&
getrlimit(RLIMIT_RTPRIO, &rlim) != 0 && rlim.rlim_cur != 0) {
return base::make_optional(true);
}
#endif
return base::nullopt;
}
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
}
Optional<ThreadPriority> GetCurrentThreadPriorityForPlatform() {
#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) {
return base::make_optional(ThreadPriority::REALTIME_AUDIO);
}
#endif
return base::nullopt;
}
} // 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

View file

@ -0,0 +1,239 @@
// 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::CanIncreaseThreadPriority(ThreadPriority priority) {
return true;
}
// static
void PlatformThread::SetCurrentThreadPriorityImpl(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][kThreadPriorityKey] =
@(static_cast<int>(priority));
}
// static
ThreadPriority PlatformThread::GetCurrentThreadPriority() {
NSNumber* priority = base::mac::ObjCCast<NSNumber>(
[[NSThread currentThread] threadDictionary][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

View file

@ -0,0 +1,361 @@
// 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/no_destructor.h"
#include "base/threading/platform_thread_internal_posix.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/threading/thread_id_name_manager.h"
#include "build/build_config.h"
#if !defined(OS_MACOSX) && !defined(OS_FUCHSIA) && !defined(OS_NACL)
#include "base/posix/can_lower_nice_to.h"
#endif
#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;
}
#if defined(OS_LINUX)
// Store the thread ids in local storage since calling the SWI can
// expensive and PlatformThread::CurrentId is used liberally. Clear
// the stored value after a fork() because forking changes the thread
// id. Forking without going through fork() (e.g. clone()) is not
// supported, but there is no known usage. Using thread_local is
// fine here (despite being banned) since it is going to be allowed
// but is blocked on a clang bug for Mac (https://crbug.com/829078)
// and we can't use ThreadLocalStorage because of re-entrancy due to
// CHECK/DCHECKs.
thread_local pid_t g_thread_id = -1;
class InitAtFork {
public:
InitAtFork() { pthread_atfork(nullptr, nullptr, internal::ClearTidCache); }
};
#endif // defined(OS_LINUX)
} // namespace
#if defined(OS_LINUX)
namespace internal {
void ClearTidCache() {
g_thread_id = -1;
}
} // namespace internal
#endif // defined(OS_LINUX)
// 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)
static NoDestructor<InitAtFork> init_at_fork;
if (g_thread_id == -1) {
g_thread_id = syscall(__NR_gettid);
} else {
DCHECK_EQ(g_thread_id, syscall(__NR_gettid))
<< "Thread id stored in TLS is different from thread id returned by "
"the system. It is likely that the process was forked without going "
"through fork().";
}
return g_thread_id;
#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.
base::internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call(
FROM_HERE, base::BlockingType::MAY_BLOCK);
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::CanIncreaseThreadPriority(ThreadPriority priority) {
#if defined(OS_NACL)
return false;
#else
auto platform_specific_ability =
internal::CanIncreaseCurrentThreadPriorityForPlatform(priority);
if (platform_specific_ability)
return platform_specific_ability.value();
return internal::CanLowerNiceTo(
internal::ThreadPriorityToNiceValue(priority));
#endif // defined(OS_NACL)
}
// static
void PlatformThread::SetCurrentThreadPriorityImpl(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.
auto platform_specific_priority =
internal::GetCurrentThreadPriorityForPlatform();
if (platform_specific_priority)
return platform_specific_priority.value();
// 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)
// static
size_t PlatformThread::GetDefaultThreadStackSize() {
pthread_attr_t attributes;
pthread_attr_init(&attributes);
return base::GetDefaultThreadStackSize(attributes);
}
} // namespace base

View file

@ -0,0 +1,458 @@
// 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_win.h"
#include <stddef.h>
#include "base/debug/activity_tracker.h"
#include "base/debug/alias.h"
#include "base/debug/crash_logging.h"
#include "base/debug/profiler.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/process/memory.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/threading/thread_id_name_manager.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time_override.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
#include "build/build_config.h"
#include <windows.h>
namespace base {
namespace {
// The most common value returned by ::GetThreadPriority() after background
// thread mode is enabled on Windows 7.
constexpr int kWin7BackgroundThreadModePriority = 4;
// Value sometimes returned by ::GetThreadPriority() after thread priority is
// set to normal on Windows 7.
constexpr int kWin7NormalPriority = 3;
// These values are sometimes returned by ::GetThreadPriority().
constexpr int kWinNormalPriority1 = 5;
constexpr int kWinNormalPriority2 = 6;
// 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;
#if defined(ARCH_CPU_32_BITS)
} else {
// The process stack size is increased to give spaces to |RendererMain| in
// |chrome/BUILD.gn|, but keep the default stack size of other threads to
// 1MB for the address space pressure.
flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
stack_size = 1024 * 1024;
#endif
}
ThreadParams* params = new ThreadParams;
params->delegate = delegate;
params->joinable = out_thread_handle != nullptr;
params->priority = priority;
// 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
void* thread_handle =
::CreateThread(nullptr, stack_size, ThreadFunc, params, flags, nullptr);
if (!thread_handle) {
DWORD last_error = ::GetLastError();
switch (last_error) {
case ERROR_NOT_ENOUGH_MEMORY:
case ERROR_OUTOFMEMORY:
case ERROR_COMMITMENT_LIMIT:
TerminateBecauseOutOfMemory(stack_size);
break;
default:
static auto* last_error_crash_key = debug::AllocateCrashKeyString(
"create_thread_last_error", debug::CrashKeySize::Size32);
debug::SetCrashKeyString(last_error_crash_key,
base::NumberToString(last_error));
break;
}
delete params;
return false;
}
if (out_thread_handle)
*out_thread_handle = PlatformThreadHandle(thread_handle);
else
CloseHandle(thread_handle);
return true;
}
} // namespace
namespace internal {
void AssertMemoryPriority(HANDLE thread, int memory_priority) {
#if DCHECK_IS_ON()
static const auto get_thread_information_fn =
reinterpret_cast<decltype(&::GetThreadInformation)>(::GetProcAddress(
::GetModuleHandle(L"Kernel32.dll"), "GetThreadInformation"));
if (!get_thread_information_fn) {
DCHECK_EQ(win::GetVersion(), win::Version::WIN7);
return;
}
MEMORY_PRIORITY_INFORMATION memory_priority_information = {};
DCHECK(get_thread_information_fn(thread, ::ThreadMemoryPriority,
&memory_priority_information,
sizeof(memory_priority_information)));
DCHECK_EQ(memory_priority,
static_cast<int>(memory_priority_information.MemoryPriority));
#endif
}
} // namespace internal
// 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.
// PlatformThread::Sleep doesn't support mock-time, so this always uses
// real-time.
const TimeTicks end = subtle::TimeTicksNowIgnoringOverride() + duration;
for (TimeTicks now = subtle::TimeTicksNowIgnoringOverride(); now < end;
now = subtle::TimeTicksNowIgnoringOverride()) {
::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.
static 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());
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);
base::internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call(
FROM_HERE, base::BlockingType::MAY_BLOCK);
// 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::CanIncreaseThreadPriority(ThreadPriority priority) {
return true;
}
// static
void PlatformThread::SetCurrentThreadPriorityImpl(ThreadPriority priority) {
PlatformThreadHandle::Handle thread_handle =
PlatformThread::CurrentHandle().platform_handle();
if (priority != ThreadPriority::BACKGROUND) {
// Exit background mode if the new priority is not BACKGROUND. This is a
// no-op if not in background mode.
::SetThreadPriority(thread_handle, THREAD_MODE_BACKGROUND_END);
internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_NORMAL);
}
int desired_priority = THREAD_PRIORITY_ERROR_RETURN;
switch (priority) {
case ThreadPriority::BACKGROUND:
// Using THREAD_MODE_BACKGROUND_BEGIN instead of THREAD_PRIORITY_LOWEST
// improves input latency and navigation time. See
// https://docs.google.com/document/d/16XrOwuwTwKWdgPbcKKajTmNqtB4Am8TgS9GjbzBYLc0
//
// MSDN recommends THREAD_MODE_BACKGROUND_BEGIN for threads that perform
// background work, as it reduces disk and memory priority in addition to
// CPU priority.
desired_priority = THREAD_MODE_BACKGROUND_BEGIN;
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(thread_handle, desired_priority);
DPLOG_IF(ERROR, !success) << "Failed to set thread priority to "
<< desired_priority;
if (priority == ThreadPriority::BACKGROUND) {
// In a background process, THREAD_MODE_BACKGROUND_BEGIN lowers the memory
// and I/O priorities but not the CPU priority (kernel bug?). Use
// THREAD_PRIORITY_LOWEST to also lower the CPU priority.
// https://crbug.com/901483
if (GetCurrentThreadPriority() != ThreadPriority::BACKGROUND) {
::SetThreadPriority(thread_handle, THREAD_PRIORITY_LOWEST);
// Make sure that using THREAD_PRIORITY_LOWEST didn't affect the memory
// priority set by THREAD_MODE_BACKGROUND_BEGIN. There is no practical
// way to verify the I/O priority.
internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_VERY_LOW);
}
}
}
// static
ThreadPriority PlatformThread::GetCurrentThreadPriority() {
static_assert(
THREAD_PRIORITY_IDLE < 0,
"THREAD_PRIORITY_IDLE is >= 0 and will incorrectly cause errors.");
static_assert(
THREAD_PRIORITY_LOWEST < 0,
"THREAD_PRIORITY_LOWEST is >= 0 and will incorrectly cause errors.");
static_assert(THREAD_PRIORITY_BELOW_NORMAL < 0,
"THREAD_PRIORITY_BELOW_NORMAL is >= 0 and will incorrectly "
"cause errors.");
static_assert(
THREAD_PRIORITY_NORMAL == 0,
"The logic below assumes that THREAD_PRIORITY_NORMAL is zero. If it is "
"not, ThreadPriority::BACKGROUND may be incorrectly detected.");
static_assert(THREAD_PRIORITY_ABOVE_NORMAL >= 0,
"THREAD_PRIORITY_ABOVE_NORMAL is < 0 and will incorrectly be "
"translated to ThreadPriority::BACKGROUND.");
static_assert(THREAD_PRIORITY_HIGHEST >= 0,
"THREAD_PRIORITY_HIGHEST is < 0 and will incorrectly be "
"translated to ThreadPriority::BACKGROUND.");
static_assert(THREAD_PRIORITY_TIME_CRITICAL >= 0,
"THREAD_PRIORITY_TIME_CRITICAL is < 0 and will incorrectly be "
"translated to ThreadPriority::BACKGROUND.");
static_assert(THREAD_PRIORITY_ERROR_RETURN >= 0,
"THREAD_PRIORITY_ERROR_RETURN is < 0 and will incorrectly be "
"translated to ThreadPriority::BACKGROUND.");
const int priority =
::GetThreadPriority(PlatformThread::CurrentHandle().platform_handle());
// Negative values represent a background priority. We have observed -3, -4,
// -6 when THREAD_MODE_BACKGROUND_* is used. THREAD_PRIORITY_IDLE,
// THREAD_PRIORITY_LOWEST and THREAD_PRIORITY_BELOW_NORMAL are other possible
// negative values.
if (priority < THREAD_PRIORITY_NORMAL)
return ThreadPriority::BACKGROUND;
switch (priority) {
case kWin7BackgroundThreadModePriority:
DCHECK_EQ(win::GetVersion(), win::Version::WIN7);
return ThreadPriority::BACKGROUND;
case kWin7NormalPriority:
DCHECK_EQ(win::GetVersion(), win::Version::WIN7);
FALLTHROUGH;
case THREAD_PRIORITY_NORMAL:
return ThreadPriority::NORMAL;
case kWinNormalPriority1:
FALLTHROUGH;
case kWinNormalPriority2:
return ThreadPriority::NORMAL;
case THREAD_PRIORITY_ABOVE_NORMAL:
case THREAD_PRIORITY_HIGHEST:
return ThreadPriority::DISPLAY;
case THREAD_PRIORITY_TIME_CRITICAL:
return ThreadPriority::REALTIME_AUDIO;
case THREAD_PRIORITY_ERROR_RETURN:
DPCHECK(false) << "::GetThreadPriority error";
}
NOTREACHED() << "::GetThreadPriority returned " << priority << ".";
return ThreadPriority::NORMAL;
}
// static
size_t PlatformThread::GetDefaultThreadStackSize() {
return 0;
}
} // namespace base

View file

@ -0,0 +1,23 @@
// Copyright 2018 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_WIN_H_
#define BASE_THREADING_PLATFORM_THREAD_WIN_H_
#include "base/threading/platform_thread.h"
#include "base/base_export.h"
namespace base {
namespace internal {
// Assert that the memory priority of |thread| is |memory_priority|. No-op on
// Windows 7 because ::GetThreadInformation() is not available. Exposed for unit
// tests.
BASE_EXPORT void AssertMemoryPriority(HANDLE thread, int memory_priority);
} // namespace internal
} // namespace base
#endif // BASE_THREADING_PLATFORM_THREAD_WIN_H_

View file

@ -0,0 +1,156 @@
// 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,
scoped_refptr<SequencedTaskRunner> reply_task_runner)
: from_here_(from_here),
task_(std::move(task)),
reply_(std::move(reply)),
reply_task_runner_(std::move(reply_task_runner)) {}
PostTaskAndReplyRelay(PostTaskAndReplyRelay&&) = default;
// It is important that |reply_| always be deleted on the origin sequence
// (|reply_task_runner_|) since its destructor can be affine to it. More
// sutbly, it is also important that |task_| be destroyed on the origin
// sequence when it fails to run. This is because |task_| can own state which
// is affine to |reply_task_runner_| and was intended to be handed to
// |reply_|, e.g. https://crbug.com/829122. Since |task_| already needs to
// support deletion on the origin sequence (since the initial PostTask can
// always fail), it's safer to delete it there when PostTask succeeds but
// |task_| is later prevented from running.
//
// PostTaskAndReplyRelay's move semantics along with logic in this destructor
// enforce the above semantics in all the following cases :
// 1) Posting |task_| fails right away on the origin sequence:
// a) |reply_task_runner_| is null (i.e. during late shutdown);
// b) |reply_task_runner_| is set.
// 2) ~PostTaskAndReplyRelay() runs on the destination sequence:
// a) RunTaskAndPostReply() is cancelled before running;
// b) RunTaskAndPostReply() is skipped on shutdown;
// c) Posting RunReply() fails.
// 3) ~PostTaskAndReplyRelay() runs on the origin sequence:
// a) RunReply() is cancelled before running;
// b) RunReply() is skipped on shutdown;
// c) The DeleteSoon() posted by (2) runs.
// 4) ~PostTaskAndReplyRelay() should no-op:
// a) This relay was moved to another relay instance;
// b) RunReply() ran and completed this relay's mandate.
~PostTaskAndReplyRelay() {
// Case 1a and 4a:
if (!reply_task_runner_) {
DCHECK_EQ(task_.is_null(), reply_.is_null());
return;
}
// Case 4b:
if (!reply_) {
DCHECK(!task_);
return;
}
// Case 2:
if (!reply_task_runner_->RunsTasksInCurrentSequence()) {
DCHECK(reply_);
SequencedTaskRunner* reply_task_runner_raw = reply_task_runner_.get();
auto relay_to_delete =
std::make_unique<PostTaskAndReplyRelay>(std::move(*this));
// In case 2c, posting the DeleteSoon will also fail and |relay_to_delete|
// will be leaked. This only happens during shutdown and leaking is better
// than thread-unsafe execution.
ANNOTATE_LEAKING_OBJECT_PTR(relay_to_delete.get());
reply_task_runner_raw->DeleteSoon(from_here_, std::move(relay_to_delete));
return;
}
// Case 1b and 3: Any remaining state will be destroyed synchronously at the
// end of this scope.
}
// No assignment operator because of const member.
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 pointer to the reply TaskRunner for the PostTask() call before
// |relay| is moved into a callback.
SequencedTaskRunner* reply_task_runner_raw = relay.reply_task_runner_.get();
reply_task_runner_raw->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_;
// Not const to allow moving.
scoped_refptr<SequencedTaskRunner> reply_task_runner_;
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();
const bool has_sequenced_context = SequencedTaskRunnerHandle::IsSet();
const bool post_task_success = PostTask(
from_here,
BindOnce(&PostTaskAndReplyRelay::RunTaskAndPostReply,
PostTaskAndReplyRelay(
from_here, std::move(task), std::move(reply),
has_sequenced_context ? SequencedTaskRunnerHandle::Get()
: nullptr)));
// PostTaskAndReply() requires a SequencedTaskRunnerHandle to post the reply.
// Having no SequencedTaskRunnerHandle is allowed when posting the task fails,
// to simplify calls during shutdown (https://crbug.com/922938).
CHECK(has_sequenced_context || !post_task_success);
return post_task_success;
}
} // namespace internal
} // namespace base

View file

@ -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/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_

View file

@ -0,0 +1,77 @@
// 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"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
namespace base {
namespace {
#if DCHECK_IS_ON()
// Used to verify that the trace events used in the constructor do not result in
// instantiating a ScopedBlockingCall themselves (which would cause an infinite
// reentrancy loop).
LazyInstance<ThreadLocalBoolean>::Leaky tls_construction_in_progress =
LAZY_INSTANCE_INITIALIZER;
#endif
} // namespace
ScopedBlockingCall::ScopedBlockingCall(const Location& from_here,
BlockingType blocking_type)
: UncheckedScopedBlockingCall(from_here, blocking_type) {
#if DCHECK_IS_ON()
DCHECK(!tls_construction_in_progress.Get().Get());
tls_construction_in_progress.Get().Set(true);
#endif
internal::AssertBlockingAllowed();
TRACE_EVENT_BEGIN2("base", "ScopedBlockingCall", "file_name",
from_here.file_name(), "function_name",
from_here.function_name());
#if DCHECK_IS_ON()
tls_construction_in_progress.Get().Set(false);
#endif
}
ScopedBlockingCall::~ScopedBlockingCall() {
TRACE_EVENT_END0("base", "ScopedBlockingCall");
}
namespace internal {
ScopedBlockingCallWithBaseSyncPrimitives::
ScopedBlockingCallWithBaseSyncPrimitives(const Location& from_here,
BlockingType blocking_type)
: UncheckedScopedBlockingCall(from_here, blocking_type) {
#if DCHECK_IS_ON()
DCHECK(!tls_construction_in_progress.Get().Get());
tls_construction_in_progress.Get().Set(true);
#endif
internal::AssertBaseSyncPrimitivesAllowed();
TRACE_EVENT_BEGIN2("base", "ScopedBlockingCallWithBaseSyncPrimitives",
"file_name", from_here.file_name(), "function_name",
from_here.function_name());
#if DCHECK_IS_ON()
tls_construction_in_progress.Get().Set(false);
#endif
}
ScopedBlockingCallWithBaseSyncPrimitives::
~ScopedBlockingCallWithBaseSyncPrimitives() {
TRACE_EVENT_END0("base", "ScopedBlockingCallWithBaseSyncPrimitives");
}
} // namespace internal
} // namespace base

View file

@ -0,0 +1,116 @@
// 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/location.h"
#include "base/threading/scoped_blocking_call_internal.h"
namespace base {
// 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.
// 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
};
// This class must be instantiated in every scope where a blocking call is made
// and serves as a precise annotation of the scope that may/will block for the
// scheduler. When a ScopedBlockingCall is instantiated, it asserts that
// blocking calls are allowed in its scope with a call to
// base::AssertBlockingAllowed(). 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. Nested ScopedBlockingCalls are supported (mostly a no-op
// except for WILL_BLOCK nested within MAY_BLOCK which will result in immediate
// WILL_BLOCK semantics).
//
// Good:
// Data data;
// {
// ScopedBlockingCall scoped_blocking_call(
// FROM_HERE, BlockingType::WILL_BLOCK);
// data = GetDataFromNetwork();
// }
// CPUIntensiveProcessing(data);
//
// Bad:
// ScopedBlockingCall scoped_blocking_call(FROM_HERE,
// BlockingType::WILL_BLOCK);
// Data data = GetDataFromNetwork();
// CPUIntensiveProcessing(data); // CPU usage within a ScopedBlockingCall.
//
// Good:
// Data a;
// Data b;
// {
// ScopedBlockingCall scoped_blocking_call(
// FROM_HERE, BlockingType::MAY_BLOCK);
// a = GetDataFromMemoryCacheOrNetwork();
// b = GetDataFromMemoryCacheOrNetwork();
// }
// CPUIntensiveProcessing(a);
// CPUIntensiveProcessing(b);
//
// Bad:
// ScopedBlockingCall scoped_blocking_call(
// FROM_HERE, 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(
// FROM_HERE, BlockingType::WILL_BLOCK);
// waitable_event.Wait(); // Wait() instantiates its own ScopedBlockingCall.
//
// When a ScopedBlockingCall is instantiated from a ThreadPool 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 internal::UncheckedScopedBlockingCall {
public:
ScopedBlockingCall(const Location& from_here, BlockingType blocking_type);
~ScopedBlockingCall();
};
// Usage reserved for //base callers.
namespace internal {
// This class must be instantiated in every scope where a sync primitive is
// used. When a ScopedBlockingCallWithBaseSyncPrimitives is instantiated, it
// asserts that sync primitives are allowed in its scope with a call to
// internal::AssertBaseSyncPrimitivesAllowed(). The same guidelines as for
// ScopedBlockingCall should be followed.
class BASE_EXPORT ScopedBlockingCallWithBaseSyncPrimitives
: public UncheckedScopedBlockingCall {
public:
ScopedBlockingCallWithBaseSyncPrimitives(const Location& from_here,
BlockingType blocking_type);
~ScopedBlockingCallWithBaseSyncPrimitives();
};
} // namespace internal
} // namespace base
#endif // BASE_THREADING_SCOPED_BLOCKING_CALL_H

View file

@ -0,0 +1,81 @@
// Copyright 2020 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_internal.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/scoped_clear_last_error.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/threading/thread_local.h"
#include "build/build_config.h"
namespace base {
namespace internal {
namespace {
// The first 8 characters of sha1 of "ScopedBlockingCall".
// echo -n "ScopedBlockingCall" | sha1sum
constexpr uint32_t kActivityTrackerId = 0x11be9915;
LazyInstance<ThreadLocalPointer<internal::BlockingObserver>>::Leaky
tls_blocking_observer = LAZY_INSTANCE_INITIALIZER;
// Last ScopedBlockingCall instantiated on this thread.
LazyInstance<ThreadLocalPointer<internal::UncheckedScopedBlockingCall>>::Leaky
tls_last_scoped_blocking_call = LAZY_INSTANCE_INITIALIZER;
} // namespace
void SetBlockingObserverForCurrentThread(BlockingObserver* blocking_observer) {
DCHECK(!tls_blocking_observer.Get().Get());
tls_blocking_observer.Get().Set(blocking_observer);
}
void ClearBlockingObserverForCurrentThread() {
tls_blocking_observer.Get().Set(nullptr);
}
UncheckedScopedBlockingCall::UncheckedScopedBlockingCall(
const Location& from_here,
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_)),
scoped_activity_(from_here, 0, kActivityTrackerId, 0) {
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();
}
}
if (scoped_activity_.IsRecorded()) {
// Also record the data for extended crash reporting.
const base::TimeTicks now = base::TimeTicks::Now();
auto& user_data = scoped_activity_.user_data();
user_data.SetUint("timestamp_us", now.since_origin().InMicroseconds());
user_data.SetUint("blocking_type", static_cast<uint64_t>(blocking_type));
}
}
UncheckedScopedBlockingCall::~UncheckedScopedBlockingCall() {
// TLS affects result of GetLastError() on Windows. ScopedClearLastError
// prevents side effect.
base::internal::ScopedClearLastError save_last_error;
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
} // namespace base

View file

@ -0,0 +1,73 @@
// Copyright 2020 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_INTERNAL_H_
#define BASE_THREADING_SCOPED_BLOCKING_CALL_INTERNAL_H_
#include "base/base_export.h"
#include "base/debug/activity_tracker.h"
#include "base/macros.h"
namespace base {
enum class BlockingType;
// Implementation details of types in scoped_blocking_call.h and classes for a
// few key //base types to observe and react to blocking calls.
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 ClearBlockingObserverForCurrentThread();
// Common implementation class for both ScopedBlockingCall and
// ScopedBlockingCallWithBaseSyncPrimitives without assertions.
class BASE_EXPORT UncheckedScopedBlockingCall {
public:
explicit UncheckedScopedBlockingCall(const Location& from_here,
BlockingType blocking_type);
~UncheckedScopedBlockingCall();
private:
internal::BlockingObserver* const blocking_observer_;
// Previous ScopedBlockingCall instantiated on this thread.
UncheckedScopedBlockingCall* 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_;
base::debug::ScopedActivity scoped_activity_;
DISALLOW_COPY_AND_ASSIGN(UncheckedScopedBlockingCall);
};
} // namespace internal
} // namespace base
#endif // BASE_THREADING_SCOPED_BLOCKING_CALL_INTERNAL_H_

View file

@ -0,0 +1,54 @@
// Copyright 2019 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_thread_priority.h"
#include "base/location.h"
#include "base/threading/platform_thread.h"
#include "base/trace_event/trace_event.h"
namespace base {
namespace internal {
ScopedMayLoadLibraryAtBackgroundPriority::
ScopedMayLoadLibraryAtBackgroundPriority(const Location& from_here) {
TRACE_EVENT_BEGIN2("base", "ScopedMayLoadLibraryAtBackgroundPriority",
"file_name", from_here.file_name(), "function_name",
from_here.function_name());
}
bool ScopedMayLoadLibraryAtBackgroundPriority::OnScopeFirstEntered() {
#if defined(OS_WIN)
const base::ThreadPriority priority =
PlatformThread::GetCurrentThreadPriority();
if (priority == base::ThreadPriority::BACKGROUND) {
original_thread_priority_ = priority;
PlatformThread::SetCurrentThreadPriority(base::ThreadPriority::NORMAL);
TRACE_EVENT_BEGIN0(
"base",
"ScopedMayLoadLibraryAtBackgroundPriority : Priority Increased");
}
#endif // OS_WIN
return true;
}
ScopedMayLoadLibraryAtBackgroundPriority::
~ScopedMayLoadLibraryAtBackgroundPriority() {
// Trace events must be closed in reverse order of opening so that they nest
// correctly.
#if defined(OS_WIN)
if (original_thread_priority_) {
TRACE_EVENT_END0(
"base",
"ScopedMayLoadLibraryAtBackgroundPriority : Priority Increased");
PlatformThread::SetCurrentThreadPriority(original_thread_priority_.value());
}
#endif // OS_WIN
TRACE_EVENT_END0("base", "ScopedMayLoadLibraryAtBackgroundPriority");
}
} // namespace internal
} // namespace base

View file

@ -0,0 +1,84 @@
// Copyright 2019 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_THREAD_PRIORITY_H_
#define BASE_THREADING_SCOPED_THREAD_PRIORITY_H_
#include "base/base_export.h"
#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/optional.h"
#include "build/build_config.h"
namespace base {
class Location;
enum class ThreadPriority : int;
// INTERNAL_SCOPED_THREAD_PRIORITY_APPEND_LINE(name) produces an identifier by
// appending the current line number to |name|. This is used to avoid name
// collisions from variables defined inside a macro.
#define INTERNAL_SCOPED_THREAD_PRIORITY_CONCAT(a, b) a##b
// CONCAT1 provides extra level of indirection so that __LINE__ macro expands.
#define INTERNAL_SCOPED_THREAD_PRIORITY_CONCAT1(a, b) \
INTERNAL_SCOPED_THREAD_PRIORITY_CONCAT(a, b)
#define INTERNAL_SCOPED_THREAD_PRIORITY_APPEND_LINE(name) \
INTERNAL_SCOPED_THREAD_PRIORITY_CONCAT1(name, __LINE__)
// All code that may load a DLL on a background thread must be surrounded by a
// scope that starts with this macro.
//
// Example:
// Foo();
// {
// SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
// LoadMyDll();
// }
// Bar();
//
// The macro raises the thread priority to NORMAL for the scope when first
// encountered. On Windows, loading a DLL on a background thread can lead to a
// priority inversion on the loader lock and cause huge janks.
#define SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY() \
base::internal::ScopedMayLoadLibraryAtBackgroundPriority \
INTERNAL_SCOPED_THREAD_PRIORITY_APPEND_LINE( \
scoped_may_load_library_at_background_priority)(FROM_HERE); \
{ \
/* Thread-safe static local variable initialization ensures that */ \
/* OnScopeFirstEntered() is only invoked the first time that this is */ \
/* encountered. */ \
static bool INTERNAL_SCOPED_THREAD_PRIORITY_APPEND_LINE(invoke_once) = \
INTERNAL_SCOPED_THREAD_PRIORITY_APPEND_LINE( \
scoped_may_load_library_at_background_priority) \
.OnScopeFirstEntered(); \
ALLOW_UNUSED_LOCAL( \
INTERNAL_SCOPED_THREAD_PRIORITY_APPEND_LINE(invoke_once)); \
}
namespace internal {
class BASE_EXPORT ScopedMayLoadLibraryAtBackgroundPriority {
public:
explicit ScopedMayLoadLibraryAtBackgroundPriority(const Location& from_here);
~ScopedMayLoadLibraryAtBackgroundPriority();
// The SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY() macro invokes this the
// first time that it is encountered.
bool OnScopeFirstEntered();
private:
#if defined(OS_WIN)
// The original priority when invoking OnScopeFirstEntered().
base::Optional<ThreadPriority> original_thread_priority_;
#endif
DISALLOW_COPY_AND_ASSIGN(ScopedMayLoadLibraryAtBackgroundPriority);
};
} // namespace internal
} // namespace base
#endif // BASE_THREADING_SCOPED_THREAD_PRIORITY_H_

View file

@ -0,0 +1,319 @@
// Copyright 2018 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_BOUND_H_
#define BASE_THREADING_SEQUENCE_BOUND_H_
#include <new>
#include <type_traits>
#include "base/bind.h"
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/memory/aligned_memory.h"
#include "base/memory/ptr_util.h"
#include "base/sequenced_task_runner.h"
namespace base {
// SequenceBound facilitates owning objects that live on a specified sequence,
// which is potentially different than the owner's sequence. It encapsulates
// the work of posting tasks to the specified sequence to construct T, call
// methods on T, and destroy T.
//
// It does not provide explicit access to the underlying object directly, to
// prevent accidentally using it from the wrong sequence.
//
// Like std::unique_ptr<T>, a SequenceBound<T> may be moved between owners,
// and posted across threads. It may also be up-casted (only), to permit
// SequenceBound to be used with interfaces.
//
// Basic usage looks like this:
//
// // Some class that lives on |main_task_runner|.
// class MyClass {
// public:
// explicit MyClass(const char* widget_title) {}
// virtual ~MyClass() { ... }
// virtual void DoSomething(int arg) { ... }
// };
//
// // On any thread...
// scoped_refptr<SequencedTaskRunner> main_task_runner = ...;
// auto widget = SequenceBound<MyClass>(main_task_runner, "My Title");
//
// // Execute a single method on the object, on |main_task_runner|.
// widget.Post(FROM_HERE, &MyClass::DoSomething, 1234);
//
// // Execute an arbitrary task on |main_task_runner| with a non-const pointer
// // to the object.
// widget.PostTaskWithThisObject(
// FROM_HERE,
// base::BindOnce([](MyClass* widget) {
// // Unlike with Post, we can issue multiple calls on |widget| within
// // the same stack frame.
// widget->DoSomething(42);
// widget->DoSomething(13);
// }));
//
// // Execute an arbitrary task on |main_task_runner| with a const reference
// // to the object.
// widget.PostTaskWithThisObject(
// FROM_HERE,
// base::BindOnce([](const MyClass& widget) { ... }));
//
// Note that |widget| is constructed asynchronously on |main_task_runner|,
// but calling Post() immediately is safe, since the actual call is posted
// to |main_task_runner| as well.
//
// |widget| will be deleted on |main_task_runner| asynchronously when it goes
// out of scope, or when Reset() is called.
//
// Here is a more complicated example that shows injection and upcasting:
//
// // Some unrelated class that uses a |MyClass| to do something.
// class SomeConsumer {
// public:
// // Note that ownership of |widget| is given to us!
// explicit SomeConsumer(SequenceBound<MyClass> widget)
// : widget_(std::move(widget)) { ... }
//
// ~SomeConsumer() {
// // |widget_| will be destroyed on the associated task runner.
// }
//
// SequenceBound<MyClass> widget_;
// };
//
// // Implementation of MyClass.
// class MyDerivedClass : public MyClass { ... };
//
// auto widget =
// SequenceBound<MyDerivedClass>(main_task_runner, ctor args);
// auto c = new SomeConsumer(std::move(widget)); // upcasts to MyClass
namespace internal {
// If we can't cast |Base*| into |Derived*|, then it's a virtual base if and
// only if |Base| is actually a base class of |Derived|. Otherwise (including
// unrelated types), it isn't. We default to Derived* so that the
// specialization below will apply when the cast to |Derived*| is valid.
template <typename Base, typename Derived, typename = Derived*>
struct is_virtual_base_of : public std::is_base_of<Base, Derived> {};
// If we can cast |Base*| into |Derived*|, then it's definitely not a virtual
// base. When this happens, we'll match the default third template argument.
template <typename Base, typename Derived>
struct is_virtual_base_of<Base,
Derived,
decltype(static_cast<Derived*>(
static_cast<Base*>(nullptr)))> : std::false_type {
};
} // namespace internal
template <typename T>
class SequenceBound {
public:
// Allow explicit null.
SequenceBound() = default;
// Construct a new instance of |T| that will be accessed only on
// |task_runner|. One may post calls to it immediately upon return.
// This is marked as NO_SANITIZE because cfi doesn't like that we're casting
// uninitialized memory to a |T*|. However, it's safe since (a) the cast is
// defined (see http://eel.is/c++draft/basic.life#6 for details), and (b) we
// don't use the resulting pointer in any way that requries it to be
// constructed, except by posting such a access to |impl_task_runner_| after
// posting construction there as well.
template <typename... Args>
NO_SANITIZE("cfi-unrelated-cast")
SequenceBound(scoped_refptr<base::SequencedTaskRunner> task_runner,
Args&&... args)
: impl_task_runner_(std::move(task_runner)) {
// Allocate space for but do not construct an instance of |T|.
storage_ = AlignedAlloc(sizeof(T), alignof(T));
t_ = reinterpret_cast<T*>(storage_);
// Post construction to the impl thread.
impl_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ConstructOwnerRecord<Args...>, base::Unretained(t_),
std::forward<Args>(args)...));
}
~SequenceBound() { Reset(); }
// Move construction from the same type can just take the pointer without
// adjusting anything. This is required in addition to the move conversion
// constructor below.
SequenceBound(SequenceBound&& other) { MoveRecordFrom(other); }
// Move construction is supported from any type that's compatible with |T|.
// This case handles |From| != |T|, so we must adjust the pointer offset.
template <typename From>
SequenceBound(SequenceBound<From>&& other) {
MoveRecordFrom(other);
}
SequenceBound& operator=(SequenceBound&& other) {
// Clean up any object we currently own.
Reset();
MoveRecordFrom(other);
return *this;
}
template <typename From>
SequenceBound<T>& operator=(SequenceBound<From>&& other) {
// Clean up any object that we currently own.
Reset();
MoveRecordFrom(other);
return *this;
}
// Post a call to |method| to |impl_task_runner_|.
template <typename... MethodArgs, typename... Args>
void Post(const base::Location& from_here,
void (T::*method)(MethodArgs...),
Args&&... args) const {
DCHECK(t_);
impl_task_runner_->PostTask(from_here,
base::BindOnce(method, base::Unretained(t_),
std::forward<Args>(args)...));
}
// Posts |task| to |impl_task_runner_|, passing it a reference to the wrapped
// object. This allows arbitrary logic to be safely executed on the object's
// task runner. The object is guaranteed to remain alive for the duration of
// the task.
using ConstPostTaskCallback = base::OnceCallback<void(const T&)>;
void PostTaskWithThisObject(const base::Location& from_here,
ConstPostTaskCallback callback) const {
DCHECK(t_);
impl_task_runner_->PostTask(
from_here,
base::BindOnce([](ConstPostTaskCallback callback,
const T* t) { std::move(callback).Run(*t); },
std::move(callback), t_));
}
// Same as above, but for non-const operations. The callback takes a pointer
// to the wrapped object rather than a const ref.
using PostTaskCallback = base::OnceCallback<void(T*)>;
void PostTaskWithThisObject(const base::Location& from_here,
PostTaskCallback callback) const {
DCHECK(t_);
impl_task_runner_->PostTask(from_here,
base::BindOnce(std::move(callback), t_));
}
// TODO(liberato): Add PostOrCall(), to support cases where synchronous calls
// are okay if it's the same task runner.
// TODO(liberato): Add PostAndReply()
// TODO(liberato): Allow creation of callbacks that bind to a weak pointer,
// and thread-hop to |impl_task_runner_| if needed.
// Post destruction of any object we own, and return to the null state.
void Reset() {
if (is_null())
return;
// Destruct the object on the impl thread.
impl_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DeleteOwnerRecord, base::Unretained(t_),
base::Unretained(storage_)));
impl_task_runner_ = nullptr;
t_ = nullptr;
storage_ = nullptr;
}
// Same as above, but allows the caller to provide a closure to be invoked
// immediately after destruction. The Closure is invoked on
// |impl_task_runner_|, iff the owned object was non-null.
void ResetWithCallbackAfterDestruction(base::OnceClosure callback) {
if (is_null())
return;
impl_task_runner_->PostTask(
FROM_HERE, base::BindOnce(
[](base::OnceClosure callback, T* t, void* storage) {
DeleteOwnerRecord(t, storage);
std::move(callback).Run();
},
std::move(callback), t_, storage_));
impl_task_runner_ = nullptr;
t_ = nullptr;
storage_ = nullptr;
}
// Return whether we own anything. Note that this does not guarantee that any
// previously owned object has been destroyed. In particular, it will return
// true immediately after a call to Reset(), though the underlying object
// might still be pending destruction on the impl thread.
bool is_null() const { return !t_; }
// True if and only if we have an object, with the same caveats as is_null().
explicit operator bool() const { return !is_null(); }
private:
// Move everything from |other|, doing pointer adjustment as needed.
// This method is marked as NO_SANITIZE since (a) it might run before the
// posted ctor runs on |impl_task_runner_|, and (b) implicit conversions to
// non-virtual base classes are allowed before construction by the standard.
// See http://eel.is/c++draft/basic.life#6 for more information.
template <typename From>
void NO_SANITIZE("cfi-unrelated-cast") MoveRecordFrom(From&& other) {
// |other| might be is_null(), but that's okay.
impl_task_runner_ = std::move(other.impl_task_runner_);
// Note that static_cast<> isn't, in general, safe, since |other| might not
// be constructed yet. Implicit conversion is supported, as long as it
// doesn't convert to a virtual base. Of course, it allows only upcasts.
t_ = other.t_;
// The original storage is kept unmodified, so we can free it later.
storage_ = other.storage_;
other.storage_ = nullptr;
other.t_ = nullptr;
}
// Pointer to the object, Pointer may be modified on the owning thread.
T* t_ = nullptr;
// Original allocated storage for the object.
void* storage_ = nullptr;
// The task runner on which all access to |t_| should happen.
scoped_refptr<base::SequencedTaskRunner> impl_task_runner_;
// For move conversion.
template <typename U>
friend class SequenceBound;
// Run on impl thread to construct |t|'s storage.
template <typename... Args>
static void ConstructOwnerRecord(T* t, std::decay_t<Args>&&... args) {
new (t) T(std::move(args)...);
}
// Destruct the object associated with |t|, and delete |storage|.
static void DeleteOwnerRecord(T* t, void* storage) {
t->~T();
AlignedFree(storage);
}
// To preserve ownership semantics, we disallow copy construction / copy
// assignment. Move construction / assignment is fine.
DISALLOW_COPY_AND_ASSIGN(SequenceBound);
};
} // namespace base
#endif // BASE_THREADING_SEQUENCE_BOUND_H_

View file

@ -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

View file

@ -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_

View file

@ -0,0 +1,27 @@
// 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

View file

@ -0,0 +1,125 @@
// 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/template_util.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:
//
// int& GetSequenceLocalStorage()
// static base::NoDestructor<SequenceLocalStorageSlot<int>> sls_value;
// return sls_value->GetOrCreateValue();
// }
//
// void Read() {
// int value = GetSequenceLocalStorage();
// ...
// }
//
// void Write() {
// GetSequenceLocalStorage() = 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 ThreadPool 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;
operator bool() const { return GetValuePointer() != nullptr; }
// Default-constructs the value for the current sequence if not
// already constructed. Then, returns the value.
T& GetOrCreateValue() {
T* ptr = GetValuePointer();
if (!ptr)
ptr = emplace();
return *ptr;
}
// Returns a pointer to the value for the current sequence. May be
// nullptr if the value was not constructed on the current sequence.
T* GetValuePointer() {
void* ptr =
internal::SequenceLocalStorageMap::GetForCurrentThread().Get(slot_id_);
return static_cast<T*>(ptr);
}
const T* GetValuePointer() const {
return const_cast<SequenceLocalStorageSlot*>(this)->GetValuePointer();
}
T* operator->() { return GetValuePointer(); }
const T* operator->() const { return GetValuePointer(); }
T& operator*() { return *GetValuePointer(); }
const T& operator*() const { return *GetValuePointer(); }
void reset() { Adopt(nullptr); }
// Constructs this slot's sequence-local value with |args...| and returns a
// pointer to the created object.
template <class... Args>
T* emplace(Args&&... args) {
T* value_ptr = new T(std::forward<Args>(args)...);
Adopt(value_ptr);
return value_ptr;
}
private:
// Takes ownership of |value_ptr|.
void Adopt(T* value_ptr) {
// 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.
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));
}
// |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_

View file

@ -0,0 +1,52 @@
// 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"
namespace base {
namespace {
LazyInstance<ThreadLocalPointer<SequencedTaskRunnerHandle>>::Leaky
sequenced_task_runner_tls = LAZY_INSTANCE_INITIALIZER;
} // namespace
// static
const scoped_refptr<SequencedTaskRunner>& SequencedTaskRunnerHandle::Get() {
const SequencedTaskRunnerHandle* current =
sequenced_task_runner_tls.Pointer()->Get();
CHECK(current)
<< "Error: This caller requires a sequenced context (i.e. the current "
"task needs to run from a SequencedTaskRunner). If you're in a test "
"refer to //docs/threading_and_tasks_testing.md.";
return current->task_runner_;
}
// static
bool SequencedTaskRunnerHandle::IsSet() {
return !!sequenced_task_runner_tls.Pointer()->Get();
}
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

View file

@ -0,0 +1,49 @@
// 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/base_export.h"
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequenced_task_runner.h"
namespace base {
class ThreadTaskRunnerHandle;
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 const scoped_refptr<SequencedTaskRunner>& Get() WARN_UNUSED_RESULT;
// 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() WARN_UNUSED_RESULT;
// Binds |task_runner| to the current thread.
explicit SequencedTaskRunnerHandle(
scoped_refptr<SequencedTaskRunner> task_runner);
~SequencedTaskRunnerHandle();
private:
// Friend needed for ThreadTaskRunnerHandle::OverrideForTesting().
friend class ThreadTaskRunnerHandle;
scoped_refptr<SequencedTaskRunner> task_runner_;
DISALLOW_COPY_AND_ASSIGN(SequencedTaskRunnerHandle);
};
} // namespace base
#endif // BASE_THREADING_SEQUENCED_TASK_RUNNER_HANDLE_H_

View file

@ -0,0 +1,178 @@
// 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)
: SimpleThread(name, Options()) {}
SimpleThread::SimpleThread(const std::string& name, const Options& options)
: name_(name),
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();
ScopedAllowBaseSyncPrimitives 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);
CHECK(success);
}
PlatformThreadId SimpleThread::tid() {
DCHECK(HasBeenStarted());
return tid_;
}
bool SimpleThread::HasBeenStarted() {
return event_.IsSignaled();
}
void SimpleThread::ThreadMain() {
tid_ = PlatformThread::CurrentId();
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)
: DelegateSimpleThread(delegate, name, Options()) {}
DelegateSimpleThread::DelegateSimpleThread(Delegate* delegate,
const std::string& name,
const Options& options)
: SimpleThread(name, 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) {
std::string name(name_prefix_);
name.push_back('/');
name.append(NumberToString(i));
DelegateSimpleThread* thread = new DelegateSimpleThread(this, name);
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

View file

@ -0,0 +1,231 @@
// 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;
};
// Creates a SimpleThread. |options| should be used to manage any specific
// configuration involving the thread creation and management.
// Every thread has a name, which is a display string to identify the thread.
// The thread will not be created until Start() is called.
explicit SimpleThread(const std::string& name);
SimpleThread(const std::string& name, 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() const { 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_;
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_

View file

@ -0,0 +1,408 @@
// 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 <type_traits>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/lazy_instance.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/message_loop/message_loop_current.h"
#include "base/message_loop/message_pump.h"
#include "base/run_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/sequence_manager/sequence_manager_impl.h"
#include "base/task/sequence_manager/task_queue.h"
#include "base/task/simple_task_executor.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 "base/threading/thread_task_runner_handle.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;
class SequenceManagerThreadDelegate : public Thread::Delegate {
public:
explicit SequenceManagerThreadDelegate(
MessagePumpType message_pump_type,
OnceCallback<std::unique_ptr<MessagePump>()> message_pump_factory,
sequence_manager::TimeDomain* time_domain)
: sequence_manager_(
sequence_manager::internal::SequenceManagerImpl::CreateUnbound(
sequence_manager::SequenceManager::Settings::Builder()
.SetMessagePumpType(message_pump_type)
.Build())),
default_task_queue_(sequence_manager_->CreateTaskQueue(
sequence_manager::TaskQueue::Spec("default_tq")
.SetTimeDomain(time_domain))),
message_pump_factory_(std::move(message_pump_factory)) {
sequence_manager_->SetDefaultTaskRunner(default_task_queue_->task_runner());
}
~SequenceManagerThreadDelegate() override = default;
scoped_refptr<SingleThreadTaskRunner> GetDefaultTaskRunner() override {
// Surprisingly this might not be default_task_queue_->task_runner() which
// we set in the constructor. The Thread::Init() method could create a
// SequenceManager on top of the current one and call
// SequenceManager::SetDefaultTaskRunner which would propagate the new
// TaskRunner down to our SequenceManager. Turns out, code actually relies
// on this and somehow relies on
// SequenceManagerThreadDelegate::GetDefaultTaskRunner returning this new
// TaskRunner. So instead of returning default_task_queue_->task_runner() we
// need to query the SequenceManager for it.
// The underlying problem here is that Subclasses of Thread can do crazy
// stuff in Init() but they are not really in control of what happens in the
// Thread::Delegate, as this is passed in on calling StartWithOptions which
// could happen far away from where the Thread is created. We should
// consider getting rid of StartWithOptions, and pass them as a constructor
// argument instead.
return sequence_manager_->GetTaskRunner();
}
void BindToCurrentThread(TimerSlack timer_slack) override {
sequence_manager_->BindToMessagePump(
std::move(message_pump_factory_).Run());
sequence_manager_->SetTimerSlack(timer_slack);
simple_task_executor_.emplace(GetDefaultTaskRunner());
}
private:
std::unique_ptr<sequence_manager::internal::SequenceManagerImpl>
sequence_manager_;
scoped_refptr<sequence_manager::TaskQueue> default_task_queue_;
OnceCallback<std::unique_ptr<MessagePump>()> message_pump_factory_;
base::Optional<SimpleTaskExecutor> simple_task_executor_;
};
} // namespace
Thread::Options::Options() = default;
Thread::Options::Options(MessagePumpType type, size_t size)
: message_pump_type(type), stack_size(size) {}
Thread::Options::Options(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_pump_type = MessagePumpType::UI;
#endif
return StartWithOptions(options);
}
bool Thread::StartWithOptions(const Options& options) {
DCHECK(owning_sequence_checker_.CalledOnValidSequence());
DCHECK(!delegate_);
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_pump_type == MessagePumpType::UI));
#endif
// Reset |id_| here to support restarting the thread.
id_event_.Reset();
id_ = kInvalidThreadId;
SetThreadWasQuitProperly(false);
timer_slack_ = options.timer_slack;
if (options.delegate) {
DCHECK(!options.message_pump_factory);
DCHECK(!options.task_queue_time_domain);
delegate_ = WrapUnique(options.delegate);
} else if (options.message_pump_factory) {
delegate_ = std::make_unique<SequenceManagerThreadDelegate>(
MessagePumpType::CUSTOM, options.message_pump_factory,
options.task_queue_time_domain);
} else {
delegate_ = std::make_unique<SequenceManagerThreadDelegate>(
options.message_pump_type,
BindOnce([](MessagePumpType type) { return MessagePump::Create(type); },
options.message_pump_type),
options.task_queue_time_domain);
}
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";
return false;
}
}
joinable_ = options.joinable;
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 (!delegate_)
return false;
// https://crbug.com/918039
base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
start_event_.Wait();
return true;
}
void Thread::FlushForTesting() {
DCHECK(owning_sequence_checker_.CalledOnValidSequence());
if (!delegate_)
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 |delegate_| around
// until the thread exits. Some consumers are abusing the API. Make them stop.
PlatformThread::Join(thread_);
thread_ = base::PlatformThreadHandle();
// The thread should release |delegate_| on exit (note: Join() adds
// an implicit memory barrier and no lock is thus required for this check).
DCHECK(!delegate_);
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_ || !delegate_)
return;
stopping_ = true;
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::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
id_event_.Wait();
return id_;
}
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. |delegate_| 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 (delegate_ && !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;
#if DCHECK_IS_ON()
quit_properly = lazy_tls_bool.Pointer()->Get();
#endif
return quit_properly;
}
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(delegate_);
// This binds MessageLoopCurrent and ThreadTaskRunnerHandle.
delegate_->BindToCurrentThread(timer_slack_);
DCHECK(MessageLoopCurrent::Get());
DCHECK(ThreadTaskRunnerHandle::IsSet());
#if defined(OS_POSIX) && !defined(OS_NACL)
// Allow threads running a MessageLoopForIO to use FileDescriptorWatcher API.
std::unique_ptr<FileDescriptorWatcher> file_descriptor_watcher;
if (MessageLoopCurrentForIO::IsSet()) {
file_descriptor_watcher.reset(
new FileDescriptorWatcher(delegate_->GetDefaultTaskRunner()));
}
#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
DCHECK(GetThreadWasQuitProperly());
// We can't receive messages anymore.
// (The message loop is destructed at the end of this block)
delegate_.reset();
run_loop_ = nullptr;
}
void Thread::ThreadQuitHelper() {
DCHECK(run_loop_);
run_loop_->QuitWhenIdle();
SetThreadWasQuitProperly(true);
}
} // namespace base

View file

@ -0,0 +1,341 @@
// 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_pump_type.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;
namespace sequence_manager {
class TimeDomain;
}
// IMPORTANT: Instead of creating a base::Thread, consider using
// base::Create(Sequenced|SingleThread)TaskRunner().
//
// 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:
class BASE_EXPORT Delegate {
public:
virtual ~Delegate() {}
virtual scoped_refptr<SingleThreadTaskRunner> GetDefaultTaskRunner() = 0;
// Binds a RunLoop::Delegate and TaskRunnerHandle to the thread. The
// underlying MessagePump will have its |timer_slack| set to the specified
// amount.
virtual void BindToCurrentThread(TimerSlack timer_slack) = 0;
};
struct BASE_EXPORT Options {
using MessagePumpFactory =
RepeatingCallback<std::unique_ptr<MessagePump>()>;
Options();
Options(MessagePumpType type, size_t size);
Options(Options&& other);
~Options();
// Specifies the type of message pump that will be allocated on the thread.
// This is ignored if message_pump_factory.is_null() is false.
MessagePumpType message_pump_type = MessagePumpType::DEFAULT;
// An unbound Delegate that will be bound to the thread. Ownership
// of |delegate| will be transferred to the thread.
// TODO(alexclarke): This should be a std::unique_ptr
Delegate* delegate = nullptr;
// Specifies timer slack for thread message loop.
TimerSlack timer_slack = TIMER_SLACK_NONE;
// The time domain to be used by the task queue. This is not compatible with
// a non-null |delegate|.
sequence_manager::TimeDomain* task_queue_time_domain = nullptr;
// 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_pump_type| is created. Setting this forces the
// MessagePumpType to TYPE_CUSTOM. This is not compatible with a non-null
// |delegate|.
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(!delegate_);
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 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 {
// This class doesn't provide synchronization around |message_loop_base_|
// 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_base_| safe... Changing all of those
// callers is unfeasible; instead verify that they can reliably see
// |message_loop_base_ != nullptr| without synchronization as a proof that
// their external synchronization catches the unsynchronized effects of
// Start().
DCHECK(owning_sequence_checker_.CalledOnValidSequence() ||
(id_event_.IsSignaled() && id_ == PlatformThread::CurrentId()) ||
delegate_);
return delegate_ ? delegate_->GetDefaultTaskRunner() : 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 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();
private:
// Friends for message_loop() access:
friend class MessageLoopTaskRunnerTest;
friend class ScheduleWorkTest;
#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 Delegate and RunLoop are valid only while the thread is
// alive. Set by the created thread.
std::unique_ptr<Delegate> delegate_;
RunLoop* run_loop_ = nullptr;
// Stores Options::timer_slack_ until the sequence manager has been bound to
// a thread.
TimerSlack 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_

View file

@ -0,0 +1,148 @@
// 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/strings/string_piece.h"
#include "base/thread_annotations.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). It supports
// thread safety annotations (see base/thread_annotations.h).
//
// 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(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(thread_checker_);
// }
//
// void MyMethod() {
// DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// ... (do stuff) ...
// }
//
// void MyOtherMethod()
// VALID_CONTEXT_REQUIRED(thread_checker_) {
// foo_ = 42;
// }
//
// private:
// int foo_ GUARDED_BY_CONTEXT(thread_checker_);
//
// THREAD_CHECKER(thread_checker_);
// }
#define THREAD_CHECKER_INTERNAL_CONCAT2(a, b) a##b
#define THREAD_CHECKER_INTERNAL_CONCAT(a, b) \
THREAD_CHECKER_INTERNAL_CONCAT2(a, b)
#define THREAD_CHECKER_INTERNAL_UID(prefix) \
THREAD_CHECKER_INTERNAL_CONCAT(prefix, __LINE__)
#if DCHECK_IS_ON()
#define THREAD_CHECKER(name) base::ThreadChecker name
#define DCHECK_CALLED_ON_VALID_THREAD(name, ...) \
base::ScopedValidateThreadChecker THREAD_CHECKER_INTERNAL_UID( \
scoped_validate_thread_checker_)(name, ##__VA_ARGS__);
#define DETACH_FROM_THREAD(name) (name).DetachFromThread()
#else // DCHECK_IS_ON()
#define THREAD_CHECKER(name) static_assert(true, "")
#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.
// Note: This is only a check, not a "lock". It is marked "LOCKABLE" only in
// order to support thread_annotations.h.
class LOCKABLE ThreadCheckerDoNothing {
public:
ThreadCheckerDoNothing() = default;
// Moving between matching threads is allowed to help classes with
// ThreadCheckers that want a default move-construct/assign.
ThreadCheckerDoNothing(ThreadCheckerDoNothing&& other) = default;
ThreadCheckerDoNothing& operator=(ThreadCheckerDoNothing&& other) = 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 ThreadPool that happen to share a thread).
#if DCHECK_IS_ON()
class ThreadChecker : public ThreadCheckerImpl {
};
#else
class ThreadChecker : public ThreadCheckerDoNothing {
};
#endif // DCHECK_IS_ON()
class SCOPED_LOCKABLE ScopedValidateThreadChecker {
public:
explicit ScopedValidateThreadChecker(const ThreadChecker& checker)
EXCLUSIVE_LOCK_FUNCTION(checker) {
DCHECK(checker.CalledOnValidThread());
}
explicit ScopedValidateThreadChecker(const ThreadChecker& checker,
const StringPiece& msg)
EXCLUSIVE_LOCK_FUNCTION(checker) {
DCHECK(checker.CalledOnValidThread()) << msg;
}
~ScopedValidateThreadChecker() UNLOCK_FUNCTION() {}
private:
DISALLOW_COPY_AND_ASSIGN(ScopedValidateThreadChecker);
};
} // namespace base
#endif // BASE_THREADING_THREAD_CHECKER_H_

View file

@ -0,0 +1,107 @@
// 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/logging.h"
#include "base/threading/thread_local.h"
#include "base/threading/thread_task_runner_handle.h"
namespace base {
ThreadCheckerImpl::ThreadCheckerImpl() {
AutoLock auto_lock(lock_);
EnsureAssignedLockRequired();
}
ThreadCheckerImpl::~ThreadCheckerImpl() = default;
ThreadCheckerImpl::ThreadCheckerImpl(ThreadCheckerImpl&& other) {
// Verify that |other| is called on its associated thread and bind it now if
// it is currently detached (even if this isn't a DCHECK build).
const bool other_called_on_valid_thread = other.CalledOnValidThread();
DCHECK(other_called_on_valid_thread);
// Intentionally not using |other.lock_| to let TSAN catch racy construct from
// |other|.
thread_id_ = other.thread_id_;
task_token_ = other.task_token_;
sequence_token_ = other.sequence_token_;
other.thread_id_ = PlatformThreadRef();
other.task_token_ = TaskToken();
other.sequence_token_ = SequenceToken();
}
ThreadCheckerImpl& ThreadCheckerImpl::operator=(ThreadCheckerImpl&& other) {
DCHECK(CalledOnValidThread());
// Verify that |other| is called on its associated thread and bind it now if
// it is currently detached (even if this isn't a DCHECK build).
const bool other_called_on_valid_thread = other.CalledOnValidThread();
DCHECK(other_called_on_valid_thread);
// Intentionally not using either |lock_| to let TSAN catch racy assign.
TS_UNCHECKED_READ(thread_id_) = TS_UNCHECKED_READ(other.thread_id_);
TS_UNCHECKED_READ(task_token_) = TS_UNCHECKED_READ(other.task_token_);
TS_UNCHECKED_READ(sequence_token_) = TS_UNCHECKED_READ(other.sequence_token_);
TS_UNCHECKED_READ(other.thread_id_) = PlatformThreadRef();
TS_UNCHECKED_READ(other.task_token_) = TaskToken();
TS_UNCHECKED_READ(other.sequence_token_) = SequenceToken();
return *this;
}
bool ThreadCheckerImpl::CalledOnValidThread() const {
const bool has_thread_been_destroyed = ThreadLocalStorage::HasBeenDestroyed();
AutoLock auto_lock(lock_);
// TaskToken/SequenceToken access thread-local storage. During destruction
// the state of thread-local storage is not guaranteed to be in a consistent
// state. Further, task-runner only installs the tokens when running a task.
if (!has_thread_been_destroyed) {
EnsureAssignedLockRequired();
// 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;
}
} else if (thread_id_.is_null()) {
// We're in tls destruction but the |thread_id_| hasn't been assigned yet.
// Assign it now. This doesn't call EnsureAssigned() as to do so while in
// tls destruction may result in the wrong TaskToken/SequenceToken.
thread_id_ = PlatformThread::CurrentRef();
}
return thread_id_ == PlatformThread::CurrentRef();
}
void ThreadCheckerImpl::DetachFromThread() {
AutoLock auto_lock(lock_);
thread_id_ = PlatformThreadRef();
task_token_ = TaskToken();
sequence_token_ = SequenceToken();
}
void ThreadCheckerImpl::EnsureAssignedLockRequired() const {
if (!thread_id_.is_null())
return;
thread_id_ = PlatformThread::CurrentRef();
task_token_ = TaskToken::GetForCurrentThread();
sequence_token_ = SequenceToken::GetForCurrentThread();
}
} // namespace base

View file

@ -0,0 +1,74 @@
// 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/thread_annotations.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.
// Note: This is only a check, not a "lock". It is marked "LOCKABLE" only in
// order to support thread_annotations.h.
class LOCKABLE BASE_EXPORT ThreadCheckerImpl {
public:
ThreadCheckerImpl();
~ThreadCheckerImpl();
// Allow move construct/assign. This must be called on |other|'s associated
// thread and assignment can only be made into a ThreadCheckerImpl which is
// detached or already associated with the current thread. This isn't
// thread-safe (|this| and |other| shouldn't be in use while this move is
// performed). If the assignment was legal, the resulting ThreadCheckerImpl
// will be bound to the current thread and |other| will be detached.
ThreadCheckerImpl(ThreadCheckerImpl&& other);
ThreadCheckerImpl& operator=(ThreadCheckerImpl&& other);
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 EnsureAssignedLockRequired() const EXCLUSIVE_LOCKS_REQUIRED(lock_);
// 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_ GUARDED_BY(lock_);
// 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_ GUARDED_BY(lock_);
// SequenceToken for which CalledOnValidThread() may return true. Used to
// ensure that CalledOnValidThread() doesn't return true for ThreadPool
// tasks that happen to run on the same thread but weren't posted to the same
// SingleThreadTaskRunner.
mutable SequenceToken sequence_token_ GUARDED_BY(lock_);
};
} // namespace base
#endif // BASE_THREADING_THREAD_CHECKER_IMPL_H_

View file

@ -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

View file

@ -0,0 +1,147 @@
// 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/stl_util.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::Observer::~Observer() = default;
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::AddObserver(Observer* obs) {
AutoLock locked(lock_);
DCHECK(!base::Contains(observers_, obs));
observers_.push_back(obs);
}
void ThreadIdNameManager::RemoveObserver(Observer* obs) {
AutoLock locked(lock_);
DCHECK(base::Contains(observers_, obs));
base::Erase(observers_, obs);
}
void ThreadIdNameManager::SetName(const std::string& name) {
PlatformThreadId id = PlatformThread::CurrentId();
std::string* leaked_str = nullptr;
{
AutoLock locked(lock_);
auto 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;
}
auto id_to_handle_iter = thread_id_to_handle_.find(id);
GetThreadNameTLS().Set(const_cast<char*>(leaked_str->c_str()));
for (Observer* obs : observers_)
obs->OnThreadNameChanged(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();
auto 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();
auto 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_);
auto 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);
auto 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

View file

@ -0,0 +1,94 @@
// 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 <vector>
#include "base/base_export.h"
#include "base/callback.h"
#include "base/macros.h"
#include "base/observer_list.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();
class BASE_EXPORT Observer {
public:
virtual ~Observer();
// Called on the thread whose name is changing, 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.
//
// NOTE: Will be called while ThreadIdNameManager's lock is held, so don't
// call back into it.
virtual void OnThreadNameChanged(const char* name) = 0;
};
// Register the mapping between a thread |id| and |handle|.
void RegisterThread(PlatformThreadHandle::Handle handle, PlatformThreadId id);
void AddObserver(Observer*);
void RemoveObserver(Observer*);
// 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_;
// There's no point using a base::ObserverList behind a lock, so we just use
// an std::vector instead.
std::vector<Observer*> observers_;
DISALLOW_COPY_AND_ASSIGN(ThreadIdNameManager);
};
} // namespace base
#endif // BASE_THREADING_THREAD_ID_NAME_MANAGER_H_

View file

@ -0,0 +1,136 @@
// 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.
//
// 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 <memory>
#include "base/logging.h"
#include "base/macros.h"
#include "base/threading/thread_local_internal.h"
#include "base/threading/thread_local_storage.h"
namespace base {
template <typename T>
class ThreadLocalPointer {
public:
ThreadLocalPointer() = default;
~ThreadLocalPointer() = default;
T* Get() const { return static_cast<T*>(slot_.Get()); }
void Set(T* ptr) {
slot_.Set(const_cast<void*>(static_cast<const void*>(ptr)));
}
private:
ThreadLocalStorage::Slot slot_;
DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer<T>);
};
// A ThreadLocalOwnedPointer<T> is like a ThreadLocalPointer<T> except that
// pointers handed to it are owned and automatically deleted during their
// associated thread's exit phase (or when replaced if Set() is invoked multiple
// times on the same thread).
// The ThreadLocalOwnedPointer instance itself can only be destroyed when no
// threads, other than the one it is destroyed on, have remaining state set in
// it. Typically this means that ThreadLocalOwnedPointer instances are held in
// static storage or at the very least only recycled in the single-threaded
// phase between tests in the same process.
#if DCHECK_IS_ON()
template <typename T>
using ThreadLocalOwnedPointer = internal::CheckedThreadLocalOwnedPointer<T>;
#else // DCHECK_IS_ON()
template <typename T>
class ThreadLocalOwnedPointer {
public:
ThreadLocalOwnedPointer() = default;
~ThreadLocalOwnedPointer() {
// Assume that this thread is the only one with potential state left. This
// is verified in ~CheckedThreadLocalOwnedPointer().
Set(nullptr);
}
T* Get() const { return static_cast<T*>(slot_.Get()); }
void Set(std::unique_ptr<T> ptr) {
delete Get();
slot_.Set(const_cast<void*>(static_cast<const void*>(ptr.release())));
}
private:
static void DeleteTlsPtr(void* ptr) { delete static_cast<T*>(ptr); }
ThreadLocalStorage::Slot slot_{&DeleteTlsPtr};
DISALLOW_COPY_AND_ASSIGN(ThreadLocalOwnedPointer<T>);
};
#endif // DCHECK_IS_ON()
class ThreadLocalBoolean {
public:
ThreadLocalBoolean() = default;
~ThreadLocalBoolean() = default;
bool Get() const { 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_

View file

@ -0,0 +1,80 @@
// Copyright 2019 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_INTERNAL_H_
#define BASE_THREADING_THREAD_LOCAL_INTERNAL_H_
#if DCHECK_IS_ON()
#include <atomic>
#include <memory>
#include "base/macros.h"
#include "base/threading/thread_local_storage.h"
namespace base {
namespace internal {
// A version of ThreadLocalOwnedPointer which verifies that it's only destroyed
// when no threads, other than the one it is destroyed on, have remaining state
// set in it. A ThreadLocalOwnedPointer instance being destroyed too early would
// result in leaks per unregistering the TLS slot (and thus the DeleteTlsPtr
// hook).
template <typename T>
class CheckedThreadLocalOwnedPointer {
public:
CheckedThreadLocalOwnedPointer() = default;
~CheckedThreadLocalOwnedPointer() {
Set(nullptr);
DCHECK_EQ(num_assigned_threads_.load(std::memory_order_relaxed), 0)
<< "Memory leak: Must join all threads or release all associated "
"thread-local slots before ~ThreadLocalOwnedPointer";
}
T* Get() const {
PtrTracker* const ptr_tracker = static_cast<PtrTracker*>(slot_.Get());
return ptr_tracker ? ptr_tracker->ptr_.get() : nullptr;
}
void Set(std::unique_ptr<T> ptr) {
delete static_cast<PtrTracker*>(slot_.Get());
if (ptr)
slot_.Set(new PtrTracker(this, std::move(ptr)));
else
slot_.Set(nullptr);
}
private:
struct PtrTracker {
public:
PtrTracker(CheckedThreadLocalOwnedPointer<T>* outer, std::unique_ptr<T> ptr)
: outer_(outer), ptr_(std::move(ptr)) {
outer_->num_assigned_threads_.fetch_add(1, std::memory_order_relaxed);
}
~PtrTracker() {
outer_->num_assigned_threads_.fetch_sub(1, std::memory_order_relaxed);
}
CheckedThreadLocalOwnedPointer<T>* const outer_;
const std::unique_ptr<T> ptr_;
};
static void DeleteTlsPtr(void* ptr) { delete static_cast<PtrTracker*>(ptr); }
ThreadLocalStorage::Slot slot_{&DeleteTlsPtr};
std::atomic_int num_assigned_threads_{0};
DISALLOW_COPY_AND_ASSIGN(CheckedThreadLocalOwnedPointer<T>);
};
} // namespace internal
} // namespace base
#endif // DCHECK_IS_ON()
#endif // BASE_THREADING_THREAD_LOCAL_INTERNAL_H_

View file

@ -0,0 +1,461 @@
// 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/compiler_specific.h"
#include "base/logging.h"
#include "base/no_destructor.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 the following states. The TLS slot's lower 2 bits contain
// the state, the upper bits the TlsVectorEntry*.
// * kUninitialized: Any call to Slot::Get()/Set() will create the base
// per-thread TLS state. kUninitialized must be null.
// * kInUse: value has been created and is in use.
// * kDestroying: Set when the thread is exiting prior to deleting any of the
// values stored in the TlsVectorEntry*. This state is necessary so that
// sequence/task checks won't be done while in the process of deleting the
// tls entries (see comments in SequenceCheckerImpl for more details).
// * kDestroyed: All of the values in the vector have been deallocated and
// the TlsVectorEntry has been deleted.
//
// 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 state of the tls-entry.
enum class TlsVectorState {
kUninitialized = 0,
// In the process of destroying the entries in the vector.
kDestroying,
// All of the entries and the vector has been destroyed.
kDestroyed,
// The vector has been initialized and is in use.
kInUse,
kMaxValue = kInUse
};
// Bit-mask used to store TlsVectorState.
constexpr uintptr_t kVectorStateBitMask = 3;
static_assert(static_cast<int>(TlsVectorState::kMaxValue) <=
kVectorStateBitMask,
"number of states must fit in header");
static_assert(static_cast<int>(TlsVectorState::kUninitialized) == 0,
"kUninitialized must be null");
// 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;
// Incremented every time a slot is reused. Used to detect reuse of slots.
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;
// Sets the value and state of the vector.
void SetTlsVectorValue(PlatformThreadLocalStorage::TLSKey key,
TlsVectorEntry* tls_data,
TlsVectorState state) {
DCHECK(tls_data || (state == TlsVectorState::kUninitialized) ||
(state == TlsVectorState::kDestroyed));
PlatformThreadLocalStorage::SetTLSValue(
key, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(tls_data) |
static_cast<uintptr_t>(state)));
}
// Returns the tls vector and current state from the raw tls value.
TlsVectorState GetTlsVectorStateAndValue(void* tls_value,
TlsVectorEntry** entry = nullptr) {
if (entry) {
*entry = reinterpret_cast<TlsVectorEntry*>(
reinterpret_cast<uintptr_t>(tls_value) & ~kVectorStateBitMask);
}
return static_cast<TlsVectorState>(reinterpret_cast<uintptr_t>(tls_value) &
kVectorStateBitMask);
}
// Returns the tls vector and state using the tls key.
TlsVectorState GetTlsVectorStateAndValue(PlatformThreadLocalStorage::TLSKey key,
TlsVectorEntry** entry = nullptr) {
return GetTlsVectorStateAndValue(PlatformThreadLocalStorage::GetTLSValue(key),
entry);
}
// 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(GetTlsVectorStateAndValue(key), TlsVectorState::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.
SetTlsVectorValue(key, stack_allocated_tls_data, TlsVectorState::kInUse);
// 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));
SetTlsVectorValue(key, tls_data, TlsVectorState::kInUse);
return tls_data;
}
void OnThreadExitInternal(TlsVectorEntry* tls_data) {
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);
SetTlsVectorValue(key, stack_allocated_tls_data, TlsVectorState::kDestroying);
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.
SetTlsVectorValue(key, nullptr, TlsVectorState::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;
TlsVectorEntry* tls_vector = nullptr;
const TlsVectorState state = GetTlsVectorStateAndValue(key, &tls_vector);
// 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(state, TlsVectorState::kDestroyed);
// Maybe we have never initialized TLS for this thread.
if (state == TlsVectorState::kUninitialized)
return;
OnThreadExitInternal(tls_vector);
}
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
void PlatformThreadLocalStorage::OnThreadExit(void* value) {
// On posix this function may be called twice. The first pass calls dtors and
// sets state to kDestroyed. The second pass sets kDestroyed to
// kUninitialized.
TlsVectorEntry* tls_vector = nullptr;
const TlsVectorState state = GetTlsVectorStateAndValue(value, &tls_vector);
if (state == TlsVectorState::kDestroyed) {
PlatformThreadLocalStorage::TLSKey key =
base::subtle::NoBarrier_Load(&g_native_tls_key);
SetTlsVectorValue(key, nullptr, TlsVectorState::kUninitialized);
return;
}
OnThreadExitInternal(tls_vector);
}
#endif // defined(OS_WIN)
} // namespace internal
// static
bool ThreadLocalStorage::HasBeenDestroyed() {
PlatformThreadLocalStorage::TLSKey key =
base::subtle::NoBarrier_Load(&g_native_tls_key);
if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES)
return false;
const TlsVectorState state = GetTlsVectorStateAndValue(key);
return state == TlsVectorState::kDestroying ||
state == TlsVectorState::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 ||
GetTlsVectorStateAndValue(key) == TlsVectorState::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 = nullptr;
const TlsVectorState state = GetTlsVectorStateAndValue(
base::subtle::NoBarrier_Load(&g_native_tls_key), &tls_data);
DCHECK_NE(state, TlsVectorState::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 = nullptr;
const TlsVectorState state = GetTlsVectorStateAndValue(
base::subtle::NoBarrier_Load(&g_native_tls_key), &tls_data);
DCHECK_NE(state, TlsVectorState::kDestroyed);
if (!tls_data) {
if (!value)
return;
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

View file

@ -0,0 +1,175 @@
// 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 ui {
class TLSDestructionCheckerForX11;
}
namespace base {
class SamplingHeapProfiler;
namespace debug {
class GlobalActivityTracker;
} // namespace debug
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 is being or 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 SequenceCheckerImpl;
friend class SamplingHeapProfiler;
friend class ThreadCheckerImpl;
friend class internal::ThreadLocalStorageTestInternal;
friend class trace_event::MallocDumpProvider;
friend class debug::GlobalActivityTracker;
friend class ui::TLSDestructionCheckerForX11;
static bool HasBeenDestroyed();
DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorage);
};
} // namespace base
#endif // BASE_THREADING_THREAD_LOCAL_STORAGE_H_

View file

@ -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

View file

@ -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"

View file

@ -0,0 +1,301 @@
// 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"
#include "base/trace_event/trace_event.h"
#if DCHECK_IS_ON()
#include "base/debug/stack_trace.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/threading/thread_local.h"
#include "build/build_config.h"
namespace base {
std::ostream& operator<<(std::ostream&out, const ThreadLocalBoolean& tl) {
out << "currently set to " << (tl.Get() ? "true" : "false");
return out;
}
namespace {
#if defined(OS_NACL) || defined(OS_ANDROID)
// NaCL doesn't support stack sampling and Android is slow at stack
// sampling and this causes timeouts (crbug.com/959139).
using ThreadLocalBooleanWithStacks = ThreadLocalBoolean;
#else
class ThreadLocalBooleanWithStacks {
public:
ThreadLocalBooleanWithStacks() = default;
bool Get() const { return bool_.Get(); }
void Set(bool val) {
stack_.Set(std::make_unique<debug::StackTrace>());
bool_.Set(val);
}
friend std::ostream& operator<<(std::ostream& out,
const ThreadLocalBooleanWithStacks& tl) {
out << tl.bool_ << " by ";
if (!tl.stack_.Get())
return out << "default value\n";
out << "\n";
tl.stack_.Get()->OutputToStream(&out);
return out;
}
private:
ThreadLocalBoolean bool_;
ThreadLocalOwnedPointer<debug::StackTrace> stack_;
DISALLOW_COPY_AND_ASSIGN(ThreadLocalBooleanWithStacks);
};
#endif // defined(OS_NACL)
LazyInstance<ThreadLocalBooleanWithStacks>::Leaky g_blocking_disallowed =
LAZY_INSTANCE_INITIALIZER;
LazyInstance<ThreadLocalBooleanWithStacks>::Leaky g_singleton_disallowed =
LAZY_INSTANCE_INITIALIZER;
LazyInstance<ThreadLocalBooleanWithStacks>::Leaky
g_base_sync_primitives_disallowed = LAZY_INSTANCE_INITIALIZER;
LazyInstance<ThreadLocalBooleanWithStacks>::Leaky
g_cpu_intensive_work_disallowed = LAZY_INSTANCE_INITIALIZER;
} // namespace
namespace internal {
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 ThreadPool, it needs "
"to have MayBlock() in its TaskTraits. Otherwise, consider making "
"this blocking work asynchronous or, as a last resort, you may use "
"ScopedAllowBlocking (see its documentation for best practices).\n"
<< "g_blocking_disallowed " << g_blocking_disallowed.Get();
}
} // namespace internal
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_);
}
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.\n"
<< "g_blocking_disallowed " << g_blocking_disallowed.Get();
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_);
}
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_);
}
ScopedAllowUnresponsiveTasksForTesting::ScopedAllowUnresponsiveTasksForTesting()
: was_disallowed_base_sync_(g_base_sync_primitives_disallowed.Get().Get()),
was_disallowed_blocking_(g_blocking_disallowed.Get().Get()),
was_disallowed_cpu_(g_cpu_intensive_work_disallowed.Get().Get()) {
g_base_sync_primitives_disallowed.Get().Set(false);
g_blocking_disallowed.Get().Set(false);
g_cpu_intensive_work_disallowed.Get().Set(false);
}
ScopedAllowUnresponsiveTasksForTesting::
~ScopedAllowUnresponsiveTasksForTesting() {
DCHECK(!g_base_sync_primitives_disallowed.Get().Get());
DCHECK(!g_blocking_disallowed.Get().Get());
DCHECK(!g_cpu_intensive_work_disallowed.Get().Get());
g_base_sync_primitives_disallowed.Get().Set(was_disallowed_base_sync_);
g_blocking_disallowed.Get().Set(was_disallowed_blocking_);
g_cpu_intensive_work_disallowed.Get().Set(was_disallowed_cpu_);
}
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.\n"
<< "g_base_sync_primitives_disallowed "
<< g_base_sync_primitives_disallowed.Get()
<< "It can be useful to know that g_blocking_disallowed is "
<< g_blocking_disallowed.Get();
}
void ResetThreadRestrictionsForTesting() {
g_blocking_disallowed.Get().Set(false);
g_singleton_disallowed.Get().Set(false);
g_base_sync_primitives_disallowed.Get().Set(false);
g_cpu_intensive_work_disallowed.Get().Set(false);
}
} // namespace internal
void AssertLongCPUWorkAllowed() {
DCHECK(!g_cpu_intensive_work_disallowed.Get().Get())
<< "Function marked as CPU intensive was called from a scope that "
"disallows this kind of work! Consider making this work "
"asynchronous.\n"
<< "g_cpu_intensive_work_disallowed "
<< g_cpu_intensive_work_disallowed.Get();
}
void DisallowUnresponsiveTasks() {
DisallowBlocking();
DisallowBaseSyncPrimitives();
g_cpu_intensive_work_disallowed.Get().Set(true);
}
// 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() {
DCHECK(!g_singleton_disallowed.Get().Get())
<< "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.\n"
<< "g_singleton_disallowed " << g_singleton_disallowed.Get();
}
// 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;
}
} // namespace base
#endif // DCHECK_IS_ON()
namespace base {
ScopedAllowBlocking::ScopedAllowBlocking(const Location& from_here)
#if DCHECK_IS_ON()
: was_disallowed_(g_blocking_disallowed.Get().Get())
#endif
{
TRACE_EVENT_BEGIN2("base", "ScopedAllowBlocking", "file_name",
from_here.file_name(), "function_name",
from_here.function_name());
#if DCHECK_IS_ON()
g_blocking_disallowed.Get().Set(false);
#endif
}
ScopedAllowBlocking::~ScopedAllowBlocking() {
TRACE_EVENT_END0("base", "ScopedAllowBlocking");
#if DCHECK_IS_ON()
DCHECK(!g_blocking_disallowed.Get().Get());
g_blocking_disallowed.Get().Set(was_disallowed_);
#endif
}
ScopedAllowBaseSyncPrimitivesOutsideBlockingScope::
ScopedAllowBaseSyncPrimitivesOutsideBlockingScope(const Location& from_here)
#if DCHECK_IS_ON()
: was_disallowed_(g_base_sync_primitives_disallowed.Get().Get())
#endif
{
TRACE_EVENT_BEGIN2(
"base", "ScopedAllowBaseSyncPrimitivesOutsideBlockingScope", "file_name",
from_here.file_name(), "function_name", from_here.function_name());
#if DCHECK_IS_ON()
g_base_sync_primitives_disallowed.Get().Set(false);
#endif
}
ScopedAllowBaseSyncPrimitivesOutsideBlockingScope::
~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() {
TRACE_EVENT_END0("base", "ScopedAllowBaseSyncPrimitivesOutsideBlockingScope");
#if DCHECK_IS_ON()
DCHECK(!g_base_sync_primitives_disallowed.Get().Get());
g_base_sync_primitives_disallowed.Get().Set(was_disallowed_);
#endif
}
ThreadRestrictions::ScopedAllowIO::ScopedAllowIO(const Location& from_here)
#if DCHECK_IS_ON()
: was_allowed_(SetIOAllowed(true))
#endif
{
TRACE_EVENT_BEGIN2("base", "ScopedAllowIO", "file_name",
from_here.file_name(), "function_name",
from_here.function_name());
}
ThreadRestrictions::ScopedAllowIO::~ScopedAllowIO() {
TRACE_EVENT_END0("base", "ScopedAllowIO");
#if DCHECK_IS_ON()
SetIOAllowed(was_allowed_);
#endif
}
} // namespace base

View file

@ -0,0 +1,692 @@
// 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/location.h"
#include "base/logging.h"
#include "base/macros.h"
// -----------------------------------------------------------------------------
// Usage documentation
// -----------------------------------------------------------------------------
//
// Overview:
// This file exposes functions to ban and allow certain slow operations
// on a per-thread basis. To annotate *usage* of such slow operations, refer to
// scoped_blocking_call.h instead.
//
// Specific allowances that can be controlled in this file are:
// - 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.
//
// - Waiting on a //base sync primitive: Refers to calling one of these methods:
// - base::WaitableEvent::*Wait*
// - base::ConditionVariable::*Wait*
// - base::Process::WaitForExit*
//
// - Long CPU work: Refers to any code that takes more than 100 ms to
// run when there is no CPU contention and no hard page faults and therefore,
// is not suitable to run on a thread required to keep the browser responsive
// (where jank could be visible to the user).
//
// The following disallowance functions are offered:
// - DisallowBlocking(): Disallows blocking calls on the current thread.
// - DisallowBaseSyncPrimitives(): Disallows waiting on a //base sync primitive
// on the current thread.
// - DisallowUnresponsiveTasks() Disallows blocking calls, waiting on a //base
// sync primitive, and long cpu work on the current thread.
//
// In addition, scoped-allowance mechanisms are offered to make an exception
// within a scope for a behavior that is normally disallowed.
// - ScopedAllowBlocking(ForTesting): Allows blocking calls.
// - ScopedAllowBaseSyncPrimitives(ForTesting)(OutsideBlockingScope): Allow
// waiting on a //base sync primitive. The OutsideBlockingScope suffix allows
// uses in a scope where blocking is also disallowed.
//
// Avoid using allowances outside of unit tests. In unit tests, use allowances
// with the suffix "ForTesting".
//
// Prefer making blocking calls from tasks posted to base::ThreadPoolInstance
// with base::MayBlock().
//
// Instead of waiting on a WaitableEvent or a ConditionVariable, prefer putting
// the work that should happen after the wait in a continuation callback and
// post it 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.
//
// Where unavoidable, put ScopedAllow* instances in the narrowest scope possible
// in the caller making the blocking call but no further down. For example: if a
// Cleanup() method needs to do a blocking call, document Cleanup() as blocking
// and add a ScopedAllowBlocking instance in callers that can't avoid making
// this call from a context where blocking is banned, as such:
//
// void Client::MyMethod() {
// (...)
// {
// // Blocking is okay here because XYZ.
// ScopedAllowBlocking allow_blocking;
// my_foo_->Cleanup();
// }
// (...)
// }
//
// // This method can block.
// void Foo::Cleanup() {
// // Do NOT add the ScopedAllowBlocking in Cleanup() directly as that hides
// // its blocking nature from unknowing callers and defeats the purpose of
// // these checks.
// FlushStateToDisk();
// }
//
// Note: In rare situations where the blocking call is an implementation detail
// (i.e. the impl makes a call that invokes AssertBlockingAllowed() but it
// somehow knows that in practice this will not block), it might be okay to hide
// the ScopedAllowBlocking instance in the impl with a comment explaining why
// that's okay.
class BrowserProcessImpl;
class HistogramSynchronizer;
class KeyStorageLinux;
class NativeBackendKWallet;
class NativeDesktopMediaList;
namespace android_webview {
class AwFormDatabaseService;
class CookieManager;
class ScopedAllowInitGLBindings;
class VizCompositorThreadRunnerWebView;
}
namespace audio {
class OutputDevice;
}
namespace blink {
class RTCVideoDecoderAdapter;
class RTCVideoEncoder;
class SourceStream;
class VideoFrameResourceProvider;
class WorkerThread;
namespace scheduler {
class WorkerThread;
}
}
namespace cc {
class CompletionEvent;
class TileTaskManagerImpl;
}
namespace chromeos {
class BlockingMethodCaller;
class MojoUtils;
namespace system {
class StatisticsProviderImpl;
}
}
namespace chrome_browser_net {
class Predictor;
}
namespace chrome_cleaner {
class SystemReportComponent;
}
namespace content {
class BrowserGpuChannelHostFactory;
class BrowserMainLoop;
class BrowserProcessSubThread;
class BrowserShutdownProfileDumper;
class BrowserTestBase;
class CategorizedWorkerPool;
class DesktopCaptureDevice;
class InProcessUtilityThread;
class NestedMessagePumpAndroid;
class RenderProcessHostImpl;
class RenderWidgetHostViewMac;
class RTCVideoDecoder;
class SandboxHostLinux;
class ScopedAllowWaitForDebugURL;
class ServiceWorkerContextClient;
class SoftwareOutputDeviceMus;
class SynchronousCompositor;
class SynchronousCompositorHost;
class SynchronousCompositorSyncCallBridge;
class TextInputClientMac;
class WebContentsViewMac;
} // 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 history_report {
class HistoryReportJniBridge;
}
namespace gpu {
class GpuChannelHost;
}
namespace leveldb_env {
class DBTracker;
}
namespace media {
class AudioInputDevice;
class AudioOutputDevice;
class BlockingUrlProtocol;
class PaintCanvasVideoRenderer;
}
namespace memory_instrumentation {
class OSMetrics;
}
namespace midi {
class TaskService; // https://crbug.com/796830
}
namespace module_installer {
class ScopedAllowModulePakLoad;
}
namespace mojo {
class CoreLibraryInitializer;
class SyncCallRestrictions;
namespace core {
class ScopedIPCSupport;
}
}
namespace printing {
class PrintJobWorker;
class PrinterQuery;
}
namespace rlz_lib {
class FinancialPing;
}
namespace syncer {
class GetLocalChangesRequest;
class HttpBridge;
class ModelSafeWorker;
}
namespace ui {
class CommandBufferClientImpl;
class CommandBufferLocal;
class DrmThreadProxy;
class GpuState;
}
namespace weblayer {
class BrowserContextImpl;
class BrowserProcess;
class ProfileImpl;
class WebLayerPathProvider;
}
namespace net {
class MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives;
class MultiThreadedProxyResolverScopedAllowJoinOnIO;
class NetworkChangeNotifierMac;
class NetworkConfigWatcherMacThread;
namespace internal {
class AddressTrackerLinux;
}
}
namespace proxy_resolver {
class ScopedAllowThreadJoinForProxyResolverV8Tracing;
}
namespace remoting {
class AutoThread;
namespace protocol {
class ScopedAllowThreadJoinForWebRtcTransport;
}
}
namespace resource_coordinator {
class TabManagerDelegate;
}
namespace service_manager {
class ServiceProcessLauncher;
}
namespace shell_integration_linux {
class LaunchXdgUtilityScopedAllowBaseSyncPrimitives;
}
namespace ui {
class WindowResizeHelperMac;
}
namespace viz {
class HostGpuMemoryBufferManager;
}
namespace vr {
class VrShell;
}
namespace web {
class WebMainLoop;
class WebSubThread;
}
namespace webrtc {
class DesktopConfigurationMonitor;
}
namespace base {
namespace sequence_manager {
namespace internal {
class TaskQueueImpl;
}
} // namespace sequence_manager
namespace android {
class JavaHandlerThread;
}
namespace internal {
class JobTaskSource;
class TaskTracker;
}
class AdjustOOMScoreHelper;
class FileDescriptorWatcher;
class FilePath;
class GetAppOutputScopedAllowBaseSyncPrimitives;
class ScopedAllowThreadRecallForStackSamplingProfiler;
class SimpleThread;
class StackSamplingProfiler;
class Thread;
bool PathProviderWin(int, FilePath*);
#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
// The static_assert() eats follow-on semicolons. `= default` would work
// too, but it makes clang realize that all the Scoped classes are no-ops in
// non-dcheck builds and it starts emitting many -Wunused-variable warnings.
#define EMPTY_BODY_IF_DCHECK_IS_OFF \
{} \
static_assert(true, "")
#endif
namespace internal {
// Asserts that blocking calls are allowed in the current scope. This is an
// internal call, external code should use ScopedBlockingCall instead, which
// serves as a precise annotation of the scope that may/will block.
INLINE_IF_DCHECK_IS_OFF void AssertBlockingAllowed()
EMPTY_BODY_IF_DCHECK_IS_OFF;
} // namespace internal
// 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);
};
class BASE_EXPORT ScopedAllowBlocking {
private:
FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest, ScopedAllowBlocking);
friend class ScopedAllowBlockingForTesting;
// This can only be instantiated by friends. Use ScopedAllowBlockingForTesting
// in unit tests to avoid the friend requirement.
friend class AdjustOOMScoreHelper;
friend class StackSamplingProfiler;
friend class android_webview::ScopedAllowInitGLBindings;
friend class chromeos::MojoUtils; // http://crbug.com/1055467
friend class content::BrowserProcessSubThread;
friend class content::RenderProcessHostImpl;
friend class content::RenderWidgetHostViewMac; // http://crbug.com/121917
friend class content::WebContentsViewMac;
friend class cronet::CronetPrefsManager;
friend class cronet::CronetURLRequestContext;
friend class memory_instrumentation::OSMetrics;
friend class module_installer::ScopedAllowModulePakLoad;
friend class mojo::CoreLibraryInitializer;
friend class printing::PrintJobWorker;
friend class resource_coordinator::TabManagerDelegate; // crbug.com/778703
friend class web::WebSubThread;
friend class weblayer::BrowserContextImpl;
friend class weblayer::BrowserProcess;
friend class weblayer::ProfileImpl;
friend class weblayer::WebLayerPathProvider;
friend bool PathProviderWin(int, FilePath*);
ScopedAllowBlocking(const Location& from_here = Location::Current());
~ScopedAllowBlocking();
#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);
};
INLINE_IF_DCHECK_IS_OFF void DisallowBaseSyncPrimitives()
EMPTY_BODY_IF_DCHECK_IS_OFF;
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);
// Allowed usage:
friend class SimpleThread;
friend class base::GetAppOutputScopedAllowBaseSyncPrimitives;
friend class blink::SourceStream;
friend class blink::WorkerThread;
friend class blink::scheduler::WorkerThread;
friend class chrome_cleaner::SystemReportComponent;
friend class content::BrowserMainLoop;
friend class content::BrowserProcessSubThread;
friend class content::ServiceWorkerContextClient;
friend class functions::ExecScriptScopedAllowBaseSyncPrimitives;
friend class history_report::HistoryReportJniBridge;
friend class internal::TaskTracker;
friend class leveldb_env::DBTracker;
friend class media::BlockingUrlProtocol;
friend class mojo::core::ScopedIPCSupport;
friend class net::MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives;
friend class rlz_lib::FinancialPing;
friend class shell_integration_linux::
LaunchXdgUtilityScopedAllowBaseSyncPrimitives;
friend class syncer::HttpBridge;
friend class syncer::GetLocalChangesRequest;
friend class syncer::ModelSafeWorker;
friend class webrtc::DesktopConfigurationMonitor;
// Usage that should be fixed:
friend class ::NativeBackendKWallet; // http://crbug.com/125331
friend class ::chromeos::system::
StatisticsProviderImpl; // http://crbug.com/125385
friend class content::TextInputClientMac; // http://crbug.com/121917
friend class blink::VideoFrameResourceProvider; // http://crbug.com/878070
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);
};
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);
// Allowed usage:
friend class ::BrowserProcessImpl; // http://crbug.com/125207
friend class ::KeyStorageLinux;
friend class ::NativeDesktopMediaList;
friend class android::JavaHandlerThread;
friend class android_webview::
AwFormDatabaseService; // http://crbug.com/904431
friend class android_webview::CookieManager;
friend class android_webview::VizCompositorThreadRunnerWebView;
friend class audio::OutputDevice;
friend class base::sequence_manager::internal::TaskQueueImpl;
friend class base::FileDescriptorWatcher;
friend class base::internal::JobTaskSource;
friend class base::ScopedAllowThreadRecallForStackSamplingProfiler;
friend class base::StackSamplingProfiler;
friend class blink::RTCVideoDecoderAdapter;
friend class blink::RTCVideoEncoder;
friend class cc::TileTaskManagerImpl;
friend class content::CategorizedWorkerPool;
friend class content::DesktopCaptureDevice;
friend class content::InProcessUtilityThread;
friend class content::RTCVideoDecoder;
friend class content::SandboxHostLinux;
friend class content::ScopedAllowWaitForDebugURL;
friend class content::SynchronousCompositor;
friend class content::SynchronousCompositorHost;
friend class content::SynchronousCompositorSyncCallBridge;
friend class media::AudioInputDevice;
friend class media::AudioOutputDevice;
friend class media::PaintCanvasVideoRenderer;
friend class mojo::SyncCallRestrictions;
friend class net::NetworkConfigWatcherMacThread;
friend class ui::DrmThreadProxy;
friend class viz::HostGpuMemoryBufferManager;
friend class vr::VrShell;
// Usage that should be fixed:
friend class ::chromeos::BlockingMethodCaller; // http://crbug.com/125360
friend class base::Thread; // http://crbug.com/918039
friend class cc::CompletionEvent; // http://crbug.com/902653
friend class content::
BrowserGpuChannelHostFactory; // http://crbug.com/125248
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 remoting::protocol::
ScopedAllowThreadJoinForWebRtcTransport; // http://crbug.com/660081
friend class midi::TaskService; // https://crbug.com/796830
friend class net::internal::AddressTrackerLinux; // http://crbug.com/125097
friend class net::
MultiThreadedProxyResolverScopedAllowJoinOnIO; // http://crbug.com/69710
friend class net::NetworkChangeNotifierMac; // http://crbug.com/125097
friend class printing::PrinterQuery; // http://crbug.com/66082
friend class proxy_resolver::
ScopedAllowThreadJoinForProxyResolverV8Tracing; // http://crbug.com/69710
friend class remoting::AutoThread; // https://crbug.com/944316
// Not used in production yet, https://crbug.com/844078.
friend class service_manager::ServiceProcessLauncher;
friend class ui::WindowResizeHelperMac; // http://crbug.com/902829
ScopedAllowBaseSyncPrimitivesOutsideBlockingScope(
const Location& from_here = Location::Current());
~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope();
#if DCHECK_IS_ON()
const bool was_disallowed_;
#endif
DISALLOW_COPY_AND_ASSIGN(ScopedAllowBaseSyncPrimitivesOutsideBlockingScope);
};
// Allow base-sync-primitives in tests, doesn't require explicit friend'ing like
// ScopedAllowBaseSyncPrimitives-types aimed at production do.
// Note: For WaitableEvents in the test logic, base::TestWaitableEvent is
// exposed as a convenience to avoid the need for
// ScopedAllowBaseSyncPrimitivesForTesting.
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);
};
// Counterpart to base::DisallowUnresponsiveTasks() for tests to allow them to
// block their thread after it was banned.
class BASE_EXPORT ScopedAllowUnresponsiveTasksForTesting {
public:
ScopedAllowUnresponsiveTasksForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF;
~ScopedAllowUnresponsiveTasksForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF;
private:
#if DCHECK_IS_ON()
const bool was_disallowed_base_sync_;
const bool was_disallowed_blocking_;
const bool was_disallowed_cpu_;
#endif
DISALLOW_COPY_AND_ASSIGN(ScopedAllowUnresponsiveTasksForTesting);
};
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
// Asserts that running long CPU work is allowed in the current scope.
INLINE_IF_DCHECK_IS_OFF void AssertLongCPUWorkAllowed()
EMPTY_BODY_IF_DCHECK_IS_OFF;
INLINE_IF_DCHECK_IS_OFF void DisallowUnresponsiveTasks()
EMPTY_BODY_IF_DCHECK_IS_OFF;
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(const Location& from_here = Location::Current());
~ScopedAllowIO();
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.
// BEGIN ALLOWED USAGE.
friend class content::BrowserMainLoop;
friend class content::BrowserShutdownProfileDumper;
friend class content::BrowserTestBase;
friend class content::ScopedAllowWaitForDebugURL;
friend class ::HistogramSynchronizer;
friend class internal::TaskTracker;
friend class web::WebMainLoop;
friend class MessagePumpDefault;
friend class PlatformThread;
friend class ui::CommandBufferClientImpl;
friend class ui::CommandBufferLocal;
friend class ui::GpuState;
// END ALLOWED USAGE.
// BEGIN USAGE THAT NEEDS TO BE FIXED.
friend class chrome_browser_net::Predictor; // http://crbug.com/78451
#if !defined(OFFICIAL_BUILD)
friend class content::SoftwareOutputDeviceMus; // Interim non-production code
#endif
// 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
DISALLOW_IMPLICIT_CONSTRUCTORS(ThreadRestrictions);
};
#undef INLINE_IF_DCHECK_IS_OFF
#undef EMPTY_BODY_IF_DCHECK_IS_OFF
} // namespace base
#endif // BASE_THREADING_THREAD_RESTRICTIONS_H_

View file

@ -0,0 +1,101 @@
// 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 <memory>
#include <utility>
#include "base/bind.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/run_loop.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
const scoped_refptr<SingleThreadTaskRunner>& ThreadTaskRunnerHandle::Get() {
const 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). If you're "
"in a test refer to //docs/threading_and_tasks_testing.md.";
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
// already being set on this thread (except when it's set by the current
// ThreadTaskRunnerHandle).
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->sequenced_task_runner_handle_.task_runner_ = overriding_task_runner;
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->sequenced_task_runner_handle_.task_runner_ =
task_runner_to_restore;
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)),
sequenced_task_runner_handle_(task_runner_) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(!thread_task_runner_tls.Pointer()->Get());
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

View file

@ -0,0 +1,62 @@
// 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/scoped_refptr.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/sequenced_task_runner_handle.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 const scoped_refptr<SingleThreadTaskRunner>& Get() WARN_UNUSED_RESULT;
// Returns true if the SingleThreadTaskRunner is already created for
// the current thread.
static bool IsSet() WARN_UNUSED_RESULT;
// 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_;
// Registers |task_runner_|'s SequencedTaskRunner interface as the
// SequencedTaskRunnerHandle on this thread.
SequencedTaskRunnerHandle sequenced_task_runner_handle_;
DISALLOW_COPY_AND_ASSIGN(ThreadTaskRunnerHandle);
};
} // namespace base
#endif // BASE_THREADING_THREAD_TASK_RUNNER_HANDLE_H_

View file

@ -0,0 +1,183 @@
// 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/no_destructor.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 base::NoDestructor<StaticData> static_data;
return static_data.get();
}
} // 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();
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

View file

@ -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_