You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
373 lines
11 KiB
373 lines
11 KiB
// Copyright 2014 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <sys/timerfd.h>
|
|
|
|
#include <memory>
|
|
|
|
#include "base/bind.h"
|
|
#include "base/bind_helpers.h"
|
|
#include "base/files/file_descriptor_watcher_posix.h"
|
|
#include "base/location.h"
|
|
#include "base/macros.h"
|
|
#include "base/message_loop/message_loop.h"
|
|
#include "base/run_loop.h"
|
|
#include "base/single_thread_task_runner.h"
|
|
#include "base/threading/platform_thread.h"
|
|
#include "base/threading/thread_task_runner_handle.h"
|
|
#include "base/time/time.h"
|
|
#include "components/timers/alarm_timer_chromeos.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
// Most of these tests have been lifted right out of timer_unittest.cc with only
|
|
// cosmetic changes. We want the AlarmTimer to be a drop-in replacement for the
|
|
// regular Timer so it should pass the same tests as the Timer class.
|
|
namespace timers {
|
|
namespace {
|
|
const base::TimeDelta kTenMilliseconds = base::TimeDelta::FromMilliseconds(10);
|
|
|
|
class AlarmTimerTester {
|
|
public:
|
|
AlarmTimerTester(bool* did_run,
|
|
base::TimeDelta delay,
|
|
base::OnceClosure quit_closure)
|
|
: did_run_(did_run),
|
|
quit_closure_(std::move(quit_closure)),
|
|
delay_(delay),
|
|
timer_(new timers::SimpleAlarmTimer()) {}
|
|
void Start() {
|
|
timer_->Start(
|
|
FROM_HERE, delay_,
|
|
base::BindRepeating(&AlarmTimerTester::Run, base::Unretained(this)));
|
|
}
|
|
|
|
private:
|
|
void Run() {
|
|
*did_run_ = true;
|
|
if (quit_closure_)
|
|
std::move(quit_closure_).Run();
|
|
}
|
|
|
|
bool* did_run_;
|
|
base::OnceClosure quit_closure_;
|
|
const base::TimeDelta delay_;
|
|
std::unique_ptr<timers::SimpleAlarmTimer> timer_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(AlarmTimerTester);
|
|
};
|
|
|
|
class SelfDeletingAlarmTimerTester {
|
|
public:
|
|
SelfDeletingAlarmTimerTester(bool* did_run,
|
|
base::TimeDelta delay,
|
|
base::OnceClosure quit_closure)
|
|
: did_run_(did_run),
|
|
quit_closure_(std::move(quit_closure)),
|
|
delay_(delay),
|
|
timer_(new timers::SimpleAlarmTimer()) {}
|
|
void Start() {
|
|
timer_->Start(FROM_HERE, delay_,
|
|
base::BindRepeating(&SelfDeletingAlarmTimerTester::Run,
|
|
base::Unretained(this)));
|
|
}
|
|
|
|
private:
|
|
void Run() {
|
|
*did_run_ = true;
|
|
timer_.reset();
|
|
|
|
if (quit_closure_)
|
|
std::move(quit_closure_).Run();
|
|
}
|
|
|
|
bool* did_run_;
|
|
base::OnceClosure quit_closure_;
|
|
const base::TimeDelta delay_;
|
|
std::unique_ptr<timers::SimpleAlarmTimer> timer_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(SelfDeletingAlarmTimerTester);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Each test is run against each type of MessageLoop. That way we are sure
|
|
// that timers work properly in all configurations.
|
|
|
|
TEST(AlarmTimerTest, SimpleAlarmTimer) {
|
|
base::MessageLoopForIO loop;
|
|
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
|
|
|
|
base::RunLoop run_loop;
|
|
bool did_run = false;
|
|
AlarmTimerTester f(&did_run, kTenMilliseconds,
|
|
run_loop.QuitWhenIdleClosure());
|
|
f.Start();
|
|
|
|
run_loop.Run();
|
|
|
|
EXPECT_TRUE(did_run);
|
|
}
|
|
|
|
TEST(AlarmTimerTest, SimpleAlarmTimer_Cancel) {
|
|
base::MessageLoopForIO loop;
|
|
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
|
|
|
|
bool did_run_a = false;
|
|
AlarmTimerTester* a =
|
|
new AlarmTimerTester(&did_run_a, kTenMilliseconds, base::OnceClosure());
|
|
|
|
// This should run before the timer expires.
|
|
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, a);
|
|
|
|
// Now start the timer.
|
|
a->Start();
|
|
|
|
base::RunLoop run_loop;
|
|
bool did_run_b = false;
|
|
AlarmTimerTester b(&did_run_b, kTenMilliseconds,
|
|
run_loop.QuitWhenIdleClosure());
|
|
b.Start();
|
|
|
|
run_loop.Run();
|
|
|
|
EXPECT_FALSE(did_run_a);
|
|
EXPECT_TRUE(did_run_b);
|
|
}
|
|
|
|
// If underlying timer does not handle this properly, we will crash or fail
|
|
// in full page heap environment.
|
|
TEST(AlarmTimerTest, SelfDeletingAlarmTimer) {
|
|
base::MessageLoopForIO loop;
|
|
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
|
|
|
|
base::RunLoop run_loop;
|
|
bool did_run = false;
|
|
SelfDeletingAlarmTimerTester f(&did_run, kTenMilliseconds,
|
|
run_loop.QuitWhenIdleClosure());
|
|
f.Start();
|
|
|
|
run_loop.Run();
|
|
|
|
EXPECT_TRUE(did_run);
|
|
}
|
|
|
|
TEST(AlarmTimerTest, AlarmTimerZeroDelay) {
|
|
base::MessageLoopForIO loop;
|
|
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
|
|
|
|
base::RunLoop run_loop;
|
|
bool did_run = false;
|
|
AlarmTimerTester f(&did_run, base::TimeDelta(),
|
|
run_loop.QuitWhenIdleClosure());
|
|
f.Start();
|
|
|
|
run_loop.Run();
|
|
|
|
EXPECT_TRUE(did_run);
|
|
}
|
|
|
|
TEST(AlarmTimerTest, AlarmTimerZeroDelay_Cancel) {
|
|
base::MessageLoopForIO loop;
|
|
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
|
|
|
|
bool did_run_a = false;
|
|
AlarmTimerTester* a =
|
|
new AlarmTimerTester(&did_run_a, base::TimeDelta(), base::OnceClosure());
|
|
|
|
// This should run before the timer expires.
|
|
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, a);
|
|
|
|
// Now start the timer.
|
|
a->Start();
|
|
|
|
base::RunLoop run_loop;
|
|
bool did_run_b = false;
|
|
AlarmTimerTester b(&did_run_b, base::TimeDelta(),
|
|
run_loop.QuitWhenIdleClosure());
|
|
b.Start();
|
|
|
|
run_loop.Run();
|
|
|
|
EXPECT_FALSE(did_run_a);
|
|
EXPECT_TRUE(did_run_b);
|
|
}
|
|
|
|
TEST(AlarmTimerTest, MessageLoopShutdown) {
|
|
// This test is designed to verify that shutdown of the
|
|
// message loop does not cause crashes if there were pending
|
|
// timers not yet fired. It may only trigger exceptions
|
|
// if debug heap checking is enabled.
|
|
bool did_run = false;
|
|
{
|
|
auto loop = std::make_unique<base::MessageLoopForIO>();
|
|
auto file_descriptor_watcher =
|
|
std::make_unique<base::FileDescriptorWatcher>(loop.get());
|
|
AlarmTimerTester a(&did_run, kTenMilliseconds, base::OnceClosure());
|
|
AlarmTimerTester b(&did_run, kTenMilliseconds, base::OnceClosure());
|
|
AlarmTimerTester c(&did_run, kTenMilliseconds, base::OnceClosure());
|
|
AlarmTimerTester d(&did_run, kTenMilliseconds, base::OnceClosure());
|
|
|
|
a.Start();
|
|
b.Start();
|
|
|
|
// Allow FileDescriptorWatcher to start watching the timers. Without this,
|
|
// tasks posted by FileDescriptorWatcher::WatchReadable() are leaked.
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
// MessageLoop and FileDescriptorWatcher destruct.
|
|
file_descriptor_watcher.reset();
|
|
loop.reset();
|
|
} // SimpleAlarmTimers destruct. SHOULD NOT CRASH, of course.
|
|
|
|
EXPECT_FALSE(did_run);
|
|
}
|
|
|
|
TEST(AlarmTimerTest, NonRepeatIsRunning) {
|
|
{
|
|
base::MessageLoopForIO loop;
|
|
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
|
|
timers::SimpleAlarmTimer timer;
|
|
EXPECT_FALSE(timer.IsRunning());
|
|
timer.Start(FROM_HERE, base::TimeDelta::FromDays(1), base::DoNothing());
|
|
|
|
// Allow FileDescriptorWatcher to start watching the timer. Without this, a
|
|
// task posted by FileDescriptorWatcher::WatchReadable() is leaked.
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
EXPECT_TRUE(timer.IsRunning());
|
|
timer.Stop();
|
|
EXPECT_FALSE(timer.IsRunning());
|
|
ASSERT_FALSE(timer.user_task().is_null());
|
|
timer.Reset();
|
|
base::RunLoop().RunUntilIdle();
|
|
EXPECT_TRUE(timer.IsRunning());
|
|
}
|
|
}
|
|
|
|
TEST(AlarmTimerTest, RetainNonRepeatIsRunning) {
|
|
base::MessageLoopForIO loop;
|
|
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
|
|
timers::SimpleAlarmTimer timer;
|
|
EXPECT_FALSE(timer.IsRunning());
|
|
timer.Start(FROM_HERE, base::TimeDelta::FromDays(1), base::DoNothing());
|
|
|
|
// Allow FileDescriptorWatcher to start watching the timer. Without this, a
|
|
// task posted by FileDescriptorWatcher::WatchReadable() is leaked.
|
|
base::RunLoop().RunUntilIdle();
|
|
|
|
EXPECT_TRUE(timer.IsRunning());
|
|
timer.Reset();
|
|
base::RunLoop().RunUntilIdle();
|
|
EXPECT_TRUE(timer.IsRunning());
|
|
timer.Stop();
|
|
EXPECT_FALSE(timer.IsRunning());
|
|
timer.Reset();
|
|
base::RunLoop().RunUntilIdle();
|
|
EXPECT_TRUE(timer.IsRunning());
|
|
}
|
|
|
|
namespace {
|
|
|
|
bool g_callback_happened1 = false;
|
|
bool g_callback_happened2 = false;
|
|
|
|
void ClearAllCallbackHappened() {
|
|
g_callback_happened1 = false;
|
|
g_callback_happened2 = false;
|
|
}
|
|
|
|
void SetCallbackHappened1(base::OnceClosure quit_closure) {
|
|
g_callback_happened1 = true;
|
|
if (quit_closure)
|
|
std::move(quit_closure).Run();
|
|
}
|
|
|
|
void SetCallbackHappened2(base::OnceClosure quit_closure) {
|
|
g_callback_happened2 = true;
|
|
if (quit_closure)
|
|
std::move(quit_closure).Run();
|
|
}
|
|
|
|
TEST(AlarmTimerTest, ContinuationStopStart) {
|
|
ClearAllCallbackHappened();
|
|
base::MessageLoopForIO loop;
|
|
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
|
|
timers::SimpleAlarmTimer timer;
|
|
timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(10),
|
|
base::BindRepeating(&SetCallbackHappened1,
|
|
base::DoNothing().Repeatedly()));
|
|
timer.Stop();
|
|
|
|
base::RunLoop run_loop;
|
|
timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(40),
|
|
base::BindRepeating(&SetCallbackHappened2,
|
|
run_loop.QuitWhenIdleClosure()));
|
|
run_loop.Run();
|
|
|
|
EXPECT_FALSE(g_callback_happened1);
|
|
EXPECT_TRUE(g_callback_happened2);
|
|
}
|
|
|
|
TEST(AlarmTimerTest, ContinuationReset) {
|
|
ClearAllCallbackHappened();
|
|
base::MessageLoopForIO loop;
|
|
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
|
|
|
|
base::RunLoop run_loop;
|
|
timers::SimpleAlarmTimer timer;
|
|
timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(10),
|
|
base::BindRepeating(&SetCallbackHappened1,
|
|
run_loop.QuitWhenIdleClosure()));
|
|
timer.Reset();
|
|
ASSERT_FALSE(timer.user_task().is_null());
|
|
run_loop.Run();
|
|
EXPECT_TRUE(g_callback_happened1);
|
|
}
|
|
|
|
// Verify that no crash occurs if a timer is deleted while its callback is
|
|
// running.
|
|
TEST(AlarmTimerTest, DeleteTimerWhileCallbackIsRunning) {
|
|
base::MessageLoopForIO loop;
|
|
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
|
|
base::RunLoop run_loop;
|
|
|
|
// Will be deleted by the callback.
|
|
timers::SimpleAlarmTimer* timer = new timers::SimpleAlarmTimer;
|
|
|
|
timer->Start(
|
|
FROM_HERE, base::TimeDelta::FromMilliseconds(10),
|
|
base::BindRepeating(
|
|
[](timers::SimpleAlarmTimer* timer, base::RunLoop* run_loop) {
|
|
delete timer;
|
|
run_loop->Quit();
|
|
},
|
|
timer, &run_loop));
|
|
run_loop.Run();
|
|
}
|
|
|
|
// Verify that no crash occurs if a zero-delay timer is deleted while its
|
|
// callback is running.
|
|
TEST(AlarmTimerTest, DeleteTimerWhileCallbackIsRunningZeroDelay) {
|
|
base::MessageLoopForIO loop;
|
|
base::FileDescriptorWatcher file_descriptor_watcher(&loop);
|
|
base::RunLoop run_loop;
|
|
|
|
// Will be deleted by the callback.
|
|
timers::SimpleAlarmTimer* timer = new timers::SimpleAlarmTimer;
|
|
|
|
timer->Start(
|
|
FROM_HERE, base::TimeDelta(),
|
|
base::BindRepeating(
|
|
[](timers::SimpleAlarmTimer* timer, base::RunLoop* run_loop) {
|
|
delete timer;
|
|
run_loop->Quit();
|
|
},
|
|
timer, &run_loop));
|
|
run_loop.Run();
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace timers
|