diff --git a/base/task/common/scoped_defer_task_posting.cc b/base/task/common/scoped_defer_task_posting.cc index 5e3c8dc9cdc78..caddbb5d9f6cd 100644 --- a/base/task/common/scoped_defer_task_posting.cc +++ b/base/task/common/scoped_defer_task_posting.cc @@ -4,8 +4,8 @@ #include "base/task/common/scoped_defer_task_posting.h" -#include "base/no_destructor.h" -#include "base/threading/thread_local.h" +#include "base/compiler_specific.h" +#include "third_party/abseil-cpp/absl/base/attributes.h" namespace base { @@ -13,10 +13,8 @@ namespace { // Holds a thread-local pointer to the current scope or null when no // scope is active. -ThreadLocalPointer& GetScopedDeferTaskPostingTLS() { - static NoDestructor> tls; - return *tls; -} +ABSL_CONST_INIT thread_local ScopedDeferTaskPosting* scoped_defer_task_posting = + nullptr; } // namespace @@ -38,7 +36,12 @@ void ScopedDeferTaskPosting::PostOrDefer( // static ScopedDeferTaskPosting* ScopedDeferTaskPosting::Get() { - return GetScopedDeferTaskPostingTLS().Get(); + // Workaround false-positive MSAN use-of-uninitialized-value on + // thread_local storage for loaded libraries: + // https://github.com/google/sanitizers/issues/1265 + MSAN_UNPOISON(&scoped_defer_task_posting, sizeof(ScopedDeferTaskPosting*)); + + return scoped_defer_task_posting; } // static @@ -47,7 +50,7 @@ bool ScopedDeferTaskPosting::Set(ScopedDeferTaskPosting* scope) { // get nested scopes. In this case ignore all except the top one. if (Get() && scope) return false; - GetScopedDeferTaskPostingTLS().Set(scope); + scoped_defer_task_posting = scope; return true; } diff --git a/base/task/common/scoped_defer_task_posting.h b/base/task/common/scoped_defer_task_posting.h index 70bb179c8b9d3..5f0aadadb7264 100644 --- a/base/task/common/scoped_defer_task_posting.h +++ b/base/task/common/scoped_defer_task_posting.h @@ -23,7 +23,7 @@ namespace base { // TODO(altimin): It should be possible to get rid of this scope, but this // requires refactoring TimeDomain to ensure that TimeDomain never changes and // we can read current time without grabbing a lock. -class BASE_EXPORT ScopedDeferTaskPosting { +class BASE_EXPORT [[maybe_unused, nodiscard]] ScopedDeferTaskPosting { public: static void PostOrDefer(scoped_refptr task_runner, const Location& from_here, diff --git a/base/task/common/task_annotator.cc b/base/task/common/task_annotator.cc index 67d27741610fd..96416c886e176 100644 --- a/base/task/common/task_annotator.cc +++ b/base/task/common/task_annotator.cc @@ -8,17 +8,18 @@ #include #include +#include "base/auto_reset.h" #include "base/check_op.h" +#include "base/compiler_specific.h" #include "base/debug/alias.h" #include "base/hash/md5.h" #include "base/logging.h" -#include "base/no_destructor.h" #include "base/ranges/algorithm.h" #include "base/sys_byteorder.h" -#include "base/threading/thread_local.h" #include "base/trace_event/base_tracing.h" #include "base/tracing_buildflags.h" #include "build/build_config.h" +#include "third_party/abseil-cpp/absl/base/attributes.h" #if BUILDFLAG(ENABLE_BASE_TRACING) #include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_mojo_event_info.pbzero.h" // nogncheck @@ -30,57 +31,71 @@ namespace { TaskAnnotator::ObserverForTesting* g_task_annotator_observer = nullptr; -// Returns the TLS slot that stores the PendingTask currently in progress on -// each thread. Used to allow creating a breadcrumb of program counters on the -// stack to help identify a task's origin in crashes. -ThreadLocalPointer* GetTLSForCurrentPendingTask() { - static NoDestructor> instance; - return instance.get(); +// The PendingTask currently in progress on each thread. Used to allow creating +// a breadcrumb of program counters on the stack to help identify a task's +// origin in crashes. +ABSL_CONST_INIT thread_local PendingTask* current_pending_task = nullptr; + +// Scoped IPC-related data (IPC hash and/or IPC interface name). IPC hash or +// interface name can be known before the associated task object is created; +// thread-local so that this data can be affixed to the associated task. +ABSL_CONST_INIT thread_local TaskAnnotator::ScopedSetIpcHash* + current_scoped_ipc_hash = nullptr; + +ABSL_CONST_INIT thread_local TaskAnnotator::LongTaskTracker* + current_long_task_tracker = nullptr; + +// These functions can be removed, and the calls below replaced with direct +// variable accesses, once the MSAN workaround is not necessary. +TaskAnnotator::ScopedSetIpcHash* GetCurrentScopedIpcHash() { + // Workaround false-positive MSAN use-of-uninitialized-value on + // thread_local storage for loaded libraries: + // https://github.com/google/sanitizers/issues/1265 + MSAN_UNPOISON(¤t_scoped_ipc_hash, + sizeof(TaskAnnotator::ScopedSetIpcHash*)); + + return current_scoped_ipc_hash; } -// Returns the TLS slot that stores scoped IPC-related data (IPC hash and/or -// IPC interface name). IPC hash or interface name can be known before the -// associated task object is created; store in the TLS so that this data can be -// affixed to the associated task. -ThreadLocalPointer* -GetTLSForCurrentScopedIpcHash() { - static NoDestructor> - instance; - return instance.get(); -} +TaskAnnotator::LongTaskTracker* GetCurrentLongTaskTracker() { + // Workaround false-positive MSAN use-of-uninitialized-value on + // thread_local storage for loaded libraries: + // https://github.com/google/sanitizers/issues/1265 + MSAN_UNPOISON(¤t_long_task_tracker, + sizeof(TaskAnnotator::LongTaskTracker*)); -ThreadLocalPointer* -GetTLSForCurrentLongTaskTracker() { - static NoDestructor> - instance; - return instance.get(); + return current_long_task_tracker; } } // namespace const PendingTask* TaskAnnotator::CurrentTaskForThread() { - return GetTLSForCurrentPendingTask()->Get(); + // Workaround false-positive MSAN use-of-uninitialized-value on + // thread_local storage for loaded libraries: + // https://github.com/google/sanitizers/issues/1265 + MSAN_UNPOISON(¤t_pending_task, sizeof(PendingTask*)); + + return current_pending_task; } void TaskAnnotator::OnIPCReceived(const char* interface_name, uint32_t (*method_info)(), bool is_response) { - base::TaskAnnotator::LongTaskTracker* current_long_task_tracker = - GetTLSForCurrentLongTaskTracker()->Get(); - - if (!current_long_task_tracker) + auto* const tracker = GetCurrentLongTaskTracker(); + if (!tracker) { return; + } - current_long_task_tracker->SetIpcDetails(interface_name, method_info, - is_response); + tracker->SetIpcDetails(interface_name, method_info, is_response); } void TaskAnnotator::MarkCurrentTaskAsInterestingForTracing() { - auto* current_task = GetTLSForCurrentLongTaskTracker()->Get(); - if (!current_task) + auto* const tracker = GetCurrentLongTaskTracker(); + if (!tracker) { return; + } - current_task->is_interesting_task = true; + tracker->is_interesting_task = true; } TaskAnnotator::TaskAnnotator() = default; @@ -100,10 +115,10 @@ void TaskAnnotator::WillQueueTask(perfetto::StaticString trace_event_name, DCHECK(!pending_task->ipc_interface_name); DCHECK(!pending_task->ipc_hash); - auto* current_ipc_hash = GetTLSForCurrentScopedIpcHash()->Get(); - if (current_ipc_hash) { - pending_task->ipc_interface_name = current_ipc_hash->GetIpcInterfaceName(); - pending_task->ipc_hash = current_ipc_hash->GetIpcHash(); + const auto* const hash = GetCurrentScopedIpcHash(); + if (hash) { + pending_task->ipc_interface_name = hash->GetIpcInterfaceName(); + pending_task->ipc_hash = hash->GetIpcHash(); } const auto* parent_task = CurrentTaskForThread(); @@ -153,35 +168,35 @@ void TaskAnnotator::RunTaskImpl(PendingTask& pending_task) { reinterpret_cast(pending_task.ipc_hash); debug::Alias(&task_backtrace); - auto* tls = GetTLSForCurrentPendingTask(); - auto* previous_pending_task = tls->Get(); - tls->Set(&pending_task); + { + const AutoReset resetter(¤t_pending_task, + &pending_task); - if (g_task_annotator_observer) - g_task_annotator_observer->BeforeRunTask(&pending_task); - std::move(pending_task.task).Run(); + if (g_task_annotator_observer) { + g_task_annotator_observer->BeforeRunTask(&pending_task); + } + std::move(pending_task.task).Run(); #if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_X86_FAMILY) - // Some tasks on some machines clobber the non-volatile XMM registers in - // violation of the Windows ABI. This empty assembly language block with - // clobber directives tells the compiler to assume that these registers - // may have lost their values. This ensures that this function will not rely - // on the registers retaining their values, and it ensures that it will - // restore the values when this function ends. This is needed because the - // code-gen for at least one caller of this function in official builds relies - // on an XMM register (usually XMM7, cleared to zero) maintaining its value as - // multiple tasks are run, which causes crashes if it is corrupted, since - // "zeroed" variables end up not being zeroed. - // The third-party issue is believed to be fixed but will take a while to - // propagate to users which is why this mitigation is needed. - // For details see https://crbug.com/1218384 - asm("" - : - : - : "%xmm6", "%xmm7", "%xmm8", "%xmm9", "%xmm10", "%xmm11", "%xmm12", - "%xmm13", "%xmm14", "%xmm15"); + // Some tasks on some machines clobber the non-volatile XMM registers in + // violation of the Windows ABI. This empty assembly language block with + // clobber directives tells the compiler to assume that these registers + // may have lost their values. This ensures that this function will not rely + // on the registers retaining their values, and it ensures that it will + // restore the values when this function ends. This is needed because the + // code-gen for at least one caller of this function in official builds + // relies on an XMM register (usually XMM7, cleared to zero) maintaining its + // value as multiple tasks are run, which causes crashes if it is corrupted, + // since "zeroed" variables end up not being zeroed. The third-party issue + // is believed to be fixed but will take a while to propagate to users which + // is why this mitigation is needed. For details see + // https://crbug.com/1218384. + asm("" + : + : + : "%xmm6", "%xmm7", "%xmm8", "%xmm9", "%xmm10", "%xmm11", "%xmm12", + "%xmm13", "%xmm14", "%xmm15"); #endif - - tls->Set(previous_pending_task); + } // Stomp the markers. Otherwise they can stick around on the unused parts of // stack and cause |task_backtrace| to be associated with an unrelated stack @@ -258,14 +273,10 @@ TaskAnnotator::ScopedSetIpcHash::ScopedSetIpcHash( TaskAnnotator::ScopedSetIpcHash::ScopedSetIpcHash( uint32_t ipc_hash, - const char* ipc_interface_name) { - auto* tls_ipc_hash = GetTLSForCurrentScopedIpcHash(); - auto* current_ipc_hash = tls_ipc_hash->Get(); - old_scoped_ipc_hash_ = current_ipc_hash; - ipc_hash_ = ipc_hash; - ipc_interface_name_ = ipc_interface_name; - tls_ipc_hash->Set(this); -} + const char* ipc_interface_name) + : resetter_(¤t_scoped_ipc_hash, this), + ipc_hash_(ipc_hash), + ipc_interface_name_(ipc_interface_name) {} // Static uint32_t TaskAnnotator::ScopedSetIpcHash::MD5HashMetricName( @@ -279,32 +290,24 @@ uint32_t TaskAnnotator::ScopedSetIpcHash::MD5HashMetricName( } TaskAnnotator::ScopedSetIpcHash::~ScopedSetIpcHash() { - auto* tls_ipc_hash = GetTLSForCurrentScopedIpcHash(); - DCHECK_EQ(this, tls_ipc_hash->Get()); - tls_ipc_hash->Set(old_scoped_ipc_hash_.get()); + DCHECK_EQ(this, GetCurrentScopedIpcHash()); } TaskAnnotator::LongTaskTracker::LongTaskTracker(const TickClock* tick_clock, PendingTask& pending_task, TaskAnnotator* task_annotator) - : tick_clock_(tick_clock), + : resetter_(¤t_long_task_tracker, this), + tick_clock_(tick_clock), pending_task_(pending_task), task_annotator_(task_annotator) { - auto* tls_long_task_tracker = GetTLSForCurrentLongTaskTracker(); - old_long_task_tracker_ = tls_long_task_tracker->Get(); - TRACE_EVENT_CATEGORY_GROUP_ENABLED("scheduler.long_tasks", &is_tracing_); if (is_tracing_) { task_start_time_ = tick_clock_->NowTicks(); } - - tls_long_task_tracker->Set(this); } TaskAnnotator::LongTaskTracker::~LongTaskTracker() { - auto* tls_long_task_tracker = GetTLSForCurrentLongTaskTracker(); - DCHECK_EQ(this, tls_long_task_tracker->Get()); - tls_long_task_tracker->Set(old_long_task_tracker_.get()); + DCHECK_EQ(this, GetCurrentLongTaskTracker()); if (!is_tracing_) return; @@ -323,12 +326,6 @@ TaskAnnotator::LongTaskTracker::~LongTaskTracker() { perfetto::Track::ThreadScoped(task_annotator_), task_end_time_); } -#if !BUILDFLAG(ENABLE_BASE_TRACING) - // Suppress the unused variable warning when TRACE_EVENT macros are turned - // into no-op. - (void)pending_task_; - (void)task_annotator_; -#endif // !BUILDFLAG(ENABLE_BASE_TRACING) } void TaskAnnotator::LongTaskTracker::SetIpcDetails(const char* interface_name, diff --git a/base/task/common/task_annotator.h b/base/task/common/task_annotator.h index ec4a390ddc822..ffc9fe344d292 100644 --- a/base/task/common/task_annotator.h +++ b/base/task/common/task_annotator.h @@ -7,6 +7,7 @@ #include +#include "base/auto_reset.h" #include "base/base_export.h" #include "base/memory/raw_ptr.h" #include "base/memory/raw_ref.h" @@ -116,7 +117,7 @@ class BASE_EXPORT TaskAnnotator { #endif // BUILDFLAG(ENABLE_BASE_TRACING) }; -class BASE_EXPORT TaskAnnotator::ScopedSetIpcHash { +class BASE_EXPORT [[maybe_unused, nodiscard]] TaskAnnotator::ScopedSetIpcHash { public: explicit ScopedSetIpcHash(uint32_t ipc_hash); @@ -136,12 +137,13 @@ class BASE_EXPORT TaskAnnotator::ScopedSetIpcHash { private: ScopedSetIpcHash(uint32_t ipc_hash, const char* ipc_interface_name); - raw_ptr old_scoped_ipc_hash_ = nullptr; - uint32_t ipc_hash_ = 0; - const char* ipc_interface_name_ = nullptr; + + const AutoReset resetter_; + uint32_t ipc_hash_; + const char* ipc_interface_name_; }; -class BASE_EXPORT TaskAnnotator::LongTaskTracker { +class BASE_EXPORT [[maybe_unused, nodiscard]] TaskAnnotator::LongTaskTracker { public: explicit LongTaskTracker(const TickClock* tick_clock, PendingTask& pending_task, @@ -166,6 +168,8 @@ class BASE_EXPORT TaskAnnotator::LongTaskTracker { private: void EmitReceivedIPCDetails(perfetto::EventContext& ctx); + const AutoReset resetter_; + // For tracking task duration raw_ptr tick_clock_; // Not owned. TimeTicks task_start_time_; @@ -176,7 +180,6 @@ class BASE_EXPORT TaskAnnotator::LongTaskTracker { // Use this to ensure that tracing and NowTicks() are not called // unnecessarily. bool is_tracing_; - raw_ptr old_long_task_tracker_ = nullptr; const char* ipc_interface_name_ = nullptr; uint32_t ipc_hash_ = 0; @@ -184,8 +187,8 @@ class BASE_EXPORT TaskAnnotator::LongTaskTracker { // known. Note that this will not compile in the Native client. uint32_t (*ipc_method_info_)(); bool is_response_ = false; - const raw_ref pending_task_; - raw_ptr task_annotator_; + [[maybe_unused]] const raw_ref pending_task_; + [[maybe_unused]] raw_ptr task_annotator_; }; } // namespace base diff --git a/base/task/scoped_set_task_priority_for_current_thread.cc b/base/task/scoped_set_task_priority_for_current_thread.cc index adc77ef70ee7d..afc3f0c1fed32 100644 --- a/base/task/scoped_set_task_priority_for_current_thread.cc +++ b/base/task/scoped_set_task_priority_for_current_thread.cc @@ -4,37 +4,35 @@ #include "base/task/scoped_set_task_priority_for_current_thread.h" -#include "base/check_op.h" -#include "base/lazy_instance.h" -#include "base/threading/thread_local.h" +#include "base/compiler_specific.h" +#include "third_party/abseil-cpp/absl/base/attributes.h" namespace base { namespace internal { namespace { -LazyInstance>::Leaky - tls_task_priority_for_current_thread = LAZY_INSTANCE_INITIALIZER; +ABSL_CONST_INIT thread_local TaskPriority task_priority_for_current_thread = + TaskPriority::USER_BLOCKING; } // namespace ScopedSetTaskPriorityForCurrentThread::ScopedSetTaskPriorityForCurrentThread( TaskPriority priority) - : priority_(priority) { - DCHECK(!tls_task_priority_for_current_thread.Get().Get()); - tls_task_priority_for_current_thread.Get().Set(&priority_); -} + : resetter_(&task_priority_for_current_thread, + priority, + TaskPriority::USER_BLOCKING) {} ScopedSetTaskPriorityForCurrentThread:: - ~ScopedSetTaskPriorityForCurrentThread() { - DCHECK_EQ(&priority_, tls_task_priority_for_current_thread.Get().Get()); - tls_task_priority_for_current_thread.Get().Set(nullptr); -} + ~ScopedSetTaskPriorityForCurrentThread() = default; TaskPriority GetTaskPriorityForCurrentThread() { - const TaskPriority* priority = - tls_task_priority_for_current_thread.Get().Get(); - return priority ? *priority : TaskPriority::USER_BLOCKING; + // Workaround false-positive MSAN use-of-uninitialized-value on + // thread_local storage for loaded libraries: + // https://github.com/google/sanitizers/issues/1265 + MSAN_UNPOISON(&task_priority_for_current_thread, sizeof(TaskPriority)); + + return task_priority_for_current_thread; } } // namespace internal diff --git a/base/task/scoped_set_task_priority_for_current_thread.h b/base/task/scoped_set_task_priority_for_current_thread.h index d8fddc4756dc6..3e934936b2025 100644 --- a/base/task/scoped_set_task_priority_for_current_thread.h +++ b/base/task/scoped_set_task_priority_for_current_thread.h @@ -5,13 +5,15 @@ #ifndef BASE_TASK_SCOPED_SET_TASK_PRIORITY_FOR_CURRENT_THREAD_H_ #define BASE_TASK_SCOPED_SET_TASK_PRIORITY_FOR_CURRENT_THREAD_H_ +#include "base/auto_reset.h" #include "base/base_export.h" #include "base/task/task_traits.h" namespace base { namespace internal { -class BASE_EXPORT ScopedSetTaskPriorityForCurrentThread { +class BASE_EXPORT + [[maybe_unused, nodiscard]] ScopedSetTaskPriorityForCurrentThread { public: // Within the scope of this object, GetTaskPriorityForCurrentThread() will // return |priority|. @@ -25,7 +27,7 @@ class BASE_EXPORT ScopedSetTaskPriorityForCurrentThread { ~ScopedSetTaskPriorityForCurrentThread(); private: - const TaskPriority priority_; + const AutoReset resetter_; }; // Returns the priority of the task running on the current thread, diff --git a/base/task/sequence_manager/sequence_manager_impl.cc b/base/task/sequence_manager/sequence_manager_impl.cc index 2c8bc4dbdba16..9009b2c61922d 100644 --- a/base/task/sequence_manager/sequence_manager_impl.cc +++ b/base/task/sequence_manager/sequence_manager_impl.cc @@ -17,7 +17,6 @@ #include "base/json/json_writer.h" #include "base/logging.h" #include "base/memory/ptr_util.h" -#include "base/no_destructor.h" #include "base/notreached.h" #include "base/observer_list.h" #include "base/rand_util.h" @@ -32,23 +31,19 @@ #include "base/task/sequence_manager/work_queue_sets.h" #include "base/task/task_features.h" #include "base/threading/thread_id_name_manager.h" -#include "base/threading/thread_local.h" #include "base/time/default_tick_clock.h" #include "base/time/tick_clock.h" #include "base/trace_event/base_tracing.h" #include "build/build_config.h" +#include "third_party/abseil-cpp/absl/base/attributes.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace base { namespace sequence_manager { namespace { -base::ThreadLocalPointer* -GetTLSSequenceManagerImpl() { - static NoDestructor> - lazy_tls_ptr; - return lazy_tls_ptr.get(); -} +ABSL_CONST_INIT thread_local internal::SequenceManagerImpl* + thread_local_sequence_manager = nullptr; class TracedBaseValue : public trace_event::ConvertableToTraceFormat { public: @@ -80,10 +75,10 @@ std::unique_ptr CreateSequenceManagerOnCurrentThread( std::unique_ptr CreateSequenceManagerOnCurrentThreadWithPump( std::unique_ptr message_pump, SequenceManager::Settings settings) { - std::unique_ptr sequence_manager = + std::unique_ptr manager = internal::SequenceManagerImpl::CreateUnbound(std::move(settings)); - sequence_manager->BindToMessagePump(std::move(message_pump)); - return sequence_manager; + manager->BindToMessagePump(std::move(message_pump)); + return manager; } std::unique_ptr CreateUnboundSequenceManager( @@ -164,7 +159,12 @@ bool g_explicit_high_resolution_timer_win = false; // static SequenceManagerImpl* SequenceManagerImpl::GetCurrent() { - return GetTLSSequenceManagerImpl()->Get(); + // Workaround false-positive MSAN use-of-uninitialized-value on + // thread_local storage for loaded libraries: + // https://github.com/google/sanitizers/issues/1265 + MSAN_UNPOISON(&thread_local_sequence_manager, sizeof(SequenceManagerImpl*)); + + return thread_local_sequence_manager; } SequenceManagerImpl::SequenceManagerImpl( @@ -234,8 +234,8 @@ SequenceManagerImpl::~SequenceManagerImpl() { // OK, now make it so that no one can find us. if (GetMessagePump()) { - DCHECK_EQ(this, GetTLSSequenceManagerImpl()->Get()); - GetTLSSequenceManagerImpl()->Set(nullptr); + DCHECK_EQ(this, GetCurrent()); + thread_local_sequence_manager = nullptr; } } @@ -262,8 +262,7 @@ SequenceManagerImpl::MainThreadOnly::~MainThreadOnly() = default; std::unique_ptr SequenceManagerImpl::CreateThreadControllerImplForCurrentThread( const TickClock* clock) { - auto* sequence_manager = GetTLSSequenceManagerImpl()->Get(); - return ThreadControllerImpl::Create(sequence_manager, clock); + return ThreadControllerImpl::Create(GetCurrent(), clock); } // static @@ -359,9 +358,9 @@ void SequenceManagerImpl::CompleteInitializationOnBoundThread() { controller_->AddNestingObserver(this); main_thread_only().nesting_observer_registered_ = true; if (GetMessagePump()) { - DCHECK(!GetTLSSequenceManagerImpl()->Get()) + DCHECK(!GetCurrent()) << "Can't register a second SequenceManagerImpl on the same thread."; - GetTLSSequenceManagerImpl()->Set(this); + thread_local_sequence_manager = this; } } diff --git a/base/task/sequenced_task_runner.cc b/base/task/sequenced_task_runner.cc index 7efadd3f6e4c5..67cf5d1cfbecd 100644 --- a/base/task/sequenced_task_runner.cc +++ b/base/task/sequenced_task_runner.cc @@ -7,22 +7,16 @@ #include #include "base/functional/bind.h" -#include "base/no_destructor.h" #include "base/task/default_delayed_task_handle_delegate.h" -#include "base/threading/thread_local.h" #include "base/time/time.h" +#include "third_party/abseil-cpp/absl/base/attributes.h" namespace base { namespace { -ThreadLocalPointer& -CurrentDefaultHandleTls() { - static NoDestructor< - ThreadLocalPointer> - instance; - return *instance; -} +ABSL_CONST_INIT thread_local SequencedTaskRunner::CurrentDefaultHandle* + current_default_handle = nullptr; } // namespace @@ -86,31 +80,28 @@ bool SequencedTaskRunner::PostDelayedTaskAt( // static const scoped_refptr& SequencedTaskRunner::GetCurrentDefault() { - const CurrentDefaultHandle* current_default = CurrentDefaultHandleTls().Get(); - CHECK(current_default) + CHECK(current_default_handle) << "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_default->task_runner_; + return current_default_handle->task_runner_; } // static bool SequencedTaskRunner::HasCurrentDefault() { - return !!CurrentDefaultHandleTls().Get(); + return !!current_default_handle; } SequencedTaskRunner::CurrentDefaultHandle::CurrentDefaultHandle( scoped_refptr task_runner) - : task_runner_(std::move(task_runner)) { + : resetter_(¤t_default_handle, this, nullptr), + task_runner_(std::move(task_runner)) { DCHECK(task_runner_->RunsTasksInCurrentSequence()); - DCHECK(!SequencedTaskRunner::HasCurrentDefault()); - CurrentDefaultHandleTls().Set(this); } SequencedTaskRunner::CurrentDefaultHandle::~CurrentDefaultHandle() { DCHECK(task_runner_->RunsTasksInCurrentSequence()); - DCHECK_EQ(CurrentDefaultHandleTls().Get(), this); - CurrentDefaultHandleTls().Set(nullptr); + DCHECK_EQ(current_default_handle, this); } bool SequencedTaskRunner::DeleteOrReleaseSoonInternal( diff --git a/base/task/sequenced_task_runner.h b/base/task/sequenced_task_runner.h index 669f36a03bbac..03529c3cd3961 100644 --- a/base/task/sequenced_task_runner.h +++ b/base/task/sequenced_task_runner.h @@ -7,6 +7,7 @@ #include +#include "base/auto_reset.h" #include "base/base_export.h" #include "base/functional/callback.h" #include "base/task/delay_policy.h" @@ -314,6 +315,8 @@ class BASE_EXPORT SequencedTaskRunner : public TaskRunner { friend class SequencedTaskRunner; friend class CurrentHandleOverride; + const AutoReset resetter_; + scoped_refptr task_runner_; }; diff --git a/base/task/single_thread_task_runner.cc b/base/task/single_thread_task_runner.cc index ebf286712d46f..f11574e5161f0 100644 --- a/base/task/single_thread_task_runner.cc +++ b/base/task/single_thread_task_runner.cc @@ -9,23 +9,30 @@ #include "base/check.h" #include "base/check_op.h" +#include "base/compiler_specific.h" #include "base/dcheck_is_on.h" #include "base/functional/bind.h" #include "base/lazy_instance.h" -#include "base/no_destructor.h" #include "base/run_loop.h" -#include "base/threading/thread_local.h" +#include "third_party/abseil-cpp/absl/base/attributes.h" namespace base { namespace { -ThreadLocalPointer& -CurrentDefaultHandleTls() { - static NoDestructor< - ThreadLocalPointer> - instance; - return *instance; +ABSL_CONST_INIT thread_local SingleThreadTaskRunner::CurrentDefaultHandle* + current_default_handle = nullptr; + +// This function can be removed, and the calls below replaced with direct +// variable accesses, once the MSAN workaround is not necessary. +SingleThreadTaskRunner::CurrentDefaultHandle* GetCurrentDefaultHandle() { + // Workaround false-positive MSAN use-of-uninitialized-value on + // thread_local storage for loaded libraries: + // https://github.com/google/sanitizers/issues/1265 + MSAN_UNPOISON(¤t_default_handle, + sizeof(SingleThreadTaskRunner::CurrentDefaultHandle*)); + + return current_default_handle; } } // namespace @@ -33,9 +40,8 @@ CurrentDefaultHandleTls() { // static const scoped_refptr& SingleThreadTaskRunner::GetCurrentDefault() { - const SingleThreadTaskRunner::CurrentDefaultHandle* current_default = - CurrentDefaultHandleTls().Get(); - CHECK(current_default) + const auto* const handle = GetCurrentDefaultHandle(); + CHECK(handle) << "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." @@ -45,27 +51,25 @@ SingleThreadTaskRunner::GetCurrentDefault() { "consider using it if the current task can run from a " "SequencedTaskRunner." : ""); - return current_default->task_runner_; + return handle->task_runner_; } // static bool SingleThreadTaskRunner::HasCurrentDefault() { - return !!CurrentDefaultHandleTls().Get(); + return !!GetCurrentDefaultHandle(); } SingleThreadTaskRunner::CurrentDefaultHandle::CurrentDefaultHandle( scoped_refptr task_runner) - : task_runner_(std::move(task_runner)), + : resetter_(¤t_default_handle, this, nullptr), + task_runner_(std::move(task_runner)), sequenced_task_runner_current_default_(task_runner_) { DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK(!CurrentDefaultHandleTls().Get()); - CurrentDefaultHandleTls().Set(this); } SingleThreadTaskRunner::CurrentDefaultHandle::~CurrentDefaultHandle() { DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK_EQ(CurrentDefaultHandleTls().Get(), this); - CurrentDefaultHandleTls().Set(nullptr); + DCHECK_EQ(GetCurrentDefaultHandle(), this); } SingleThreadTaskRunner::CurrentHandleOverride::CurrentHandleOverride( @@ -88,15 +92,12 @@ SingleThreadTaskRunner::CurrentHandleOverride::CurrentHandleOverride( #if DCHECK_IS_ON() expected_task_runner_before_restore_ = overriding_task_runner.get(); #endif - SingleThreadTaskRunner::CurrentDefaultHandle* current_default = - CurrentDefaultHandleTls().Get(); + auto* const handle = GetCurrentDefaultHandle(); SequencedTaskRunner::SetCurrentDefaultHandleTaskRunner( - current_default->sequenced_task_runner_current_default_, - overriding_task_runner); - current_default->task_runner_.swap(overriding_task_runner); - // Due to the swap, now `current_default->task_runner_` points to the - // overriding task runner and `overriding_task_runner_` points to the previous - // task runner. + handle->sequenced_task_runner_current_default_, overriding_task_runner); + handle->task_runner_.swap(overriding_task_runner); + // Due to the swap, now `handle->task_runner_` points to the overriding task + // runner and `overriding_task_runner_` points to the previous task runner. task_runner_to_restore_ = std::move(overriding_task_runner); if (!allow_nested_runloop) { @@ -107,21 +108,18 @@ SingleThreadTaskRunner::CurrentHandleOverride::CurrentHandleOverride( SingleThreadTaskRunner::CurrentHandleOverride::~CurrentHandleOverride() { if (task_runner_to_restore_) { - SingleThreadTaskRunner::CurrentDefaultHandle* current_default = - CurrentDefaultHandleTls().Get(); - + auto* const handle = GetCurrentDefaultHandle(); #if DCHECK_IS_ON() - DCHECK_EQ(expected_task_runner_before_restore_, - current_default->task_runner_.get()) + DCHECK_EQ(expected_task_runner_before_restore_, handle->task_runner_.get()) << "Nested overrides must expire their " "SingleThreadTaskRunner::CurrentHandleOverride " "in LIFO order."; #endif SequencedTaskRunner::SetCurrentDefaultHandleTaskRunner( - current_default->sequenced_task_runner_current_default_, + handle->sequenced_task_runner_current_default_, task_runner_to_restore_); - current_default->task_runner_.swap(task_runner_to_restore_); + handle->task_runner_.swap(task_runner_to_restore_); } } diff --git a/base/task/single_thread_task_runner.h b/base/task/single_thread_task_runner.h index 3cbc4ee4eb61d..b4409c907b60f 100644 --- a/base/task/single_thread_task_runner.h +++ b/base/task/single_thread_task_runner.h @@ -5,6 +5,7 @@ #ifndef BASE_TASK_SINGLE_THREAD_TASK_RUNNER_H_ #define BASE_TASK_SINGLE_THREAD_TASK_RUNNER_H_ +#include "base/auto_reset.h" #include "base/base_export.h" #include "base/dcheck_is_on.h" #include "base/memory/raw_ptr_exclusion.h" @@ -77,6 +78,8 @@ class BASE_EXPORT SingleThreadTaskRunner : public SequencedTaskRunner { friend class SingleThreadTaskRunner; friend class CurrentHandleOverride; + const AutoReset resetter_; + scoped_refptr task_runner_; // Registers |task_runner_|'s SequencedTaskRunner interface as the diff --git a/base/task/task_executor.cc b/base/task/task_executor.cc index 3ea11cb4e3e74..c2253fc627741 100644 --- a/base/task/task_executor.cc +++ b/base/task/task_executor.cc @@ -7,10 +7,10 @@ #include #include "base/check.h" -#include "base/no_destructor.h" +#include "base/compiler_specific.h" #include "base/task/task_traits.h" #include "base/task/task_traits_extension.h" -#include "base/threading/thread_local.h" +#include "third_party/abseil-cpp/absl/base/attributes.h" namespace base { @@ -31,21 +31,23 @@ static_assert( TaskTraitsExtensionStorage::kInvalidExtensionId == 0, "TaskExecutorMap depends on 0 being an invalid TaskTraits extension ID"); -} // namespace +ABSL_CONST_INIT thread_local TaskExecutor* current_task_executor = nullptr; -ThreadLocalPointer* GetTLSForCurrentTaskExecutor() { - static NoDestructor> instance; - return instance.get(); -} +} // namespace void SetTaskExecutorForCurrentThread(TaskExecutor* task_executor) { - DCHECK(!task_executor || !GetTLSForCurrentTaskExecutor()->Get() || - GetTLSForCurrentTaskExecutor()->Get() == task_executor); - GetTLSForCurrentTaskExecutor()->Set(task_executor); + DCHECK(!task_executor || !GetTaskExecutorForCurrentThread() || + GetTaskExecutorForCurrentThread() == task_executor); + current_task_executor = task_executor; } TaskExecutor* GetTaskExecutorForCurrentThread() { - return GetTLSForCurrentTaskExecutor()->Get(); + // Workaround false-positive MSAN use-of-uninitialized-value on + // thread_local storage for loaded libraries: + // https://github.com/google/sanitizers/issues/1265 + MSAN_UNPOISON(¤t_task_executor, sizeof(TaskExecutor*)); + + return current_task_executor; } void RegisterTaskExecutor(uint8_t extension_id, TaskExecutor* task_executor) { diff --git a/base/task/thread_pool/task_tracker.cc b/base/task/thread_pool/task_tracker.cc index 51f2e15b1b5d4..fcc106cfa8444 100644 --- a/base/task/thread_pool/task_tracker.cc +++ b/base/task/thread_pool/task_tracker.cc @@ -17,7 +17,6 @@ #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" -#include "base/no_destructor.h" #include "base/notreached.h" #include "base/sequence_token.h" #include "base/strings/string_util.h" @@ -33,6 +32,7 @@ #include "base/trace_event/base_tracing.h" #include "base/values.h" #include "build/build_config.h" +#include "third_party/abseil-cpp/absl/base/attributes.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace base { @@ -121,11 +121,7 @@ auto EmitThreadPoolTraceEventMetadata(perfetto::EventContext& ctx, #endif // BUILDFLAG(ENABLE_BASE_TRACING) } -base::ThreadLocalBoolean& GetFizzleBlockShutdownTaskFlag() { - static base::NoDestructor - fizzle_block_shutdown_tasks; - return *fizzle_block_shutdown_tasks; -} +ABSL_CONST_INIT thread_local bool fizzle_block_shutdown_tasks = false; } // namespace @@ -318,8 +314,7 @@ bool TaskTracker::WillPostTask(Task* task, // A non BLOCK_SHUTDOWN task is allowed to be posted iff shutdown hasn't // started and the task is not delayed. if (shutdown_behavior != TaskShutdownBehavior::BLOCK_SHUTDOWN || - !task->delayed_run_time.is_null() || - GetFizzleBlockShutdownTaskFlag().Get()) { + !task->delayed_run_time.is_null() || fizzle_block_shutdown_tasks) { return false; } @@ -424,11 +419,11 @@ bool TaskTracker::IsShutdownComplete() const { } void TaskTracker::BeginFizzlingBlockShutdownTasks() { - GetFizzleBlockShutdownTaskFlag().Set(true); + fizzle_block_shutdown_tasks = true; } void TaskTracker::EndFizzlingBlockShutdownTasks() { - GetFizzleBlockShutdownTaskFlag().Set(false); + fizzle_block_shutdown_tasks = false; } void TaskTracker::RunTask(Task task, diff --git a/base/task/thread_pool/thread_group.cc b/base/task/thread_pool/thread_group.cc index 2855a21fd4229..f5aca2f952d5e 100644 --- a/base/task/thread_pool/thread_group.cc +++ b/base/task/thread_pool/thread_group.cc @@ -9,11 +9,10 @@ #include "base/feature_list.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" -#include "base/lazy_instance.h" #include "base/task/task_features.h" #include "base/task/thread_pool/task_tracker.h" -#include "base/threading/thread_local.h" #include "build/build_config.h" +#include "third_party/abseil-cpp/absl/base/attributes.h" #if BUILDFLAG(IS_WIN) #include "base/win/com_init_check_hook.h" @@ -26,12 +25,7 @@ namespace internal { namespace { // ThreadGroup that owns the current thread, if any. -LazyInstance>::Leaky - tls_current_thread_group = LAZY_INSTANCE_INITIALIZER; - -const ThreadGroup* GetCurrentThreadGroup() { - return tls_current_thread_group.Get().Get(); -} +ABSL_CONST_INIT thread_local const ThreadGroup* current_thread_group = nullptr; } // namespace @@ -82,17 +76,17 @@ ThreadGroup::ThreadGroup(TrackedRef task_tracker, ThreadGroup::~ThreadGroup() = default; void ThreadGroup::BindToCurrentThread() { - DCHECK(!GetCurrentThreadGroup()); - tls_current_thread_group.Get().Set(this); + DCHECK(!CurrentThreadHasGroup()); + current_thread_group = this; } void ThreadGroup::UnbindFromCurrentThread() { - DCHECK(GetCurrentThreadGroup()); - tls_current_thread_group.Get().Set(nullptr); + DCHECK(IsBoundToCurrentThread()); + current_thread_group = nullptr; } bool ThreadGroup::IsBoundToCurrentThread() const { - return GetCurrentThreadGroup() == this; + return current_thread_group == this; } void ThreadGroup::Start() { @@ -346,7 +340,7 @@ ThreadGroup::GetScopedWindowsThreadEnvironment(WorkerEnvironment environment) { // static bool ThreadGroup::CurrentThreadHasGroup() { - return GetCurrentThreadGroup() != nullptr; + return current_thread_group != nullptr; } } // namespace internal diff --git a/base/task/thread_pool/thread_group_impl_unittest.cc b/base/task/thread_pool/thread_group_impl_unittest.cc index c4644a832d6a2..70a3d18503381 100644 --- a/base/task/thread_pool/thread_group_impl_unittest.cc +++ b/base/task/thread_pool/thread_group_impl_unittest.cc @@ -47,7 +47,6 @@ #include "base/threading/simple_thread.h" #include "base/threading/thread.h" #include "base/threading/thread_checker_impl.h" -#include "base/threading/thread_local_storage.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "build/build_config.h" @@ -594,46 +593,6 @@ TEST_F(BackgroundThreadGroupImplTest, UpdatePriorityBlockingStarted) { namespace { -constexpr size_t kMagicTlsValue = 42; - -class ThreadGroupImplCheckTlsReuse : public ThreadGroupImplImplTest { - public: - ThreadGroupImplCheckTlsReuse(const ThreadGroupImplCheckTlsReuse&) = delete; - ThreadGroupImplCheckTlsReuse& operator=(const ThreadGroupImplCheckTlsReuse&) = - delete; - - void SetTlsValueAndWait() { - slot_.Set(reinterpret_cast(kMagicTlsValue)); - waiter_.Wait(); - } - - void CountZeroTlsValuesAndWait(TestWaitableEvent* count_waiter) { - if (!slot_.Get()) - subtle::NoBarrier_AtomicIncrement(&zero_tls_values_, 1); - - count_waiter->Signal(); - waiter_.Wait(); - } - - protected: - ThreadGroupImplCheckTlsReuse() = default; - - void SetUp() override { - CreateAndStartThreadGroup(kReclaimTimeForCleanupTests, kMaxTasks); - } - - subtle::Atomic32 zero_tls_values_ = 0; - - TestWaitableEvent waiter_; - - private: - ThreadLocalStorage::Slot slot_; -}; - -} // namespace - -namespace { - class ThreadGroupImplStandbyPolicyTest : public ThreadGroupImplImplTestBase, public testing::Test { public: diff --git a/base/threading/hang_watcher.cc b/base/threading/hang_watcher.cc index 3277d40e39f92..e50e540c3c097 100644 --- a/base/threading/hang_watcher.cc +++ b/base/threading/hang_watcher.cc @@ -222,7 +222,9 @@ constexpr auto kMonitoringPeriod = base::Seconds(10); WatchHangsInScope::WatchHangsInScope(TimeDelta timeout) { internal::HangWatchState* current_hang_watch_state = - HangWatcher::IsEnabled() ? hang_watch_state : nullptr; + HangWatcher::IsEnabled() + ? internal::HangWatchState::GetHangWatchStateForCurrentThread() + : nullptr; DCHECK(timeout >= base::TimeDelta()) << "Negative timeouts are invalid."; @@ -274,44 +276,45 @@ WatchHangsInScope::~WatchHangsInScope() { } // If the thread was unregistered since construction there is also nothing to - // do . - if (!hang_watch_state) { + // do. + auto* const state = + internal::HangWatchState::GetHangWatchStateForCurrentThread(); + if (!state) { return; } // If a hang is currently being captured we should block here so execution // stops and we avoid recording unrelated stack frames in the crash. - if (hang_watch_state->IsFlagSet( - internal::HangWatchDeadline::Flag::kShouldBlockOnHang)) { + if (state->IsFlagSet(internal::HangWatchDeadline::Flag::kShouldBlockOnHang)) { base::HangWatcher::GetInstance()->BlockIfCaptureInProgress(); } #if DCHECK_IS_ON() // Verify that no Scope was destructed out of order. - DCHECK_EQ(this, hang_watch_state->GetCurrentWatchHangsInScope()); - hang_watch_state->SetCurrentWatchHangsInScope(previous_watch_hangs_in_scope_); + DCHECK_EQ(this, state->GetCurrentWatchHangsInScope()); + state->SetCurrentWatchHangsInScope(previous_watch_hangs_in_scope_); #endif - if (hang_watch_state->nesting_level() == 1) { + if (state->nesting_level() == 1) { // If a call to InvalidateActiveExpectations() suspended hang watching // during the lifetime of this or any nested WatchHangsInScope it can now // safely be reactivated by clearing the ignore bit since this is the // outer-most scope. - hang_watch_state->UnsetIgnoreCurrentWatchHangsInScope(); + state->UnsetIgnoreCurrentWatchHangsInScope(); } else if (set_hangs_ignored_on_exit_) { // Return to ignoring hangs since this was the previous state before hang // watching was temporarily enabled for this WatchHangsInScope only in the // constructor. - hang_watch_state->SetIgnoreCurrentWatchHangsInScope(); + state->SetIgnoreCurrentWatchHangsInScope(); } // Reset the deadline to the value it had before entering this // WatchHangsInScope. - hang_watch_state->SetDeadline(previous_deadline_); + state->SetDeadline(previous_deadline_); // TODO(crbug.com/1034046): Log when a WatchHangsInScope exits after its // deadline and that went undetected by the HangWatcher. - hang_watch_state->DecrementNestingLevel(); + state->DecrementNestingLevel(); } // static @@ -437,11 +440,13 @@ bool HangWatcher::IsCrashReportingEnabled() { // static void HangWatcher::InvalidateActiveExpectations() { - if (!hang_watch_state) { + auto* const state = + internal::HangWatchState::GetHangWatchStateForCurrentThread(); + if (!state) { // If the current thread is not under watch there is nothing to invalidate. return; } - hang_watch_state->SetIgnoreCurrentWatchHangsInScope(); + state->SetIgnoreCurrentWatchHangsInScope(); } HangWatcher::HangWatcher() @@ -975,8 +980,10 @@ void HangWatcher::BlockIfCaptureInProgress() { void HangWatcher::UnregisterThread() { AutoLock auto_lock(watch_state_lock_); - auto it = ranges::find(watch_states_, hang_watch_state, - &std::unique_ptr::get); + auto it = ranges::find( + watch_states_, + internal::HangWatchState::GetHangWatchStateForCurrentThread(), + &std::unique_ptr::get); // Thread should be registered to get unregistered. DCHECK(it != watch_states_.end()); @@ -1169,7 +1176,7 @@ HangWatchState::HangWatchState(HangWatcher::ThreadType thread_type) HangWatchState::~HangWatchState() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DCHECK_EQ(hang_watch_state, this); + DCHECK_EQ(GetHangWatchStateForCurrentThread(), this); #if DCHECK_IS_ON() // Destroying the HangWatchState should not be done if there are live @@ -1187,7 +1194,7 @@ HangWatchState::CreateHangWatchStateForCurrentThread( std::make_unique(thread_type); // Setting the thread local worked. - DCHECK_EQ(hang_watch_state, hang_state.get()); + DCHECK_EQ(GetHangWatchStateForCurrentThread(), hang_state.get()); // Transfer ownership to caller. return hang_state; @@ -1254,6 +1261,11 @@ void HangWatchState::DecrementNestingLevel() { // static HangWatchState* HangWatchState::GetHangWatchStateForCurrentThread() { + // Workaround false-positive MSAN use-of-uninitialized-value on + // thread_local storage for loaded libraries: + // https://github.com/google/sanitizers/issues/1265 + MSAN_UNPOISON(&hang_watch_state, sizeof(internal::HangWatchState*)); + return hang_watch_state; } diff --git a/base/threading/scoped_blocking_call_internal.cc b/base/threading/scoped_blocking_call_internal.cc index 9145d54bbbbf7..695e74360d49a 100644 --- a/base/threading/scoped_blocking_call_internal.cc +++ b/base/threading/scoped_blocking_call_internal.cc @@ -8,6 +8,7 @@ #include #include "base/check_op.h" +#include "base/compiler_specific.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" #include "base/no_destructor.h" @@ -32,6 +33,26 @@ ABSL_CONST_INIT thread_local BlockingObserver* blocking_observer = nullptr; ABSL_CONST_INIT thread_local UncheckedScopedBlockingCall* last_scoped_blocking_call = nullptr; +// These functions can be removed, and the calls below replaced with direct +// variable accesses, once the MSAN workaround is not necessary. +BlockingObserver* GetBlockingObserver() { + // Workaround false-positive MSAN use-of-uninitialized-value on + // thread_local storage for loaded libraries: + // https://github.com/google/sanitizers/issues/1265 + MSAN_UNPOISON(&blocking_observer, sizeof(BlockingObserver*)); + + return blocking_observer; +} +UncheckedScopedBlockingCall* GetLastScopedBlockingCall() { + // Workaround false-positive MSAN use-of-uninitialized-value on + // thread_local storage for loaded libraries: + // https://github.com/google/sanitizers/issues/1265 + MSAN_UNPOISON(&last_scoped_blocking_call, + sizeof(UncheckedScopedBlockingCall*)); + + return last_scoped_blocking_call; +} + // Set to true by scoped_blocking_call_unittest to ensure unrelated threads // entering ScopedBlockingCalls don't affect test outcomes. bool g_only_monitor_observed_threads = false; @@ -45,7 +66,7 @@ bool IsBackgroundPriorityWorker() { void SetBlockingObserverForCurrentThread( BlockingObserver* new_blocking_observer) { - DCHECK(!blocking_observer); + DCHECK(!GetBlockingObserver()); blocking_observer = new_blocking_observer; } @@ -297,8 +318,8 @@ IOJankReportingCallback& IOJankMonitoringWindow::reporting_callback_storage() { UncheckedScopedBlockingCall::UncheckedScopedBlockingCall( BlockingType blocking_type, BlockingCallType blocking_call_type) - : blocking_observer_(blocking_observer), - previous_scoped_blocking_call_(last_scoped_blocking_call), + : blocking_observer_(GetBlockingObserver()), + previous_scoped_blocking_call_(GetLastScopedBlockingCall()), resetter_(&last_scoped_blocking_call, this), is_will_block_(blocking_type == BlockingType::WILL_BLOCK || (previous_scoped_blocking_call_ && @@ -333,7 +354,7 @@ UncheckedScopedBlockingCall::~UncheckedScopedBlockingCall() { // TLS affects result of GetLastError() on Windows. ScopedClearLastError // prevents side effect. ScopedClearLastError save_last_error; - DCHECK_EQ(this, last_scoped_blocking_call); + DCHECK_EQ(this, GetLastScopedBlockingCall()); if (blocking_observer_ && !previous_scoped_blocking_call_) blocking_observer_->BlockingEnded(); }