#include <memory> #include <string> #include <thread> #include <utility> #include <gtest/gtest.h> #include <pdx/rpc/message_buffer.h> namespace android { namespace pdx { namespace rpc { class ThreadLocalBufferTest { public: // Returns the unique address of the thread-local buffer. Used to test the // correct behavior of the type-based thread local storage slot mapping // mechanism. template <typename Slot> static std::uintptr_t GetSlotAddress() { return reinterpret_cast<std::uintptr_t>(&MessageBuffer<Slot>::buffer_); } // Returns the raw value of the thread local buffer. Used to test the behavior // of backing buffer initialization. template <typename Slot> static std::uintptr_t GetSlotValue() { return reinterpret_cast<std::uintptr_t>(MessageBuffer<Slot>::buffer_); } }; } // namespace rpc } // namespace pdx } // namespace android using namespace android::pdx::rpc; namespace { struct TypeTagA; struct TypeTagB; constexpr std::size_t kSendBufferIndex = 0; constexpr std::size_t kReceiveBufferIndex = 1; using SendSlotA = ThreadLocalSlot<TypeTagA, kSendBufferIndex>; using SendSlotB = ThreadLocalSlot<TypeTagB, kSendBufferIndex>; using ReceiveSlotA = ThreadLocalSlot<TypeTagA, kReceiveBufferIndex>; using ReceiveSlotB = ThreadLocalSlot<TypeTagB, kReceiveBufferIndex>; } // anonymous namespace // Tests that index and type-based thread-local slot addressing works by // checking that the slot address is the same when the same index/type // combination is used and different when different combinations are used. TEST(ThreadLocalBufferTest, TypeSlots) { auto id1 = ThreadLocalBufferTest::GetSlotAddress<SendSlotA>(); auto id2 = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotA>(); auto id3 = ThreadLocalBufferTest::GetSlotAddress<SendSlotB>(); auto id4 = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotB>(); EXPECT_NE(id1, id2); EXPECT_NE(id3, id4); EXPECT_NE(id1, id3); EXPECT_NE(id2, id4); auto id1_alias = ThreadLocalBufferTest::GetSlotAddress<SendSlotA>(); auto id2_alias = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotA>(); auto id3_alias = ThreadLocalBufferTest::GetSlotAddress<SendSlotB>(); auto id4_alias = ThreadLocalBufferTest::GetSlotAddress<ReceiveSlotB>(); EXPECT_EQ(id1, id1_alias); EXPECT_EQ(id2, id2_alias); EXPECT_EQ(id3, id3_alias); EXPECT_EQ(id4, id4_alias); } // Tests that different threads get different buffers for the same slot address. TEST(ThreadLocalBufferTest, ThreadSlots) { auto id1 = ThreadLocalBufferTest::GetSlotAddress<SendBuffer>(); std::uintptr_t id2 = 0U; std::thread thread([&id2]() mutable { id2 = ThreadLocalBufferTest::GetSlotAddress<SendBuffer>(); }); thread.join(); EXPECT_NE(0U, id1); EXPECT_NE(0U, id2); EXPECT_NE(id1, id2); } // Tests that thread-local buffers are allocated at the first buffer request. TEST(ThreadLocalBufferTest, InitialValue) { struct TypeTagX; using SendSlotX = ThreadLocalSlot<TypeTagX, kSendBufferIndex>; auto value1 = ThreadLocalBufferTest::GetSlotValue<SendSlotX>(); MessageBuffer<SendSlotX>::GetBuffer(); auto value2 = ThreadLocalBufferTest::GetSlotValue<SendSlotX>(); EXPECT_EQ(0U, value1); EXPECT_NE(0U, value2); } // Tests that the underlying buffers are the same for a given index/type pair // and different across index/type combinations. TEST(ThreadLocalBufferTest, BackingBuffer) { auto& buffer1 = MessageBuffer<SendSlotA>::GetBuffer(); auto& buffer2 = MessageBuffer<SendSlotA>::GetBuffer(); auto& buffer3 = MessageBuffer<SendSlotB>::GetBuffer(); auto& buffer4 = MessageBuffer<SendSlotB>::GetBuffer(); EXPECT_EQ(buffer1.data(), buffer2.data()); EXPECT_EQ(buffer3.data(), buffer4.data()); EXPECT_NE(buffer1.data(), buffer3.data()); EXPECT_NE(buffer2.data(), buffer4.data()); }