arena_impl.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  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 <google/protobuf/stubs/common.h>
  36. #include <google/protobuf/stubs/logging.h>
  37. #include <google/protobuf/stubs/port.h>
  38. #ifdef ADDRESS_SANITIZER
  39. #include <sanitizer/asan_interface.h>
  40. #endif // ADDRESS_SANITIZER
  41. namespace google {
  42. namespace protobuf {
  43. namespace internal {
  44. inline size_t AlignUpTo8(size_t n) {
  45. // Align n to next multiple of 8 (from Hacker's Delight, Chapter 3.)
  46. return (n + 7) & -8;
  47. }
  48. // This class provides the core Arena memory allocation library. Different
  49. // implementations only need to implement the public interface below.
  50. // Arena is not a template type as that would only be useful if all protos
  51. // in turn would be templates, which will/cannot happen. However separating
  52. // the memory allocation part from the cruft of the API users expect we can
  53. // use #ifdef the select the best implementation based on hardware / OS.
  54. class LIBPROTOBUF_EXPORT ArenaImpl {
  55. public:
  56. struct Options {
  57. size_t start_block_size;
  58. size_t max_block_size;
  59. char* initial_block;
  60. size_t initial_block_size;
  61. void* (*block_alloc)(size_t);
  62. void (*block_dealloc)(void*, size_t);
  63. template <typename O>
  64. explicit Options(const O& options)
  65. : start_block_size(options.start_block_size),
  66. max_block_size(options.max_block_size),
  67. initial_block(options.initial_block),
  68. initial_block_size(options.initial_block_size),
  69. block_alloc(options.block_alloc),
  70. block_dealloc(options.block_dealloc) {}
  71. };
  72. template <typename O>
  73. explicit ArenaImpl(const O& options) : options_(options) {
  74. if (options_.initial_block != NULL && options_.initial_block_size > 0) {
  75. GOOGLE_CHECK_GE(options_.initial_block_size, sizeof(Block))
  76. << ": Initial block size too small for header.";
  77. initial_block_ = reinterpret_cast<Block*>(options_.initial_block);
  78. } else {
  79. initial_block_ = NULL;
  80. }
  81. Init();
  82. }
  83. // Destructor deletes all owned heap allocated objects, and destructs objects
  84. // that have non-trivial destructors, except for proto2 message objects whose
  85. // destructors can be skipped. Also, frees all blocks except the initial block
  86. // if it was passed in.
  87. ~ArenaImpl();
  88. uint64 Reset();
  89. uint64 SpaceAllocated() const;
  90. uint64 SpaceUsed() const;
  91. void* AllocateAligned(size_t n);
  92. void* AllocateAlignedAndAddCleanup(size_t n, void (*cleanup)(void*));
  93. // Add object pointer and cleanup function pointer to the list.
  94. void AddCleanup(void* elem, void (*cleanup)(void*));
  95. private:
  96. void* AllocateAlignedFallback(size_t n);
  97. void* AllocateAlignedAndAddCleanupFallback(size_t n, void (*cleanup)(void*));
  98. void AddCleanupFallback(void* elem, void (*cleanup)(void*));
  99. // Node contains the ptr of the object to be cleaned up and the associated
  100. // cleanup function ptr.
  101. struct CleanupNode {
  102. void* elem; // Pointer to the object to be cleaned up.
  103. void (*cleanup)(void*); // Function pointer to the destructor or deleter.
  104. };
  105. // Cleanup uses a chunked linked list, to reduce pointer chasing.
  106. struct CleanupChunk {
  107. static size_t SizeOf(size_t i) {
  108. return sizeof(CleanupChunk) + (sizeof(CleanupNode) * (i - 1));
  109. }
  110. size_t size; // Total elements in the list.
  111. CleanupChunk* next; // Next node in the list.
  112. CleanupNode nodes[1]; // True length is |size|.
  113. };
  114. class Block;
  115. // A thread-unsafe Arena that can only be used within its owning thread.
  116. class LIBPROTOBUF_EXPORT SerialArena {
  117. public:
  118. // The allocate/free methods here are a little strange, since SerialArena is
  119. // allocated inside a Block which it also manages. This is to avoid doing
  120. // an extra allocation for the SerialArena itself.
  121. // Creates a new SerialArena inside Block* and returns it.
  122. static SerialArena* New(Block* b, void* owner, ArenaImpl* arena);
  123. // Destroys this SerialArena, freeing all blocks with the given dealloc
  124. // function, except any block equal to |initial_block|.
  125. static uint64 Free(SerialArena* serial, Block* initial_block,
  126. void (*block_dealloc)(void*, size_t));
  127. void CleanupList();
  128. uint64 SpaceUsed() const;
  129. void* AllocateAligned(size_t n) {
  130. GOOGLE_DCHECK_EQ(internal::AlignUpTo8(n), n); // Must be already aligned.
  131. GOOGLE_DCHECK_GE(limit_, ptr_);
  132. if (GOOGLE_PREDICT_FALSE(static_cast<size_t>(limit_ - ptr_) < n)) {
  133. return AllocateAlignedFallback(n);
  134. }
  135. void* ret = ptr_;
  136. ptr_ += n;
  137. #ifdef ADDRESS_SANITIZER
  138. ASAN_UNPOISON_MEMORY_REGION(ret, n);
  139. #endif // ADDRESS_SANITIZER
  140. return ret;
  141. }
  142. void AddCleanup(void* elem, void (*cleanup)(void*)) {
  143. if (GOOGLE_PREDICT_FALSE(cleanup_ptr_ == cleanup_limit_)) {
  144. AddCleanupFallback(elem, cleanup);
  145. return;
  146. }
  147. cleanup_ptr_->elem = elem;
  148. cleanup_ptr_->cleanup = cleanup;
  149. cleanup_ptr_++;
  150. }
  151. void* AllocateAlignedAndAddCleanup(size_t n, void (*cleanup)(void*)) {
  152. void* ret = AllocateAligned(n);
  153. AddCleanup(ret, cleanup);
  154. return ret;
  155. }
  156. void* owner() const { return owner_; }
  157. SerialArena* next() const { return next_; }
  158. void set_next(SerialArena* next) { next_ = next; }
  159. private:
  160. void* AllocateAlignedFallback(size_t n);
  161. void AddCleanupFallback(void* elem, void (*cleanup)(void*));
  162. void CleanupListFallback();
  163. ArenaImpl* arena_; // Containing arena.
  164. void* owner_; // &ThreadCache of this thread;
  165. Block* head_; // Head of linked list of blocks.
  166. CleanupChunk* cleanup_; // Head of cleanup list.
  167. SerialArena* next_; // Next SerialArena in this linked list.
  168. // Next pointer to allocate from. Always 8-byte aligned. Points inside
  169. // head_ (and head_->pos will always be non-canonical). We keep these
  170. // here to reduce indirection.
  171. char* ptr_;
  172. char* limit_;
  173. // Next CleanupList members to append to. These point inside cleanup_.
  174. CleanupNode* cleanup_ptr_;
  175. CleanupNode* cleanup_limit_;
  176. };
  177. // Blocks are variable length malloc-ed objects. The following structure
  178. // describes the common header for all blocks.
  179. class LIBPROTOBUF_EXPORT Block {
  180. public:
  181. Block(size_t size, Block* next);
  182. char* Pointer(size_t n) {
  183. GOOGLE_DCHECK(n <= size_);
  184. return reinterpret_cast<char*>(this) + n;
  185. }
  186. Block* next() const { return next_; }
  187. size_t pos() const { return pos_; }
  188. size_t size() const { return size_; }
  189. void set_pos(size_t pos) { pos_ = pos; }
  190. private:
  191. Block* next_; // Next block for this thread.
  192. size_t pos_;
  193. size_t size_;
  194. // data follows
  195. };
  196. struct ThreadCache {
  197. #if defined(GOOGLE_PROTOBUF_NO_THREADLOCAL)
  198. // If we are using the ThreadLocalStorage class to store the ThreadCache,
  199. // then the ThreadCache's default constructor has to be responsible for
  200. // initializing it.
  201. ThreadCache() : last_lifecycle_id_seen(-1), last_serial_arena(NULL) {}
  202. #endif
  203. // The ThreadCache is considered valid as long as this matches the
  204. // lifecycle_id of the arena being used.
  205. int64 last_lifecycle_id_seen;
  206. SerialArena* last_serial_arena;
  207. };
  208. static std::atomic<int64> lifecycle_id_generator_;
  209. #if defined(GOOGLE_PROTOBUF_NO_THREADLOCAL)
  210. // Android ndk does not support GOOGLE_THREAD_LOCAL keyword so we use a custom thread
  211. // local storage class we implemented.
  212. // iOS also does not support the GOOGLE_THREAD_LOCAL keyword.
  213. static ThreadCache& thread_cache();
  214. #elif defined(PROTOBUF_USE_DLLS)
  215. // Thread local variables cannot be exposed through DLL interface but we can
  216. // wrap them in static functions.
  217. static ThreadCache& thread_cache();
  218. #else
  219. static GOOGLE_THREAD_LOCAL ThreadCache thread_cache_;
  220. static ThreadCache& thread_cache() { return thread_cache_; }
  221. #endif
  222. void Init();
  223. // Free all blocks and return the total space used which is the sums of sizes
  224. // of the all the allocated blocks.
  225. uint64 FreeBlocks();
  226. // Delete or Destruct all objects owned by the arena.
  227. void CleanupList();
  228. inline void CacheSerialArena(SerialArena* serial) {
  229. thread_cache().last_serial_arena = serial;
  230. thread_cache().last_lifecycle_id_seen = lifecycle_id_;
  231. // TODO(haberman): evaluate whether we would gain efficiency by getting rid
  232. // of hint_. It's the only write we do to ArenaImpl in the allocation path,
  233. // which will dirty the cache line.
  234. hint_.store(serial, std::memory_order_release);
  235. }
  236. std::atomic<SerialArena*>
  237. threads_; // Pointer to a linked list of SerialArena.
  238. std::atomic<SerialArena*> hint_; // Fast thread-local block access
  239. std::atomic<size_t> space_allocated_; // Total size of all allocated blocks.
  240. Block *initial_block_; // If non-NULL, points to the block that came from
  241. // user data.
  242. Block* NewBlock(Block* last_block, size_t min_bytes);
  243. SerialArena* GetSerialArena();
  244. bool GetSerialArenaFast(SerialArena** arena);
  245. SerialArena* GetSerialArenaFallback(void* me);
  246. int64 lifecycle_id_; // Unique for each arena. Changes on Reset().
  247. Options options_;
  248. GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ArenaImpl);
  249. // All protos have pointers back to the arena hence Arena must have
  250. // pointer stability.
  251. ArenaImpl(ArenaImpl&&) = delete;
  252. ArenaImpl& operator=(ArenaImpl&&) = delete;
  253. public:
  254. // kBlockHeaderSize is sizeof(Block), aligned up to the nearest multiple of 8
  255. // to protect the invariant that pos is always at a multiple of 8.
  256. static const size_t kBlockHeaderSize = (sizeof(Block) + 7) & -8;
  257. static const size_t kSerialArenaSize = (sizeof(SerialArena) + 7) & -8;
  258. static_assert(kBlockHeaderSize % 8 == 0,
  259. "kBlockHeaderSize must be a multiple of 8.");
  260. static_assert(kSerialArenaSize % 8 == 0,
  261. "kSerialArenaSize must be a multiple of 8.");
  262. };
  263. } // namespace internal
  264. } // namespace protobuf
  265. } // namespace google
  266. #endif // GOOGLE_PROTOBUF_ARENA_IMPL_H__