Create an ExitHandle interface

This is a safer alternative to our existing pattern of capturing the
pointer in a lambda, and it's more Rust-friendly.

Change-Id: Id0f3fe5a2badcf1a4ae871d0cc7c3ff48d1c22f8
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
diff --git a/aos/events/simulated_event_loop_test.cc b/aos/events/simulated_event_loop_test.cc
index 1a35b69..eddacb1 100644
--- a/aos/events/simulated_event_loop_test.cc
+++ b/aos/events/simulated_event_loop_test.cc
@@ -2076,6 +2076,19 @@
   EXPECT_DEATH({ factory.RunFor(dt * 2); }, "Event loop");
 }
 
+// Test that an ExitHandle outliving its factory is caught.
+TEST(SimulatedEventLoopDeathTest, ExitHandleOutlivesFactory) {
+  aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+      aos::configuration::ReadConfig(
+          ArtifactPath("aos/events/multinode_pingpong_test_split_config.json"));
+  auto factory = std::make_unique<SimulatedEventLoopFactory>(&config.message());
+  NodeEventLoopFactory *pi1 = factory->GetNodeEventLoopFactory("pi1");
+  std::unique_ptr<EventLoop> loop = pi1->MakeEventLoop("foo");
+  auto exit_handle = factory->MakeExitHandle();
+  EXPECT_DEATH(factory.reset(),
+               "All ExitHandles must be destroyed before the factory");
+}
+
 // Tests that messages don't survive a reboot of a node.
 TEST(SimulatedEventLoopTest, ChannelClearedOnReboot) {
   aos::FlatbufferDetachedBuffer<aos::Configuration> config =