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

via libcxx-commits libcxx-commits at lists.llvm.org
Wed Oct 14 00:29:38 PDT 2026


Author: Nikolas Klauser
Date: 2026-10-14T09:29:32+02:00
New Revision: 98505e6e7322069e890d1f7b0c923ed64ad5b862

URL: https://github.com/llvm/llvm-project/commit/98505e6e7322069e890d1f7b0c923ed64ad5b862
DIFF: https://github.com/llvm/llvm-project/commit/98505e6e7322069e890d1f7b0c923ed64ad5b862.diff

LOG: [libc++] Use a custom once_flag implementation for locale::id::__get() (#212235)

This has multiple benefits:
- it removes the need to load multiple values and potentially acquire
locks
- it allows for a more compact representation of `locale::id`
- `__locale` doesn't make use of anything in `<mutex>` anymore

This also improves the startup time of programs a tiny bit.

The old `__flag_` member is now used for the ID, since that has been
zero-initialized in all versions of libc++, which isn't the case for the
old `__id_` member.

Added: 
    

Modified: 
    libcxx/docs/ABIGuarantees.md
    libcxx/include/__configuration/abi.h
    libcxx/include/__locale_dir/locale.h
    libcxx/src/include/atomic_support.h
    libcxx/src/locale.cpp
    libcxx/test/libcxx/localization/locales/locale/locale.types/locale.id/id.abi.compile.pass.cpp
    libcxx/utils/libcxx/test/features/libcxx_macros.py

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/ABIGuarantees.md b/libcxx/docs/ABIGuarantees.md
index 7e8ca40cde763..e895a8c8c5bb1 100644
--- a/libcxx/docs/ABIGuarantees.md
+++ b/libcxx/docs/ABIGuarantees.md
@@ -131,6 +131,11 @@ 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 
diff erent releases of libc++
 
 libc++ supports linking TUs which have been compiled against 
diff erent releases of libc++ by marking symbols with

diff  --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index 86f59cb548a8f..9343eacf39344 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -78,6 +78,7 @@
 #  define _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION
 #  define _LIBCPP_ABI_TRIVIALLY_COPYABLE_BIT_ITERATOR
 #  define _LIBCPP_ABI_USE_SMALL_DEQUE_BLOCK_SIZE
+#  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_dir/locale.h b/libcxx/include/__locale_dir/locale.h
index 67c108c859687..dd4604e841906 100644
--- a/libcxx/include/__locale_dir/locale.h
+++ b/libcxx/include/__locale_dir/locale.h
@@ -19,7 +19,6 @@
 #  include <__locale_dir/locale_base_api.h>
 #  include <__memory/addressof.h>
 #  include <__memory/shared_count.h>
-#  include <__mutex/once_flag.h>
 #  include <__utility/no_destroy.h>
 #  include <__utility/private_constructor_tag.h>
 #  include <__utility/swap.h>
@@ -160,11 +159,20 @@ class _LIBCPP_EXPORTED_FROM_ABI locale::facet : public __shared_count {
 };
 
 class _LIBCPP_EXPORTED_FROM_ABI locale::id {
-  once_flag __flag_;
-  int32_t __id_;
+#  ifdef _LIBCPP_ABI_COMPACT_LOCALE_ID
+  using __id_type _LIBCPP_NODEBUG = int32_t;
+#  else
+  using __id_type _LIBCPP_NODEBUG = uintptr_t;
+#  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/include/atomic_support.h b/libcxx/src/include/atomic_support.h
index 305a53e2ee2e0..04484f261060a 100644
--- a/libcxx/src/include/atomic_support.h
+++ b/libcxx/src/include/atomic_support.h
@@ -60,6 +60,16 @@ inline _LIBCPP_HIDE_FROM_ABI bool __libcpp_atomic_compare_exchange(
   return __atomic_compare_exchange_n(__val, __expected, __after, true, __success_order, __fail_order);
 }
 
+template <class _ValueType>
+inline _LIBCPP_HIDE_FROM_ABI bool __libcpp_atomic_compare_exchange_strong(
+    _ValueType* __val,
+    _ValueType* __expected,
+    _ValueType __after,
+    int __success_order = _AO_Seq,
+    int __fail_order    = _AO_Seq) {
+  return __atomic_compare_exchange_n(__val, __expected, __after, false, __success_order, __fail_order);
+}
+
 #else // _LIBCPP_HAS_THREADS
 
 enum __libcpp_atomic_order { _AO_Relaxed, _AO_Consume, _AO_Acquire, _AO_Release, _AO_Acq_Rel, _AO_Seq };
@@ -103,6 +113,17 @@ __libcpp_atomic_compare_exchange(_ValueType* __val, _ValueType* __expected, _Val
   return false;
 }
 
+template <class _ValueType>
+inline _LIBCPP_HIDE_FROM_ABI bool __libcpp_atomic_compare_exchange_strong(
+    _ValueType* __val, _ValueType* __expected, _ValueType __after, int = 0, int = 0) {
+  if (*__val == *__expected) {
+    *__val = __after;
+    return true;
+  }
+  *__expected = *__val;
+  return false;
+}
+
 #endif // _LIBCPP_HAS_THREADS
 
 _LIBCPP_END_NAMESPACE_STD

diff  --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index 058bc0ec6f901..7795e9eab2fc7 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_Relaxed);
+  if (val >= first_id)
+    return val - first_id;
+
+  if (val == uninitialized && __libcpp_atomic_compare_exchange_strong(&__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>

diff  --git a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.id/id.abi.compile.pass.cpp b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.id/id.abi.compile.pass.cpp
index 4db686c5941aa..abd2c878a9692 100644
--- a/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.id/id.abi.compile.pass.cpp
+++ b/libcxx/test/libcxx/localization/locales/locale/locale.types/locale.id/id.abi.compile.pass.cpp
@@ -8,6 +8,8 @@
 //
 // UNSUPPORTED: no-threads, c++03
 
+// UNSUPPORTED: libcpp-abi-compact-locale-id
+
 // <mutex>
 
 // Check the ABI of std::locale::id

diff  --git a/libcxx/utils/libcxx/test/features/libcxx_macros.py b/libcxx/utils/libcxx/test/features/libcxx_macros.py
index 33d569e75e6bc..38f761af009a7 100644
--- a/libcxx/utils/libcxx/test/features/libcxx_macros.py
+++ b/libcxx/utils/libcxx/test/features/libcxx_macros.py
@@ -35,6 +35,7 @@
     "_LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING": "libcpp-abi-no-compressed-pair-padding",
     "_LIBCPP_PSTL_BACKEND_LIBDISPATCH": "libcpp-pstl-backend-libdispatch",
     "_LIBCPP_ABI_USE_SMALL_DEQUE_BLOCK_SIZE": "libcpp-abi-use-small-deque-block-size",
+    "_LIBCPP_ABI_COMPACT_LOCALE_ID": "libcpp-abi-compact-locale-id",
 }
 for macro, feature in macros.items():
     features.append(


        


More information about the libcxx-commits mailing list