#include "base/threading/scoped_blocking_call.h"
#include <array>
#include <memory>
#include <optional>
#include <utility>
#include <vector>
#include "base/barrier_closure.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/task/thread_pool.h"
#include "base/task/thread_pool/environment_config.h"
#include "base/task/thread_pool/thread_pool_impl.h"
#include "base/test/bind.h"
#include "base/test/gtest_util.h"
#include "base/test/task_environment.h"
#include "base/test/test_waitable_event.h"
#include "base/threading/scoped_blocking_call_internal.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time_override.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::ElementsAre;
namespace base {
namespace {
class MockBlockingObserver : public internal::BlockingObserver {
public:
MockBlockingObserver() = default;
MockBlockingObserver(const MockBlockingObserver&) = delete;
MockBlockingObserver& operator=(const MockBlockingObserver&) = delete;
MOCK_METHOD1(BlockingStarted, void(BlockingType));
MOCK_METHOD0(BlockingTypeUpgraded, void());
MOCK_METHOD0(BlockingEnded, void());
};
class ScopedBlockingCallTest : public testing::Test {
public:
ScopedBlockingCallTest(const ScopedBlockingCallTest&) = delete;
ScopedBlockingCallTest& operator=(const ScopedBlockingCallTest&) = delete;
protected:
ScopedBlockingCallTest() {
internal::SetBlockingObserverForCurrentThread(&observer_);
}
~ScopedBlockingCallTest() override {
internal::ClearBlockingObserverForCurrentThread();
}
testing::StrictMock<MockBlockingObserver> observer_;
};
}
TEST_F(ScopedBlockingCallTest, MayBlock) {
EXPECT_CALL(observer_, BlockingStarted(BlockingType::MAY_BLOCK));
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
testing::Mock::VerifyAndClear(&observer_);
EXPECT_CALL(observer_, BlockingEnded());
}
TEST_F(ScopedBlockingCallTest, WillBlock) {
EXPECT_CALL(observer_, BlockingStarted(BlockingType::WILL_BLOCK));
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::WILL_BLOCK);
testing::Mock::VerifyAndClear(&observer_);
EXPECT_CALL(observer_, BlockingEnded());
}
TEST_F(ScopedBlockingCallTest, MayBlockWillBlock) {
EXPECT_CALL(observer_, BlockingStarted(BlockingType::MAY_BLOCK));
ScopedBlockingCall scoped_blocking_call_a(FROM_HERE, BlockingType::MAY_BLOCK);
testing::Mock::VerifyAndClear(&observer_);
{
EXPECT_CALL(observer_, BlockingTypeUpgraded());
ScopedBlockingCall scoped_blocking_call_b(FROM_HERE,
BlockingType::WILL_BLOCK);
testing::Mock::VerifyAndClear(&observer_);
}
EXPECT_CALL(observer_, BlockingEnded());
}
TEST_F(ScopedBlockingCallTest, WillBlockMayBlock) {
EXPECT_CALL(observer_, BlockingStarted(BlockingType::WILL_BLOCK));
ScopedBlockingCall scoped_blocking_call_a(FROM_HERE,
BlockingType::WILL_BLOCK);
testing::Mock::VerifyAndClear(&observer_);
{
ScopedBlockingCall scoped_blocking_call_b(FROM_HERE,
BlockingType::MAY_BLOCK);
}
EXPECT_CALL(observer_, BlockingEnded());
}
TEST_F(ScopedBlockingCallTest, MayBlockMayBlock) {
EXPECT_CALL(observer_, BlockingStarted(BlockingType::MAY_BLOCK));
ScopedBlockingCall scoped_blocking_call_a(FROM_HERE, BlockingType::MAY_BLOCK);
testing::Mock::VerifyAndClear(&observer_);
{
ScopedBlockingCall scoped_blocking_call_b(FROM_HERE,
BlockingType::MAY_BLOCK);
}
EXPECT_CALL(observer_, BlockingEnded());
}
TEST_F(ScopedBlockingCallTest, WillBlockWillBlock) {
EXPECT_CALL(observer_, BlockingStarted(BlockingType::WILL_BLOCK));
ScopedBlockingCall scoped_blocking_call_a(FROM_HERE,
BlockingType::WILL_BLOCK);
testing::Mock::VerifyAndClear(&observer_);
{
ScopedBlockingCall scoped_blocking_call_b(FROM_HERE,
BlockingType::WILL_BLOCK);
}
EXPECT_CALL(observer_, BlockingEnded());
}
TEST_F(ScopedBlockingCallTest, MayBlockWillBlockTwice) {
EXPECT_CALL(observer_, BlockingStarted(BlockingType::MAY_BLOCK));
ScopedBlockingCall scoped_blocking_call_a(FROM_HERE, BlockingType::MAY_BLOCK);
testing::Mock::VerifyAndClear(&observer_);
{
EXPECT_CALL(observer_, BlockingTypeUpgraded());
ScopedBlockingCall scoped_blocking_call_b(FROM_HERE,
BlockingType::WILL_BLOCK);
testing::Mock::VerifyAndClear(&observer_);
{
ScopedBlockingCall scoped_blocking_call_c(FROM_HERE,
BlockingType::MAY_BLOCK);
ScopedBlockingCall scoped_blocking_call_d(FROM_HERE,
BlockingType::WILL_BLOCK);
}
}
EXPECT_CALL(observer_, BlockingEnded());
}
TEST(ScopedBlockingCallDestructionOrderTest, InvalidDestructionOrder) {
auto scoped_blocking_call_a =
std::make_unique<ScopedBlockingCall>(FROM_HERE, BlockingType::WILL_BLOCK);
auto scoped_blocking_call_b =
std::make_unique<ScopedBlockingCall>(FROM_HERE, BlockingType::WILL_BLOCK);
EXPECT_DCHECK_DEATH({ scoped_blocking_call_a.reset(); });
}
namespace {
class ScopedBlockingCallIOJankMonitoringTest : public testing::Test {
public:
explicit ScopedBlockingCallIOJankMonitoringTest(
test::TaskEnvironment::TimeSource time_source =
test::TaskEnvironment::TimeSource::MOCK_TIME)
: task_environment_(std::in_place, time_source) {}
void SetUp() override {
EnableIOJankMonitoringForProcess(
BindLambdaForTesting(
[&](int janky_intervals_per_minute, int total_janks_per_minute) {
reports_.emplace_back(janky_intervals_per_minute,
total_janks_per_minute);
}),
OnlyObservedThreadsForTest(true));
internal::SetBlockingObserverForCurrentThread(&main_thread_observer);
}
void StopMonitoring() {
task_environment_.reset();
internal::IOJankMonitoringWindow::CancelMonitoringForTesting();
internal::ClearBlockingObserverForCurrentThread();
}
void TearDown() override {
if (task_environment_) {
StopMonitoring();
}
}
protected:
struct SetSynchronousThreadStart {
SetSynchronousThreadStart() {
internal::ThreadPoolImpl::SetSynchronousThreadStartForTesting(true);
}
~SetSynchronousThreadStart() {
internal::ThreadPoolImpl::SetSynchronousThreadStartForTesting(false);
}
} set_synchronous_thread_start_;
std::vector<std::pair<int, int>> reports_;
std::optional<test::TaskEnvironment> task_environment_;
testing::NiceMock<MockBlockingObserver> main_thread_observer;
};
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, Basic) {
constexpr auto kJankTiming =
internal::IOJankMonitoringWindow::kIOJankInterval * 7;
{
ScopedBlockingCall blocked_for_7s(FROM_HERE, BlockingType::MAY_BLOCK);
task_environment_->FastForwardBy(kJankTiming);
}
EXPECT_THAT(reports_, ElementsAre());
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow - kJankTiming);
EXPECT_THAT(reports_, ElementsAre(std::make_pair(7, 7)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, NestedDoesntMatter) {
constexpr auto kJankTiming =
internal::IOJankMonitoringWindow::kIOJankInterval * 7;
{
ScopedBlockingCall blocked_for_7s(FROM_HERE, BlockingType::MAY_BLOCK);
ScopedBlockingCall nested(FROM_HERE, BlockingType::MAY_BLOCK);
task_environment_->FastForwardBy(kJankTiming);
}
EXPECT_THAT(reports_, ElementsAre());
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
EXPECT_THAT(reports_, ElementsAre(std::make_pair(7, 7)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, ManyInAWindow) {
constexpr auto kJankTiming =
internal::IOJankMonitoringWindow::kIOJankInterval * 7;
constexpr auto kIdleTiming = Seconds(3);
for (int i = 0; i < 3; ++i) {
{
ScopedBlockingCall blocked_for_7s(FROM_HERE, BlockingType::MAY_BLOCK);
task_environment_->FastForwardBy(kJankTiming);
}
task_environment_->FastForwardBy(kIdleTiming);
}
EXPECT_THAT(reports_, ElementsAre());
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow -
(kJankTiming + kIdleTiming) * 3);
EXPECT_THAT(reports_, ElementsAre(std::make_pair(7 * 3, 7 * 3)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, OverlappingMultipleWindows) {
constexpr auto kJankTiming =
internal::IOJankMonitoringWindow::kMonitoringWindow * 3 +
internal::IOJankMonitoringWindow::kIOJankInterval * 5;
{
ScopedBlockingCall blocked_for_3windows(FROM_HERE, BlockingType::MAY_BLOCK);
task_environment_->FastForwardBy(kJankTiming);
}
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
EXPECT_THAT(reports_,
ElementsAre(std::make_pair(60, 60), std::make_pair(60, 60),
std::make_pair(60, 60), std::make_pair(5, 5)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, InstantUnblockReportsZero) {
{ ScopedBlockingCall instant_unblock(FROM_HERE, BlockingType::MAY_BLOCK); }
EXPECT_THAT(reports_, ElementsAre());
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
EXPECT_THAT(reports_, ElementsAre(std::make_pair(0, 0)));
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
EXPECT_THAT(reports_,
ElementsAre(std::make_pair(0, 0), std::make_pair(0, 0)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, Jank7sMidInterval) {
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kIOJankInterval / 3);
constexpr auto kJankTiming =
internal::IOJankMonitoringWindow::kIOJankInterval * 7;
{
ScopedBlockingCall blocked_for_7s(FROM_HERE, BlockingType::MAY_BLOCK);
task_environment_->FastForwardBy(kJankTiming);
}
EXPECT_THAT(reports_, ElementsAre());
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
EXPECT_THAT(reports_, ElementsAre(std::make_pair(7, 7)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, Jank1sMidInterval) {
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kIOJankInterval / 3);
constexpr auto kJankTiming =
internal::IOJankMonitoringWindow::kIOJankInterval;
{
ScopedBlockingCall blocked_for_1s(FROM_HERE, BlockingType::MAY_BLOCK);
task_environment_->FastForwardBy(kJankTiming);
}
EXPECT_THAT(reports_, ElementsAre());
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
EXPECT_THAT(reports_, ElementsAre(std::make_pair(1, 1)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, JankRoundDown) {
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kIOJankInterval * 0.9);
constexpr auto kJankTiming =
internal::IOJankMonitoringWindow::kIOJankInterval * 1.3;
{
ScopedBlockingCall blocked_for_1s(FROM_HERE, BlockingType::MAY_BLOCK);
task_environment_->FastForwardBy(kJankTiming);
}
EXPECT_THAT(reports_, ElementsAre());
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
EXPECT_THAT(reports_, ElementsAre(std::make_pair(1, 1)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, JankRoundUp) {
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kIOJankInterval * 0.5);
constexpr auto kJankTiming =
internal::IOJankMonitoringWindow::kIOJankInterval * 1.7;
{
ScopedBlockingCall blocked_for_1s(FROM_HERE, BlockingType::MAY_BLOCK);
task_environment_->FastForwardBy(kJankTiming);
}
EXPECT_THAT(reports_, ElementsAre());
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
EXPECT_THAT(reports_, ElementsAre(std::make_pair(2, 2)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, NoJankMidInterval) {
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kIOJankInterval / 3);
{
ScopedBlockingCall non_janky(FROM_HERE, BlockingType::MAY_BLOCK);
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kIOJankInterval - Milliseconds(1));
}
EXPECT_THAT(reports_, ElementsAre());
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
EXPECT_THAT(reports_, ElementsAre(std::make_pair(0, 0)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, MultiThreaded) {
constexpr auto kJankTiming =
internal::IOJankMonitoringWindow::kIOJankInterval * 7;
const int kNumJankyTasks =
test::TaskEnvironment::kNumForegroundThreadPoolThreads;
TestWaitableEvent all_threads_blocked;
auto on_thread_blocked = BarrierClosure(
kNumJankyTasks,
BindOnce(&TestWaitableEvent::Signal, Unretained(&all_threads_blocked)));
TestWaitableEvent resume_all_threads;
for (int i = 0; i < kNumJankyTasks; ++i) {
base::ThreadPool::PostTask(
FROM_HERE, {MayBlock()}, BindLambdaForTesting([&] {
ScopedBlockingCall blocked_until_signal(FROM_HERE,
BlockingType::MAY_BLOCK);
on_thread_blocked.Run();
ScopedAllowBaseSyncPrimitivesForTesting allow_wait;
resume_all_threads.Wait();
}));
}
all_threads_blocked.Wait();
task_environment_->AdvanceClock(kJankTiming);
resume_all_threads.Signal();
task_environment_->RunUntilIdle();
EXPECT_THAT(reports_, ElementsAre());
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
EXPECT_THAT(reports_, ElementsAre(std::make_pair(7, 7 * kNumJankyTasks)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, MultiThreadedOverlapped) {
static const int kNumJankyTasks = 3;
static_assert(
kNumJankyTasks <= test::TaskEnvironment::kNumForegroundThreadPoolThreads,
"");
TestWaitableEvent next_task_is_blocked(WaitableEvent::ResetPolicy::AUTOMATIC);
std::array<TestWaitableEvent, kNumJankyTasks> resume_thread = {};
std::array<TestWaitableEvent, kNumJankyTasks> exited_blocking_scope = {};
auto blocking_task = BindLambdaForTesting([&](int task_index) {
{
ScopedBlockingCall blocked_until_signal(FROM_HERE,
BlockingType::MAY_BLOCK);
next_task_is_blocked.Signal();
ScopedAllowBaseSyncPrimitivesForTesting allow_wait;
resume_thread[task_index].Wait();
}
exited_blocking_scope[task_index].Signal();
});
base::ThreadPool::PostTask(FROM_HERE, {MayBlock()},
BindOnce(blocking_task, 0));
next_task_is_blocked.Wait();
task_environment_->AdvanceClock(
internal::IOJankMonitoringWindow::kIOJankInterval);
base::ThreadPool::PostTask(FROM_HERE, {MayBlock()},
BindOnce(blocking_task, 1));
next_task_is_blocked.Wait();
task_environment_->AdvanceClock(
internal::IOJankMonitoringWindow::kIOJankInterval);
base::ThreadPool::PostTask(FROM_HERE, {MayBlock()},
BindOnce(blocking_task, 2));
next_task_is_blocked.Wait();
task_environment_->AdvanceClock(
internal::IOJankMonitoringWindow::kIOJankInterval);
for (int i = 0; i < kNumJankyTasks; ++i) {
resume_thread[i].Signal();
exited_blocking_scope[i].Wait();
task_environment_->AdvanceClock(
internal::IOJankMonitoringWindow::kIOJankInterval);
}
EXPECT_THAT(reports_, ElementsAre());
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
EXPECT_THAT(reports_, ElementsAre(std::make_pair(5, 9)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, MultiThreadedOverlappedWindows) {
constexpr int kNumJankyTasks = 3;
static_assert(
kNumJankyTasks <= test::TaskEnvironment::kNumForegroundThreadPoolThreads,
"");
TestWaitableEvent next_task_is_blocked(WaitableEvent::ResetPolicy::AUTOMATIC);
std::array<TestWaitableEvent, kNumJankyTasks> resume_thread = {};
std::array<TestWaitableEvent, kNumJankyTasks> exited_blocking_scope = {};
auto blocking_task = BindLambdaForTesting([&](int task_index) {
{
ScopedBlockingCall blocked_until_signal(FROM_HERE,
BlockingType::MAY_BLOCK);
next_task_is_blocked.Signal();
ScopedAllowBaseSyncPrimitivesForTesting allow_wait;
resume_thread[task_index].Wait();
}
exited_blocking_scope[task_index].Signal();
});
task_environment_->AdvanceClock(
internal::IOJankMonitoringWindow::kTimeDiscrepancyTimeout -
Milliseconds(1));
base::ThreadPool::PostTask(FROM_HERE, {MayBlock()},
BindOnce(blocking_task, 0));
next_task_is_blocked.Wait();
task_environment_->AdvanceClock(
internal::IOJankMonitoringWindow::kMonitoringWindow);
base::ThreadPool::PostTask(FROM_HERE, {MayBlock()},
BindOnce(blocking_task, 1));
next_task_is_blocked.Wait();
task_environment_->AdvanceClock(
internal::IOJankMonitoringWindow::kMonitoringWindow);
base::ThreadPool::PostTask(FROM_HERE, {MayBlock()},
BindOnce(blocking_task, 2));
next_task_is_blocked.Wait();
task_environment_->AdvanceClock(
internal::IOJankMonitoringWindow::kMonitoringWindow);
for (int i = 0; i < kNumJankyTasks; ++i) {
resume_thread[i].Signal();
exited_blocking_scope[i].Wait();
task_environment_->AdvanceClock(
internal::IOJankMonitoringWindow::kMonitoringWindow);
}
task_environment_->RunUntilIdle();
EXPECT_THAT(reports_,
ElementsAre(std::make_pair(51, 51), std::make_pair(60, 111),
std::make_pair(60, 171), std::make_pair(60, 129),
std::make_pair(60, 69), std::make_pair(9, 9)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, CancellationAcrossSleep) {
constexpr auto kJankTiming =
internal::IOJankMonitoringWindow::kIOJankInterval * 7;
{
ScopedBlockingCall blocked_for_7s(FROM_HERE, BlockingType::MAY_BLOCK);
task_environment_->FastForwardBy(kJankTiming);
}
task_environment_->AdvanceClock(
internal::IOJankMonitoringWindow::kMonitoringWindow +
internal::IOJankMonitoringWindow::kTimeDiscrepancyTimeout - kJankTiming);
task_environment_->RunUntilIdle();
EXPECT_THAT(reports_, ElementsAre());
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow - Seconds(1));
EXPECT_THAT(reports_, ElementsAre());
task_environment_->FastForwardBy(Seconds(1));
EXPECT_THAT(reports_, ElementsAre(std::make_pair(0, 0)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, SleepWithLongJank) {
{
ScopedBlockingCall blocked_through_sleep(FROM_HERE,
BlockingType::MAY_BLOCK);
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow * 3 - Seconds(1));
task_environment_->AdvanceClock(
Seconds(1) + internal::IOJankMonitoringWindow::kTimeDiscrepancyTimeout);
}
EXPECT_THAT(reports_,
ElementsAre(std::make_pair(60, 60), std::make_pair(60, 60)));
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow - Seconds(1));
EXPECT_THAT(reports_,
ElementsAre(std::make_pair(60, 60), std::make_pair(60, 60)));
task_environment_->FastForwardBy(Seconds(1));
EXPECT_THAT(reports_,
ElementsAre(std::make_pair(60, 60), std::make_pair(60, 60),
std::make_pair(0, 0)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, BackgroundBlockingCallsIgnored) {
constexpr auto kJankTiming =
internal::IOJankMonitoringWindow::kIOJankInterval * 7;
TestWaitableEvent task_running;
TestWaitableEvent resume_task;
base::ThreadPool::PostTask(
FROM_HERE, {TaskPriority::BEST_EFFORT, MayBlock()},
BindLambdaForTesting([&] {
ScopedBlockingCall blocked_for_7s(FROM_HERE, BlockingType::MAY_BLOCK);
task_running.Signal();
ScopedAllowBaseSyncPrimitivesForTesting allow_wait;
resume_task.Wait();
}));
task_running.Wait();
task_environment_->AdvanceClock(kJankTiming);
resume_task.Signal();
EXPECT_THAT(reports_, ElementsAre());
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
if (internal::CanUseBackgroundThreadTypeForWorkerThread()) {
EXPECT_THAT(reports_, ElementsAre(std::make_pair(0, 0)));
} else {
EXPECT_THAT(reports_, ElementsAre(std::make_pair(7, 7)));
}
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest,
BackgroundAndForegroundCallsMixed) {
constexpr auto kJankTiming =
internal::IOJankMonitoringWindow::kIOJankInterval * 7;
TestWaitableEvent tasks_running;
auto on_task_running = BarrierClosure(
2, BindOnce(&TestWaitableEvent::Signal, Unretained(&tasks_running)));
TestWaitableEvent resume_tasks;
base::ThreadPool::PostTask(
FROM_HERE, {TaskPriority::BEST_EFFORT, MayBlock()},
BindLambdaForTesting([&] {
ScopedBlockingCall blocked_for_7s(FROM_HERE, BlockingType::MAY_BLOCK);
on_task_running.Run();
ScopedAllowBaseSyncPrimitivesForTesting allow_wait;
resume_tasks.Wait();
}));
base::ThreadPool::PostTask(
FROM_HERE, {TaskPriority::USER_BLOCKING, MayBlock()},
BindLambdaForTesting([&] {
ScopedBlockingCall blocked_for_7s(FROM_HERE, BlockingType::MAY_BLOCK);
on_task_running.Run();
ScopedAllowBaseSyncPrimitivesForTesting allow_wait;
resume_tasks.Wait();
}));
tasks_running.Wait();
task_environment_->AdvanceClock(kJankTiming);
resume_tasks.Signal();
EXPECT_THAT(reports_, ElementsAre());
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
if (internal::CanUseBackgroundThreadTypeForWorkerThread()) {
EXPECT_THAT(reports_, ElementsAre(std::make_pair(7, 7)));
} else {
EXPECT_THAT(reports_, ElementsAre(std::make_pair(7, 14)));
}
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, WillBlockNotMonitored) {
constexpr auto kBlockedTiming =
internal::IOJankMonitoringWindow::kIOJankInterval * 7;
{
ScopedBlockingCall blocked_for_7s(FROM_HERE, BlockingType::WILL_BLOCK);
task_environment_->FastForwardBy(kBlockedTiming);
}
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
EXPECT_THAT(reports_, ElementsAre(std::make_pair(0, 0)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest,
NestedWillBlockCancelsMonitoring) {
constexpr auto kBlockedTiming =
internal::IOJankMonitoringWindow::kIOJankInterval * 7;
{
ScopedBlockingCall blocked_for_14s(FROM_HERE, BlockingType::MAY_BLOCK);
task_environment_->FastForwardBy(kBlockedTiming);
ScopedBlockingCall will_block_for_7s(FROM_HERE, BlockingType::WILL_BLOCK);
task_environment_->FastForwardBy(kBlockedTiming);
}
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
EXPECT_THAT(reports_, ElementsAre(std::make_pair(0, 0)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, NestedMayBlockIgnored) {
constexpr auto kBlockedTiming =
internal::IOJankMonitoringWindow::kIOJankInterval * 7;
{
ScopedBlockingCall blocked_for_14s(FROM_HERE, BlockingType::MAY_BLOCK);
task_environment_->FastForwardBy(kBlockedTiming);
ScopedBlockingCall may_block_for_7s(FROM_HERE, BlockingType::MAY_BLOCK);
task_environment_->FastForwardBy(kBlockedTiming);
}
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
EXPECT_THAT(reports_, ElementsAre(std::make_pair(14, 14)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest, BaseSyncPrimitivesNotMonitored) {
constexpr auto kBlockedTiming =
internal::IOJankMonitoringWindow::kIOJankInterval * 7;
{
internal::ScopedBlockingCallWithBaseSyncPrimitives
base_sync_primitives_for_7s(FROM_HERE, BlockingType::MAY_BLOCK);
task_environment_->FastForwardBy(kBlockedTiming);
}
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
EXPECT_THAT(reports_, ElementsAre(std::make_pair(0, 0)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest,
NestedBaseSyncPrimitivesCancels) {
constexpr auto kBlockedTiming =
internal::IOJankMonitoringWindow::kIOJankInterval * 7;
{
ScopedBlockingCall blocked_for_14s(FROM_HERE, BlockingType::MAY_BLOCK);
task_environment_->FastForwardBy(kBlockedTiming);
internal::ScopedBlockingCallWithBaseSyncPrimitives
base_sync_primitives_for_7s(FROM_HERE, BlockingType::MAY_BLOCK);
task_environment_->FastForwardBy(kBlockedTiming);
}
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow);
EXPECT_THAT(reports_, ElementsAre(std::make_pair(0, 0)));
}
TEST_F(ScopedBlockingCallIOJankMonitoringTest,
RacySampleNearMonitoringWindowBoundary) {
constexpr auto kDeltaFromBoundary = Milliseconds(1);
const int kNumBlockedIntervals = 7;
constexpr auto kBlockedTiming =
internal::IOJankMonitoringWindow::kIOJankInterval * kNumBlockedIntervals;
static_assert(kBlockedTiming <=
internal::IOJankMonitoringWindow::kTimeDiscrepancyTimeout,
"");
task_environment_->FastForwardBy(
internal::IOJankMonitoringWindow::kMonitoringWindow - kDeltaFromBoundary);
const int kNumRacingThreads =
test::TaskEnvironment::kNumForegroundThreadPoolThreads;
TestWaitableEvent all_threads_blocked;
auto on_thread_blocked = BarrierClosure(
kNumRacingThreads,
BindOnce(&TestWaitableEvent::Signal, Unretained(&all_threads_blocked)));
TestWaitableEvent unblock_worker_threads;
for (int i = 0; i < kNumRacingThreads; ++i) {
ThreadPool::PostTask(FROM_HERE, {MayBlock()}, BindLambdaForTesting([&] {
on_thread_blocked.Run();
unblock_worker_threads.Wait();
}));
}
all_threads_blocked.Wait();
unblock_worker_threads.Signal();
task_environment_->RunUntilIdle();
all_threads_blocked.Reset();
on_thread_blocked = BarrierClosure(
kNumRacingThreads,
BindOnce(&TestWaitableEvent::Signal, Unretained(&all_threads_blocked)));
unblock_worker_threads.Reset();
for (int i = 0; i < kNumRacingThreads; ++i) {
ThreadPool::PostTask(FROM_HERE, {MayBlock()}, BindLambdaForTesting([&] {
ScopedBlockingCall blocked_for_14s(
FROM_HERE, BlockingType::MAY_BLOCK);
on_thread_blocked.Run();
unblock_worker_threads.Wait();
}));
}
task_environment_->AdvanceClock(kDeltaFromBoundary);
{
ScopedBlockingCall trigger_window(FROM_HERE, BlockingType::MAY_BLOCK);
}
all_threads_blocked.Wait();
task_environment_->AdvanceClock(kBlockedTiming);
unblock_worker_threads.Signal();
task_environment_->RunUntilIdle();
StopMonitoring();
ASSERT_EQ(reports_.size(), 2U);
auto [janky_intervals_count, total_jank_count] = reports_[0];
EXPECT_GE(janky_intervals_count, 0);
EXPECT_LE(janky_intervals_count, 1);
EXPECT_GE(total_jank_count, 0);
EXPECT_LE(total_jank_count, kNumRacingThreads);
std::tie(janky_intervals_count, total_jank_count) = reports_[1];
EXPECT_GE(janky_intervals_count, kNumBlockedIntervals - 1);
EXPECT_LE(janky_intervals_count, kNumBlockedIntervals);
EXPECT_GE(total_jank_count, (kNumBlockedIntervals - 1) * kNumRacingThreads);
EXPECT_LE(total_jank_count, kNumBlockedIntervals * kNumRacingThreads);
}
}