Skip to content

Commit

Permalink
Use thread_local: base/ misc.
Browse files Browse the repository at this point in the history
Bug: 1416710
Change-Id: Icd9e91dc573daf52fc560d279bd4263121099303
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4321127
Reviewed-by: Francois Pierre Doray <fdoray@chromium.org>
Commit-Queue: Peter Kasting <pkasting@chromium.org>
Auto-Submit: Peter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1115921}
  • Loading branch information
pkasting authored and Chromium LUCI CQ committed Mar 10, 2023
1 parent 084bb54 commit 6e7b5ca
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 97 deletions.
13 changes: 9 additions & 4 deletions base/observer_list_threadsafe.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@
// found in the LICENSE file.

#include "base/observer_list_threadsafe.h"
#include "third_party/abseil-cpp/absl/base/attributes.h"

namespace base {
namespace internal {

LazyInstance<ThreadLocalPointer<
const ObserverListThreadSafeBase::NotificationDataBase>>::Leaky
ObserverListThreadSafeBase::tls_current_notification_ =
LAZY_INSTANCE_INITIALIZER;
ABSL_CONST_INIT thread_local const ObserverListThreadSafeBase::
NotificationDataBase* current_notification = nullptr;

// static
const ObserverListThreadSafeBase::NotificationDataBase*&
ObserverListThreadSafeBase::GetCurrentNotification() {
return current_notification;
}

} // namespace internal
} // namespace base
41 changes: 17 additions & 24 deletions base/observer_list_threadsafe.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@
#include <unordered_map>
#include <utility>

#include "base/auto_reset.h"
#include "base/base_export.h"
#include "base/check.h"
#include "base/check_op.h"
#include "base/containers/contains.h"
#include "base/dcheck_is_on.h"
#include "base/functional/bind.h"
#include "base/lazy_instance.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
#include "base/synchronization/lock.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread_local.h"
#include "build/build_config.h"
#include "third_party/abseil-cpp/absl/base/attributes.h"

///////////////////////////////////////////////////////////////////////////////
//
Expand Down Expand Up @@ -60,6 +60,14 @@ namespace internal {
class BASE_EXPORT ObserverListThreadSafeBase
: public RefCountedThreadSafe<ObserverListThreadSafeBase> {
public:
struct NotificationDataBase {
NotificationDataBase(void* observer_list_in, const Location& from_here_in)
: observer_list(observer_list_in), from_here(from_here_in) {}

raw_ptr<void> observer_list;
Location from_here;
};

ObserverListThreadSafeBase() = default;
ObserverListThreadSafeBase(const ObserverListThreadSafeBase&) = delete;
ObserverListThreadSafeBase& operator=(const ObserverListThreadSafeBase&) =
Expand All @@ -78,19 +86,10 @@ class BASE_EXPORT ObserverListThreadSafeBase
}
};

struct NotificationDataBase {
NotificationDataBase(void* observer_list_in, const Location& from_here_in)
: observer_list(observer_list_in), from_here(from_here_in) {}

raw_ptr<void> observer_list;
Location from_here;
};
static const NotificationDataBase*& GetCurrentNotification();

virtual ~ObserverListThreadSafeBase() = default;

static LazyInstance<ThreadLocalPointer<const NotificationDataBase>>::Leaky
tls_current_notification_;

private:
friend class RefCountedThreadSafe<ObserverListThreadSafeBase>;
};
Expand Down Expand Up @@ -147,9 +146,9 @@ class ObserverListThreadSafe : public internal::ObserverListThreadSafeBase {
// may not make it to |observer| depending on the outcome of the race to
// |lock_|).
if (policy_ == ObserverListPolicy::ALL) {
const NotificationDataBase* current_notification =
tls_current_notification_.Get().Get();
if (current_notification && current_notification->observer_list == this) {
if (const NotificationDataBase* const current_notification =
GetCurrentNotification();
current_notification && current_notification->observer_list == this) {
const NotificationData* notification_data =
static_cast<const NotificationData*>(current_notification);
task_runner->PostTask(
Expand Down Expand Up @@ -253,20 +252,14 @@ class ObserverListThreadSafe : public internal::ObserverListThreadSafeBase {
// Keep track of the notification being dispatched on the current thread.
// This will be used if the callback below calls AddObserver().
//
// Note: |tls_current_notification_| may not be nullptr if this runs in a
// Note: GetCurrentNotification() may not return null if this runs in a
// nested loop started by a notification callback. In that case, it is
// important to save the previous value to restore it later.
auto& tls_current_notification = tls_current_notification_.Get();
const NotificationDataBase* const previous_notification =
tls_current_notification.Get();
tls_current_notification.Set(&notification);
const AutoReset<const NotificationDataBase*> resetter_(
&GetCurrentNotification(), &notification);

// Invoke the callback.
notification.method.Run(observer);

// Reset the notification being dispatched on the current thread to its
// previous value.
tls_current_notification.Set(previous_notification);
}

const ObserverListPolicy policy_ = ObserverListPolicy::ALL;
Expand Down
51 changes: 18 additions & 33 deletions base/run_loop.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,19 @@
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/no_destructor.h"
#include "base/observer_list.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread_local.h"
#include "base/trace_event/base_tracing.h"
#include "build/build_config.h"
#include "third_party/abseil-cpp/absl/base/attributes.h"

namespace base {

namespace {

ThreadLocalPointer<RunLoop::Delegate>& GetTlsDelegate() {
static NoDestructor<ThreadLocalPointer<RunLoop::Delegate>> instance;
return *instance;
}
ABSL_CONST_INIT thread_local RunLoop::Delegate* delegate = nullptr;
ABSL_CONST_INIT thread_local const RunLoop::RunLoopTimeout* run_loop_timeout =
nullptr;

// Runs |closure| immediately if this is called on |task_runner|, otherwise
// forwards |closure| to it.
Expand All @@ -35,11 +33,6 @@ void ProxyToTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner,
task_runner->PostTask(FROM_HERE, std::move(closure));
}

ThreadLocalPointer<const RunLoop::RunLoopTimeout>& RunLoopTimeoutTLS() {
static NoDestructor<ThreadLocalPointer<const RunLoop::RunLoopTimeout>> tls;
return *tls;
}

void OnRunLoopTimeout(RunLoop* run_loop,
const Location& location,
OnceCallback<void(const Location&)> on_timeout) {
Expand All @@ -62,8 +55,8 @@ RunLoop::Delegate::~Delegate() {
// be on its creation thread (e.g. a Thread that fails to start) and
// shouldn't disrupt that thread's state.
if (bound_) {
DCHECK_EQ(this, GetTlsDelegate().Get());
GetTlsDelegate().Set(nullptr);
DCHECK_EQ(this, delegate);
delegate = nullptr;
}
}

Expand All @@ -78,22 +71,22 @@ bool RunLoop::Delegate::ShouldQuitWhenIdle() {
}

// static
void RunLoop::RegisterDelegateForCurrentThread(Delegate* delegate) {
void RunLoop::RegisterDelegateForCurrentThread(Delegate* new_delegate) {
// Bind |delegate| to this thread.
DCHECK(!delegate->bound_);
DCHECK_CALLED_ON_VALID_THREAD(delegate->bound_thread_checker_);
DCHECK(!new_delegate->bound_);
DCHECK_CALLED_ON_VALID_THREAD(new_delegate->bound_thread_checker_);

// There can only be one RunLoop::Delegate per thread.
DCHECK(!GetTlsDelegate().Get())
DCHECK(!delegate)
<< "Error: Multiple RunLoop::Delegates registered on the same thread.\n\n"
"Hint: You perhaps instantiated a second "
"MessageLoop/TaskEnvironment on a thread that already had one?";
GetTlsDelegate().Set(delegate);
delegate = new_delegate;
delegate->bound_ = true;
}

RunLoop::RunLoop(Type type)
: delegate_(GetTlsDelegate().Get()),
: delegate_(delegate),
type_(type),
origin_task_runner_(SingleThreadTaskRunner::GetCurrentDefault()) {
DCHECK(delegate_) << "A RunLoop::Delegate must be bound to this thread prior "
Expand Down Expand Up @@ -232,34 +225,29 @@ bool RunLoop::AnyQuitCalled() {

// static
bool RunLoop::IsRunningOnCurrentThread() {
Delegate* delegate = GetTlsDelegate().Get();
return delegate && !delegate->active_run_loops_.empty();
}

// static
bool RunLoop::IsNestedOnCurrentThread() {
Delegate* delegate = GetTlsDelegate().Get();
return delegate && delegate->active_run_loops_.size() > 1;
}

// static
void RunLoop::AddNestingObserverOnCurrentThread(NestingObserver* observer) {
Delegate* delegate = GetTlsDelegate().Get();
DCHECK(delegate);
delegate->nesting_observers_.AddObserver(observer);
}

// static
void RunLoop::RemoveNestingObserverOnCurrentThread(NestingObserver* observer) {
Delegate* delegate = GetTlsDelegate().Get();
DCHECK(delegate);
delegate->nesting_observers_.RemoveObserver(observer);
}

// static
void RunLoop::QuitCurrentDeprecated() {
DCHECK(IsRunningOnCurrentThread());
Delegate* delegate = GetTlsDelegate().Get();
DCHECK(delegate->active_run_loops_.top()->allow_quit_current_deprecated_)
<< "Please migrate off QuitCurrentDeprecated(), e.g. to QuitClosure().";
delegate->active_run_loops_.top()->Quit();
Expand All @@ -268,7 +256,6 @@ void RunLoop::QuitCurrentDeprecated() {
// static
void RunLoop::QuitCurrentWhenIdleDeprecated() {
DCHECK(IsRunningOnCurrentThread());
Delegate* delegate = GetTlsDelegate().Get();
DCHECK(delegate->active_run_loops_.top()->allow_quit_current_deprecated_)
<< "Please migrate off QuitCurrentWhenIdleDeprecated(), e.g. to "
"QuitWhenIdleClosure().";
Expand All @@ -278,7 +265,6 @@ void RunLoop::QuitCurrentWhenIdleDeprecated() {
// static
RepeatingClosure RunLoop::QuitCurrentWhenIdleClosureDeprecated() {
// TODO(844016): Fix callsites and enable this check, or remove the API.
// Delegate* delegate = GetTlsDelegate().Get();
// DCHECK(delegate->active_run_loops_.top()->allow_quit_current_deprecated_)
// << "Please migrate off QuitCurrentWhenIdleClosureDeprecated(), e.g to "
// "QuitWhenIdleClosure().";
Expand All @@ -287,16 +273,15 @@ RepeatingClosure RunLoop::QuitCurrentWhenIdleClosureDeprecated() {

#if DCHECK_IS_ON()
ScopedDisallowRunningRunLoop::ScopedDisallowRunningRunLoop()
: current_delegate_(GetTlsDelegate().Get()),
previous_run_allowance_(
current_delegate_ ? current_delegate_->allow_running_for_testing_
: false) {
: current_delegate_(delegate),
previous_run_allowance_(current_delegate_ &&
current_delegate_->allow_running_for_testing_) {
if (current_delegate_)
current_delegate_->allow_running_for_testing_ = false;
}

ScopedDisallowRunningRunLoop::~ScopedDisallowRunningRunLoop() {
DCHECK_EQ(current_delegate_, GetTlsDelegate().Get());
DCHECK_EQ(current_delegate_, delegate);
if (current_delegate_)
current_delegate_->allow_running_for_testing_ = previous_run_allowance_;
}
Expand All @@ -314,12 +299,12 @@ RunLoop::RunLoopTimeout::~RunLoopTimeout() = default;

// static
void RunLoop::SetTimeoutForCurrentThread(const RunLoopTimeout* timeout) {
RunLoopTimeoutTLS().Set(timeout);
run_loop_timeout = timeout;
}

// static
const RunLoop::RunLoopTimeout* RunLoop::GetTimeoutForCurrentThread() {
return RunLoopTimeoutTLS().Get();
return run_loop_timeout;
}

bool RunLoop::BeforeRun() {
Expand Down
4 changes: 2 additions & 2 deletions base/run_loop.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ class BASE_EXPORT RunLoop {
// Registers |delegate| on the current thread. Must be called once and only
// once per thread before using RunLoop methods on it. |delegate| is from then
// on forever bound to that thread (including its destruction).
static void RegisterDelegateForCurrentThread(Delegate* delegate);
static void RegisterDelegateForCurrentThread(Delegate* new_delegate);

// Quits the active RunLoop (when idle) -- there must be one. These were
// introduced as prefered temporary replacements to the long deprecated
Expand Down Expand Up @@ -347,7 +347,7 @@ class BASE_EXPORT RunLoop {
// single RunLoop::Delegate per thread and RunLoop::Run() should only be invoked
// from it (or it would result in incorrectly driving TaskRunner A while in
// TaskRunner B's context).
class BASE_EXPORT ScopedDisallowRunningRunLoop {
class BASE_EXPORT [[maybe_unused, nodiscard]] ScopedDisallowRunningRunLoop {
public:
ScopedDisallowRunningRunLoop();
ScopedDisallowRunningRunLoop(const ScopedDisallowRunningRunLoop&) = delete;
Expand Down
47 changes: 18 additions & 29 deletions base/sequence_token.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
#include "base/sequence_token.h"

#include "base/atomic_sequence_num.h"
#include "base/check_op.h"
#include "base/no_destructor.h"
#include "base/threading/thread_local.h"
#include "third_party/abseil-cpp/absl/base/attributes.h"

namespace base {

Expand All @@ -17,15 +15,8 @@ base::AtomicSequenceNumber g_sequence_token_generator;

base::AtomicSequenceNumber g_task_token_generator;

ThreadLocalPointer<const SequenceToken>& GetTlsCurrentSequenceToken() {
static base::NoDestructor<ThreadLocalPointer<const SequenceToken>> instance;
return *instance;
}

ThreadLocalPointer<const TaskToken>& GetTlsCurrentTaskToken() {
static base::NoDestructor<ThreadLocalPointer<const TaskToken>> instance;
return *instance;
}
ABSL_CONST_INIT thread_local SequenceToken current_sequence_token;
ABSL_CONST_INIT thread_local TaskToken current_task_token;

} // namespace

Expand All @@ -50,9 +41,7 @@ SequenceToken SequenceToken::Create() {
}

SequenceToken SequenceToken::GetForCurrentThread() {
const SequenceToken* current_sequence_token =
GetTlsCurrentSequenceToken().Get();
return current_sequence_token ? *current_sequence_token : SequenceToken();
return current_sequence_token;
}

bool TaskToken::operator==(const TaskToken& other) const {
Expand All @@ -72,25 +61,25 @@ TaskToken TaskToken::Create() {
}

TaskToken TaskToken::GetForCurrentThread() {
const TaskToken* current_task_token = GetTlsCurrentTaskToken().Get();
return current_task_token ? *current_task_token : TaskToken();
return current_task_token;
}

ScopedSetSequenceTokenForCurrentThread::ScopedSetSequenceTokenForCurrentThread(
const SequenceToken& sequence_token)
: sequence_token_(sequence_token), task_token_(TaskToken::Create()) {
DCHECK(!GetTlsCurrentSequenceToken().Get());
DCHECK(!GetTlsCurrentTaskToken().Get());
GetTlsCurrentSequenceToken().Set(&sequence_token_);
GetTlsCurrentTaskToken().Set(&task_token_);
}
// The lambdas here exist because invalid tokens don't compare equal, so
// passing invalid sequence/task tokens as the third args to AutoReset
// constructors doesn't work.
: sequence_token_resetter_(&current_sequence_token,
[&sequence_token]() {
DCHECK(!current_sequence_token.IsValid());
return sequence_token;
}()),
task_token_resetter_(&current_task_token, [] {
DCHECK(!current_task_token.IsValid());
return TaskToken::Create();
}()) {}

ScopedSetSequenceTokenForCurrentThread::
~ScopedSetSequenceTokenForCurrentThread() {
DCHECK_EQ(GetTlsCurrentSequenceToken().Get(), &sequence_token_);
DCHECK_EQ(GetTlsCurrentTaskToken().Get(), &task_token_);
GetTlsCurrentSequenceToken().Set(nullptr);
GetTlsCurrentTaskToken().Set(nullptr);
}
~ScopedSetSequenceTokenForCurrentThread() = default;

} // namespace base

0 comments on commit 6e7b5ca

Please sign in to comment.