Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
335
TMessagesProj/jni/voip/webrtc/base/threading/hang_watcher.cc
Normal file
335
TMessagesProj/jni/voip/webrtc/base/threading/hang_watcher.cc
Normal 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
|
||||
249
TMessagesProj/jni/voip/webrtc/base/threading/hang_watcher.h
Normal file
249
TMessagesProj/jni/voip/webrtc/base/threading/hang_watcher.h
Normal 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_
|
||||
|
|
@ -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
|
||||
259
TMessagesProj/jni/voip/webrtc/base/threading/platform_thread.h
Normal file
259
TMessagesProj/jni/voip/webrtc/base/threading/platform_thread.h
Normal 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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
319
TMessagesProj/jni/voip/webrtc/base/threading/sequence_bound.h
Normal file
319
TMessagesProj/jni/voip/webrtc/base/threading/sequence_bound.h
Normal 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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
178
TMessagesProj/jni/voip/webrtc/base/threading/simple_thread.cc
Normal file
178
TMessagesProj/jni/voip/webrtc/base/threading/simple_thread.cc
Normal 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
|
||||
231
TMessagesProj/jni/voip/webrtc/base/threading/simple_thread.h
Normal file
231
TMessagesProj/jni/voip/webrtc/base/threading/simple_thread.h
Normal 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_
|
||||
408
TMessagesProj/jni/voip/webrtc/base/threading/thread.cc
Normal file
408
TMessagesProj/jni/voip/webrtc/base/threading/thread.cc
Normal 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
|
||||
341
TMessagesProj/jni/voip/webrtc/base/threading/thread.h
Normal file
341
TMessagesProj/jni/voip/webrtc/base/threading/thread.h
Normal 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_
|
||||
148
TMessagesProj/jni/voip/webrtc/base/threading/thread_checker.h
Normal file
148
TMessagesProj/jni/voip/webrtc/base/threading/thread_checker.h
Normal 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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
136
TMessagesProj/jni/voip/webrtc/base/threading/thread_local.h
Normal file
136
TMessagesProj/jni/voip/webrtc/base/threading/thread_local.h
Normal 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_
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
183
TMessagesProj/jni/voip/webrtc/base/threading/watchdog.cc
Normal file
183
TMessagesProj/jni/voip/webrtc/base/threading/watchdog.cc
Normal 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
|
||||
96
TMessagesProj/jni/voip/webrtc/base/threading/watchdog.h
Normal file
96
TMessagesProj/jni/voip/webrtc/base/threading/watchdog.h
Normal 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_
|
||||
Loading…
Add table
Add a link
Reference in a new issue