1 #ifndef ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_ 2 #define ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_ 3 4 #include <cstdint> 5 #include <memory> 6 #include <vector> 7 8 #include <pdx/rpc/default_initialization_allocator.h> 9 #include <pdx/trace.h> 10 11 namespace android { 12 namespace pdx { 13 namespace rpc { 14 15 // Utility class to distinguish between different thread local entries or 16 // "slots" in the thread local variable table. Each slot is uniquely identified 17 // by (T,Index) and is independent of any other slot. 18 template <typename T, std::size_t Index> 19 struct ThreadLocalSlot; 20 21 // Utility class to specify thread local slots using only a type. 22 template <typename T> 23 struct ThreadLocalTypeSlot; 24 25 // Utility class to specify thread local slots using only an index. 26 template <std::size_t Index> 27 struct ThreadLocalIndexSlot; 28 29 // Initial capacity of thread local buffer, unless otherwise specified. 30 constexpr std::size_t InitialBufferCapacity = 4096; 31 32 // Thread local slots for buffers used by this library to send, receive, and 33 // reply to messages. 34 using SendBuffer = ThreadLocalIndexSlot<0>; 35 using ReceiveBuffer = ThreadLocalIndexSlot<1>; 36 using ReplyBuffer = ThreadLocalIndexSlot<2>; 37 38 // Provides a simple interface to thread local buffers for large IPC messages. 39 // Slot provides multiple thread local slots for a given T, Allocator, Capacity 40 // combination. 41 template <typename T, typename Allocator = DefaultInitializationAllocator<T>, 42 std::size_t Capacity = InitialBufferCapacity, 43 typename Slot = ThreadLocalSlot<void, 0>> 44 class ThreadLocalBuffer { 45 public: 46 using BufferType = std::vector<T, Allocator>; 47 using ValueType = T; 48 49 // Reserves |capacity| number of elements of capacity in the underlying 50 // buffer. Call this during startup to avoid allocation during use. Reserve(std::size_t capacity)51 static void Reserve(std::size_t capacity) { 52 PDX_TRACE_NAME("ThreadLocalBuffer::Reserve"); 53 InitializeBuffer(capacity); 54 buffer_->reserve(capacity); 55 } 56 57 // Resizes the buffer to |size| elements. Resize(std::size_t size)58 static void Resize(std::size_t size) { 59 PDX_TRACE_NAME("ThreadLocalBuffer::Resize"); 60 InitializeBuffer(size); 61 buffer_->resize(size); 62 } 63 64 // Gets a reference to the underlying buffer after reserving |capacity| 65 // elements. The current size of the buffer is left intact. The returned 66 // reference is valid until FreeBuffer() is called. 67 static BufferType& GetBuffer(std::size_t capacity = Capacity) { 68 PDX_TRACE_NAME("ThreadLocalBuffer::GetBuffer"); 69 Reserve(capacity); 70 return *buffer_; 71 } 72 73 // Gets a reference to the underlying buffer after reserving |Capacity| 74 // elements. The current size of the buffer is set to zero. The returned 75 // reference is valid until FreeBuffer() is called. GetEmptyBuffer()76 static BufferType& GetEmptyBuffer() { 77 PDX_TRACE_NAME("ThreadLocalBuffer::GetEmptyBuffer"); 78 Reserve(Capacity); 79 buffer_->clear(); 80 return *buffer_; 81 } 82 83 // Gets a reference to the underlying buffer after resizing it to |size| 84 // elements. The returned reference is valid until FreeBuffer() is called. 85 static BufferType& GetSizedBuffer(std::size_t size = Capacity) { 86 PDX_TRACE_NAME("ThreadLocalBuffer::GetSizedBuffer"); 87 Resize(size); 88 return *buffer_; 89 } 90 91 // Frees the underlying buffer. The buffer will be reallocated if any of the 92 // methods above are called. FreeBuffer()93 static void FreeBuffer() { 94 if (buffer_) { 95 GetBufferGuard().reset(buffer_ = nullptr); 96 } 97 } 98 99 private: 100 friend class ThreadLocalBufferTest; 101 InitializeBuffer(std::size_t capacity)102 static void InitializeBuffer(std::size_t capacity) { 103 if (!buffer_) { 104 GetBufferGuard().reset(buffer_ = new BufferType(capacity)); 105 } 106 } 107 108 // Work around performance issues with thread-local dynamic initialization 109 // semantics by using a normal pointer in parallel with a std::unique_ptr. The 110 // std::unique_ptr is never dereferenced, only assigned, to avoid the high 111 // cost of dynamic initialization checks, while still providing automatic 112 // cleanup. The normal pointer provides fast access to the buffer object. 113 // Never dereference buffer_guard or performance could be severely impacted 114 // by slow implementations of TLS dynamic initialization. 115 static thread_local BufferType* buffer_; 116 GetBufferGuard()117 static std::unique_ptr<BufferType>& GetBufferGuard() { 118 PDX_TRACE_NAME("ThreadLocalBuffer::GetBufferGuard"); 119 static thread_local std::unique_ptr<BufferType> buffer_guard; 120 return buffer_guard; 121 } 122 }; 123 124 // Instantiation of the static ThreadLocalBuffer::buffer_ member. 125 template <typename T, typename Allocator, std::size_t Capacity, typename Slot> 126 thread_local 127 typename ThreadLocalBuffer<T, Allocator, Capacity, Slot>::BufferType* 128 ThreadLocalBuffer<T, Allocator, Capacity, Slot>::buffer_; 129 130 } // namespace rpc 131 } // namespace pdx 132 } // namespace android 133 134 #endif // ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_ 135