blob: e6c3dad42dd4d65555609791e6e513781b603f6d [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/task/sequence_manager/work_tracker.h"
#include "base/check.h"
#include "base/task/common/scoped_defer_task_posting.h"
#include "base/threading/thread_restrictions.h"
namespace base::sequence_manager::internal {
SyncWorkAuthorization::SyncWorkAuthorization(SyncWorkAuthorization&& other)
: tracker_(other.tracker_) {
other.tracker_ = nullptr;
}
SyncWorkAuthorization& SyncWorkAuthorization::operator=(
SyncWorkAuthorization&& other) {
tracker_ = other.tracker_;
other.tracker_ = nullptr;
return *this;
}
SyncWorkAuthorization::~SyncWorkAuthorization() {
if (!tracker_) {
return;
}
{
base::internal::CheckedAutoLock auto_lock(tracker_->active_sync_work_lock_);
uint32_t prev = tracker_->state_.fetch_and(
~WorkTracker::kActiveSyncWork, WorkTracker::kMemoryReleaseAllowWork);
DCHECK(prev & WorkTracker::kActiveSyncWork);
}
tracker_->active_sync_work_cv_.Signal();
}
SyncWorkAuthorization::SyncWorkAuthorization(WorkTracker* state)
: tracker_(state) {}
WorkTracker::WorkTracker() {
DETACH_FROM_THREAD(thread_checker_);
}
WorkTracker::~WorkTracker() = default;
void WorkTracker::SetRunTaskSynchronouslyAllowed(
bool can_run_tasks_synchronously) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (can_run_tasks_synchronously) {
state_.fetch_or(kSyncWorkSupported, kMemoryReleaseAllowWork);
} else {
// After this returns, non-sync work may run without being tracked by
// `this`. Ensures that such work is correctly sequenced with sync work by:
// - Waiting until sync work is complete.
// - Acquiring memory written by sync work (`kMemoryAcquireBeforeWork` here
// is paired with `kMemoryReleaseAllowWork` in `~SyncWorkAuthorization`).
uint32_t prev =
state_.fetch_and(~kSyncWorkSupported, kMemoryAcquireBeforeWork);
if (prev & kActiveSyncWork) {
WaitNoSyncWork();
}
}
}
void WorkTracker::WaitNoSyncWork() {
// Do not process new PostTasks, defer them. Tracing can call PostTask, but
// it will try to grab locks that are not allowed here.
ScopedDeferTaskPosting disallow_task_posting;
ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow;
// `std::memory_order_relaxed` instead of `kMemoryAcquireBeforeWork` because
// the lock implicitly acquires memory released by `~SyncWorkAuthorization`.
base::internal::CheckedAutoLock auto_lock(active_sync_work_lock_);
uint32_t prev = state_.load(std::memory_order_relaxed);
while (prev & kActiveSyncWork) {
active_sync_work_cv_.Wait();
prev = state_.load(std::memory_order_relaxed);
}
}
void WorkTracker::WillRequestReloadImmediateWorkQueue() {
// May be called from any thread.
// Sync work is disallowed until `WillReloadImmediateWorkQueues()` and
// `OnIdle()` are called.
state_.fetch_or(kImmediateWorkQueueNeedsReload,
kMemoryRelaxedNotAllowOrBeforeWork);
}
void WorkTracker::WillReloadImmediateWorkQueues() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Sync work is disallowed until `OnIdle()` is called.
state_.fetch_and(
~(kImmediateWorkQueueNeedsReload | kWorkQueuesEmptyAndNoWorkRunning),
kMemoryRelaxedNotAllowOrBeforeWork);
}
void WorkTracker::OnBeginWork() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
uint32_t prev = state_.fetch_and(~kWorkQueuesEmptyAndNoWorkRunning,
kMemoryAcquireBeforeWork);
if (prev & kActiveSyncWork) {
DCHECK(prev & kSyncWorkSupported);
WaitNoSyncWork();
}
}
void WorkTracker::OnIdle() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// This may allow sync work. "release" so that sync work that runs after this
// sees all writes issued by previous sequenced work.
state_.fetch_or(kWorkQueuesEmptyAndNoWorkRunning, std::memory_order_release);
}
SyncWorkAuthorization WorkTracker::TryAcquireSyncWorkAuthorization() {
// May be called from any thread.
uint32_t state = state_.load(std::memory_order_relaxed);
// "acquire" so that sync work sees writes issued by sequenced work that
// precedes it.
if (state == (kSyncWorkSupported | kWorkQueuesEmptyAndNoWorkRunning) &&
state_.compare_exchange_strong(state, state | kActiveSyncWork,
std::memory_order_acquire,
std::memory_order_relaxed)) {
return SyncWorkAuthorization(this);
}
return SyncWorkAuthorization(nullptr);
}
void WorkTracker::AssertHasWork() {
CHECK(!(state_.load(std::memory_order_relaxed) &
kWorkQueuesEmptyAndNoWorkRunning));
}
} // namespace base::sequence_manager::internal