[libcxx-commits] [libcxx] [libc++] Consolidate ordering/hashing/formatting of std::thread::id. (PR #195763)
Petr Hosek via libcxx-commits
libcxx-commits at lists.llvm.org
Tue May 5 00:19:26 PDT 2026
================
@@ -35,24 +39,70 @@ template <>
struct hash<__thread_id>;
class __thread_id {
- // FIXME: pthread_t is a pointer on Darwin but a long on Linux.
- // NULL is the no-thread value on Darwin. Someone needs to check
- // on other platforms. We assume 0 works everywhere for now.
__libcpp_thread_id __id_;
- static _LIBCPP_HIDE_FROM_ABI bool
- __lt_impl(__thread_id __x, __thread_id __y) _NOEXCEPT { // id==0 is always less than any other thread_id
+ // Even though __libcpp_thread_id is provided by underlying threading implementation
+ // (e.g. C11, pthreads, or Windows) its type may still be unspecified. E.g. for pthreads
+ // implementation __libcpp_thread_id is an alias for pthread_t, which is left unspecified
+ // in POSIX. Typically it's either an integral type (glibc) or a pointer (Apple systems),
+ // but it can also be an opaque type on some systems / libc implementations.
+ //
+ // Note that in order to satisfy standard requirements on std::thread::id, we need:
+ // * strong total order
+ // * formatter support
+ // * std::hash implementation
+ // Currently, we can implement all of the above only on pointer and integral types.
+ using _Tp = __libcpp_thread_id;
+ static_assert(is_pointer<_Tp>::value || is_integral<_Tp>::value,
+ "unsupported thread::id type, please file a bug report");
+
+ // Strong total order implementation.
+ // Here we provide a best-effort implementation of strong total order, comparing
+ // integral types as-is and routing pointers through uintptr_t for a well-defined comparison.
+ template <typename Thr, typename enable_if<is_pointer<typename Thr::_Tp>::value, int>::type = 0>
+ static _LIBCPP_HIDE_FROM_ABI bool __eq_impl(Thr __x, Thr __y) _NOEXCEPT {
+ return reinterpret_cast<uintptr_t>(__x.__id_) == reinterpret_cast<uintptr_t>(__y.__id_);
+ }
+ template <typename Thr, typename enable_if<is_integral<typename Thr::_Tp>::value, int>::type = 0>
+ static _LIBCPP_HIDE_FROM_ABI bool __eq_impl(Thr __x, Thr __y) _NOEXCEPT {
+ return __x.__id_ == __y.__id_;
+ }
+
+ template <typename Thr, typename enable_if<is_pointer<typename Thr::_Tp>::value, int>::type = 0>
+ static _LIBCPP_HIDE_FROM_ABI bool __lt_impl(Thr __x, Thr __y) _NOEXCEPT {
+ return reinterpret_cast<uintptr_t>(__x.__id_) < reinterpret_cast<uintptr_t>(__y.__id_);
+ }
+ template <typename Thr, typename enable_if<is_integral<typename Thr::_Tp>::value, int>::type = 0>
+ static _LIBCPP_HIDE_FROM_ABI bool __lt_impl(Thr __x, Thr __y) _NOEXCEPT {
+ // For integral thread IDs, assume 0 is always less than any other thread_id.
if (__x.__id_ == 0)
return __y.__id_ != 0;
if (__y.__id_ == 0)
return false;
- return __libcpp_thread_id_less(__x.__id_, __y.__id_);
+ return __x.__id_ < __y.__id_;
}
+ // Hashing implementation.
+ // Simply use the underlying pointer or integral types as-is.
+ using _HashTp = _Tp;
+ _LIBCPP_HIDE_FROM_ABI _HashTp __hash_value() const { return __id_; }
+
+ // Formatter implementation.
+ // Note the output should match what the stream operator does. Since
+ // the ostream operator has been shipped years before the formatter
+ // was added to the Standard, our logic mimics what the stream
+ // operator does (i.e. prints thread-id as integer, but uses a hexadecimal
+ // format if it's represented by a pointer)
+ using _FormatterTp = conditional<is_integral<_Tp>::value, _Tp, uintptr_t>::type;
+
+ static _LIBCPP_CONSTEXPR const bool __PRINT_AS_HEX = is_pointer<_Tp>::value;
+
+ _LIBCPP_HIDE_FROM_ABI _FormatterTp __get_formatter_value() const { return reinterpret_cast<_FormatterTp>(__id_); }
+
public:
- _LIBCPP_HIDE_FROM_ABI __thread_id() _NOEXCEPT : __id_(0) {}
+ _LIBCPP_HIDE_FROM_ABI __thread_id() _NOEXCEPT : __id_(__libcpp_thread_id()) {}
- _LIBCPP_HIDE_FROM_ABI void __reset() { __id_ = 0; }
+ _LIBCPP_HIDE_FROM_ABI void __reset() { __id_ = __libcpp_thread_id(); }
----------------
petrhosek wrote:
We already have the `_LIBCPP_NULL_THREAD` macro to handle the initialization case which should be used here.
```suggestion
_LIBCPP_HIDE_FROM_ABI __thread_id() _NOEXCEPT : __id_(_LIBCPP_NULL_THREAD) {}
_LIBCPP_HIDE_FROM_ABI void __reset() { __id_ = _LIBCPP_NULL_THREAD; }
```
https://github.com/llvm/llvm-project/pull/195763
More information about the libcxx-commits
mailing list