[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
Tue Sep 15 02:43:20 PDT 2026
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/212235
>From 0c17d3e69c78faac4a69f688593be4ba0de7f97e 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_dir/locale.h | 16 +++++++++---
libcxx/src/include/atomic_support.h | 21 +++++++++++++++
libcxx/src/locale.cpp | 26 ++++++++++++++++---
.../locale.id/id.abi.compile.pass.cpp | 2 ++
.../libcxx/test/features/libcxx_macros.py | 1 +
7 files changed, 64 insertions(+), 7 deletions(-)
diff --git a/libcxx/docs/ABIGuarantees.rst b/libcxx/docs/ABIGuarantees.rst
index ee9d1296757c4..dc07a7ac46a1d 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_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 2228624901ac4..11246cf91e29a 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 9ac22e07c549c..ca7acdd5029d1 100644
--- a/libcxx/utils/libcxx/test/features/libcxx_macros.py
+++ b/libcxx/utils/libcxx/test/features/libcxx_macros.py
@@ -36,6 +36,7 @@
"_LIBCPP_PSTL_BACKEND_LIBDISPATCH": "libcpp-pstl-backend-libdispatch",
"_LIBCPP_ABI_USE_SMALL_DEQUE_BLOCK_SIZE": "libcpp-abi-use-small-deque-block-size",
"_LIBCPP_ABI_VECTORIZED_MERSENNE_TWISTER_ENGINE": "libcpp-abi-vectorized-mersenne-twister-engine",
+ "_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