Split arena_impl.h into serial_arena.h and thread_safe_arena.h

PiperOrigin-RevId: 495340630
diff --git a/src/file_lists.cmake b/src/file_lists.cmake
index 1e326bc..b7367ef 100644
--- a/src/file_lists.cmake
+++ b/src/file_lists.cmake
@@ -103,7 +103,6 @@
   ${protobuf_SOURCE_DIR}/src/google/protobuf/arena_allocation_policy.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/arena_cleanup.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/arena_config.h
-  ${protobuf_SOURCE_DIR}/src/google/protobuf/arena_impl.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/arenastring.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/arenaz_sampler.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/importer.h
@@ -170,6 +169,7 @@
   ${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_field.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_ptr_field.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/service.h
+  ${protobuf_SOURCE_DIR}/src/google/protobuf/serial_arena.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/callback.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/common.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/logging.h
@@ -178,6 +178,7 @@
   ${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/port.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/status_macros.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/text_format.h
+  ${protobuf_SOURCE_DIR}/src/google/protobuf/thread_safe_arena.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/unknown_field_set.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/util/delimited_message_util.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/util/field_comparator.h
@@ -227,7 +228,6 @@
   ${protobuf_SOURCE_DIR}/src/google/protobuf/arena_allocation_policy.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/arena_cleanup.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/arena_config.h
-  ${protobuf_SOURCE_DIR}/src/google/protobuf/arena_impl.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/arenastring.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/arenaz_sampler.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/endian.h
@@ -258,6 +258,7 @@
   ${protobuf_SOURCE_DIR}/src/google/protobuf/port_undef.inc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_field.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_ptr_field.h
+  ${protobuf_SOURCE_DIR}/src/google/protobuf/serial_arena.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/callback.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/common.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/logging.h
@@ -265,6 +266,7 @@
   ${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/platform_macros.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/port.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/status_macros.h
+  ${protobuf_SOURCE_DIR}/src/google/protobuf/thread_safe_arena.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/wire_format_lite.h
 )
 
diff --git a/src/google/protobuf/BUILD.bazel b/src/google/protobuf/BUILD.bazel
index 4902ce2..9581b91 100644
--- a/src/google/protobuf/BUILD.bazel
+++ b/src/google/protobuf/BUILD.bazel
@@ -244,8 +244,9 @@
     hdrs = [
         "arena.h",
         "arena_config.h",
-        "arena_impl.h",
         "arenaz_sampler.h",
+        "serial_arena.h",
+        "thread_safe_arena.h",
     ],
     include_prefix = "google/protobuf",
     visibility = [
@@ -284,7 +285,6 @@
     hdrs = [
         "any.h",
         "arena.h",
-        "arena_impl.h",
         "arenastring.h",
         "arenaz_sampler.h",
         "endian.h",
@@ -308,6 +308,8 @@
         "port.h",
         "repeated_field.h",
         "repeated_ptr_field.h",
+        "serial_arena.h",
+        "thread_safe_arena.h",
         "wire_format_lite.h",
     ],
     copts = COPTS + select({
@@ -326,6 +328,7 @@
     # In Bazel 6.0+, these will be `interface_deps`:
     deps = [
         ":arena",
+        ":arena_align",
         ":arena_config",
         "//src/google/protobuf/io",
         "//src/google/protobuf/stubs:lite",
diff --git a/src/google/protobuf/arena.cc b/src/google/protobuf/arena.cc
index 2a62414..2ba9ff8 100644
--- a/src/google/protobuf/arena.cc
+++ b/src/google/protobuf/arena.cc
@@ -40,9 +40,10 @@
 #include "absl/base/attributes.h"
 #include "absl/synchronization/mutex.h"
 #include "google/protobuf/arena_allocation_policy.h"
-#include "google/protobuf/arena_impl.h"
 #include "google/protobuf/arenaz_sampler.h"
 #include "google/protobuf/port.h"
+#include "google/protobuf/serial_arena.h"
+#include "google/protobuf/thread_safe_arena.h"
 
 
 #ifdef ADDRESS_SANITIZER
diff --git a/src/google/protobuf/arena.h b/src/google/protobuf/arena.h
index 8a7d1b4..06acc3d 100644
--- a/src/google/protobuf/arena.h
+++ b/src/google/protobuf/arena.h
@@ -51,8 +51,9 @@
 #include <type_traits>
 #include "google/protobuf/arena_align.h"
 #include "google/protobuf/arena_config.h"
-#include "google/protobuf/arena_impl.h"
 #include "google/protobuf/port.h"
+#include "google/protobuf/serial_arena.h"
+#include "google/protobuf/thread_safe_arena.h"
 
 // Must be included last.
 #include "google/protobuf/port_def.inc"
diff --git a/src/google/protobuf/arena_impl.h b/src/google/protobuf/serial_arena.h
similarity index 60%
rename from src/google/protobuf/arena_impl.h
rename to src/google/protobuf/serial_arena.h
index 0d39c19..171483b 100644
--- a/src/google/protobuf/arena_impl.h
+++ b/src/google/protobuf/serial_arena.h
@@ -1,5 +1,5 @@
 // Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc.  All rights reserved.
+// Copyright 2022 Google Inc.  All rights reserved.
 // https://developers.google.com/protocol-buffers/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -27,32 +27,27 @@
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This file defines the internal class SerialArena
 
-// This file defines an Arena allocator for better allocation performance.
-
-#ifndef GOOGLE_PROTOBUF_ARENA_IMPL_H__
-#define GOOGLE_PROTOBUF_ARENA_IMPL_H__
+#ifndef GOOGLE_PROTOBUF_SERIAL_ARENA_H__
+#define GOOGLE_PROTOBUF_SERIAL_ARENA_H__
 
 #include <algorithm>
 #include <atomic>
-#include <limits>
-#include <string>
 #include <type_traits>
 #include <typeinfo>
+#include <utility>
 
 #include "google/protobuf/stubs/common.h"
 #include "google/protobuf/stubs/logging.h"
 #include "absl/numeric/bits.h"
-#include "absl/strings/cord.h"
-#include "absl/synchronization/mutex.h"
 #include "google/protobuf/arena_align.h"
-#include "google/protobuf/arena_allocation_policy.h"
 #include "google/protobuf/arena_cleanup.h"
 #include "google/protobuf/arena_config.h"
 #include "google/protobuf/arenaz_sampler.h"
 #include "google/protobuf/port.h"
 
-
 // Must be included last.
 #include "google/protobuf/port_def.inc"
 
@@ -382,234 +377,10 @@
       ArenaAlignDefault::Ceil(sizeof(ArenaBlock));
 };
 
-// This class provides the core Arena memory allocation library. Different
-// implementations only need to implement the public interface below.
-// Arena is not a template type as that would only be useful if all protos
-// in turn would be templates, which will/cannot happen. However separating
-// the memory allocation part from the cruft of the API users expect we can
-// use #ifdef the select the best implementation based on hardware / OS.
-class PROTOBUF_EXPORT ThreadSafeArena {
- public:
-  ThreadSafeArena();
-
-  ThreadSafeArena(char* mem, size_t size);
-
-  explicit ThreadSafeArena(void* mem, size_t size,
-                           const AllocationPolicy& policy);
-
-  // All protos have pointers back to the arena hence Arena must have
-  // pointer stability.
-  ThreadSafeArena(const ThreadSafeArena&) = delete;
-  ThreadSafeArena& operator=(const ThreadSafeArena&) = delete;
-  ThreadSafeArena(ThreadSafeArena&&) = delete;
-  ThreadSafeArena& operator=(ThreadSafeArena&&) = delete;
-
-  // Destructor deletes all owned heap allocated objects, and destructs objects
-  // that have non-trivial destructors, except for proto2 message objects whose
-  // destructors can be skipped. Also, frees all blocks except the initial block
-  // if it was passed in.
-  ~ThreadSafeArena();
-
-  uint64_t Reset();
-
-  uint64_t SpaceAllocated() const;
-  uint64_t SpaceUsed() const;
-
-  template <AllocationClient alloc_client = AllocationClient::kDefault>
-  void* AllocateAligned(size_t n) {
-    SerialArena* arena;
-    if (PROTOBUF_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
-      return arena->AllocateAligned<alloc_client>(n);
-    } else {
-      return AllocateAlignedFallback<alloc_client>(n);
-    }
-  }
-
-  void ReturnArrayMemory(void* p, size_t size) {
-    SerialArena* arena;
-    if (PROTOBUF_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
-      arena->ReturnArrayMemory(p, size);
-    }
-  }
-
-  // This function allocates n bytes if the common happy case is true and
-  // returns true. Otherwise does nothing and returns false. This strange
-  // semantics is necessary to allow callers to program functions that only
-  // have fallback function calls in tail position. This substantially improves
-  // code for the happy path.
-  PROTOBUF_NDEBUG_INLINE bool MaybeAllocateAligned(size_t n, void** out) {
-    SerialArena* arena;
-    if (PROTOBUF_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
-      return arena->MaybeAllocateAligned(n, out);
-    }
-    return false;
-  }
-
-  void* AllocateAlignedWithCleanup(size_t n, size_t align,
-                                   void (*destructor)(void*));
-
-  // Add object pointer and cleanup function pointer to the list.
-  void AddCleanup(void* elem, void (*cleanup)(void*));
-
- private:
-  friend class ArenaBenchmark;
-  friend class TcParser;
-  friend class SerialArena;
-  friend struct SerialArenaChunkHeader;
-  static uint64_t GetNextLifeCycleId();
-
-  class SerialArenaChunk;
-
-  // Returns a new SerialArenaChunk that has {id, serial} at slot 0. It may
-  // grow based on "prev_num_slots".
-  static SerialArenaChunk* NewSerialArenaChunk(uint32_t prev_capacity, void* id,
-                                               SerialArena* serial);
-  static SerialArenaChunk* SentrySerialArenaChunk();
-
-  // Returns the first ArenaBlock* for the first SerialArena. If users provide
-  // one, use it if it's acceptable. Otherwise returns a sentry block.
-  ArenaBlock* FirstBlock(void* buf, size_t size);
-  // Same as the above but returns a valid block if "policy" is not default.
-  ArenaBlock* FirstBlock(void* buf, size_t size,
-                         const AllocationPolicy& policy);
-
-  // Adds SerialArena to the chunked list. May create a new chunk.
-  void AddSerialArena(void* id, SerialArena* serial);
-
-  // Members are declared here to track sizeof(ThreadSafeArena) and hotness
-  // centrally.
-
-  // Unique for each arena. Changes on Reset().
-  uint64_t tag_and_id_ = 0;
-
-  TaggedAllocationPolicyPtr alloc_policy_;  // Tagged pointer to AllocPolicy.
-  ThreadSafeArenaStatsHandle arena_stats_;
-
-  // Adding a new chunk to head_ must be protected by mutex_.
-  absl::Mutex mutex_;
-  // Pointer to a linked list of SerialArenaChunk.
-  std::atomic<SerialArenaChunk*> head_{nullptr};
-
-  void* first_owner_;
-  // Must be declared after alloc_policy_; otherwise, it may lose info on
-  // user-provided initial block.
-  SerialArena first_arena_;
-
-  static_assert(std::is_trivially_destructible<SerialArena>{},
-                "SerialArena needs to be trivially destructible.");
-
-  const AllocationPolicy* AllocPolicy() const { return alloc_policy_.get(); }
-  void InitializeWithPolicy(const AllocationPolicy& policy);
-  void* AllocateAlignedWithCleanupFallback(size_t n, size_t align,
-                                           void (*destructor)(void*));
-
-  void Init();
-
-  // Delete or Destruct all objects owned by the arena.
-  void CleanupList();
-
-  inline void CacheSerialArena(SerialArena* serial) {
-    thread_cache().last_serial_arena = serial;
-    thread_cache().last_lifecycle_id_seen = tag_and_id_;
-  }
-
-  PROTOBUF_NDEBUG_INLINE bool GetSerialArenaFast(SerialArena** arena) {
-    // If this thread already owns a block in this arena then try to use that.
-    // This fast path optimizes the case where multiple threads allocate from
-    // the same arena.
-    ThreadCache* tc = &thread_cache();
-    if (PROTOBUF_PREDICT_TRUE(tc->last_lifecycle_id_seen == tag_and_id_)) {
-      *arena = tc->last_serial_arena;
-      return true;
-    }
-    return false;
-  }
-
-  // Finds SerialArena or creates one if not found. When creating a new one,
-  // create a big enough block to accommodate n bytes.
-  SerialArena* GetSerialArenaFallback(size_t n);
-
-  template <AllocationClient alloc_client = AllocationClient::kDefault>
-  void* AllocateAlignedFallback(size_t n);
-
-  // Executes callback function over SerialArenaChunk. Passes const
-  // SerialArenaChunk*.
-  template <typename Functor>
-  void WalkConstSerialArenaChunk(Functor fn) const;
-
-  // Executes callback function over SerialArenaChunk.
-  template <typename Functor>
-  void WalkSerialArenaChunk(Functor fn);
-
-  // Executes callback function over SerialArena in chunked list in reverse
-  // chronological order. Passes const SerialArena*.
-  template <typename Functor>
-  void PerConstSerialArenaInChunk(Functor fn) const;
-
-  // Releases all memory except the first block which it returns. The first
-  // block might be owned by the user and thus need some extra checks before
-  // deleting.
-  SerialArena::Memory Free(size_t* space_allocated);
-
-#ifdef _MSC_VER
-#pragma warning(disable : 4324)
-#endif
-  struct alignas(kCacheAlignment) ThreadCache {
-    // Number of per-thread lifecycle IDs to reserve. Must be power of two.
-    // To reduce contention on a global atomic, each thread reserves a batch of
-    // IDs.  The following number is calculated based on a stress test with
-    // ~6500 threads all frequently allocating a new arena.
-    static constexpr size_t kPerThreadIds = 256;
-    // Next lifecycle ID available to this thread. We need to reserve a new
-    // batch, if `next_lifecycle_id & (kPerThreadIds - 1) == 0`.
-    uint64_t next_lifecycle_id{0};
-    // The ThreadCache is considered valid as long as this matches the
-    // lifecycle_id of the arena being used.
-    uint64_t last_lifecycle_id_seen{static_cast<uint64_t>(-1)};
-    SerialArena* last_serial_arena{nullptr};
-  };
-
-  // Lifecycle_id can be highly contended variable in a situation of lots of
-  // arena creation. Make sure that other global variables are not sharing the
-  // cacheline.
-#ifdef _MSC_VER
-#pragma warning(disable : 4324)
-#endif
-  using LifecycleId = uint64_t;
-  ABSL_CONST_INIT alignas(
-      kCacheAlignment) static std::atomic<LifecycleId> lifecycle_id_;
-#if defined(PROTOBUF_NO_THREADLOCAL)
-  // iOS does not support __thread keyword so we use a custom thread local
-  // storage class we implemented.
-  static ThreadCache& thread_cache();
-#elif defined(PROTOBUF_USE_DLLS)
-  // Thread local variables cannot be exposed through DLL interface but we can
-  // wrap them in static functions.
-  static ThreadCache& thread_cache();
-#else
-  ABSL_CONST_INIT static PROTOBUF_THREAD_LOCAL ThreadCache thread_cache_;
-  static ThreadCache& thread_cache() { return thread_cache_; }
-#endif
-
- public:
-  // kBlockHeaderSize is sizeof(ArenaBlock), aligned up to the nearest multiple
-  // of 8 to protect the invariant that pos is always at a multiple of 8.
-  static constexpr size_t kBlockHeaderSize = SerialArena::kBlockHeaderSize;
-  static constexpr size_t kSerialArenaSize =
-      (sizeof(SerialArena) + 7) & static_cast<size_t>(-8);
-  static constexpr size_t kAllocPolicySize =
-      ArenaAlignDefault::Ceil(sizeof(AllocationPolicy));
-  static constexpr size_t kMaxCleanupNodeSize = 16;
-  static_assert(kBlockHeaderSize % 8 == 0,
-                "kBlockHeaderSize must be a multiple of 8.");
-  static_assert(kSerialArenaSize % 8 == 0,
-                "kSerialArenaSize must be a multiple of 8.");
-};
-
 }  // namespace internal
 }  // namespace protobuf
 }  // namespace google
 
 #include "google/protobuf/port_undef.inc"
 
-#endif  // GOOGLE_PROTOBUF_ARENA_IMPL_H__
+#endif  // GOOGLE_PROTOBUF_SERIAL_ARENA_H__
diff --git a/src/google/protobuf/thread_safe_arena.h b/src/google/protobuf/thread_safe_arena.h
new file mode 100644
index 0000000..a5ef1c2
--- /dev/null
+++ b/src/google/protobuf/thread_safe_arena.h
@@ -0,0 +1,308 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2022 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This file defines the internal class ThreadSafeArena
+
+#ifndef GOOGLE_PROTOBUF_THREAD_SAFE_ARENA_H__
+#define GOOGLE_PROTOBUF_THREAD_SAFE_ARENA_H__
+
+#include <algorithm>
+#include <atomic>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "absl/synchronization/mutex.h"
+#include "google/protobuf/arena_align.h"
+#include "google/protobuf/arena_allocation_policy.h"
+#include "google/protobuf/arena_cleanup.h"
+#include "google/protobuf/arena_config.h"
+#include "google/protobuf/arenaz_sampler.h"
+#include "google/protobuf/port.h"
+#include "google/protobuf/serial_arena.h"
+
+// Must be included last.
+#include "google/protobuf/port_def.inc"
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// Tag type used to invoke the constructor of message-owned arena.
+// Only message-owned arenas use this constructor for creation.
+// Such constructors are internal implementation details of the library.
+struct MessageOwned {
+  explicit MessageOwned() = default;
+};
+
+// This class provides the core Arena memory allocation library. Different
+// implementations only need to implement the public interface below.
+// Arena is not a template type as that would only be useful if all protos
+// in turn would be templates, which will/cannot happen. However separating
+// the memory allocation part from the cruft of the API users expect we can
+// use #ifdef the select the best implementation based on hardware / OS.
+class PROTOBUF_EXPORT ThreadSafeArena {
+ public:
+  ThreadSafeArena();
+
+  // Constructor solely used by message-owned arena.
+  explicit ThreadSafeArena(internal::MessageOwned);
+
+  ThreadSafeArena(char* mem, size_t size);
+
+  explicit ThreadSafeArena(void* mem, size_t size,
+                           const AllocationPolicy& policy);
+
+  // All protos have pointers back to the arena hence Arena must have
+  // pointer stability.
+  ThreadSafeArena(const ThreadSafeArena&) = delete;
+  ThreadSafeArena& operator=(const ThreadSafeArena&) = delete;
+  ThreadSafeArena(ThreadSafeArena&&) = delete;
+  ThreadSafeArena& operator=(ThreadSafeArena&&) = delete;
+
+  // Destructor deletes all owned heap allocated objects, and destructs objects
+  // that have non-trivial destructors, except for proto2 message objects whose
+  // destructors can be skipped. Also, frees all blocks except the initial block
+  // if it was passed in.
+  ~ThreadSafeArena();
+
+  uint64_t Reset();
+
+  uint64_t SpaceAllocated() const;
+  uint64_t SpaceUsed() const;
+
+  template <AllocationClient alloc_client = AllocationClient::kDefault>
+  void* AllocateAligned(size_t n) {
+    SerialArena* arena;
+    if (PROTOBUF_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
+      return arena->AllocateAligned<alloc_client>(n);
+    } else {
+      return AllocateAlignedFallback<alloc_client>(n);
+    }
+  }
+
+  void ReturnArrayMemory(void* p, size_t size) {
+    SerialArena* arena;
+    if (PROTOBUF_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
+      arena->ReturnArrayMemory(p, size);
+    }
+  }
+
+  // This function allocates n bytes if the common happy case is true and
+  // returns true. Otherwise does nothing and returns false. This strange
+  // semantics is necessary to allow callers to program functions that only
+  // have fallback function calls in tail position. This substantially improves
+  // code for the happy path.
+  PROTOBUF_NDEBUG_INLINE bool MaybeAllocateAligned(size_t n, void** out) {
+    SerialArena* arena;
+    if (PROTOBUF_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
+      return arena->MaybeAllocateAligned(n, out);
+    }
+    return false;
+  }
+
+  void* AllocateAlignedWithCleanup(size_t n, size_t align,
+                                   void (*destructor)(void*));
+
+  // Add object pointer and cleanup function pointer to the list.
+  void AddCleanup(void* elem, void (*cleanup)(void*));
+
+  // Checks whether this arena is message-owned.
+  PROTOBUF_ALWAYS_INLINE bool IsMessageOwned() const {
+    return tag_and_id_ & kMessageOwnedArena;
+  }
+
+ private:
+  friend class ArenaBenchmark;
+  friend class TcParser;
+  friend class SerialArena;
+  friend struct SerialArenaChunkHeader;
+  static uint64_t GetNextLifeCycleId();
+
+  class SerialArenaChunk;
+
+  // Returns a new SerialArenaChunk that has {id, serial} at slot 0. It may
+  // grow based on "prev_num_slots".
+  static SerialArenaChunk* NewSerialArenaChunk(uint32_t prev_capacity, void* id,
+                                               SerialArena* serial);
+  static SerialArenaChunk* SentrySerialArenaChunk();
+
+  // Returns the first ArenaBlock* for the first SerialArena. If users provide
+  // one, use it if it's acceptable. Otherwise returns a sentry block.
+  ArenaBlock* FirstBlock(void* buf, size_t size);
+  // Same as the above but returns a valid block if "policy" is not default.
+  ArenaBlock* FirstBlock(void* buf, size_t size,
+                         const AllocationPolicy& policy);
+
+  // Adds SerialArena to the chunked list. May create a new chunk.
+  void AddSerialArena(void* id, SerialArena* serial);
+
+  // Members are declared here to track sizeof(ThreadSafeArena) and hotness
+  // centrally.
+
+  // Unique for each arena. Changes on Reset().
+  uint64_t tag_and_id_ = 0;
+
+  TaggedAllocationPolicyPtr alloc_policy_;  // Tagged pointer to AllocPolicy.
+  ThreadSafeArenaStatsHandle arena_stats_;
+
+  // Adding a new chunk to head_ must be protected by mutex_.
+  absl::Mutex mutex_;
+  // Pointer to a linked list of SerialArenaChunk.
+  std::atomic<SerialArenaChunk*> head_{nullptr};
+
+  void* first_owner_;
+  // Must be declared after alloc_policy_; otherwise, it may lose info on
+  // user-provided initial block.
+  SerialArena first_arena_;
+
+  // The LSB of tag_and_id_ indicates if the arena is message-owned.
+  enum : uint64_t { kMessageOwnedArena = 1 };
+
+  static_assert(std::is_trivially_destructible<SerialArena>{},
+                "SerialArena needs to be trivially destructible.");
+
+  const AllocationPolicy* AllocPolicy() const { return alloc_policy_.get(); }
+  void InitializeWithPolicy(const AllocationPolicy& policy);
+  void* AllocateAlignedWithCleanupFallback(size_t n, size_t align,
+                                           void (*destructor)(void*));
+
+  void Init();
+
+  // Delete or Destruct all objects owned by the arena.
+  void CleanupList();
+
+  inline void CacheSerialArena(SerialArena* serial) {
+    if (!IsMessageOwned()) {
+      thread_cache().last_serial_arena = serial;
+      thread_cache().last_lifecycle_id_seen = tag_and_id_;
+    }
+  }
+
+  PROTOBUF_NDEBUG_INLINE bool GetSerialArenaFast(SerialArena** arena) {
+    // If this thread already owns a block in this arena then try to use that.
+    // This fast path optimizes the case where multiple threads allocate from
+    // the same arena.
+    ThreadCache* tc = &thread_cache();
+    if (PROTOBUF_PREDICT_TRUE(tc->last_lifecycle_id_seen == tag_and_id_)) {
+      *arena = tc->last_serial_arena;
+      return true;
+    }
+    return false;
+  }
+
+  // Finds SerialArena or creates one if not found. When creating a new one,
+  // create a big enough block to accommodate n bytes.
+  SerialArena* GetSerialArenaFallback(size_t n);
+
+  template <AllocationClient alloc_client = AllocationClient::kDefault>
+  void* AllocateAlignedFallback(size_t n);
+
+  // Executes callback function over SerialArenaChunk. Passes const
+  // SerialArenaChunk*.
+  template <typename Functor>
+  void WalkConstSerialArenaChunk(Functor fn) const;
+
+  // Executes callback function over SerialArenaChunk.
+  template <typename Functor>
+  void WalkSerialArenaChunk(Functor fn);
+
+  // Executes callback function over SerialArena in chunked list in reverse
+  // chronological order. Passes const SerialArena*.
+  template <typename Functor>
+  void PerConstSerialArenaInChunk(Functor fn) const;
+
+  // Releases all memory except the first block which it returns. The first
+  // block might be owned by the user and thus need some extra checks before
+  // deleting.
+  SerialArena::Memory Free(size_t* space_allocated);
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4324)
+#endif
+  struct alignas(kCacheAlignment) ThreadCache {
+    // Number of per-thread lifecycle IDs to reserve. Must be power of two.
+    // To reduce contention on a global atomic, each thread reserves a batch of
+    // IDs.  The following number is calculated based on a stress test with
+    // ~6500 threads all frequently allocating a new arena.
+    static constexpr size_t kPerThreadIds = 256;
+    // Next lifecycle ID available to this thread. We need to reserve a new
+    // batch, if `next_lifecycle_id & (kPerThreadIds - 1) == 0`.
+    uint64_t next_lifecycle_id{0};
+    // The ThreadCache is considered valid as long as this matches the
+    // lifecycle_id of the arena being used.
+    uint64_t last_lifecycle_id_seen{static_cast<uint64_t>(-1)};
+    SerialArena* last_serial_arena{nullptr};
+  };
+
+  // Lifecycle_id can be highly contended variable in a situation of lots of
+  // arena creation. Make sure that other global variables are not sharing the
+  // cacheline.
+#ifdef _MSC_VER
+#pragma warning(disable : 4324)
+#endif
+  using LifecycleId = uint64_t;
+  ABSL_CONST_INIT alignas(
+      kCacheAlignment) static std::atomic<LifecycleId> lifecycle_id_;
+#if defined(PROTOBUF_NO_THREADLOCAL)
+  // iOS does not support __thread keyword so we use a custom thread local
+  // storage class we implemented.
+  static ThreadCache& thread_cache();
+#elif defined(PROTOBUF_USE_DLLS)
+  // Thread local variables cannot be exposed through DLL interface but we can
+  // wrap them in static functions.
+  static ThreadCache& thread_cache();
+#else
+  ABSL_CONST_INIT static PROTOBUF_THREAD_LOCAL ThreadCache thread_cache_;
+  static ThreadCache& thread_cache() { return thread_cache_; }
+#endif
+
+ public:
+  // kBlockHeaderSize is sizeof(ArenaBlock), aligned up to the nearest multiple
+  // of 8 to protect the invariant that pos is always at a multiple of 8.
+  static constexpr size_t kBlockHeaderSize = SerialArena::kBlockHeaderSize;
+  static constexpr size_t kSerialArenaSize =
+      (sizeof(SerialArena) + 7) & static_cast<size_t>(-8);
+  static constexpr size_t kAllocPolicySize =
+      ArenaAlignDefault::Ceil(sizeof(AllocationPolicy));
+  static constexpr size_t kMaxCleanupNodeSize = 16;
+  static_assert(kBlockHeaderSize % 8 == 0,
+                "kBlockHeaderSize must be a multiple of 8.");
+  static_assert(kSerialArenaSize % 8 == 0,
+                "kSerialArenaSize must be a multiple of 8.");
+};
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include "google/protobuf/port_undef.inc"
+
+#endif  // GOOGLE_PROTOBUF_THREAD_SAFE_ARENA_H__