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 11namespace android { 12namespace pdx { 13namespace 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. 18template <typename T, std::size_t Index> 19struct ThreadLocalSlot; 20 21// Utility class to specify thread local slots using only a type. 22template <typename T> 23struct ThreadLocalTypeSlot; 24 25// Utility class to specify thread local slots using only an index. 26template <std::size_t Index> 27struct ThreadLocalIndexSlot; 28 29// Initial capacity of thread local buffer, unless otherwise specified. 30constexpr std::size_t InitialBufferCapacity = 4096; 31 32// Thread local slots for buffers used by this library to send, receive, and 33// reply to messages. 34using SendBuffer = ThreadLocalIndexSlot<0>; 35using ReceiveBuffer = ThreadLocalIndexSlot<1>; 36using 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. 41template <typename T, typename Allocator = DefaultInitializationAllocator<T>, 42 std::size_t Capacity = InitialBufferCapacity, 43 typename Slot = ThreadLocalSlot<void, 0>> 44class 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. 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. 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. 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. 93 static void FreeBuffer() { 94 if (buffer_) { 95 GetBufferGuard().reset(buffer_ = nullptr); 96 } 97 } 98 99 private: 100 friend class ThreadLocalBufferTest; 101 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 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. 125template <typename T, typename Allocator, std::size_t Capacity, typename Slot> 126thread_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