blob: e9d044a96bdecf82f9861539c7762d4585b4ff5a [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 <optional>
#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<SyncWorkAuthorization> 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<SyncWorkAuthorization> 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<SyncWorkAuthorization> 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