Allow detaching buffers from Sender::Builders to send later

This can be helpful, because unlike the Builder itself a detached buffer
is moveable.

Change-Id: I246750fcd7622c2eed0a72172f90fe02fcc9be07
diff --git a/aos/events/channel_preallocated_allocator.h b/aos/events/channel_preallocated_allocator.h
index 8a5d68f..5ca370b 100644
--- a/aos/events/channel_preallocated_allocator.h
+++ b/aos/events/channel_preallocated_allocator.h
@@ -16,7 +16,7 @@
   ChannelPreallocatedAllocator(const ChannelPreallocatedAllocator &) = delete;
   ChannelPreallocatedAllocator(ChannelPreallocatedAllocator &&other)
       : data_(other.data_), size_(other.size_), channel_(other.channel_) {
-    CHECK(!is_allocated());
+    CHECK(!is_allocated()) << ": May not overwrite in-use allocator";
     CHECK(!other.is_allocated());
   }
 
@@ -24,7 +24,7 @@
       const ChannelPreallocatedAllocator &) = delete;
   ChannelPreallocatedAllocator &operator=(
       ChannelPreallocatedAllocator &&other) {
-    CHECK(!is_allocated());
+    CHECK(!is_allocated()) << ": May not overwrite in-use allocator";
     CHECK(!other.is_allocated());
     data_ = other.data_;
     size_ = other.size_;
diff --git a/aos/events/event_loop.h b/aos/events/event_loop.h
index c36b195..a1a617e 100644
--- a/aos/events/event_loop.h
+++ b/aos/events/event_loop.h
@@ -295,6 +295,15 @@
       CHECK(!allocator_->is_allocated()) << ": Message was not sent yet";
     }
 
+    // Detaches a buffer, for later use calling Sender::Send directly.
+    //
+    // Note that the underlying memory remains with the Sender, so creating
+    // another Builder before destroying the FlatbufferDetachedBuffer will fail.
+    FlatbufferDetachedBuffer<T> Detach(flatbuffers::Offset<T> offset) {
+      fbb_.Finish(offset);
+      return fbb_.Release();
+    }
+
    private:
     flatbuffers::FlatBufferBuilder fbb_;
     ChannelPreallocatedAllocator *allocator_;
@@ -313,6 +322,10 @@
   // Sends a prebuilt flatbuffer.
   bool Send(const Flatbuffer<T> &flatbuffer);
 
+  // Sends a prebuilt flatbuffer which was detached from a Builder created via
+  // MakeBuilder() on this object.
+  bool SendDetached(FlatbufferDetachedBuffer<T> detached);
+
   // Returns the name of the underlying queue.
   const Channel *channel() const { return sender_->channel(); }
 
diff --git a/aos/events/event_loop_param_test.cc b/aos/events/event_loop_param_test.cc
index 71bef37..378c411 100644
--- a/aos/events/event_loop_param_test.cc
+++ b/aos/events/event_loop_param_test.cc
@@ -43,6 +43,35 @@
   EXPECT_TRUE(happened);
 }
 
+// Tests that watcher can receive messages from a sender, sent via SendDetached.
+TEST_P(AbstractEventLoopTest, BasicSendDetached) {
+  auto loop1 = Make();
+  auto loop2 = MakePrimary();
+
+  aos::Sender<TestMessage> sender = loop1->MakeSender<TestMessage>("/test");
+
+  FlatbufferDetachedBuffer<TestMessage> detached =
+      flatbuffers::DetachedBuffer();
+  {
+    aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+    TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+    builder.add_value(100);
+    detached = msg.Detach(builder.Finish());
+  }
+  detached = flatbuffers::DetachedBuffer();
+  {
+    aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
+    TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
+    builder.add_value(200);
+    detached = msg.Detach(builder.Finish());
+  }
+  ASSERT_TRUE(sender.SendDetached(std::move(detached)));
+
+  auto fetcher = loop2->MakeFetcher<TestMessage>("/test");
+  ASSERT_TRUE(fetcher.Fetch());
+  EXPECT_EQ(fetcher->value(), 200);
+}
+
 // Verifies that a no-arg watcher will not have a data pointer.
 TEST_P(AbstractEventLoopTest, NoArgNoData) {
   auto loop1 = Make();
@@ -1645,5 +1674,63 @@
   // use them to create message_gateway.
 }
 
+// Tests creating multiple Builders from a single Sender at the same time.
+TEST_P(AbstractEventLoopDeathTest, MultipleBuilders) {
+  auto loop1 = Make();
+  aos::Sender<TestMessage> sender = loop1->MakeSender<TestMessage>("/test");
+
+  { auto builder = sender.MakeBuilder(); }
+  {
+    auto builder = sender.MakeBuilder();
+    builder.MakeBuilder<TestMessage>().Finish();
+  }
+  {
+    // Creating this after the first one was destroyed should be fine.
+    auto builder = sender.MakeBuilder();
+    builder.MakeBuilder<TestMessage>().Finish();
+    // But not a second one.
+    EXPECT_DEATH(sender.MakeBuilder().MakeBuilder<TestMessage>().Finish(),
+                 "May not overwrite in-use allocator");
+  }
+
+  FlatbufferDetachedBuffer<TestMessage> detached =
+      flatbuffers::DetachedBuffer();
+  {
+    auto builder = sender.MakeBuilder();
+    detached = builder.Detach(builder.MakeBuilder<TestMessage>().Finish());
+  }
+  {
+    // This is the second one, after the detached one, so it should fail.
+    EXPECT_DEATH(sender.MakeBuilder().MakeBuilder<TestMessage>().Finish(),
+                 "May not overwrite in-use allocator");
+  }
+
+  // Clear the detached one, and then we should be able to create another.
+  detached = flatbuffers::DetachedBuffer();
+  {
+    auto builder = sender.MakeBuilder();
+    builder.MakeBuilder<TestMessage>().Finish();
+  }
+
+  // And then detach another one.
+  {
+    auto builder = sender.MakeBuilder();
+    detached = builder.Detach(builder.MakeBuilder<TestMessage>().Finish());
+  }
+}
+
+// Tests sending a buffer detached from a different builder.
+TEST_P(AbstractEventLoopDeathTest, WrongDetachedBuffer) {
+  auto loop1 = Make();
+  aos::Sender<TestMessage> sender1 = loop1->MakeSender<TestMessage>("/test");
+  aos::Sender<TestMessage> sender2 = loop1->MakeSender<TestMessage>("/test");
+
+  auto builder = sender1.MakeBuilder();
+  FlatbufferDetachedBuffer<TestMessage> detached =
+      builder.Detach(builder.MakeBuilder<TestMessage>().Finish());
+  EXPECT_DEATH(sender2.SendDetached(std::move(detached)),
+               "May only send the buffer detached from this Sender");
+}
+
 }  // namespace testing
 }  // namespace aos
diff --git a/aos/events/event_loop_tmpl.h b/aos/events/event_loop_tmpl.h
index 877a946..a39a338 100644
--- a/aos/events/event_loop_tmpl.h
+++ b/aos/events/event_loop_tmpl.h
@@ -339,6 +339,15 @@
   return sender_->Send(flatbuffer.data(), flatbuffer.size());
 }
 
+template <typename T>
+bool Sender<T>::SendDetached(FlatbufferDetachedBuffer<T> detached) {
+  CHECK_EQ(
+      static_cast<void *>(detached.data() + detached.size() - sender_->size()),
+      sender_->data())
+      << ": May only send the buffer detached from this Sender";
+  return sender_->Send(detached.size());
+}
+
 }  // namespace aos
 
 #endif  // AOS_EVENTS_EVENT_LOOP_TMPL_H
diff --git a/aos/flatbuffers.h b/aos/flatbuffers.h
index 6fca458..0a06e00 100644
--- a/aos/flatbuffers.h
+++ b/aos/flatbuffers.h
@@ -61,13 +61,13 @@
   PreallocatedAllocator(const PreallocatedAllocator &) = delete;
   PreallocatedAllocator(PreallocatedAllocator &&other)
       : data_(other.data_), size_(other.size_) {
-    CHECK(!is_allocated());
+    CHECK(!is_allocated()) << ": May not overwrite in-use allocator";
     CHECK(!other.is_allocated());
   }
 
   PreallocatedAllocator &operator=(const PreallocatedAllocator &) = delete;
   PreallocatedAllocator &operator=(PreallocatedAllocator &&other) {
-    CHECK(!is_allocated());
+    CHECK(!is_allocated()) << ": May not overwrite in-use allocator";
     CHECK(!other.is_allocated());
     data_ = other.data_;
     size_ = other.size_;
@@ -242,6 +242,12 @@
     builder_.ForceDefaults(true);
   }
 
+  void Reset() {
+    allocator_.Reset();
+    builder_ = flatbuffers::FlatBufferBuilder(Size, &allocator_);
+    builder_.ForceDefaults(true);
+  }
+
   flatbuffers::FlatBufferBuilder *Builder() {
     if (allocator_.allocated()) {
       LOG(FATAL) << "Array backed flatbuffer can only be built once";