blob: e5ef2aa787921feeb0b1b1cea7d8a4dab245d13f [file] [log] [blame]
#ifndef ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_
#define ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_
#include <cstdint>
#include <memory>
#include <vector>
#include <pdx/rpc/default_initialization_allocator.h>
#include <pdx/trace.h>
namespace android {
namespace pdx {
namespace rpc {
// Utility class to distinguish between different thread local entries or
// "slots" in the thread local variable table. Each slot is uniquely identified
// by (T,Index) and is independent of any other slot.
template <typename T, std::size_t Index>
struct ThreadLocalSlot;
// Utility class to specify thread local slots using only a type.
template <typename T>
struct ThreadLocalTypeSlot;
// Utility class to specify thread local slots using only an index.
template <std::size_t Index>
struct ThreadLocalIndexSlot;
// Initial capacity of thread local buffer, unless otherwise specified.
constexpr std::size_t InitialBufferCapacity = 4096;
// Thread local slots for buffers used by this library to send, receive, and
// reply to messages.
using SendBuffer = ThreadLocalIndexSlot<0>;
using ReceiveBuffer = ThreadLocalIndexSlot<1>;
using ReplyBuffer = ThreadLocalIndexSlot<2>;
// Provides a simple interface to thread local buffers for large IPC messages.
// Slot provides multiple thread local slots for a given T, Allocator, Capacity
// combination.
template <typename T, typename Allocator = DefaultInitializationAllocator<T>,
std::size_t Capacity = InitialBufferCapacity,
typename Slot = ThreadLocalSlot<void, 0>>
class ThreadLocalBuffer {
public:
using BufferType = std::vector<T, Allocator>;
using ValueType = T;
// Reserves |capacity| number of elements of capacity in the underlying
// buffer. Call this during startup to avoid allocation during use.
static void Reserve(std::size_t capacity) {
PDX_TRACE_NAME("ThreadLocalBuffer::Reserve");
InitializeBuffer(capacity);
buffer_->reserve(capacity);
}
// Resizes the buffer to |size| elements.
static void Resize(std::size_t size) {
PDX_TRACE_NAME("ThreadLocalBuffer::Resize");
InitializeBuffer(size);
buffer_->resize(size);
}
// Gets a reference to the underlying buffer after reserving |capacity|
// elements. The current size of the buffer is left intact. The returned
// reference is valid until FreeBuffer() is called.
static BufferType& GetBuffer(std::size_t capacity = Capacity) {
PDX_TRACE_NAME("ThreadLocalBuffer::GetBuffer");
Reserve(capacity);
return *buffer_;
}
// Gets a reference to the underlying buffer after reserving |Capacity|
// elements. The current size of the buffer is set to zero. The returned
// reference is valid until FreeBuffer() is called.
static BufferType& GetEmptyBuffer() {
PDX_TRACE_NAME("ThreadLocalBuffer::GetEmptyBuffer");
Reserve(Capacity);
buffer_->clear();
return *buffer_;
}
// Gets a reference to the underlying buffer after resizing it to |size|
// elements. The returned reference is valid until FreeBuffer() is called.
static BufferType& GetSizedBuffer(std::size_t size = Capacity) {
PDX_TRACE_NAME("ThreadLocalBuffer::GetSizedBuffer");
Resize(size);
return *buffer_;
}
// Frees the underlying buffer. The buffer will be reallocated if any of the
// methods above are called.
static void FreeBuffer() {
if (buffer_) {
GetBufferGuard().reset(buffer_ = nullptr);
}
}
private:
friend class ThreadLocalBufferTest;
static void InitializeBuffer(std::size_t capacity) {
if (!buffer_) {
GetBufferGuard().reset(buffer_ = new BufferType(capacity));
}
}
// Work around performance issues with thread-local dynamic initialization
// semantics by using a normal pointer in parallel with a std::unique_ptr. The
// std::unique_ptr is never dereferenced, only assigned, to avoid the high
// cost of dynamic initialization checks, while still providing automatic
// cleanup. The normal pointer provides fast access to the buffer object.
// Never dereference buffer_guard or performance could be severely impacted
// by slow implementations of TLS dynamic initialization.
static thread_local BufferType* buffer_;
static std::unique_ptr<BufferType>& GetBufferGuard() {
PDX_TRACE_NAME("ThreadLocalBuffer::GetBufferGuard");
static thread_local std::unique_ptr<BufferType> buffer_guard;
return buffer_guard;
}
};
// Instantiation of the static ThreadLocalBuffer::buffer_ member.
template <typename T, typename Allocator, std::size_t Capacity, typename Slot>
thread_local
typename ThreadLocalBuffer<T, Allocator, Capacity, Slot>::BufferType*
ThreadLocalBuffer<T, Allocator, Capacity, Slot>::buffer_;
} // namespace rpc
} // namespace pdx
} // namespace android
#endif // ANDROID_PDX_RPC_THREAD_LOCAL_BUFFER_H_