arena.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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. #include <google/protobuf/arena.h>
  31. #include <algorithm>
  32. #include <limits>
  33. #ifdef ADDRESS_SANITIZER
  34. #include <sanitizer/asan_interface.h>
  35. #endif // ADDRESS_SANITIZER
  36. #include <google/protobuf/stubs/port.h>
  37. namespace google {
  38. static const size_t kMinCleanupListElements = 8;
  39. static const size_t kMaxCleanupListElements = 64; // 1kB on 64-bit.
  40. namespace protobuf {
  41. namespace internal {
  42. std::atomic<int64> ArenaImpl::lifecycle_id_generator_;
  43. #if defined(GOOGLE_PROTOBUF_NO_THREADLOCAL)
  44. ArenaImpl::ThreadCache& ArenaImpl::thread_cache() {
  45. static internal::ThreadLocalStorage<ThreadCache>* thread_cache_ =
  46. new internal::ThreadLocalStorage<ThreadCache>();
  47. return *thread_cache_->Get();
  48. }
  49. #elif defined(PROTOBUF_USE_DLLS)
  50. ArenaImpl::ThreadCache& ArenaImpl::thread_cache() {
  51. static GOOGLE_THREAD_LOCAL ThreadCache thread_cache_ = { -1, NULL };
  52. return thread_cache_;
  53. }
  54. #else
  55. GOOGLE_THREAD_LOCAL ArenaImpl::ThreadCache ArenaImpl::thread_cache_ = {-1, NULL};
  56. #endif
  57. void ArenaImpl::Init() {
  58. lifecycle_id_ =
  59. lifecycle_id_generator_.fetch_add(1, std::memory_order_relaxed);
  60. hint_.store(nullptr, std::memory_order_relaxed);
  61. threads_.store(nullptr, std::memory_order_relaxed);
  62. if (initial_block_) {
  63. // Thread which calls Init() owns the first block. This allows the
  64. // single-threaded case to allocate on the first block without having to
  65. // perform atomic operations.
  66. new (initial_block_) Block(options_.initial_block_size, NULL);
  67. SerialArena* serial =
  68. SerialArena::New(initial_block_, &thread_cache(), this);
  69. serial->set_next(NULL);
  70. threads_.store(serial, std::memory_order_relaxed);
  71. space_allocated_.store(options_.initial_block_size,
  72. std::memory_order_relaxed);
  73. CacheSerialArena(serial);
  74. } else {
  75. space_allocated_.store(0, std::memory_order_relaxed);
  76. }
  77. }
  78. ArenaImpl::~ArenaImpl() {
  79. // Have to do this in a first pass, because some of the destructors might
  80. // refer to memory in other blocks.
  81. CleanupList();
  82. FreeBlocks();
  83. }
  84. uint64 ArenaImpl::Reset() {
  85. // Have to do this in a first pass, because some of the destructors might
  86. // refer to memory in other blocks.
  87. CleanupList();
  88. uint64 space_allocated = FreeBlocks();
  89. Init();
  90. return space_allocated;
  91. }
  92. ArenaImpl::Block* ArenaImpl::NewBlock(Block* last_block, size_t min_bytes) {
  93. size_t size;
  94. if (last_block) {
  95. // Double the current block size, up to a limit.
  96. size = std::min(2 * last_block->size(), options_.max_block_size);
  97. } else {
  98. size = options_.start_block_size;
  99. }
  100. // Verify that min_bytes + kBlockHeaderSize won't overflow.
  101. GOOGLE_CHECK_LE(min_bytes, std::numeric_limits<size_t>::max() - kBlockHeaderSize);
  102. size = std::max(size, kBlockHeaderSize + min_bytes);
  103. void* mem = options_.block_alloc(size);
  104. Block* b = new (mem) Block(size, last_block);
  105. space_allocated_.fetch_add(size, std::memory_order_relaxed);
  106. return b;
  107. }
  108. ArenaImpl::Block::Block(size_t size, Block* next)
  109. : next_(next), pos_(kBlockHeaderSize), size_(size) {}
  110. GOOGLE_PROTOBUF_ATTRIBUTE_NOINLINE
  111. void ArenaImpl::SerialArena::AddCleanupFallback(void* elem,
  112. void (*cleanup)(void*)) {
  113. size_t size = cleanup_ ? cleanup_->size * 2 : kMinCleanupListElements;
  114. size = std::min(size, kMaxCleanupListElements);
  115. size_t bytes = internal::AlignUpTo8(CleanupChunk::SizeOf(size));
  116. CleanupChunk* list = reinterpret_cast<CleanupChunk*>(AllocateAligned(bytes));
  117. list->next = cleanup_;
  118. list->size = size;
  119. cleanup_ = list;
  120. cleanup_ptr_ = &list->nodes[0];
  121. cleanup_limit_ = &list->nodes[size];
  122. AddCleanup(elem, cleanup);
  123. }
  124. GOOGLE_PROTOBUF_ATTRIBUTE_FUNC_ALIGN(32)
  125. void* ArenaImpl::AllocateAligned(size_t n) {
  126. SerialArena* arena;
  127. if (GOOGLE_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
  128. return arena->AllocateAligned(n);
  129. } else {
  130. return AllocateAlignedFallback(n);
  131. }
  132. }
  133. void* ArenaImpl::AllocateAlignedAndAddCleanup(size_t n,
  134. void (*cleanup)(void*)) {
  135. SerialArena* arena;
  136. if (GOOGLE_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
  137. return arena->AllocateAlignedAndAddCleanup(n, cleanup);
  138. } else {
  139. return AllocateAlignedAndAddCleanupFallback(n, cleanup);
  140. }
  141. }
  142. void ArenaImpl::AddCleanup(void* elem, void (*cleanup)(void*)) {
  143. SerialArena* arena;
  144. if (GOOGLE_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
  145. arena->AddCleanup(elem, cleanup);
  146. } else {
  147. return AddCleanupFallback(elem, cleanup);
  148. }
  149. }
  150. GOOGLE_PROTOBUF_ATTRIBUTE_NOINLINE
  151. void* ArenaImpl::AllocateAlignedFallback(size_t n) {
  152. return GetSerialArena()->AllocateAligned(n);
  153. }
  154. GOOGLE_PROTOBUF_ATTRIBUTE_NOINLINE
  155. void* ArenaImpl::AllocateAlignedAndAddCleanupFallback(size_t n,
  156. void (*cleanup)(void*)) {
  157. return GetSerialArena()->AllocateAlignedAndAddCleanup(n, cleanup);
  158. }
  159. GOOGLE_PROTOBUF_ATTRIBUTE_NOINLINE
  160. void ArenaImpl::AddCleanupFallback(void* elem, void (*cleanup)(void*)) {
  161. GetSerialArena()->AddCleanup(elem, cleanup);
  162. }
  163. inline GOOGLE_PROTOBUF_ATTRIBUTE_ALWAYS_INLINE
  164. bool ArenaImpl::GetSerialArenaFast(ArenaImpl::SerialArena** arena) {
  165. // If this thread already owns a block in this arena then try to use that.
  166. // This fast path optimizes the case where multiple threads allocate from the
  167. // same arena.
  168. ThreadCache* tc = &thread_cache();
  169. if (GOOGLE_PREDICT_TRUE(tc->last_lifecycle_id_seen == lifecycle_id_)) {
  170. *arena = tc->last_serial_arena;
  171. return true;
  172. }
  173. // Check whether we own the last accessed SerialArena on this arena. This
  174. // fast path optimizes the case where a single thread uses multiple arenas.
  175. SerialArena* serial = hint_.load(std::memory_order_acquire);
  176. if (GOOGLE_PREDICT_TRUE(serial != NULL && serial->owner() == tc)) {
  177. *arena = serial;
  178. return true;
  179. }
  180. return false;
  181. }
  182. ArenaImpl::SerialArena* ArenaImpl::GetSerialArena() {
  183. SerialArena* arena;
  184. if (GOOGLE_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
  185. return arena;
  186. } else {
  187. return GetSerialArenaFallback(&thread_cache());
  188. }
  189. }
  190. GOOGLE_PROTOBUF_ATTRIBUTE_NOINLINE
  191. void* ArenaImpl::SerialArena::AllocateAlignedFallback(size_t n) {
  192. // Sync back to current's pos.
  193. head_->set_pos(head_->size() - (limit_ - ptr_));
  194. head_ = arena_->NewBlock(head_, n);
  195. ptr_ = head_->Pointer(head_->pos());
  196. limit_ = head_->Pointer(head_->size());
  197. #ifdef ADDRESS_SANITIZER
  198. ASAN_POISON_MEMORY_REGION(ptr_, limit_ - ptr_);
  199. #endif // ADDRESS_SANITIZER
  200. return AllocateAligned(n);
  201. }
  202. uint64 ArenaImpl::SpaceAllocated() const {
  203. return space_allocated_.load(std::memory_order_relaxed);
  204. }
  205. uint64 ArenaImpl::SpaceUsed() const {
  206. SerialArena* serial = threads_.load(std::memory_order_acquire);
  207. uint64 space_used = 0;
  208. for ( ; serial; serial = serial->next()) {
  209. space_used += serial->SpaceUsed();
  210. }
  211. return space_used;
  212. }
  213. uint64 ArenaImpl::SerialArena::SpaceUsed() const {
  214. // Get current block's size from ptr_ (since we can't trust head_->pos().
  215. uint64 space_used = ptr_ - head_->Pointer(kBlockHeaderSize);
  216. // Get subsequent block size from b->pos().
  217. for (Block* b = head_->next(); b; b = b->next()) {
  218. space_used += (b->pos() - kBlockHeaderSize);
  219. }
  220. // Remove the overhead of the SerialArena itself.
  221. space_used -= kSerialArenaSize;
  222. return space_used;
  223. }
  224. uint64 ArenaImpl::FreeBlocks() {
  225. uint64 space_allocated = 0;
  226. // By omitting an Acquire barrier we ensure that any user code that doesn't
  227. // properly synchronize Reset() or the destructor will throw a TSAN warning.
  228. SerialArena* serial = threads_.load(std::memory_order_relaxed);
  229. while (serial) {
  230. // This is inside a block we are freeing, so we need to read it now.
  231. SerialArena* next = serial->next();
  232. space_allocated += ArenaImpl::SerialArena::Free(serial, initial_block_,
  233. options_.block_dealloc);
  234. // serial is dead now.
  235. serial = next;
  236. }
  237. return space_allocated;
  238. }
  239. uint64 ArenaImpl::SerialArena::Free(ArenaImpl::SerialArena* serial,
  240. Block* initial_block,
  241. void (*block_dealloc)(void*, size_t)) {
  242. uint64 space_allocated = 0;
  243. // We have to be careful in this function, since we will be freeing the Block
  244. // that contains this SerialArena. Be careful about accessing |serial|.
  245. for (Block* b = serial->head_; b; ) {
  246. // This is inside the block we are freeing, so we need to read it now.
  247. Block* next_block = b->next();
  248. space_allocated += (b->size());
  249. #ifdef ADDRESS_SANITIZER
  250. // This memory was provided by the underlying allocator as unpoisoned, so
  251. // return it in an unpoisoned state.
  252. ASAN_UNPOISON_MEMORY_REGION(b->Pointer(0), b->size());
  253. #endif // ADDRESS_SANITIZER
  254. if (b != initial_block) {
  255. block_dealloc(b, b->size());
  256. }
  257. b = next_block;
  258. }
  259. return space_allocated;
  260. }
  261. void ArenaImpl::CleanupList() {
  262. // By omitting an Acquire barrier we ensure that any user code that doesn't
  263. // properly synchronize Reset() or the destructor will throw a TSAN warning.
  264. SerialArena* serial = threads_.load(std::memory_order_relaxed);
  265. for ( ; serial; serial = serial->next()) {
  266. serial->CleanupList();
  267. }
  268. }
  269. void ArenaImpl::SerialArena::CleanupList() {
  270. if (cleanup_ != NULL) {
  271. CleanupListFallback();
  272. }
  273. }
  274. void ArenaImpl::SerialArena::CleanupListFallback() {
  275. // Cleanup newest chunk: ptrs give us length.
  276. size_t n = cleanup_ptr_ - &cleanup_->nodes[0];
  277. CleanupNode* node = cleanup_ptr_;
  278. for (size_t i = 0; i < n; i++) {
  279. --node;
  280. node->cleanup(node->elem);
  281. }
  282. // Cleanup older chunks, which are known to be full.
  283. CleanupChunk* list = cleanup_->next;
  284. while (list) {
  285. size_t n = list->size;
  286. CleanupNode* node = &list->nodes[list->size];
  287. for (size_t i = 0; i < n; i++) {
  288. --node;
  289. node->cleanup(node->elem);
  290. }
  291. list = list->next;
  292. }
  293. }
  294. ArenaImpl::SerialArena* ArenaImpl::SerialArena::New(Block* b, void* owner,
  295. ArenaImpl* arena) {
  296. GOOGLE_DCHECK_EQ(b->pos(), kBlockHeaderSize); // Should be a fresh block
  297. GOOGLE_DCHECK_LE(kBlockHeaderSize + kSerialArenaSize, b->size());
  298. SerialArena* serial =
  299. reinterpret_cast<SerialArena*>(b->Pointer(kBlockHeaderSize));
  300. b->set_pos(kBlockHeaderSize + kSerialArenaSize);
  301. serial->arena_ = arena;
  302. serial->owner_ = owner;
  303. serial->head_ = b;
  304. serial->ptr_ = b->Pointer(b->pos());
  305. serial->limit_ = b->Pointer(b->size());
  306. serial->cleanup_ = NULL;
  307. serial->cleanup_ptr_ = NULL;
  308. serial->cleanup_limit_ = NULL;
  309. return serial;
  310. }
  311. GOOGLE_PROTOBUF_ATTRIBUTE_NOINLINE
  312. ArenaImpl::SerialArena* ArenaImpl::GetSerialArenaFallback(void* me) {
  313. // Look for this SerialArena in our linked list.
  314. SerialArena* serial = threads_.load(std::memory_order_acquire);
  315. for ( ; serial; serial = serial->next()) {
  316. if (serial->owner() == me) {
  317. break;
  318. }
  319. }
  320. if (!serial) {
  321. // This thread doesn't have any SerialArena, which also means it doesn't
  322. // have any blocks yet. So we'll allocate its first block now.
  323. Block* b = NewBlock(NULL, kSerialArenaSize);
  324. serial = SerialArena::New(b, me, this);
  325. SerialArena* head = threads_.load(std::memory_order_relaxed);
  326. do {
  327. serial->set_next(head);
  328. } while (!threads_.compare_exchange_weak(
  329. head, serial, std::memory_order_release, std::memory_order_relaxed));
  330. }
  331. CacheSerialArena(serial);
  332. return serial;
  333. }
  334. } // namespace internal
  335. void Arena::CallDestructorHooks() {
  336. uint64 space_allocated = impl_.SpaceAllocated();
  337. // Call the reset hook
  338. if (on_arena_reset_ != NULL) {
  339. on_arena_reset_(this, hooks_cookie_, space_allocated);
  340. }
  341. // Call the destruction hook
  342. if (on_arena_destruction_ != NULL) {
  343. on_arena_destruction_(this, hooks_cookie_, space_allocated);
  344. }
  345. }
  346. void Arena::OnArenaAllocation(const std::type_info* allocated_type,
  347. size_t n) const {
  348. if (on_arena_allocation_ != NULL) {
  349. on_arena_allocation_(allocated_type, n, hooks_cookie_);
  350. }
  351. }
  352. } // namespace protobuf
  353. } // namespace google