[libcxx-commits] [libcxx] [libc++] Use a custom once_flag implementation for locale::id::__get() (PR #212235)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jul 27 06:26:58 PDT 2026


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/212235

>From 7412f724e1ce09a72864bedeaa41f374745a5040 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 27 Jul 2026 14:22:32 +0200
Subject: [PATCH] [libc++] Use a custom once_flag implementation for
 locale::id::__get()

---
 libcxx/docs/ABIGuarantees.rst        |  4 ++++
 libcxx/include/__configuration/abi.h |  1 +
 libcxx/include/__locale              | 18 ++++++++++++++----
 libcxx/src/locale.cpp                | 26 +++++++++++++++++++++++---
 4 files changed, 42 insertions(+), 7 deletions(-)

diff --git a/libcxx/docs/ABIGuarantees.rst b/libcxx/docs/ABIGuarantees.rst
index c5ebe9ad76be8..1a71d40a537f8 100644
--- a/libcxx/docs/ABIGuarantees.rst
+++ b/libcxx/docs/ABIGuarantees.rst
@@ -134,6 +134,10 @@ flag removes that artificial padding.
 ``char_traits<char_type>::eq_int_type()`` cannot distinguish between ``WEOF`` and ``WCHAR_MAX``. This flag changes
 ``basic_ios`` to instead track whether the fill value has been initialized using a separate boolean.
 
+``_LIBCPP_ABI_COMPACT_LOCALE_ID``
+---------------------------------
+This flag removes some ``locale::id`` members and makes the remaining ones smaller. In an old version ``locale::id``
+used ``once_flag``, which got replaced by a custom implementation that doesn't require as much memory.
 
 Linking TUs which have been compiled against different releases of libc++
 =========================================================================
diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index 757ba69cb17b4..994b90a299406 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -79,6 +79,7 @@
 #  define _LIBCPP_ABI_TRIVIALLY_COPYABLE_BIT_ITERATOR
 #  define _LIBCPP_ABI_USE_SMALL_DEQUE_BLOCK_SIZE
 #  define _LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE
+#  define _LIBCPP_ABI_COMPACT_LOCALE_ID
 
 #elif _LIBCPP_ABI_VERSION == 1
 // Feature macros for disabling pre ABI v1 features. All of these options
diff --git a/libcxx/include/__locale b/libcxx/include/__locale
index f878aa48071ca..c04320a064653 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -18,7 +18,6 @@
 #  include <__locale_dir/locale_base_api.h>
 #  include <__memory/addressof.h>
 #  include <__memory/shared_count.h>
-#  include <__mutex/once_flag.h>
 #  include <__type_traits/make_unsigned.h>
 #  include <__utility/no_destroy.h>
 #  include <__utility/private_constructor_tag.h>
@@ -159,11 +158,22 @@ private:
 };
 
 class _LIBCPP_EXPORTED_FROM_ABI locale::id {
-  once_flag __flag_;
-  int32_t __id_;
+#  ifdef _LIBCPP_ABI_COMPACT_LOCALE_ID
+  using __id_type = int32_t;
+#  elif defined(_LIBCPP_ABI_MICROSOFT)
+  using __id_type = uintptr_t;
+#  else
+  using __id_type = unsigned long;
+#  endif
+
+  __id_type __id_ = 0;
+
+#ifndef _LIBCPP_ABI_COMPACT_LOCALE_ID
+  [[__maybe_unused__]] int32_t __dummy_ = 0;
+#endif
 
 public:
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR id() : __id_(0) {}
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR id() = default;
   void operator=(const id&) = delete;
   id(const id&)             = delete;
 
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index 40613e72b1a9d..deb1ebe591aad 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -563,9 +563,29 @@ void locale::facet::__on_zero_shared() noexcept { delete this; }
 // locale::id
 
 long locale::id::__get() {
-  constinit static int32_t next_id = 0;
-  call_once(__flag_, [&] { __id_ = __libcpp_atomic_add(&next_id, 1); });
-  return __id_ - 1;
+  constexpr __id_type uninitialized = 0;
+  constexpr __id_type pending       = 1;
+  constexpr __id_type first_id      = 2;
+
+  constinit static __id_type next_id = first_id;
+
+  auto val = __libcpp_atomic_load(&__id_, _AO_Acquire);
+  if (val >= first_id)
+    return val - first_id;
+
+  if (val == uninitialized && __libcpp_atomic_compare_exchange(&__id_, &val, pending)) {
+      auto new_id = __libcpp_atomic_add(&next_id, 1) - 1;
+      __libcpp_atomic_store(&__id_, new_id);
+      return new_id - first_id;
+  }
+
+  // Another thread is already initializing __id_. Wait for that in a spin loop. The initialization should be incredibly
+  // fast, a spin loop should be efficient enough.
+  while (true) {
+    auto id = __libcpp_atomic_load(&__id_);
+    if (id >= first_id)
+      return id - first_id;
+  }
 }
 
 // template <> class collate_byname<char>



More information about the libcxx-commits mailing list