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