Convert control loop tests over to simulated event loop

This makes it so that we properly only use ShmEventLoop for running in
realtime on a robot.  Very nice.

Change-Id: I46b770b336f59e08cfaf28511b3bd5689f72fff1
diff --git a/aos/controls/BUILD b/aos/controls/BUILD
index 30fe5c6..a612ecb 100644
--- a/aos/controls/BUILD
+++ b/aos/controls/BUILD
@@ -27,7 +27,7 @@
         "control_loop_test.h",
     ],
     deps = [
-        "//aos/events:shm-event-loop",
+        "//aos/events:simulated_event_loop",
         "//aos/logging:queue_logging",
         "//aos/robot_state",
         "//aos/testing:googletest",
diff --git a/aos/controls/control_loop-tmpl.h b/aos/controls/control_loop-tmpl.h
index 2b57a0b..4f48737 100644
--- a/aos/controls/control_loop-tmpl.h
+++ b/aos/controls/control_loop-tmpl.h
@@ -23,26 +23,7 @@
 }
 
 template <class T>
-void ControlLoop<T>::Iterate() {
-  if (!has_iterate_fetcher_) {
-    iterate_position_fetcher_ =
-        event_loop_->MakeFetcher<PositionType>(name_ + ".position");
-    has_iterate_fetcher_ = true;
-  }
-  const bool did_fetch = iterate_position_fetcher_.Fetch();
-  if (!did_fetch) {
-    LOG(FATAL, "Failed to fetch from position queue\n");
-  }
-  IteratePosition(*iterate_position_fetcher_);
-}
-
-template <class T>
 void ControlLoop<T>::IteratePosition(const PositionType &position) {
-  // Since Exit() isn't async safe, we want to call Exit from the periodic
-  // handler.
-  if (!run_) {
-    event_loop_->Exit();
-  }
   no_goal_.Print();
   no_sensor_state_.Print();
   motors_off_log_.Print();
@@ -119,28 +100,5 @@
   status.Send();
 }
 
-template <class T>
-void ControlLoop<T>::Run() {
-  struct sigaction action;
-  action.sa_handler = &ControlLoop<T>::Quit;
-  sigemptyset(&action.sa_mask);
-  action.sa_flags = SA_RESETHAND;
-
-  PCHECK(sigaction(SIGTERM, &action, nullptr));
-  PCHECK(sigaction(SIGQUIT, &action, nullptr));
-  PCHECK(sigaction(SIGINT, &action, nullptr));
-
-  event_loop_->MakeWatcher(name_ + ".position",
-                           [this](const PositionType &position) {
-                             this->IteratePosition(position);
-                           });
-
-  event_loop_->Run();
-  LOG(INFO, "Shutting down\n");
-}
-
-template <class T>
-::std::atomic<bool> ControlLoop<T>::run_{true};
-
 }  // namespace controls
 }  // namespace aos
diff --git a/aos/controls/control_loop.h b/aos/controls/control_loop.h
index 56353c1..fe72022 100644
--- a/aos/controls/control_loop.h
+++ b/aos/controls/control_loop.h
@@ -15,16 +15,6 @@
 namespace aos {
 namespace controls {
 
-// Interface to describe runnable jobs.
-class Runnable {
- public:
-  virtual ~Runnable() {}
-  // Runs forever.
-  virtual void Run() = 0;
-  // Does one quick piece of work and return.  Does _not_ block.
-  virtual void Iterate() = 0;
-};
-
 // Control loops run this often, "starting" at time 0.
 constexpr ::std::chrono::nanoseconds kLoopFrequency =
     ::std::chrono::milliseconds(5);
@@ -35,7 +25,7 @@
 // It will then call the RunIteration method every cycle that it has enough
 // valid data for the control loop to run.
 template <class T>
-class ControlLoop : public Runnable {
+class ControlLoop {
  public:
   // Create some convenient typedefs to reference the Goal, Position, Status,
   // and Output structures.
@@ -58,6 +48,11 @@
         event_loop_->MakeFetcher<::aos::RobotState>(".aos.robot_state");
     joystick_state_fetcher_ =
         event_loop_->MakeFetcher<::aos::JoystickState>(".aos.joystick_state");
+
+    event_loop_->MakeWatcher(name_ + ".position",
+                             [this](const PositionType &position) {
+                               this->IteratePosition(position);
+                             });
   }
 
   const ::aos::RobotState &robot_state() const { return *robot_state_fetcher_; }
@@ -87,22 +82,12 @@
   // subsystem.
   virtual void Zero(OutputType *output) { output->Zero(); }
 
-  // Runs the loop forever.
-  // TODO(austin): This should move to the event loop once it gets hoisted out.
-  void Run() override;
-
-  // Runs one cycle of the loop.
-  // TODO(austin): This should go away when all the tests use event loops
-  // directly.
-  void Iterate() override;
-
  protected:
+  // Runs one cycle of the loop.
   void IteratePosition(const PositionType &position);
 
   EventLoop *event_loop() { return event_loop_; }
 
-  static void Quit(int /*signum*/) { run_ = false; }
-
   // Runs an iteration of the control loop.
   // goal is the last goal that was sent.  It might be any number of cycles old
   // or nullptr if we haven't ever received a goal.
@@ -126,7 +111,6 @@
       ::std::chrono::milliseconds(100);
 
   // Pointer to the queue group
-  ::std::unique_ptr<ShmEventLoop> shm_event_loop_;
   EventLoop *event_loop_;
   ::std::string name_;
 
@@ -154,8 +138,6 @@
       SimpleLogInterval(kStaleLogInterval, WARNING, "motors disabled");
   SimpleLogInterval no_goal_ =
       SimpleLogInterval(kStaleLogInterval, ERROR, "no goal");
-
-  static ::std::atomic<bool> run_;
 };
 
 }  // namespace controls
diff --git a/aos/controls/control_loop_test.h b/aos/controls/control_loop_test.h
index 95f1c7d..85d610e 100644
--- a/aos/controls/control_loop_test.h
+++ b/aos/controls/control_loop_test.h
@@ -5,7 +5,7 @@
 
 #include "gtest/gtest.h"
 
-#include "aos/events/shm-event-loop.h"
+#include "aos/events/simulated-event-loop.h"
 #include "aos/logging/queue_logging.h"
 #include "aos/robot_state/robot_state.q.h"
 #include "aos/testing/test_shm.h"
@@ -22,68 +22,43 @@
 template <typename TestBaseClass>
 class ControlLoopTestTemplated : public TestBaseClass {
  public:
-  ControlLoopTestTemplated() {
+  ControlLoopTestTemplated(::std::chrono::nanoseconds dt = kTimeTick)
+      : dt_(dt), robot_status_event_loop_(MakeEventLoop()) {
     robot_state_sender_ =
-        test_event_loop_.MakeSender<::aos::RobotState>(".aos.robot_state");
+        robot_status_event_loop_->MakeSender<::aos::RobotState>(
+            ".aos.robot_state");
     joystick_state_sender_ =
-        test_event_loop_.MakeSender<::aos::JoystickState>(".aos.joystick_state");
+        robot_status_event_loop_->MakeSender<::aos::JoystickState>(
+            ".aos.joystick_state");
 
-    ::aos::time::EnableMockTime(current_time_);
+    // Schedule the robot status send 1 nanosecond before the loop runs.
+    send_robot_state_phased_loop_ = robot_status_event_loop_->AddPhasedLoop(
+        [this](int) { SendRobotState(); }, dt_,
+        dt - ::std::chrono::nanoseconds(1));
 
-    SendMessages(false);
+    send_joystick_state_timer_ =
+        robot_status_event_loop_->AddTimer([this]() { SendJoystickState(); });
+
+    robot_status_event_loop_->OnRun([this]() {
+      send_joystick_state_timer_->Setup(
+          robot_status_event_loop_->monotonic_now(), dt_);
+    });
   }
-  virtual ~ControlLoopTestTemplated() {
-    ::aos::time::DisableMockTime();
-  }
+  virtual ~ControlLoopTestTemplated() {}
 
   void set_team_id(uint16_t team_id) { team_id_ = team_id; }
   uint16_t team_id() const { return team_id_; }
 
-  // Sends out all of the required queue messages.
-  void SendMessages(bool enabled) {
-    if (current_time_ >= kDSPacketTime + last_ds_time_ ||
-        last_enabled_ != enabled) {
-      last_ds_time_ = current_time_;
-      auto new_state = joystick_state_sender_.MakeMessage();
-      new_state->fake = true;
-
-      new_state->enabled = enabled;
-      new_state->autonomous = false;
-      new_state->team_id = team_id_;
-
-      new_state.Send();
-      last_enabled_ = enabled;
+  // Sets the enabled/disabled bit and (potentially) rebroadcasts the robot
+  // state messages.
+  void SetEnabled(bool enabled) {
+    if (enabled_ != enabled) {
+      enabled_ = enabled;
+      SendJoystickState();
+      SendRobotState();
+      send_joystick_state_timer_->Setup(
+          robot_status_event_loop_->monotonic_now(), dt_);
     }
-
-    {
-      auto new_state = robot_state_sender_.MakeMessage();
-
-      new_state->reader_pid = reader_pid_;
-      new_state->outputs_enabled = enabled;
-      new_state->browned_out = false;
-
-      new_state->is_3v3_active = true;
-      new_state->is_5v_active = true;
-      new_state->voltage_3v3 = 3.3;
-      new_state->voltage_5v = 5.0;
-
-      new_state->voltage_roborio_in = battery_voltage_;
-      new_state->voltage_battery = battery_voltage_;
-
-      LOG_STRUCT(INFO, "robot_state", *new_state);
-      new_state.Send();
-    }
-  }
-  // Ticks time for a single control loop cycle.
-  void TickTime(::std::chrono::nanoseconds dt = kTimeTick) {
-    ::aos::time::SetMockTime(current_time_ += dt);
-  }
-
-  // Simulates everything that happens during 1 loop time step.
-  void SimulateTimestep(bool enabled,
-                        ::std::chrono::nanoseconds dt = kTimeTick) {
-    SendMessages(enabled);
-    TickTime(dt);
   }
 
   // Simulate a reset of the process reading sensors, which tells loops that all
@@ -97,33 +72,91 @@
     battery_voltage_ = battery_voltage;
   }
 
+  ::std::unique_ptr<::aos::EventLoop> MakeEventLoop() {
+    return event_loop_factory_.MakeEventLoop();
+  }
+
+  void RunFor(monotonic_clock::duration duration) {
+    event_loop_factory_.RunFor(duration);
+  }
+
+  ::aos::monotonic_clock::time_point monotonic_now() {
+    return event_loop_factory_.monotonic_now();
+  }
+
+  ::std::chrono::nanoseconds dt() const { return dt_; }
+
  private:
-  static constexpr ::std::chrono::milliseconds kTimeTick{5};
+  // Sends out all of the required queue messages.
+  void SendJoystickState() {
+    if (monotonic_now() >= kDSPacketTime + last_ds_time_ ||
+        last_enabled_ != enabled_) {
+      auto new_state = joystick_state_sender_.MakeMessage();
+      new_state->fake = true;
+
+      new_state->enabled = enabled_;
+      new_state->autonomous = false;
+      new_state->team_id = team_id_;
+
+      LOG_STRUCT(INFO, "joystick_state", *new_state);
+      new_state.Send();
+
+      last_ds_time_ = monotonic_now();
+      last_enabled_ = enabled_;
+    }
+  }
+
+  bool last_enabled_ = false;
+
+  void SendRobotState() {
+    auto new_state = robot_state_sender_.MakeMessage();
+
+    new_state->reader_pid = reader_pid_;
+    new_state->outputs_enabled = enabled_;
+    new_state->browned_out = false;
+
+    new_state->is_3v3_active = true;
+    new_state->is_5v_active = true;
+    new_state->voltage_3v3 = 3.3;
+    new_state->voltage_5v = 5.0;
+
+    new_state->voltage_roborio_in = battery_voltage_;
+    new_state->voltage_battery = battery_voltage_;
+
+    LOG_STRUCT(INFO, "robot_state", *new_state);
+    new_state.Send();
+  }
+
+  static constexpr ::std::chrono::microseconds kTimeTick{5000};
   static constexpr ::std::chrono::milliseconds kDSPacketTime{20};
 
+  const ::std::chrono::nanoseconds dt_;
+
+  SimulatedEventLoopFactory event_loop_factory_;
+
   uint16_t team_id_ = 971;
   int32_t reader_pid_ = 1;
   double battery_voltage_ = 12.4;
 
   ::aos::monotonic_clock::time_point last_ds_time_ =
-      ::aos::monotonic_clock::epoch();
-  ::aos::monotonic_clock::time_point current_time_ =
-      ::aos::monotonic_clock::epoch();
+      ::aos::monotonic_clock::min_time;
 
-  ::aos::testing::TestSharedMemory my_shm_;
+  bool enabled_ = false;
 
-  bool last_enabled_ = false;
-
-  ::aos::ShmEventLoop test_event_loop_;
+  ::std::unique_ptr<::aos::EventLoop> robot_status_event_loop_;
 
   ::aos::Sender<::aos::RobotState> robot_state_sender_;
   ::aos::Sender<::aos::JoystickState> joystick_state_sender_;
+
+  ::aos::PhasedLoopHandler *send_robot_state_phased_loop_ = nullptr;
+  ::aos::TimerHandler *send_joystick_state_timer_ = nullptr;
 };
 
 typedef ControlLoopTestTemplated<::testing::Test> ControlLoopTest;
 
 template <typename TestBaseClass>
-constexpr ::std::chrono::milliseconds ControlLoopTestTemplated<TestBaseClass>::kTimeTick;
+constexpr ::std::chrono::microseconds
+    ControlLoopTestTemplated<TestBaseClass>::kTimeTick;
 
 template <typename TestBaseClass>
 constexpr ::std::chrono::milliseconds ControlLoopTestTemplated<TestBaseClass>::kDSPacketTime;
diff --git a/aos/events/BUILD b/aos/events/BUILD
index aaa1c86..330ad40 100644
--- a/aos/events/BUILD
+++ b/aos/events/BUILD
@@ -64,7 +64,8 @@
     srcs = ["event-loop_param_test.cc"],
     hdrs = ["event-loop_param_test.h"],
     deps = [
-        "event-loop",
+        ":event-loop",
+        "//aos/logging:queue_logging",
         "//aos/testing:googletest",
     ],
 )
diff --git a/aos/events/event-loop.h b/aos/events/event-loop.h
index 1435723..4d6fda5 100644
--- a/aos/events/event-loop.h
+++ b/aos/events/event-loop.h
@@ -131,9 +131,6 @@
 
   // TODO(austin): OnExit
 
-  // Stops receiving events
-  virtual void Exit() = 0;
-
   // Sets the scheduler priority to run the event loop at.  This may not be
   // called after we go into "real-time-mode".
   virtual void SetRuntimeRealtimePriority(int priority) = 0;
diff --git a/aos/events/event-loop_param_test.cc b/aos/events/event-loop_param_test.cc
index d6f41ab..1dcc258 100644
--- a/aos/events/event-loop_param_test.cc
+++ b/aos/events/event-loop_param_test.cc
@@ -5,6 +5,8 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
+#include "aos/logging/queue_logging.h"
+
 namespace aos {
 namespace testing {
 namespace {
@@ -24,13 +26,6 @@
   TestMessage() { Zero(); }
 };
 
-// Ends the given event loop at the given time from now.
-void EndEventLoop(EventLoop *loop, ::std::chrono::milliseconds duration) {
-  auto end_timer = loop->AddTimer([loop]() { loop->Exit(); });
-  end_timer->Setup(loop->monotonic_now() +
-                   ::std::chrono::milliseconds(duration));
-}
-
 // Tests that watcher can receive messages from a sender.
 // Also tests that OnRun() works.
 TEST_P(AbstractEventLoopTest, Basic) {
@@ -51,7 +46,7 @@
 
   loop2->MakeWatcher("/test", [&](const TestMessage &message) {
     EXPECT_EQ(message.msg_value, 200);
-    loop2->Exit();
+    this->Exit();
   });
 
   EXPECT_FALSE(happened);
@@ -94,7 +89,7 @@
   loop2->MakeWatcher("/test", [&](const TestMessage &message) {
     values.push_back(message.msg_value);
     if (values.size() == 2) {
-      loop2->Exit();
+      this->Exit();
     }
   });
 
@@ -149,7 +144,7 @@
   });
 
   // Add a timer to actually quit.
-  auto test_timer = loop2->AddTimer([&loop2]() { loop2->Exit(); });
+  auto test_timer = loop2->AddTimer([this]() { this->Exit(); });
   loop2->OnRun([&test_timer, &loop2]() {
     test_timer->Setup(loop2->monotonic_now(), ::std::chrono::milliseconds(100));
   });
@@ -180,11 +175,11 @@
   }
 
   // Add a timer to actually quit.
-  auto test_timer = loop2->AddTimer([&loop2, &fetcher, &values]() {
+  auto test_timer = loop2->AddTimer([&fetcher, &values, this]() {
     while (fetcher.FetchNext()) {
       values.push_back(fetcher->msg_value);
     }
-    loop2->Exit();
+    this->Exit();
   });
 
   loop2->OnRun([&test_timer, &loop2]() {
@@ -218,11 +213,11 @@
   auto fetcher = loop2->MakeFetcher<TestMessage>("/test");
 
   // Add a timer to actually quit.
-  auto test_timer = loop2->AddTimer([&loop2, &fetcher, &values]() {
+  auto test_timer = loop2->AddTimer([&fetcher, &values, this]() {
     while (fetcher.FetchNext()) {
       values.push_back(fetcher->msg_value);
     }
-    loop2->Exit();
+    this->Exit();
   });
 
   loop2->OnRun([&test_timer, &loop2]() {
@@ -257,7 +252,7 @@
   auto fetcher = loop2->MakeFetcher<TestMessage>("/test");
 
   // Add a timer to actually quit.
-  auto test_timer = loop2->AddTimer([&loop2, &fetcher, &values]() {
+  auto test_timer = loop2->AddTimer([&fetcher, &values, this]() {
     if (fetcher.Fetch()) {
       values.push_back(fetcher->msg_value);
     }
@@ -265,7 +260,7 @@
     if (fetcher.Fetch()) {
       values.push_back(fetcher->msg_value);
     }
-    loop2->Exit();
+    this->Exit();
   });
 
   loop2->OnRun([&test_timer, &loop2]() {
@@ -299,7 +294,7 @@
   auto fetcher = loop2->MakeFetcher<TestMessage>("/test");
 
   // Add a timer to actually quit.
-  auto test_timer = loop2->AddTimer([&loop2, &fetcher, &values, &sender]() {
+  auto test_timer = loop2->AddTimer([&fetcher, &values, &sender, this]() {
     if (fetcher.Fetch()) {
       values.push_back(fetcher->msg_value);
     }
@@ -328,7 +323,7 @@
       values.push_back(fetcher->msg_value);
     }
 
-    loop2->Exit();
+    this->Exit();
   });
 
   loop2->OnRun([&test_timer, &loop2]() {
@@ -444,7 +439,7 @@
   loop2->MakeWatcher("/test1", [&](const TestMessage &) {});
   loop2->MakeWatcher("/test2", [&](const TestMessage &message) {
     EXPECT_EQ(message.msg_value, 200);
-    loop2->Exit();
+    this->Exit();
   });
 
   auto sender = loop1->MakeSender<TestMessage>("/test2");
@@ -566,12 +561,12 @@
 
   // Run kCount iterations.
   loop1->AddPhasedLoop(
-      [&times, &loop1](int count) {
+      [&times, &loop1, this](int count) {
         EXPECT_EQ(count, 1);
         times.push_back(loop1->monotonic_now());
         LOG(INFO, "%zu\n", times.size());
         if (times.size() == kCount) {
-          loop1->Exit();
+          this->Exit();
         }
       },
       chrono::seconds(1), kOffset);
@@ -627,7 +622,7 @@
   auto sender = loop1->MakeSender<TestMessage>("/test");
   auto fetcher = loop2->MakeFetcher<TestMessage>("/test");
 
-  auto test_timer = loop1->AddTimer([&sender, &fetcher, &loop1]() {
+  auto test_timer = loop1->AddTimer([&sender, &fetcher, this]() {
     for (int i = 0; i < 100000; ++i) {
       auto msg = sender.MakeMessage();
       msg->msg_value = i;
@@ -643,7 +638,7 @@
       ++last;
     }
 
-    loop1->Exit();
+    this->Exit();
   });
 
   loop1->OnRun([&test_timer, &loop1]() {
diff --git a/aos/events/event-loop_param_test.h b/aos/events/event-loop_param_test.h
index 26d869d..83f0b37 100644
--- a/aos/events/event-loop_param_test.h
+++ b/aos/events/event-loop_param_test.h
@@ -22,6 +22,9 @@
   // Runs the loops until they quit.
   virtual void Run() = 0;
 
+  // Quits the loops.
+  virtual void Exit() = 0;
+
   // Advances time by sleeping.  Can't be called from inside a loop.
   virtual void SleepFor(::std::chrono::nanoseconds duration) = 0;
 };
@@ -36,9 +39,19 @@
 
   void Run() { return factory_->Run(); }
 
+  void Exit() { return factory_->Exit(); }
+
   void SleepFor(::std::chrono::nanoseconds duration) {
     return factory_->SleepFor(duration);
   }
+
+  // Ends the given event loop at the given time from now.
+  void EndEventLoop(EventLoop *loop, ::std::chrono::milliseconds duration) {
+    auto end_timer = loop->AddTimer([this]() { this->Exit(); });
+    end_timer->Setup(loop->monotonic_now() +
+                     ::std::chrono::milliseconds(duration));
+  }
+
   // You can implement all the usual fixture class members here.
   // To access the test parameter, call GetParam() from class
   // TestWithParam<T>.
diff --git a/aos/events/raw-event-loop.h b/aos/events/raw-event-loop.h
index 7ab2810..d221a6e 100644
--- a/aos/events/raw-event-loop.h
+++ b/aos/events/raw-event-loop.h
@@ -156,12 +156,6 @@
       const monotonic_clock::duration interval,
       const monotonic_clock::duration offset = ::std::chrono::seconds(0)) = 0;
 
-  // Stops receiving events.
-  virtual void Exit() = 0;
-
-  // TODO(austin): This shouldn't belong here.
-  virtual void Run() = 0;
-
  protected:
   friend class EventScheduler;
   void set_is_running(bool value) { is_running_.store(value); }
diff --git a/aos/events/shm-event-loop.cc b/aos/events/shm-event-loop.cc
index eead9f5..e44a54b 100644
--- a/aos/events/shm-event-loop.cc
+++ b/aos/events/shm-event-loop.cc
@@ -408,7 +408,10 @@
 
   ::aos::SetCurrentThreadName(thread_state_.name());
 
-  // Now, all the threads are up.  Go RT.
+  // Now, all the threads are up.  Lock everything into memory and go RT.
+  if (thread_state_.priority_ != -1) {
+    ::aos::InitRT();
+  }
   thread_state_.MaybeSetCurrentThreadRealtimePriority();
   set_is_running(true);
 
diff --git a/aos/events/shm-event-loop.h b/aos/events/shm-event-loop.h
index 24cd909..5db8319 100644
--- a/aos/events/shm-event-loop.h
+++ b/aos/events/shm-event-loop.h
@@ -50,8 +50,8 @@
           ::std::chrono::seconds(0)) override;
 
   void OnRun(::std::function<void()> on_run) override;
-  void Run() override;
-  void Exit() override;
+  void Run();
+  void Exit();
 
   // TODO(austin): Add a function to register control-C call.
 
diff --git a/aos/events/shm-event-loop_test.cc b/aos/events/shm-event-loop_test.cc
index 8d0f552..8af00f0 100644
--- a/aos/events/shm-event-loop_test.cc
+++ b/aos/events/shm-event-loop_test.cc
@@ -24,6 +24,8 @@
 
   void Run() override { CHECK_NOTNULL(primary_event_loop_)->Run(); }
 
+  void Exit() override { CHECK_NOTNULL(primary_event_loop_)->Exit(); }
+
   void SleepFor(::std::chrono::nanoseconds duration) override {
     ::std::this_thread::sleep_for(duration);
   }
@@ -80,10 +82,10 @@
   bool did_timer = false;
   bool did_watcher = false;
 
-  auto timer = loop->AddTimer([&did_timer, &loop]() {
+  auto timer = loop->AddTimer([&did_timer, &loop, &factory]() {
     EXPECT_TRUE(IsRealtime());
     did_timer = true;
-    loop->Exit();
+    factory.Exit();
   });
 
   loop->MakeWatcher("/test", [&did_watcher](const TestMessage &) {
@@ -118,7 +120,7 @@
   constexpr chrono::milliseconds kOffset = chrono::milliseconds(400);
 
   loop1->AddPhasedLoop(
-      [&times, &loop1, &kOffset](int count) {
+      [&times, &loop1, &kOffset, &factory](int count) {
         const ::aos::monotonic_clock::time_point monotonic_now =
             loop1->monotonic_now();
 
@@ -143,7 +145,7 @@
 
         times.push_back(loop1->monotonic_now());
         if (times.size() == 2) {
-          loop1->Exit();
+          factory.Exit();
         }
 
         // Now, add a large delay.  This should push us up to 3 cycles.
diff --git a/aos/events/simulated-event-loop.cc b/aos/events/simulated-event-loop.cc
index 71b0c79..a3edd9b 100644
--- a/aos/events/simulated-event-loop.cc
+++ b/aos/events/simulated-event-loop.cc
@@ -231,13 +231,6 @@
   void OnRun(::std::function<void()> on_run) override {
     scheduler_->Schedule(scheduler_->monotonic_now(), on_run);
   }
-  void Run() override {
-    LOG(FATAL, "Run from the factory instead\n");
-    scheduler_->Run();
-  }
-  void Exit() override {
-    scheduler_->Exit();
-  }
 
   void set_name(const char *name) override { name_ = name; }
 
diff --git a/aos/events/simulated-event-loop.h b/aos/events/simulated-event-loop.h
index 81b181f..0828382 100644
--- a/aos/events/simulated-event-loop.h
+++ b/aos/events/simulated-event-loop.h
@@ -189,11 +189,17 @@
  public:
   ::std::unique_ptr<EventLoop> MakeEventLoop();
 
+  // Starts executing the event loops unconditionally.
   void Run() { scheduler_.Run(); }
+  // Executes the event loops for a duration.
   void RunFor(monotonic_clock::duration duration) {
     scheduler_.RunFor(duration);
   }
 
+  // Stops executing all event loops.  Meant to be called from within an event
+  // loop handler.
+  void Exit() { scheduler_.Exit(); }
+
   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 be52243..e987a11 100644
--- a/aos/events/simulated-event-loop_test.cc
+++ b/aos/events/simulated-event-loop_test.cc
@@ -17,6 +17,7 @@
   }
 
   void Run() override { event_loop_factory_.Run(); }
+  void Exit() override { event_loop_factory_.Exit(); }
 
   // TODO(austin): Implement this.  It's used currently for a phased loop test.
   // I'm not sure how much that matters.
diff --git a/aos/init.cc b/aos/init.cc
index aa609ec..8ce70ea 100644
--- a/aos/init.cc
+++ b/aos/init.cc
@@ -120,15 +120,19 @@
   GoRT(relative_priority);
 }
 
+void InitRT() {
+  LockAllMemory();
+
+  // Only let rt processes run for 3 seconds straight.
+  SetSoftRLimit(RLIMIT_RTTIME, 3000000, true);
+
+  // Allow rt processes up to priority 40.
+  SetSoftRLimit(RLIMIT_RTPRIO, 40, false);
+}
+
 void GoRT(int relative_priority) {
   if (ShouldBeRealtime()) {
-    LockAllMemory();
-
-    // Only let rt processes run for 3 seconds straight.
-    SetSoftRLimit(RLIMIT_RTTIME, 3000000, true);
-
-    // Allow rt processes up to priority 40.
-    SetSoftRLimit(RLIMIT_RTPRIO, 40, false);
+    InitRT();
 
     // Set our process to the appropriate priority.
     struct sched_param param;
@@ -137,9 +141,10 @@
       PDie("%s-init: setting SCHED_FIFO failed", program_invocation_short_name);
     }
   } else {
-    fprintf(stderr, "%s not doing realtime initialization because environment"
-            " variable %s is set\n", program_invocation_short_name,
-            kNoRealtimeEnvironmentVariable);
+    fprintf(stderr,
+            "%s not doing realtime initialization because environment"
+            " variable %s is set\n",
+            program_invocation_short_name, kNoRealtimeEnvironmentVariable);
     printf("no realtime for %s. see stderr\n", program_invocation_short_name);
   }
 
diff --git a/aos/init.h b/aos/init.h
index 4489c5d..be99a24 100644
--- a/aos/init.h
+++ b/aos/init.h
@@ -23,6 +23,11 @@
 // exit gracefully).
 void Cleanup();
 
+// Locks everything into memory and sets the limits.  This plus InitNRT are
+// everything you need to do before SetCurrentThreadRealtimePriority will make
+// your thread RT.  Called as part of ShmEventLoop::Run()
+void InitRT();
+
 // Performs the realtime parts of initialization after InitNRT(true) has been called.
 void GoRT(int relative_priority = 0);
 
diff --git a/aos/input/joystick_input.cc b/aos/input/joystick_input.cc
index a208f1e..0bbba41 100644
--- a/aos/input/joystick_input.cc
+++ b/aos/input/joystick_input.cc
@@ -10,10 +10,6 @@
 namespace aos {
 namespace input {
 
-::std::atomic<bool> JoystickInput::run_;
-
-void JoystickInput::Quit(int /*signum*/) { run_ = false; }
-
 void JoystickInput::HandleData(const ::aos::JoystickState &joystick_state) {
   data_.Update(joystick_state);
 
@@ -63,27 +59,6 @@
   }
 
   RunIteration(data_);
-
-  if (!run_) {
-    event_loop_->Exit();
-  }
-}
-
-void JoystickInput::Run() {
-  // TODO(austin): We need a better sigint story for event loops in general.
-  run_ = true;
-  struct sigaction action;
-  action.sa_handler = &JoystickInput::Quit;
-  sigemptyset(&action.sa_mask);
-  action.sa_flags = SA_RESETHAND;
-
-  PCHECK(sigaction(SIGTERM, &action, nullptr));
-  PCHECK(sigaction(SIGQUIT, &action, nullptr));
-  PCHECK(sigaction(SIGINT, &action, nullptr));
-
-  event_loop_->Run();
-
-  LOG(INFO, "Shutting down\n");
 }
 
 }  // namespace input
diff --git a/aos/input/joystick_input.h b/aos/input/joystick_input.h
index 98a89b1..ed72e78 100644
--- a/aos/input/joystick_input.h
+++ b/aos/input/joystick_input.h
@@ -23,10 +23,9 @@
         [this](const ::aos::JoystickState &joystick_state) {
           this->HandleData(joystick_state);
         });
+    event_loop->SetRuntimeRealtimePriority(29);
   }
 
-  void Run();
-
  protected:
   int mode() const { return mode_; }
 
@@ -36,10 +35,6 @@
   // Subclasses should do whatever they want with data here.
   virtual void RunIteration(const driver_station::Data &data) = 0;
 
-  static void Quit(int /*signum*/);
-
-  static ::std::atomic<bool> run_;
-
   EventLoop *event_loop_;
   driver_station::Data data_;
 
diff --git a/aos/util/log_interval.h b/aos/util/log_interval.h
index 69170e4..acd32e8 100644
--- a/aos/util/log_interval.h
+++ b/aos/util/log_interval.h
@@ -28,6 +28,7 @@
 
   void WantToLog() {
     if (count_ == 0) {
+      // TODO(austin): event loops!
       last_done_ = ::aos::monotonic_clock::now();
     }
     ++count_;