Add RunFor to SimulatedEventLoopFactory
Turns out we really want to be able to run for short periods of time in
simulation. So add support and tests for it.
Change-Id: I9276e1330a0d4eaea717f949516b290b1a01ed89
diff --git a/aos/events/event-loop.h b/aos/events/event-loop.h
index 2a3673e..d1885bb 100644
--- a/aos/events/event-loop.h
+++ b/aos/events/event-loop.h
@@ -127,9 +127,6 @@
// Use this to run code once the thread goes into "real-time-mode",
virtual void OnRun(std::function<void()>) = 0;
- // Starts receiving events.
- virtual void Run() = 0;
-
// TODO(austin): Sort out how to switch to realtime on run.
// virtual void RunRealtime() = 0;
diff --git a/aos/events/event-loop_param_test.cc b/aos/events/event-loop_param_test.cc
index aa03643..6f1e5be 100644
--- a/aos/events/event-loop_param_test.cc
+++ b/aos/events/event-loop_param_test.cc
@@ -28,7 +28,7 @@
TEST_P(AbstractEventLoopTest, Basic) {
auto loop1 = Make();
auto loop2 = Make();
- auto loop3 = Make();
+ auto loop3 = MakePrimary();
auto sender = loop1->MakeSender<TestMessage>("/test");
@@ -54,7 +54,7 @@
});
EXPECT_FALSE(happened);
- loop3->Run();
+ Run();
EXPECT_TRUE(happened);
}
@@ -91,7 +91,7 @@
// Verify that Quit() works when there are multiple watchers.
TEST_P(AbstractEventLoopTest, MultipleWatcherQuit) {
auto loop1 = Make();
- auto loop2 = Make();
+ auto loop2 = MakePrimary();
auto sender = loop1->MakeSender<TestMessage>("/test2");
{
@@ -105,12 +105,12 @@
EXPECT_EQ(message.msg_value, 200);
loop2->Exit();
});
- loop2->Run();
+ Run();
}
// Verify that timer intervals and duration function properly.
TEST_P(AbstractEventLoopTest, TimerIntervalAndDuration) {
- auto loop = Make();
+ auto loop = MakePrimary();
::std::vector<::aos::monotonic_clock::time_point> iteration_list;
auto test_timer = loop->AddTimer([&iteration_list, &loop]() {
@@ -122,14 +122,14 @@
// Testing that the timer thread waits for the event loop to start before
// running
::std::this_thread::sleep_for(std::chrono::milliseconds(2));
- loop->Run();
+ Run();
EXPECT_EQ(iteration_list.size(), 8);
}
// Verify that we can change a timer's parameters during execution.
TEST_P(AbstractEventLoopTest, TimerChangeParameters) {
- auto loop = Make();
+ auto loop = MakePrimary();
::std::vector<::aos::monotonic_clock::time_point> iteration_list;
auto test_timer = loop->AddTimer([&iteration_list, &loop]() {
@@ -145,14 +145,14 @@
modifier_timer->Setup(loop->monotonic_now() +
::std::chrono::milliseconds(45));
EndEventLoop(loop.get(), ::std::chrono::milliseconds(150));
- loop->Run();
+ Run();
EXPECT_EQ(iteration_list.size(), 7);
}
// Verify that we can disable a timer during execution.
TEST_P(AbstractEventLoopTest, TimerDisable) {
- auto loop = Make();
+ auto loop = MakePrimary();
::std::vector<::aos::monotonic_clock::time_point> iteration_list;
auto test_timer = loop->AddTimer([&iteration_list, &loop]() {
@@ -167,14 +167,14 @@
ender_timer->Setup(loop->monotonic_now() +
::std::chrono::milliseconds(45));
EndEventLoop(loop.get(), ::std::chrono::milliseconds(150));
- loop->Run();
+ Run();
EXPECT_EQ(iteration_list.size(), 3);
}
// Verify that the send time on a message is roughly right.
TEST_P(AbstractEventLoopTest, MessageSendTime) {
- auto loop1 = Make();
+ auto loop1 = MakePrimary();
auto loop2 = Make();
auto sender = loop1->MakeSender<TestMessage>("/test");
auto fetcher = loop2->MakeFetcher<TestMessage>("/test");
@@ -188,7 +188,7 @@
test_timer->Setup(loop1->monotonic_now() + ::std::chrono::seconds(1));
EndEventLoop(loop1.get(), ::std::chrono::seconds(2));
- loop1->Run();
+ Run();
EXPECT_TRUE(fetcher.Fetch());
diff --git a/aos/events/event-loop_param_test.h b/aos/events/event-loop_param_test.h
index a4e4b8e..b5574ba 100644
--- a/aos/events/event-loop_param_test.h
+++ b/aos/events/event-loop_param_test.h
@@ -13,7 +13,14 @@
public:
virtual ~EventLoopTestFactory() {}
+ // Makes a connected event loop.
virtual std::unique_ptr<EventLoop> Make() = 0;
+ // Makes a primary event loop. This is the one the tests will try to use for
+ // anything blocking.
+ virtual std::unique_ptr<EventLoop> MakePrimary() = 0;
+
+ // Runs the loops until they quit.
+ virtual void Run() = 0;
};
class AbstractEventLoopTest
@@ -21,12 +28,15 @@
public:
AbstractEventLoopTest() { factory_.reset(GetParam()()); }
- std::unique_ptr<EventLoop> Make() { return factory_->Make(); }
+ ::std::unique_ptr<EventLoop> Make() { return factory_->Make(); }
+ ::std::unique_ptr<EventLoop> MakePrimary() { return factory_->MakePrimary(); }
+
+ void Run() { return factory_->Run(); }
// You can implement all the usual fixture class members here.
// To access the test parameter, call GetParam() from class
// TestWithParam<T>.
private:
- std::unique_ptr<EventLoopTestFactory> factory_;
+ ::std::unique_ptr<EventLoopTestFactory> factory_;
};
} // namespace testing
diff --git a/aos/events/raw-event-loop.h b/aos/events/raw-event-loop.h
index d12187a..fd9ea8f 100644
--- a/aos/events/raw-event-loop.h
+++ b/aos/events/raw-event-loop.h
@@ -114,6 +114,8 @@
virtual void Disable() = 0;
};
+class EventScheduler;
+
// Virtual base class for all event queue-types.
class RawEventLoop {
public:
@@ -124,7 +126,7 @@
// The passed in function will be called when the event loop starts.
// Use this to run code once the thread goes into "real-time-mode",
- virtual void OnRun(std::function<void()> on_run) = 0;
+ virtual void OnRun(::std::function<void()> on_run) = 0;
bool is_running() const { return is_running_.load(); }
@@ -132,13 +134,14 @@
// Returns a TimerHandle for configuration of the timer
virtual TimerHandler *AddTimer(::std::function<void()> callback) = 0;
- // Starts receiving events.
- virtual void Run() = 0;
-
// Stops receiving events.
virtual void Exit() = 0;
+ // TODO(austin): This shouldn't belong.
+ virtual void Run() = 0;
+
protected:
+ friend class EventScheduler;
void set_is_running(bool value) { is_running_.store(value); }
// Will send new messages from (path, type).
@@ -155,7 +158,7 @@
std::function<void(const Message *message)> watcher) = 0;
private:
- std::atomic<bool> is_running_{false};
+ ::std::atomic<bool> is_running_{false};
};
} // namespace aos
diff --git a/aos/events/shm-event-loop_test.cc b/aos/events/shm-event-loop_test.cc
index d6f493f..0878e12 100644
--- a/aos/events/shm-event-loop_test.cc
+++ b/aos/events/shm-event-loop_test.cc
@@ -10,11 +10,23 @@
class ShmEventLoopTestFactory : public EventLoopTestFactory {
public:
- std::unique_ptr<EventLoop> Make() override {
- return std::unique_ptr<EventLoop>(new ShmEventLoop());
+ ::std::unique_ptr<EventLoop> Make() override {
+ return ::std::unique_ptr<EventLoop>(new ShmEventLoop());
}
+ ::std::unique_ptr<EventLoop> MakePrimary() override {
+ ::std::unique_ptr<ShmEventLoop> loop =
+ ::std::unique_ptr<ShmEventLoop>(new ShmEventLoop());
+ primary_event_loop_ = loop.get();
+ return ::std::move(loop);
+ }
+
+ void Run() override { CHECK_NOTNULL(primary_event_loop_)->Run(); }
+
+ private:
::aos::testing::TestSharedMemory my_shm_;
+
+ ::aos::ShmEventLoop *primary_event_loop_;
};
INSTANTIATE_TEST_CASE_P(ShmEventLoopTest, AbstractEventLoopTest,
diff --git a/aos/events/simulated-event-loop.cc b/aos/events/simulated-event-loop.cc
index 02d15a6..5a9960e 100644
--- a/aos/events/simulated-event-loop.cc
+++ b/aos/events/simulated-event-loop.cc
@@ -124,8 +124,10 @@
EventScheduler *scheduler,
::std::map<::std::pair<::std::string, QueueTypeInfo>, SimulatedQueue>
*queues)
- : scheduler_(scheduler), queues_(queues) {}
- ~SimulatedEventLoop() override {};
+ : scheduler_(scheduler), queues_(queues) {
+ scheduler_->AddRawEventLoop(this);
+ }
+ ~SimulatedEventLoop() override { scheduler_->RemoveRawEventLoop(this); };
::aos::monotonic_clock::time_point monotonic_now() override {
return scheduler_->monotonic_now();
@@ -150,11 +152,10 @@
scheduler_->Schedule(scheduler_->monotonic_now(), on_run);
}
void Run() override {
- set_is_running(true);
+ LOG(FATAL, "Run from the factory instead\n");
scheduler_->Run();
}
void Exit() override {
- set_is_running(false);
scheduler_->Exit();
}
@@ -180,7 +181,36 @@
events_list_.erase(token);
}
+void EventScheduler::RunFor(monotonic_clock::duration duration) {
+ const ::aos::monotonic_clock::time_point end_time =
+ monotonic_now() + duration;
+ for (RawEventLoop *event_loop : raw_event_loops_) {
+ event_loop->set_is_running(true);
+ }
+ is_running_ = true;
+ while (!events_list_.empty() && is_running_) {
+ auto iter = events_list_.begin();
+ ::aos::monotonic_clock::time_point next_time = iter->first;
+ if (next_time > end_time) {
+ break;
+ }
+ now_ = iter->first;
+ ::std::function<void()> callback = ::std::move(iter->second);
+ events_list_.erase(iter);
+ callback();
+ }
+ now_ = end_time;
+ if (!is_running_) {
+ for (RawEventLoop *event_loop : raw_event_loops_) {
+ event_loop->set_is_running(false);
+ }
+ }
+}
+
void EventScheduler::Run() {
+ for (RawEventLoop *event_loop : raw_event_loops_) {
+ event_loop->set_is_running(true);
+ }
is_running_ = true;
while (!events_list_.empty() && is_running_) {
auto iter = events_list_.begin();
@@ -189,6 +219,11 @@
events_list_.erase(iter);
callback();
}
+ if (!is_running_) {
+ for (RawEventLoop *event_loop : raw_event_loops_) {
+ event_loop->set_is_running(false);
+ }
+ }
}
void SimulatedEventLoop::MakeRawWatcher(
diff --git a/aos/events/simulated-event-loop.h b/aos/events/simulated-event-loop.h
index 9a0aa6e..f2981c8 100644
--- a/aos/events/simulated-event-loop.h
+++ b/aos/events/simulated-event-loop.h
@@ -1,6 +1,7 @@
#ifndef _AOS_EVENTS_SIMULATED_EVENT_LOOP_H_
#define _AOS_EVENTS_SIMULATED_EVENT_LOOP_H_
+#include <algorithm>
#include <map>
#include <memory>
#include <unordered_set>
@@ -92,8 +93,19 @@
void Deschedule(Token token);
void Run();
+ void RunFor(::aos::monotonic_clock::duration duration);
- void Exit() { is_running_ = false; }
+ void Exit() {
+ is_running_ = false;
+ }
+
+ void AddRawEventLoop(RawEventLoop *event_loop) {
+ raw_event_loops_.push_back(event_loop);
+ }
+ void RemoveRawEventLoop(RawEventLoop *event_loop) {
+ raw_event_loops_.erase(::std::find(raw_event_loops_.begin(),
+ raw_event_loops_.end(), event_loop));
+ }
::aos::monotonic_clock::time_point monotonic_now() const { return now_; }
@@ -101,6 +113,7 @@
::aos::monotonic_clock::time_point now_ = ::aos::monotonic_clock::epoch();
QueueType events_list_;
bool is_running_ = false;
+ ::std::vector<RawEventLoop *> raw_event_loops_;
};
class SimulatedQueue {
@@ -147,6 +160,9 @@
::std::unique_ptr<EventLoop> MakeEventLoop();
void Run() { scheduler_.Run(); }
+ void RunFor(monotonic_clock::duration duration) {
+ scheduler_.RunFor(duration);
+ }
monotonic_clock::time_point monotonic_now() const {
return scheduler_.monotonic_now();
diff --git a/aos/events/simulated-event-loop_test.cc b/aos/events/simulated-event-loop_test.cc
index f652fd9..41d4dde 100644
--- a/aos/events/simulated-event-loop_test.cc
+++ b/aos/events/simulated-event-loop_test.cc
@@ -10,10 +10,16 @@
class SimulatedEventLoopTestFactory : public EventLoopTestFactory {
public:
::std::unique_ptr<EventLoop> Make() override {
- return event_loop.MakeEventLoop();
+ return event_loop_factory_.MakeEventLoop();
}
+ ::std::unique_ptr<EventLoop> MakePrimary() override {
+ return event_loop_factory_.MakeEventLoop();
+ }
+
+ void Run() override { event_loop_factory_.Run(); }
+
private:
- SimulatedEventLoopFactory event_loop;
+ SimulatedEventLoopFactory event_loop_factory_;
};
INSTANTIATE_TEST_CASE_P(SimulatedEventLoopTest, AbstractEventLoopTest,
@@ -48,5 +54,44 @@
scheduler.Run();
EXPECT_EQ(counter, 0);
}
+
+// Test that running for a time period with no handlers causes time to progress
+// correctly.
+TEST(SimulatedEventLoopTest, RunForNoHandlers) {
+ SimulatedEventLoopFactory simulated_event_loop_factory;
+ ::std::unique_ptr<EventLoop> event_loop =
+ simulated_event_loop_factory.MakeEventLoop();
+
+ simulated_event_loop_factory.RunFor(chrono::seconds(1));
+
+ EXPECT_EQ(::aos::monotonic_clock::epoch() + chrono::seconds(1),
+ simulated_event_loop_factory.monotonic_now());
+ EXPECT_EQ(::aos::monotonic_clock::epoch() + chrono::seconds(1),
+ event_loop->monotonic_now());
+}
+
+// Test that running for a time with a periodic handler causes time to end
+// correctly.
+TEST(SimulatedEventLoopTest, RunForTimerHandler) {
+ SimulatedEventLoopFactory simulated_event_loop_factory;
+ ::std::unique_ptr<EventLoop> event_loop =
+ simulated_event_loop_factory.MakeEventLoop();
+
+ int counter = 0;
+ auto timer = event_loop->AddTimer([&counter, &event_loop]() { ++counter; });
+ event_loop->OnRun([&event_loop, &timer] {
+ timer->Setup(event_loop->monotonic_now() + chrono::milliseconds(50),
+ chrono::milliseconds(100));
+ });
+
+ simulated_event_loop_factory.RunFor(chrono::seconds(1));
+
+ EXPECT_EQ(::aos::monotonic_clock::epoch() + chrono::seconds(1),
+ simulated_event_loop_factory.monotonic_now());
+ EXPECT_EQ(::aos::monotonic_clock::epoch() + chrono::seconds(1),
+ event_loop->monotonic_now());
+ EXPECT_EQ(counter, 10);
+}
+
} // namespace testing
} // namespace aos