// 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 #include "base/test/bind.h" #include "base/test/test_timeouts.h" #include "base/threading/platform_thread.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" namespace base::sequence_manager::internal { // Verify that no sync work authorization is granted unless allowed by // `SetRunTaskSynchronouslyAllowed()`. TEST(SequenceManagerWorkTrackerTest, SetRunTaskSynchronouslyAllowed) { WorkTracker tracker; EXPECT_FALSE(tracker.TryAcquireSyncWorkAuthorization().IsValid()); tracker.OnBeginWork(); tracker.OnIdle(); EXPECT_FALSE(tracker.TryAcquireSyncWorkAuthorization().IsValid()); tracker.WillRequestReloadImmediateWorkQueue(); tracker.WillReloadImmediateWorkQueues(); tracker.OnIdle(); EXPECT_FALSE(tracker.TryAcquireSyncWorkAuthorization().IsValid()); tracker.SetRunTaskSynchronouslyAllowed(true); EXPECT_TRUE(tracker.TryAcquireSyncWorkAuthorization().IsValid()); tracker.SetRunTaskSynchronouslyAllowed(false); EXPECT_FALSE(tracker.TryAcquireSyncWorkAuthorization().IsValid()); } // Verify that `SetRunTaskSynchronouslyAllowed(false)` blocks until there is no // valid sync work authorization. TEST(SequenceManagerWorkTrackerTest, SetRunTaskSynchronouslyAllowedBlocks) { WorkTracker tracker; tracker.SetRunTaskSynchronouslyAllowed(true); WaitableEvent did_acquire_sync_work_auth; bool will_release_sync_work_auth = false; Thread other_thread("OtherThread"); other_thread.Start(); other_thread.task_runner()->PostTask( FROM_HERE, BindLambdaForTesting([&] { std::optional auth = tracker.TryAcquireSyncWorkAuthorization(); EXPECT_TRUE(auth->IsValid()); did_acquire_sync_work_auth.Signal(); PlatformThread::Sleep(TestTimeouts::tiny_timeout()); will_release_sync_work_auth = true; auth.reset(); })); did_acquire_sync_work_auth.Wait(); tracker.SetRunTaskSynchronouslyAllowed(false); // `will_release_sync_work_auth` must be true (with no data race detected by // TSAN) when the call above returns. EXPECT_TRUE(will_release_sync_work_auth); other_thread.FlushForTesting(); } // Verify that after `WillRequestReloadImmediateWorkQueue()`, // `WillReloadImmediateWorkQueues()` and `OnIdle()` must be called in sequence // for a sync work authorization to be granted. TEST(SequenceManagerWorkTrackerTest, WillRequestReloadImmediateWorkQueue) { WorkTracker tracker; tracker.SetRunTaskSynchronouslyAllowed(true); EXPECT_TRUE(tracker.TryAcquireSyncWorkAuthorization().IsValid()); tracker.WillRequestReloadImmediateWorkQueue(); EXPECT_FALSE(tracker.TryAcquireSyncWorkAuthorization().IsValid()); tracker.WillReloadImmediateWorkQueues(); EXPECT_FALSE(tracker.TryAcquireSyncWorkAuthorization().IsValid()); tracker.OnIdle(); EXPECT_TRUE(tracker.TryAcquireSyncWorkAuthorization().IsValid()); tracker.WillRequestReloadImmediateWorkQueue(); EXPECT_FALSE(tracker.TryAcquireSyncWorkAuthorization().IsValid()); // `OnIdle()` without `WillReloadImmediateWorkQueues()` is not sufficient for // a sync work authorization to be granted. tracker.OnIdle(); EXPECT_FALSE(tracker.TryAcquireSyncWorkAuthorization().IsValid()); } // Verify that after `OnBeginWork()`, `OnIdle()` must be called for a sync // work authorization to be granted. TEST(SequenceManagerWorkTrackerTest, OnBeginWork) { WorkTracker tracker; tracker.SetRunTaskSynchronouslyAllowed(true); EXPECT_TRUE(tracker.TryAcquireSyncWorkAuthorization().IsValid()); tracker.OnBeginWork(); EXPECT_FALSE(tracker.TryAcquireSyncWorkAuthorization().IsValid()); tracker.OnIdle(); EXPECT_TRUE(tracker.TryAcquireSyncWorkAuthorization().IsValid()); } // Verify that its not possible to simultaneously acquire two sync work // authorizations. TEST(SequenceManagerWorkTrackerTest, TwoSyncWorkAuthorizations) { WorkTracker tracker; tracker.SetRunTaskSynchronouslyAllowed(true); std::optional first = tracker.TryAcquireSyncWorkAuthorization(); EXPECT_TRUE(first->IsValid()); SyncWorkAuthorization second = tracker.TryAcquireSyncWorkAuthorization(); EXPECT_FALSE(second.IsValid()); first.reset(); // `second` is invalid so doesn't prevent acquiring another sync work // authorization. EXPECT_TRUE(tracker.TryAcquireSyncWorkAuthorization().IsValid()); } // Verify that `OnBeginWork()` blocks until there is no valid sync work // authorization. TEST(SequenceManagerWorkTrackerTest, OnBeginWorkBlocks) { WorkTracker tracker; tracker.SetRunTaskSynchronouslyAllowed(true); WaitableEvent did_acquire_sync_work_auth; bool will_release_sync_work_auth = false; Thread other_thread("OtherThread"); other_thread.Start(); other_thread.task_runner()->PostTask( FROM_HERE, BindLambdaForTesting([&] { std::optional auth = tracker.TryAcquireSyncWorkAuthorization(); EXPECT_TRUE(auth->IsValid()); did_acquire_sync_work_auth.Signal(); PlatformThread::Sleep(TestTimeouts::tiny_timeout()); will_release_sync_work_auth = true; auth.reset(); })); did_acquire_sync_work_auth.Wait(); tracker.OnBeginWork(); // `will_release_sync_work_auth` must be true (with no data race detected by // TSAN) when the call above returns. EXPECT_TRUE(will_release_sync_work_auth); other_thread.FlushForTesting(); } } // namespace base::sequence_manager::internal