Context
threaded_pipeline_t (#197) and H5Qall.hpp (#195) require C++20. Three primitives drive that requirement:
| Primitive |
Header |
C++17 substitute |
std::bit_ceil |
<bit> |
3-line constexpr helper — trivial |
std::stop_token / std::stop_source |
<stop_token> |
std::atomic<bool> flag — easy |
std::jthread |
<thread> |
std::thread + RAII dtor wrapper — easy |
std::atomic::wait() / notify_one() / notify_all() |
<atomic> |
std::mutex + std::condition_variable — meaningful |
The atomic wait/notify maps directly to OS futex primitives (Linux futex, Windows WaitOnAddress, macOS ulock_wait) with no mutex — that is what makes the queues lock-free on the contention path. The condvar replacement reintroduces a mutex per wait/notify call, breaking lock-freedom.
For h5cpp's actual workload (large chunks, CPU-bound compression, H5Dwrite_chunk serialized to the main thread) the condvar overhead is below the noise floor. Correctness is fully preserved.
Approach
Do not modify H5Qall.hpp — it is a self-contained MIT-licensed component and stays C++20-pure.
Introduce h5cpp/detail/doorbell.hpp selected at compile time via the C++20 feature-test macro:
#ifdef __cpp_lib_atomic_wait
using doorbell_t = atomic_doorbell_t; // lock-free, C++20
#else
using doorbell_t = condvar_doorbell_t; // mutex+condvar, C++17
#endif
H5Zpipeline_threaded.hpp builds its internal queues on doorbell_t instead of directly on std::atomic. The C++20 path is unchanged. The C++17 path is correct and requires no downstream changes.
std::jthread and std::stop_token in H5Zpipeline_threaded.hpp get the same treatment via a thin detail::stoppable_thread_t wrapper over std::thread + std::atomic<bool>.
Constraints
H5Qall.hpp must remain C++20-only — do not add #ifdef guards inside it.
- The C++20 code path must be bit-for-bit identical to the current implementation.
- Two commits: (1)
detail/doorbell.hpp + detail/stoppable_thread.hpp abstractions, (2) wire H5Zpipeline_threaded.hpp onto them and drop the hard #error guard.
- C++17 minimum: GCC 9, Clang 9, MSVC 19.14 (VS 2017 15.7).
Motivation
Targets HPC environments and older toolchains that mandate C++17. Decision on priority pending outcome of HDFGroup platform discussion (Gerd Heber, 2026-05-23).
Context
threaded_pipeline_t(#197) andH5Qall.hpp(#195) require C++20. Three primitives drive that requirement:std::bit_ceil<bit>std::stop_token/std::stop_source<stop_token>std::atomic<bool>flag — easystd::jthread<thread>std::thread+ RAII dtor wrapper — easystd::atomic::wait()/notify_one()/notify_all()<atomic>std::mutex+std::condition_variable— meaningfulThe atomic wait/notify maps directly to OS futex primitives (Linux
futex, WindowsWaitOnAddress, macOSulock_wait) with no mutex — that is what makes the queues lock-free on the contention path. The condvar replacement reintroduces a mutex per wait/notify call, breaking lock-freedom.For h5cpp's actual workload (large chunks, CPU-bound compression,
H5Dwrite_chunkserialized to the main thread) the condvar overhead is below the noise floor. Correctness is fully preserved.Approach
Do not modify
H5Qall.hpp— it is a self-contained MIT-licensed component and stays C++20-pure.Introduce
h5cpp/detail/doorbell.hppselected at compile time via the C++20 feature-test macro:H5Zpipeline_threaded.hppbuilds its internal queues ondoorbell_tinstead of directly onstd::atomic. The C++20 path is unchanged. The C++17 path is correct and requires no downstream changes.std::jthreadandstd::stop_tokeninH5Zpipeline_threaded.hppget the same treatment via a thindetail::stoppable_thread_twrapper overstd::thread+std::atomic<bool>.Constraints
H5Qall.hppmust remain C++20-only — do not add#ifdefguards inside it.detail/doorbell.hpp+detail/stoppable_thread.hppabstractions, (2) wireH5Zpipeline_threaded.hpponto them and drop the hard#errorguard.Motivation
Targets HPC environments and older toolchains that mandate C++17. Decision on priority pending outcome of HDFGroup platform discussion (Gerd Heber, 2026-05-23).