File indexing completed on 2026-09-22 08:53:40
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #ifndef GOOGLE_PROTOBUF_THREAD_SAFE_ARENA_H__
0011 #define GOOGLE_PROTOBUF_THREAD_SAFE_ARENA_H__
0012
0013 #include <atomic>
0014 #include <cstddef>
0015 #include <cstdint>
0016 #include <type_traits>
0017 #include <vector>
0018
0019 #include "absl/base/attributes.h"
0020 #include "absl/base/optimization.h"
0021 #include "absl/synchronization/mutex.h"
0022 #include "google/protobuf/arena_align.h"
0023 #include "google/protobuf/arena_allocation_policy.h"
0024 #include "google/protobuf/arena_cleanup.h"
0025 #include "google/protobuf/arenaz_sampler.h"
0026 #include "google/protobuf/port.h"
0027 #include "google/protobuf/serial_arena.h"
0028
0029
0030 #include "google/protobuf/port_def.inc"
0031
0032 namespace google {
0033 namespace protobuf {
0034 namespace internal {
0035
0036
0037
0038
0039
0040
0041
0042 class PROTOBUF_EXPORT ThreadSafeArena {
0043 public:
0044 ThreadSafeArena();
0045
0046 ThreadSafeArena(char* mem, size_t size);
0047
0048 explicit ThreadSafeArena(void* mem, size_t size,
0049 const AllocationPolicy& policy);
0050
0051
0052
0053 ThreadSafeArena(const ThreadSafeArena&) = delete;
0054 ThreadSafeArena& operator=(const ThreadSafeArena&) = delete;
0055 ThreadSafeArena(ThreadSafeArena&&) = delete;
0056 ThreadSafeArena& operator=(ThreadSafeArena&&) = delete;
0057
0058
0059
0060
0061
0062 ~ThreadSafeArena();
0063
0064 uint64_t Reset();
0065
0066 uint64_t SpaceAllocated() const;
0067 uint64_t SpaceUsed() const;
0068
0069 template <AllocationClient alloc_client = AllocationClient::kDefault>
0070 void* AllocateAligned(size_t n) {
0071 SerialArena* arena;
0072 if (ABSL_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
0073 return arena->AllocateAligned<alloc_client>(n);
0074 } else {
0075 return AllocateAlignedFallback<alloc_client>(n);
0076 }
0077 }
0078
0079 void ReturnArrayMemory(void* p, size_t size) {
0080 SerialArena* arena = nullptr;
0081 if (ABSL_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
0082 arena->ReturnArrayMemory(p, size);
0083 }
0084 }
0085
0086
0087
0088
0089
0090
0091 PROTOBUF_NDEBUG_INLINE bool MaybeAllocateAligned(size_t n, void** out) {
0092 SerialArena* arena = nullptr;
0093 if (ABSL_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
0094 return arena->MaybeAllocateAligned(n, out);
0095 }
0096 return false;
0097 }
0098
0099 void* AllocateAlignedWithCleanup(size_t n, size_t align,
0100 void (*destructor)(void*));
0101
0102
0103 void AddCleanup(void* elem, void (*cleanup)(void*));
0104
0105 void* AllocateFromStringBlock();
0106
0107 std::vector<void*> PeekCleanupListForTesting();
0108
0109 private:
0110 friend class ArenaBenchmark;
0111 friend class TcParser;
0112 friend class SerialArena;
0113 friend struct SerialArenaChunkHeader;
0114 friend class cleanup::ChunkList;
0115 static uint64_t GetNextLifeCycleId();
0116
0117 class SerialArenaChunk;
0118
0119
0120
0121 static SerialArenaChunk* NewSerialArenaChunk(uint32_t prev_capacity, void* id,
0122 SerialArena* serial);
0123 static SerialArenaChunk* SentrySerialArenaChunk();
0124
0125
0126
0127 ArenaBlock* FirstBlock(void* buf, size_t size);
0128
0129 ArenaBlock* FirstBlock(void* buf, size_t size,
0130 const AllocationPolicy& policy);
0131
0132
0133 void AddSerialArena(void* id, SerialArena* serial);
0134
0135 void UnpoisonAllArenaBlocks() const;
0136
0137
0138
0139
0140
0141 uint64_t tag_and_id_ = 0;
0142
0143 TaggedAllocationPolicyPtr alloc_policy_;
0144 ThreadSafeArenaStatsHandle arena_stats_;
0145
0146
0147 absl::Mutex mutex_;
0148
0149 std::atomic<SerialArenaChunk*> head_{nullptr};
0150
0151 void* first_owner_;
0152
0153
0154 SerialArena first_arena_;
0155
0156 static_assert(std::is_trivially_destructible<SerialArena>{},
0157 "SerialArena needs to be trivially destructible.");
0158
0159 const AllocationPolicy* AllocPolicy() const { return alloc_policy_.get(); }
0160 void InitializeWithPolicy(const AllocationPolicy& policy);
0161 void* AllocateAlignedWithCleanupFallback(size_t n, size_t align,
0162 void (*destructor)(void*));
0163
0164 void Init();
0165
0166
0167 void CleanupList();
0168
0169 inline void CacheSerialArena(SerialArena* serial) {
0170 thread_cache().last_serial_arena = serial;
0171 thread_cache().last_lifecycle_id_seen = tag_and_id_;
0172 }
0173
0174 PROTOBUF_NDEBUG_INLINE bool GetSerialArenaFast(SerialArena** arena) {
0175
0176
0177
0178 ThreadCache* tc = &thread_cache();
0179 if (ABSL_PREDICT_TRUE(tc->last_lifecycle_id_seen == tag_and_id_)) {
0180 *arena = tc->last_serial_arena;
0181 return true;
0182 }
0183 return false;
0184 }
0185
0186
0187
0188 SerialArena* GetSerialArenaFallback(size_t n);
0189
0190 SerialArena* GetSerialArena();
0191
0192 template <AllocationClient alloc_client = AllocationClient::kDefault>
0193 void* AllocateAlignedFallback(size_t n);
0194
0195
0196
0197 template <typename Callback>
0198 void WalkConstSerialArenaChunk(Callback fn) const;
0199
0200
0201 template <typename Callback>
0202 void WalkSerialArenaChunk(Callback fn);
0203
0204
0205
0206
0207 template <typename Callback>
0208 void VisitSerialArena(Callback fn) const;
0209
0210
0211
0212
0213 SizedPtr Free();
0214
0215
0216
0217 static constexpr size_t kThreadCacheAlignment = 32;
0218
0219 #ifdef _MSC_VER
0220 #pragma warning(disable : 4324)
0221 #endif
0222 struct alignas(kThreadCacheAlignment) ThreadCache {
0223
0224
0225
0226
0227 static constexpr size_t kPerThreadIds = 256;
0228
0229
0230 uint64_t next_lifecycle_id{0};
0231
0232
0233 uint64_t last_lifecycle_id_seen{static_cast<uint64_t>(-1)};
0234 SerialArena* last_serial_arena{nullptr};
0235 };
0236 static_assert(sizeof(ThreadCache) <= kThreadCacheAlignment,
0237 "ThreadCache may span several cache lines");
0238
0239
0240
0241
0242 #ifdef _MSC_VER
0243 #pragma warning(disable : 4324)
0244 #endif
0245 using LifecycleId = uint64_t;
0246 alignas(kCacheAlignment) ABSL_CONST_INIT
0247 static std::atomic<LifecycleId> lifecycle_id_;
0248 #if defined(PROTOBUF_NO_THREADLOCAL)
0249
0250
0251 static ThreadCache& thread_cache();
0252 #elif defined(PROTOBUF_USE_DLLS) && defined(_WIN32)
0253
0254
0255 static ThreadCache& thread_cache();
0256 #else
0257 PROTOBUF_CONSTINIT static PROTOBUF_THREAD_LOCAL ThreadCache thread_cache_;
0258 static ThreadCache& thread_cache() { return thread_cache_; }
0259 #endif
0260
0261 public:
0262
0263
0264 static constexpr size_t kBlockHeaderSize = SerialArena::kBlockHeaderSize;
0265 static constexpr size_t kSerialArenaSize =
0266 (sizeof(SerialArena) + 7) & static_cast<size_t>(-8);
0267 static constexpr size_t kAllocPolicySize =
0268 ArenaAlignDefault::Ceil(sizeof(AllocationPolicy));
0269 static constexpr size_t kMaxCleanupNodeSize = 16;
0270 static_assert(kBlockHeaderSize % 8 == 0,
0271 "kBlockHeaderSize must be a multiple of 8.");
0272 static_assert(kSerialArenaSize % 8 == 0,
0273 "kSerialArenaSize must be a multiple of 8.");
0274 };
0275
0276 }
0277 }
0278 }
0279
0280 #include "google/protobuf/port_undef.inc"
0281
0282 #endif