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