• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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