You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

arena_impl.h 25 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2008 Google Inc. All rights reserved.
  3. // https://developers.google.com/protocol-buffers/
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are
  7. // met:
  8. //
  9. // * Redistributions of source code must retain the above copyright
  10. // notice, this list of conditions and the following disclaimer.
  11. // * Redistributions in binary form must reproduce the above
  12. // copyright notice, this list of conditions and the following disclaimer
  13. // in the documentation and/or other materials provided with the
  14. // distribution.
  15. // * Neither the name of Google Inc. nor the names of its
  16. // contributors may be used to endorse or promote products derived from
  17. // this software without specific prior written permission.
  18. //
  19. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. // This file defines an Arena allocator for better allocation performance.
  31. #ifndef GOOGLE_PROTOBUF_ARENA_IMPL_H__
  32. #define GOOGLE_PROTOBUF_ARENA_IMPL_H__
  33. #include <atomic>
  34. #include <limits>
  35. #include <typeinfo>
  36. #include <google/protobuf/stubs/common.h>
  37. #include <google/protobuf/stubs/logging.h>
  38. #include <google/protobuf/stubs/port.h>
  39. #ifdef ADDRESS_SANITIZER
  40. #include <sanitizer/asan_interface.h>
  41. #endif // ADDRESS_SANITIZER
  42. #include <google/protobuf/arenaz_sampler.h>
  43. // Must be included last.
  44. #include <google/protobuf/port_def.inc>
  45. namespace google {
  46. namespace protobuf {
  47. namespace internal {
  48. // To prevent sharing cache lines between threads
  49. #ifdef __cpp_aligned_new
  50. enum { kCacheAlignment = 64 };
  51. #else
  52. enum { kCacheAlignment = alignof(max_align_t) }; // do the best we can
  53. #endif
  54. inline constexpr size_t AlignUpTo8(size_t n) {
  55. // Align n to next multiple of 8 (from Hacker's Delight, Chapter 3.)
  56. return (n + 7) & static_cast<size_t>(-8);
  57. }
  58. using LifecycleIdAtomic = uint64_t;
  59. // MetricsCollector collects stats for a particular arena.
  60. class PROTOBUF_EXPORT ArenaMetricsCollector {
  61. public:
  62. ArenaMetricsCollector(bool record_allocs) : record_allocs_(record_allocs) {}
  63. // Invoked when the arena is about to be destroyed. This method will
  64. // typically finalize any metric collection and delete the collector.
  65. // space_allocated is the space used by the arena.
  66. virtual void OnDestroy(uint64_t space_allocated) = 0;
  67. // OnReset() is called when the associated arena is reset.
  68. // space_allocated is the space used by the arena just before the reset.
  69. virtual void OnReset(uint64_t space_allocated) = 0;
  70. // OnAlloc is called when an allocation happens.
  71. // type_info is promised to be static - its lifetime extends to
  72. // match program's lifetime (It is given by typeid operator).
  73. // Note: typeid(void) will be passed as allocated_type every time we
  74. // intentionally want to avoid monitoring an allocation. (i.e. internal
  75. // allocations for managing the arena)
  76. virtual void OnAlloc(const std::type_info* allocated_type,
  77. uint64_t alloc_size) = 0;
  78. // Does OnAlloc() need to be called? If false, metric collection overhead
  79. // will be reduced since we will not do extra work per allocation.
  80. bool RecordAllocs() { return record_allocs_; }
  81. protected:
  82. // This class is destructed by the call to OnDestroy().
  83. ~ArenaMetricsCollector() = default;
  84. const bool record_allocs_;
  85. };
  86. struct AllocationPolicy {
  87. static constexpr size_t kDefaultStartBlockSize = 256;
  88. static constexpr size_t kDefaultMaxBlockSize = 8192;
  89. size_t start_block_size = kDefaultStartBlockSize;
  90. size_t max_block_size = kDefaultMaxBlockSize;
  91. void* (*block_alloc)(size_t) = nullptr;
  92. void (*block_dealloc)(void*, size_t) = nullptr;
  93. ArenaMetricsCollector* metrics_collector = nullptr;
  94. bool IsDefault() const {
  95. return start_block_size == kDefaultMaxBlockSize &&
  96. max_block_size == kDefaultMaxBlockSize && block_alloc == nullptr &&
  97. block_dealloc == nullptr && metrics_collector == nullptr;
  98. }
  99. };
  100. // Tagged pointer to an AllocationPolicy.
  101. class TaggedAllocationPolicyPtr {
  102. public:
  103. constexpr TaggedAllocationPolicyPtr() : policy_(0) {}
  104. explicit TaggedAllocationPolicyPtr(AllocationPolicy* policy)
  105. : policy_(reinterpret_cast<uintptr_t>(policy)) {}
  106. void set_policy(AllocationPolicy* policy) {
  107. auto bits = policy_ & kTagsMask;
  108. policy_ = reinterpret_cast<uintptr_t>(policy) | bits;
  109. }
  110. AllocationPolicy* get() {
  111. return reinterpret_cast<AllocationPolicy*>(policy_ & kPtrMask);
  112. }
  113. const AllocationPolicy* get() const {
  114. return reinterpret_cast<const AllocationPolicy*>(policy_ & kPtrMask);
  115. }
  116. AllocationPolicy& operator*() { return *get(); }
  117. const AllocationPolicy& operator*() const { return *get(); }
  118. AllocationPolicy* operator->() { return get(); }
  119. const AllocationPolicy* operator->() const { return get(); }
  120. bool is_user_owned_initial_block() const {
  121. return static_cast<bool>(get_mask<kUserOwnedInitialBlock>());
  122. }
  123. void set_is_user_owned_initial_block(bool v) {
  124. set_mask<kUserOwnedInitialBlock>(v);
  125. }
  126. bool should_record_allocs() const {
  127. return static_cast<bool>(get_mask<kRecordAllocs>());
  128. }
  129. void set_should_record_allocs(bool v) { set_mask<kRecordAllocs>(v); }
  130. uintptr_t get_raw() const { return policy_; }
  131. inline void RecordAlloc(const std::type_info* allocated_type,
  132. size_t n) const {
  133. get()->metrics_collector->OnAlloc(allocated_type, n);
  134. }
  135. private:
  136. enum : uintptr_t {
  137. kUserOwnedInitialBlock = 1,
  138. kRecordAllocs = 2,
  139. };
  140. static constexpr uintptr_t kTagsMask = 7;
  141. static constexpr uintptr_t kPtrMask = ~kTagsMask;
  142. template <uintptr_t kMask>
  143. uintptr_t get_mask() const {
  144. return policy_ & kMask;
  145. }
  146. template <uintptr_t kMask>
  147. void set_mask(bool v) {
  148. if (v) {
  149. policy_ |= kMask;
  150. } else {
  151. policy_ &= ~kMask;
  152. }
  153. }
  154. uintptr_t policy_;
  155. };
  156. enum class AllocationClient { kDefault, kArray };
  157. // A simple arena allocator. Calls to allocate functions must be properly
  158. // serialized by the caller, hence this class cannot be used as a general
  159. // purpose allocator in a multi-threaded program. It serves as a building block
  160. // for ThreadSafeArena, which provides a thread-safe arena allocator.
  161. //
  162. // This class manages
  163. // 1) Arena bump allocation + owning memory blocks.
  164. // 2) Maintaining a cleanup list.
  165. // It delagetes the actual memory allocation back to ThreadSafeArena, which
  166. // contains the information on block growth policy and backing memory allocation
  167. // used.
  168. class PROTOBUF_EXPORT SerialArena {
  169. public:
  170. struct Memory {
  171. void* ptr;
  172. size_t size;
  173. };
  174. // Node contains the ptr of the object to be cleaned up and the associated
  175. // cleanup function ptr.
  176. struct CleanupNode {
  177. void* elem; // Pointer to the object to be cleaned up.
  178. void (*cleanup)(void*); // Function pointer to the destructor or deleter.
  179. };
  180. void CleanupList();
  181. uint64_t SpaceAllocated() const {
  182. return space_allocated_.load(std::memory_order_relaxed);
  183. }
  184. uint64_t SpaceUsed() const;
  185. bool HasSpace(size_t n) const {
  186. return n <= static_cast<size_t>(limit_ - ptr_);
  187. }
  188. // See comments on `cached_blocks_` member for details.
  189. PROTOBUF_ALWAYS_INLINE void* TryAllocateFromCachedBlock(size_t size) {
  190. if (PROTOBUF_PREDICT_FALSE(size < 16)) return nullptr;
  191. // We round up to the next larger block in case the memory doesn't match
  192. // the pattern we are looking for.
  193. const size_t index = Bits::Log2FloorNonZero64(size - 1) - 3;
  194. if (index >= cached_block_length_) return nullptr;
  195. auto& cached_head = cached_blocks_[index];
  196. if (cached_head == nullptr) return nullptr;
  197. void* ret = cached_head;
  198. #ifdef ADDRESS_SANITIZER
  199. ASAN_UNPOISON_MEMORY_REGION(ret, size);
  200. #endif // ADDRESS_SANITIZER
  201. cached_head = cached_head->next;
  202. return ret;
  203. }
  204. // In kArray mode we look through cached blocks.
  205. // We do not do this by default because most non-array allocations will not
  206. // have the right size and will fail to find an appropriate cached block.
  207. //
  208. // TODO(sbenza): Evaluate if we should use cached blocks for message types of
  209. // the right size. We can statically know if the allocation size can benefit
  210. // from it.
  211. template <AllocationClient alloc_client = AllocationClient::kDefault>
  212. void* AllocateAligned(size_t n, const AllocationPolicy* policy) {
  213. GOOGLE_DCHECK_EQ(internal::AlignUpTo8(n), n); // Must be already aligned.
  214. GOOGLE_DCHECK_GE(limit_, ptr_);
  215. if (alloc_client == AllocationClient::kArray) {
  216. if (void* res = TryAllocateFromCachedBlock(n)) {
  217. return res;
  218. }
  219. }
  220. if (PROTOBUF_PREDICT_FALSE(!HasSpace(n))) {
  221. return AllocateAlignedFallback(n, policy);
  222. }
  223. return AllocateFromExisting(n);
  224. }
  225. private:
  226. void* AllocateFromExisting(size_t n) {
  227. void* ret = ptr_;
  228. ptr_ += n;
  229. #ifdef ADDRESS_SANITIZER
  230. ASAN_UNPOISON_MEMORY_REGION(ret, n);
  231. #endif // ADDRESS_SANITIZER
  232. return ret;
  233. }
  234. // See comments on `cached_blocks_` member for details.
  235. void ReturnArrayMemory(void* p, size_t size) {
  236. // We only need to check for 32-bit platforms.
  237. // In 64-bit platforms the minimum allocation size from Repeated*Field will
  238. // be 16 guaranteed.
  239. if (sizeof(void*) < 8) {
  240. if (PROTOBUF_PREDICT_FALSE(size < 16)) return;
  241. } else {
  242. GOOGLE_DCHECK(size >= 16);
  243. }
  244. // We round down to the next smaller block in case the memory doesn't match
  245. // the pattern we are looking for. eg, someone might have called Reserve()
  246. // on the repeated field.
  247. const size_t index = Bits::Log2FloorNonZero64(size) - 4;
  248. if (PROTOBUF_PREDICT_FALSE(index >= cached_block_length_)) {
  249. // We can't put this object on the freelist so make this object the
  250. // freelist. It is guaranteed it is larger than the one we have, and
  251. // large enough to hold another allocation of `size`.
  252. CachedBlock** new_list = static_cast<CachedBlock**>(p);
  253. size_t new_size = size / sizeof(CachedBlock*);
  254. std::copy(cached_blocks_, cached_blocks_ + cached_block_length_,
  255. new_list);
  256. std::fill(new_list + cached_block_length_, new_list + new_size, nullptr);
  257. cached_blocks_ = new_list;
  258. // Make the size fit in uint8_t. This is the power of two, so we don't
  259. // need anything larger.
  260. cached_block_length_ =
  261. static_cast<uint8_t>(std::min(size_t{64}, new_size));
  262. return;
  263. }
  264. auto& cached_head = cached_blocks_[index];
  265. auto* new_node = static_cast<CachedBlock*>(p);
  266. new_node->next = cached_head;
  267. cached_head = new_node;
  268. #ifdef ADDRESS_SANITIZER
  269. ASAN_POISON_MEMORY_REGION(p, size);
  270. #endif // ADDRESS_SANITIZER
  271. }
  272. public:
  273. // Allocate space if the current region provides enough space.
  274. bool MaybeAllocateAligned(size_t n, void** out) {
  275. GOOGLE_DCHECK_EQ(internal::AlignUpTo8(n), n); // Must be already aligned.
  276. GOOGLE_DCHECK_GE(limit_, ptr_);
  277. if (PROTOBUF_PREDICT_FALSE(!HasSpace(n))) return false;
  278. *out = AllocateFromExisting(n);
  279. return true;
  280. }
  281. std::pair<void*, CleanupNode*> AllocateAlignedWithCleanup(
  282. size_t n, const AllocationPolicy* policy) {
  283. GOOGLE_DCHECK_EQ(internal::AlignUpTo8(n), n); // Must be already aligned.
  284. if (PROTOBUF_PREDICT_FALSE(!HasSpace(n + kCleanupSize))) {
  285. return AllocateAlignedWithCleanupFallback(n, policy);
  286. }
  287. return AllocateFromExistingWithCleanupFallback(n);
  288. }
  289. private:
  290. std::pair<void*, CleanupNode*> AllocateFromExistingWithCleanupFallback(
  291. size_t n) {
  292. void* ret = ptr_;
  293. ptr_ += n;
  294. limit_ -= kCleanupSize;
  295. #ifdef ADDRESS_SANITIZER
  296. ASAN_UNPOISON_MEMORY_REGION(ret, n);
  297. ASAN_UNPOISON_MEMORY_REGION(limit_, kCleanupSize);
  298. #endif // ADDRESS_SANITIZER
  299. return CreatePair(ret, reinterpret_cast<CleanupNode*>(limit_));
  300. }
  301. public:
  302. void AddCleanup(void* elem, void (*cleanup)(void*),
  303. const AllocationPolicy* policy) {
  304. auto res = AllocateAlignedWithCleanup(0, policy);
  305. res.second->elem = elem;
  306. res.second->cleanup = cleanup;
  307. }
  308. void* owner() const { return owner_; }
  309. SerialArena* next() const { return next_; }
  310. void set_next(SerialArena* next) { next_ = next; }
  311. private:
  312. friend class ThreadSafeArena;
  313. friend class ArenaBenchmark;
  314. // Creates a new SerialArena inside mem using the remaining memory as for
  315. // future allocations.
  316. static SerialArena* New(SerialArena::Memory mem, void* owner,
  317. ThreadSafeArenaStats* stats);
  318. // Free SerialArena returning the memory passed in to New
  319. template <typename Deallocator>
  320. Memory Free(Deallocator deallocator);
  321. // Blocks are variable length malloc-ed objects. The following structure
  322. // describes the common header for all blocks.
  323. struct Block {
  324. Block(Block* next, size_t size) : next(next), size(size), start(nullptr) {}
  325. char* Pointer(size_t n) {
  326. GOOGLE_DCHECK(n <= size);
  327. return reinterpret_cast<char*>(this) + n;
  328. }
  329. Block* const next;
  330. const size_t size;
  331. CleanupNode* start;
  332. // data follows
  333. };
  334. void* owner_; // &ThreadCache of this thread;
  335. Block* head_; // Head of linked list of blocks.
  336. SerialArena* next_; // Next SerialArena in this linked list.
  337. size_t space_used_ = 0; // Necessary for metrics.
  338. std::atomic<size_t> space_allocated_;
  339. // Next pointer to allocate from. Always 8-byte aligned. Points inside
  340. // head_ (and head_->pos will always be non-canonical). We keep these
  341. // here to reduce indirection.
  342. char* ptr_;
  343. // Limiting address up to which memory can be allocated from the head block.
  344. char* limit_;
  345. // For holding sampling information. The pointer is owned by the
  346. // ThreadSafeArena that holds this serial arena.
  347. ThreadSafeArenaStats* arena_stats_;
  348. // Repeated*Field and Arena play together to reduce memory consumption by
  349. // reusing blocks. Currently, natural growth of the repeated field types makes
  350. // them allocate blocks of size `8 + 2^N, N>=3`.
  351. // When the repeated field grows returns the previous block and we put it in
  352. // this free list.
  353. // `cached_blocks_[i]` points to the free list for blocks of size `8+2^(i+3)`.
  354. // The array of freelists is grown when needed in `ReturnArrayMemory()`.
  355. struct CachedBlock {
  356. // Simple linked list.
  357. CachedBlock* next;
  358. };
  359. uint8_t cached_block_length_ = 0;
  360. CachedBlock** cached_blocks_ = nullptr;
  361. // Constructor is private as only New() should be used.
  362. inline SerialArena(Block* b, void* owner, ThreadSafeArenaStats* stats);
  363. void* AllocateAlignedFallback(size_t n, const AllocationPolicy* policy);
  364. std::pair<void*, CleanupNode*> AllocateAlignedWithCleanupFallback(
  365. size_t n, const AllocationPolicy* policy);
  366. void AllocateNewBlock(size_t n, const AllocationPolicy* policy);
  367. std::pair<void*, CleanupNode*> CreatePair(void* ptr, CleanupNode* node) {
  368. return {ptr, node};
  369. }
  370. public:
  371. static constexpr size_t kBlockHeaderSize = AlignUpTo8(sizeof(Block));
  372. static constexpr size_t kCleanupSize = AlignUpTo8(sizeof(CleanupNode));
  373. };
  374. // Tag type used to invoke the constructor of message-owned arena.
  375. // Only message-owned arenas use this constructor for creation.
  376. // Such constructors are internal implementation details of the library.
  377. struct MessageOwned {
  378. explicit MessageOwned() = default;
  379. };
  380. // This class provides the core Arena memory allocation library. Different
  381. // implementations only need to implement the public interface below.
  382. // Arena is not a template type as that would only be useful if all protos
  383. // in turn would be templates, which will/cannot happen. However separating
  384. // the memory allocation part from the cruft of the API users expect we can
  385. // use #ifdef the select the best implementation based on hardware / OS.
  386. class PROTOBUF_EXPORT ThreadSafeArena {
  387. public:
  388. ThreadSafeArena() { Init(); }
  389. // Constructor solely used by message-owned arena.
  390. ThreadSafeArena(internal::MessageOwned) : tag_and_id_(kMessageOwnedArena) {
  391. Init();
  392. }
  393. ThreadSafeArena(char* mem, size_t size) { InitializeFrom(mem, size); }
  394. explicit ThreadSafeArena(void* mem, size_t size,
  395. const AllocationPolicy& policy) {
  396. InitializeWithPolicy(mem, size, policy);
  397. }
  398. // Destructor deletes all owned heap allocated objects, and destructs objects
  399. // that have non-trivial destructors, except for proto2 message objects whose
  400. // destructors can be skipped. Also, frees all blocks except the initial block
  401. // if it was passed in.
  402. ~ThreadSafeArena();
  403. uint64_t Reset();
  404. uint64_t SpaceAllocated() const;
  405. uint64_t SpaceUsed() const;
  406. template <AllocationClient alloc_client = AllocationClient::kDefault>
  407. void* AllocateAligned(size_t n, const std::type_info* type) {
  408. SerialArena* arena;
  409. if (PROTOBUF_PREDICT_TRUE(!alloc_policy_.should_record_allocs() &&
  410. GetSerialArenaFast(&arena))) {
  411. return arena->AllocateAligned<alloc_client>(n, AllocPolicy());
  412. } else {
  413. return AllocateAlignedFallback(n, type);
  414. }
  415. }
  416. void ReturnArrayMemory(void* p, size_t size) {
  417. SerialArena* arena;
  418. if (PROTOBUF_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
  419. arena->ReturnArrayMemory(p, size);
  420. }
  421. }
  422. // This function allocates n bytes if the common happy case is true and
  423. // returns true. Otherwise does nothing and returns false. This strange
  424. // semantics is necessary to allow callers to program functions that only
  425. // have fallback function calls in tail position. This substantially improves
  426. // code for the happy path.
  427. PROTOBUF_NDEBUG_INLINE bool MaybeAllocateAligned(size_t n, void** out) {
  428. SerialArena* arena;
  429. if (PROTOBUF_PREDICT_TRUE(!alloc_policy_.should_record_allocs() &&
  430. GetSerialArenaFromThreadCache(&arena))) {
  431. return arena->MaybeAllocateAligned(n, out);
  432. }
  433. return false;
  434. }
  435. std::pair<void*, SerialArena::CleanupNode*> AllocateAlignedWithCleanup(
  436. size_t n, const std::type_info* type);
  437. // Add object pointer and cleanup function pointer to the list.
  438. void AddCleanup(void* elem, void (*cleanup)(void*));
  439. // Checks whether this arena is message-owned.
  440. PROTOBUF_ALWAYS_INLINE bool IsMessageOwned() const {
  441. return tag_and_id_ & kMessageOwnedArena;
  442. }
  443. private:
  444. // Unique for each arena. Changes on Reset().
  445. uint64_t tag_and_id_ = 0;
  446. // The LSB of tag_and_id_ indicates if the arena is message-owned.
  447. enum : uint64_t { kMessageOwnedArena = 1 };
  448. TaggedAllocationPolicyPtr alloc_policy_; // Tagged pointer to AllocPolicy.
  449. static_assert(std::is_trivially_destructible<SerialArena>{},
  450. "SerialArena needs to be trivially destructible.");
  451. // Pointer to a linked list of SerialArena.
  452. std::atomic<SerialArena*> threads_;
  453. std::atomic<SerialArena*> hint_; // Fast thread-local block access
  454. const AllocationPolicy* AllocPolicy() const { return alloc_policy_.get(); }
  455. void InitializeFrom(void* mem, size_t size);
  456. void InitializeWithPolicy(void* mem, size_t size, AllocationPolicy policy);
  457. void* AllocateAlignedFallback(size_t n, const std::type_info* type);
  458. std::pair<void*, SerialArena::CleanupNode*>
  459. AllocateAlignedWithCleanupFallback(size_t n, const std::type_info* type);
  460. void Init();
  461. void SetInitialBlock(void* mem, size_t size);
  462. // Delete or Destruct all objects owned by the arena.
  463. void CleanupList();
  464. inline uint64_t LifeCycleId() const {
  465. return tag_and_id_ & ~kMessageOwnedArena;
  466. }
  467. inline void CacheSerialArena(SerialArena* serial) {
  468. thread_cache().last_serial_arena = serial;
  469. thread_cache().last_lifecycle_id_seen = tag_and_id_;
  470. // TODO(haberman): evaluate whether we would gain efficiency by getting rid
  471. // of hint_. It's the only write we do to ThreadSafeArena in the allocation
  472. // path, which will dirty the cache line.
  473. hint_.store(serial, std::memory_order_release);
  474. }
  475. PROTOBUF_NDEBUG_INLINE bool GetSerialArenaFast(SerialArena** arena) {
  476. if (GetSerialArenaFromThreadCache(arena)) return true;
  477. // Check whether we own the last accessed SerialArena on this arena. This
  478. // fast path optimizes the case where a single thread uses multiple arenas.
  479. ThreadCache* tc = &thread_cache();
  480. SerialArena* serial = hint_.load(std::memory_order_acquire);
  481. if (PROTOBUF_PREDICT_TRUE(serial != nullptr && serial->owner() == tc)) {
  482. *arena = serial;
  483. return true;
  484. }
  485. return false;
  486. }
  487. PROTOBUF_NDEBUG_INLINE bool GetSerialArenaFromThreadCache(
  488. SerialArena** arena) {
  489. // If this thread already owns a block in this arena then try to use that.
  490. // This fast path optimizes the case where multiple threads allocate from
  491. // the same arena.
  492. ThreadCache* tc = &thread_cache();
  493. if (PROTOBUF_PREDICT_TRUE(tc->last_lifecycle_id_seen == tag_and_id_)) {
  494. *arena = tc->last_serial_arena;
  495. return true;
  496. }
  497. return false;
  498. }
  499. SerialArena* GetSerialArenaFallback(void* me);
  500. template <typename Functor>
  501. void PerSerialArena(Functor fn) {
  502. // By omitting an Acquire barrier we ensure that any user code that doesn't
  503. // properly synchronize Reset() or the destructor will throw a TSAN warning.
  504. SerialArena* serial = threads_.load(std::memory_order_relaxed);
  505. for (; serial; serial = serial->next()) fn(serial);
  506. }
  507. // Releases all memory except the first block which it returns. The first
  508. // block might be owned by the user and thus need some extra checks before
  509. // deleting.
  510. SerialArena::Memory Free(size_t* space_allocated);
  511. #ifdef _MSC_VER
  512. #pragma warning(disable : 4324)
  513. #endif
  514. struct alignas(kCacheAlignment) ThreadCache {
  515. #if defined(GOOGLE_PROTOBUF_NO_THREADLOCAL)
  516. // If we are using the ThreadLocalStorage class to store the ThreadCache,
  517. // then the ThreadCache's default constructor has to be responsible for
  518. // initializing it.
  519. ThreadCache()
  520. : next_lifecycle_id(0),
  521. last_lifecycle_id_seen(-1),
  522. last_serial_arena(nullptr) {}
  523. #endif
  524. // Number of per-thread lifecycle IDs to reserve. Must be power of two.
  525. // To reduce contention on a global atomic, each thread reserves a batch of
  526. // IDs. The following number is calculated based on a stress test with
  527. // ~6500 threads all frequently allocating a new arena.
  528. static constexpr size_t kPerThreadIds = 256;
  529. // Next lifecycle ID available to this thread. We need to reserve a new
  530. // batch, if `next_lifecycle_id & (kPerThreadIds - 1) == 0`.
  531. uint64_t next_lifecycle_id;
  532. // The ThreadCache is considered valid as long as this matches the
  533. // lifecycle_id of the arena being used.
  534. uint64_t last_lifecycle_id_seen;
  535. SerialArena* last_serial_arena;
  536. };
  537. // Lifecycle_id can be highly contended variable in a situation of lots of
  538. // arena creation. Make sure that other global variables are not sharing the
  539. // cacheline.
  540. #ifdef _MSC_VER
  541. #pragma warning(disable : 4324)
  542. #endif
  543. struct alignas(kCacheAlignment) CacheAlignedLifecycleIdGenerator {
  544. std::atomic<LifecycleIdAtomic> id;
  545. };
  546. static CacheAlignedLifecycleIdGenerator lifecycle_id_generator_;
  547. #if defined(GOOGLE_PROTOBUF_NO_THREADLOCAL)
  548. // iOS does not support __thread keyword so we use a custom thread local
  549. // storage class we implemented.
  550. static ThreadCache& thread_cache();
  551. #elif defined(PROTOBUF_USE_DLLS)
  552. // Thread local variables cannot be exposed through DLL interface but we can
  553. // wrap them in static functions.
  554. static ThreadCache& thread_cache();
  555. #else
  556. static PROTOBUF_THREAD_LOCAL ThreadCache thread_cache_;
  557. static ThreadCache& thread_cache() { return thread_cache_; }
  558. #endif
  559. ThreadSafeArenaStatsHandle arena_stats_;
  560. GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ThreadSafeArena);
  561. // All protos have pointers back to the arena hence Arena must have
  562. // pointer stability.
  563. ThreadSafeArena(ThreadSafeArena&&) = delete;
  564. ThreadSafeArena& operator=(ThreadSafeArena&&) = delete;
  565. public:
  566. // kBlockHeaderSize is sizeof(Block), aligned up to the nearest multiple of 8
  567. // to protect the invariant that pos is always at a multiple of 8.
  568. static constexpr size_t kBlockHeaderSize = SerialArena::kBlockHeaderSize;
  569. static constexpr size_t kSerialArenaSize =
  570. (sizeof(SerialArena) + 7) & static_cast<size_t>(-8);
  571. static_assert(kBlockHeaderSize % 8 == 0,
  572. "kBlockHeaderSize must be a multiple of 8.");
  573. static_assert(kSerialArenaSize % 8 == 0,
  574. "kSerialArenaSize must be a multiple of 8.");
  575. };
  576. } // namespace internal
  577. } // namespace protobuf
  578. } // namespace google
  579. #include <google/protobuf/port_undef.inc>
  580. #endif // GOOGLE_PROTOBUF_ARENA_IMPL_H__