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";