|
ThreadSchedule 3.0.0
Modern C++ thread management library
|
Version 3.0.0 is a deliberate, source-breaking API reset with a smaller portable core, explicit error handling, and a separate advanced API for specialized and platform-specific functionality.
thread, jthread, thread_view, thread_pool, scheduled_pool, scheduled_task, thread_registry, and their configuration/value types. There are no deprecated aliases or compatibility wrappers.<threadschedule/threadschedule.hpp> and the new <threadschedule/core.hpp> include the complete core, but no longer include profiles, topology, futures, chaos testing, task groups, or specialized pools.<threadschedule/advanced/> and to the threadschedule::advanced namespace. <threadschedule/advanced.hpp> is their complete umbrella.cgroup.threads/tasks controls and never falls back to the process-wide cgroup.procs file or creates lookalike files in an ordinary directory.result<T>, an alias for the library-owned expected<T, std::error_code>. Direct construction remains the normal, potentially throwing path; create(...) and explicitly named *_or_throw operations make the alternate policy visible.expected library-owned in every language mode and added implicit conversion to matching C++23 std::expected specializations, including rvalue conversion of move-only values and errors.expected state changes exception-safe, aligned copy/move traits with the stored types, avoided constructing an inactive error in expected<void, E>, and supported void results from both transform specializations.ThreadWrapper with the std::thread-backed thread. It joins on destruction, supports direct construction, and accepts an optional thread_config containing name, portable scheduling, and affinity.jthread with standard stop-token injection and move-only argument forwarding. The C++17 JThreadWrapper = ThreadWrapper fallback from 2.4.0 was removed.ThreadWrapperView with the control-only thread_view; ownership and lifecycle operations stay on the original standard or ThreadSchedule thread. It accepts std::thread and thread, plus both jthread forms under C++20. The 2.4 PThreadWrapper and ThreadInfo families were removed.advanced::thread_by_name_view as the lowercase replacement for ThreadByNameView. Exact singular lookup rejects duplicate names, find_all returns deterministic matches, and every control operation checks both kernel-visible TID liveness and its start-time generation before acting. Registry control blocks share the same identity check.this_thread operations for configuring the calling thread without first wrapping or registering it.scheduling_config, priority_level, schedule::* factories, and thread_affinity. Native handles and raw OS IDs live under advanced; native scheduling is performed through the platform API rather than a duplicate public type system.cpu_id, thread_id, nice_value, realtime_priority, and worker_count value types plus explicit worker_registration. Invalid direct construction throws invalid_argument; matching create(...) factories return result<T>. Pool worker count zero no longer acts as an automatic sentinel.thread_id values that cannot fit the platform thread-ID type instead of narrowing them onto a different thread. advanced::native_id(thread_id) therefore returns result<native_thread_id>.thread_config is a patch: omitted properties remain unchanged, while set_name("") is distinct from clear_name().system_error.TIME_CRITICAL for portable requests.thread, jthread, and thread_view objects created from already-running standard threads because their kernel TID cannot be recovered portably.thread, jthread, thread_view, this_thread, and registry-backed views. Linux SCHED_IDLE reads as the effective lowest priority/nice 19, while policies without meaningful nice semantics report operation_not_supported.-20..19 and realtime priorities as 1..99, preserve concrete pthread/Win32 errors, reject affinity masks that cannot be represented losslessly, and verify affinity changes by exact readback with rollback where possible.group * 64 + index, while a single affinity mask may target only one group.shutdown_for() calls honor the deadline without joining an already-running callable; queued work is discarded at expiry and a later blocking shutdown or destruction joins the remaining workers.parallel_for_each consistently reject work after shutdown with operation_canceled, including inline_pool, and protected work-stealing worker startup from random_device failures.move_only_function, matching std::function.lightweight_pool::shutdown_for() that could delay a finite timed shutdown until its deadline or block an effectively unbounded shutdown indefinitely as the final active task completed.shutdown_for() calls apply their deadline while waiting for another shutdown operation, rather than first blocking indefinitely on lifecycle serialization.inline_pool use the same storage semantics.move_only_function fallback.value_too_large and replaced unchecked steady_clock arithmetic that could overflow and execute a maximally delayed task immediately.shutdown_for timeouts at steady_clock::time_point::max() instead of overflowing during conversion from milliseconds; non-positive timeouts expire immediately.drop_pending when a due task had already moved from the scheduler queue into the worker queue but had not started.scheduled_pool and specialized scheduled-pool destruction from one of their own callbacks transfer cleanup to a reaper thread, matching the safe last-owner behavior of thread_pool.thread_pool facade. submit() and post() are non-throwing submission operations; their throwing counterparts are submit_or_throw() and post_or_throw(). Construction, worker configuration, waiting, and shutdown have matching result-based paths.thread_pool_config with worker count, worker registration, a shared thread_config, destruction policy, and a task-error callback. This replaces the PoolWithErrors/ThreadPoolWithErrors adapter family for ordinary pool use.advanced types. The generic 2.4.0 pool bases and policy types are no longer public extension points. The advanced types are composition facades with worker_count, portable configuration, result<T>, explicit throwing names, and public statistics values.scheduled_pool, scheduled_pool_config, and scheduled_task. Worker and scheduler-thread settings, shutdown policy, registration, and task-error reporting can be supplied at construction.when_any executes deferred futures instead of waiting forever, and a throwing future_with_error_handler callback no longer replaces the future's original exception.resource_deadlock_would_occur, moved-from objects remain safely inspectable, dropped tasks release futures and captures, worker-construction failures unwind, and concurrent shutdown/submission paths preserve accepted work according to the selected policy.thread_pool owner from one of its own tasks hand cleanup to a reaper thread instead of terminating when the backend attempts to join the current worker.parallel_for_each at compile time. task_group::wait() now includes child tasks submitted to the same group by work it is already tracking.task_group fork/wait deadlocks on saturated pools by executing recursive and same-worker child submissions inline, and rejected direct waits from a task tracked by that group with resource_deadlock_would_occur.task_group was destroyed by another worker while tracked work was still submitting child tasks; destruction now waits for that work, and task wrappers retain a lifetime-independent context token.chaos_controller destruction reliably interrupt long wait intervals without a lost wake-up, captured predicate failures instead of terminating the process, and added a fallback seed when random_device is unavailable.thread_pool::size() reports its configured worker count after shutdown and returns zero only after move.auto_register_current_thread retain the selected owning registry backend so a guard can safely outlive its local thread_registry facade without dereferencing freed storage during unregister.ThreadRegistry, RegisteredThreadInfo, and AutoRegisterCurrentThread with thread_registry, registered_thread, and auto_register_current_thread. The public registry exposes portable snapshots and configuration by live native ID instead of the 2.4.0 control-block and chainable-query implementation.registry() to global_registry() and replaced set_external_registry() with scoped global_registry_binding. The binding owns backend lifetime and restores the previous registry. Registration retains a guarded native control object so stale, exited, unregistered, or replaced entries cannot be configured.global_registry_binding objects are move-assigned, and made out-of-order binding destruction skip predecessors whose scopes have already ended.advanced::composite_thread_registry and cgroup attachment to the advanced Linux surface.ThreadSchedule::Runtime, but changed its C++ ABI and made it opt-in by default. It now exports only the internal registry-storage hooks and current_build_mode(); all participating binaries must be rebuilt with the same v3 headers and a compatible toolchain.<threadschedule/advanced/testing/chaos_controller.hpp>.task_error and error_callback; task_error::std_id is clearly distinct from the registry's native thread_id.ThreadSchedule::Module, coroutine task/generator helpers, C++26 reflection APIs, reflection-backed registry queries, standard-dependent ranges overloads, and the public concepts/callable utility headers.auto_register_current_thread or pool configuration.ThreadSchedule::ThreadSchedule; the optional shared registry remains ThreadSchedule::Runtime. The interface target now requires C++17 without raising a consumer's selected newer standard.THREADSCHEDULE_MODULE, THREADSCHEDULE_ENABLE_REFLECTION, THREADSCHEDULE_STABLE_ABI, and THREADSCHEDULE_STABLE_ABI_STRICT options. THREADSCHEDULE_RUNTIME and THREADSCHEDULE_BUILD_DOCS now default to OFF.AnyNewerVersion to SameMajorVersion and stopped rewriting a parent project's MSVC runtime flags or injecting _WIN32_WINNT into consumers.test_package for both the header-only package and the optional shared runtime.jthread.scope_exit, Win32 unique_handle, try_result, and a single SBO-capable move_only_function<Signature, InlineSize> whose representation does not vary between C++17 and C++26. Removed the former internal move/SBO callable paths and manual owned-handle cleanup.THREADSCHEDULE_WARNINGS_AS_ERRORS for strict local and CI builds without applying -Werror to third-party source files, and handled Google Benchmark's Clang 22 __COUNTER__ diagnostic at the dependency boundary.This release adds an explicit stable-ABI subset for shared-runtime / DSO boundaries, introduces a migration path with deprecations before hard enforcement, and expands ABI-focused regression coverage.
threadschedule::abi::* helpers with opaque registry_handle, POD-style thread_info_view, stable status codes, and a dedicated abi::AutoRegisterCurrentThread path for cross-DSO integration. (abi.hpp, runtime_registry.cpp)threadschedule::abi::is_abi_stable_v<T> trait and THREADSCHEDULE_VALIDATE_STABLE_ABI_EXPORT(...) macro let library and downstream code mark and enforce signatures that are safe to export across a stable ABI boundary. (abi.hpp)THREADSCHEDULE_STABLE_ABI=ON for migration builds and THREADSCHEDULE_STABLE_ABI_STRICT=ON for hard enforcement. In runtime mode, the strict path rejects ABI-unsafe entry points such as registry(), set_external_registry(ThreadRegistry*), and the legacy AutoRegisterCurrentThread constructors at compile time. (CMakeLists.txt, thread_registry.hpp)THREADSCHEDULE_STABLE_ABI=ON is enabled without strict mode, the existing runtime-facing C++ helpers remain available but are marked deprecated with guidance toward threadschedule::abi::*. This keeps default builds source-compatible while making ABI-unsafe usage visible before it becomes a hard error. (export.hpp, thread_registry.hpp)thread_registry.hpp, runtime_registry.cpp, chaos.hpp)ThreadPriority now supports the POSIX real-time priority range up to 99 while preserving nice-style ordering for regular scheduling. Windows priority mapping was corrected so higher ThreadSchedule priorities map to higher Win32 thread priorities, and FIFO/RR policies now accept native real-time values where larger numbers mean higher priority. (scheduler_policy.hpp, pthread_wrapper.hpp, profiles.hpp)ThreadRegistry* exports are rejected, and runtime registry() usage transitions from deprecation in migration mode to hard failure in strict mode. (tests/abi_test.cpp, tests/CMakeLists.txt)integration_tests/runtime_abi_compat/*)This release focuses on ABI hardening for mixed-standard consumers of the shared runtime.
threadschedule::expected is now always the library-owned type** – the public expected alias no longer flips over to std::expected in C++23+. This stabilizes exported signatures across consumers compiled with different language modes and avoids cross-DSO ABI mismatches when an intermediate library exposes ThreadSchedule result types. (expected.hpp)THREADSCHEDULE_RUNTIME define so consumers call into the shared runtime instead of accidentally instantiating a separate header-only registry. (thread_registry.hpp, CMakeLists.txt)conanfile.py, CMakeLists.txt)This release adds an opt-in GCC 16/C++26 reflection surface, modernizes callable/callback storage paths for newer standard libraries, expands the benchmark and reporting tooling, and improves current-thread
ThreadInfohandling for more direct native-thread operations.
THREADSCHEDULE_ENABLE_REFLECTION=ON, and working -freflection support, the library now exports threadschedule::reflect::* helpers for field metadata, field visitation, compile-time projection, and type/field naming. Reflection support is now disabled by default and only activates on supported toolchains when explicitly requested. (reflection.hpp, threadschedule.cppm, threadschedule.hpp, CMakeLists.txt)ThreadRegistry and QueryView now expose field-oriented helpers such as where<registered_thread_fields::componentTag()>(...), where_if<registered_thread_fields::alive()>(...), find_by<registered_thread_fields::name()>(...), contains<...>(...), and project<...>() when reflection is enabled. (thread_registry.hpp)callable.hpp now centralizes modern callable selection with fallback aliases for std::move_only_function, std::copyable_function, and std::function_ref, while preserving compatibility on older standard libraries. (callable.hpp)ThreadInfo now prefers the native handle – default-constructed ThreadInfo binds the current thread's native handle and uses the more direct pthread/HANDLE-based paths for current-thread name, affinity, policy, and priority operations, while ThreadInfo(Tid) remains available for explicit TID-bound access. (thread_wrapper.hpp, scheduler_policy.hpp)filter(...).map(...) layering when callers opt into the new reflection APIs. This reduces intermediate traversal and avoids some full-entry transformation work for hot query paths. (thread_registry.hpp)std::define_static_string(...) and reused through consteval helpers such as field_names<T>(), reducing repeated compile-time reconstruction of the same metadata. (reflection.hpp)callable.hpp, thread_pool.hpp, thread_registry.hpp, error_handler.hpp)benchmarks/callable_std_benchmarks.cpp, benchmarks/reflection_registry_benchmarks.cpp, benchmarks/threadpool_benchmarks.cpp, benchmarks/CMakeLists.txt)run_benchmark_graphs.sh, benchmarks/generate_benchmark_report.py, benchmarks/generate_readme_graphs.py, docs/benchmarks/*.svg, benchmarks/README.md)README.md)THREADSCHEDULE_ENABLE_REFLECTION, its default-off behavior, and the GCC 16+/C++26 activation path. (docs/CMAKE_REFERENCE.md)tests/reflection_test.cpp, tests/registry_query_test.cpp, tests/CMakeLists.txt)function_ref behavior and public callback alias usage on the new callable abstraction paths. (tests/callable_test.cpp, tests/CMakeLists.txt)ThreadInfo regression coverage – tests now verify the default-construction path can still resolve current-thread identity and read current-thread metadata while the explicit Tid constructor continues to control a remote target thread. (tests/thread_config_test.cpp)ubuntu-24.04 jobs for reflection-enabled GCC 16/C++26 validation: one job builds and runs the reflection-focused test cases, and a second job verifies the reflection-enabled module build path. This makes the new THREADSCHEDULE_ENABLE_REFLECTION surface visible in CI instead of relying only on the generic C++26 matrix entry. (.github/workflows/tests.yml).github/workflows/tests.yml)No intended API/ABI breaking changes. This release extends thread-control coverage to library-owned background threads and expands
ThreadInfointo a lightweight per-thread control handle.
ThreadInfo now supports bound thread IDs** – it can be default-constructed for the current thread or explicitly constructed from a Tid, then used to set_name, get_name, set_priority, set_scheduling_policy, set_affinity, get_affinity, get_policy, and get_priority. The existing static convenience methods remain available. (thread_wrapper.hpp)ScheduledThreadPoolT exposes scheduler_thread_info() and configure_scheduler_thread(...), and ChaosController exposes thread_info() and configure_thread(...), so the scheduler/control threads are no longer anonymous internal std::threads. (scheduled_pool.hpp, chaos.hpp)ThreadWrappers and receive stable default names, keeping thread-control behavior consistent across the library. (scheduled_pool.hpp, chaos.hpp)std::move_only_function (C++23+ libraries), reusable hooks/callbacks can use std::copyable_function (C++26-capable libraries), and older standards keep the std::function fallback. Public aliases remain source-compatible while new templated setter/registration overloads avoid unnecessary type-erasure constraints. (callable.hpp, thread_pool.hpp, scheduled_pool.hpp, error_handler.hpp, thread_registry.hpp, thread_pool_with_errors.hpp, pthread_wrapper.hpp)post/try_post and scheduler one-shot dispatch can carry move-only captures directly instead of forcing a copyable std::function path on newer standard libraries. This reduces adaptation overhead for fire-and-forget workloads and enables more modern task payloads without wrapper glue. (thread_pool.hpp, scheduled_pool.hpp, thread_pool_with_errors.hpp, pthread_wrapper.hpp)post tasks, move-only scheduled tasks, move-only FutureWithErrorHandler::on_error(...) callbacks, PoolWithErrors with move-only arguments, and ThreadInfo(Tid) invalid-target behavior. (thread_pool_v2_test.cpp, futures_test.cpp, thread_config_test.cpp)callable_benchmarks compares small capture, large capture, and move-only capture posting overhead on ThreadPool and HighPerformancePool as a local performance validation tool. (benchmarks/callable_benchmarks.cpp, benchmarks/CMakeLists.txt)ubuntu-24.04 jobs for gcc-16/g++-16 and clang-22/clang++-22, extending verification of the modern callable and C++26 code paths without replacing the existing matrix. (.github/workflows/tests.yml)No API/ABI breaking changes. All modifications are bug fixes (aligning behaviour with documented API), internal optimizations, additive overloads, new classes, and new tests/infrastructure.
when_all<T> no longer requires default-constructible T** – the results.emplace_back() on the exception path was removed. The vector is never consumed when an exception is rethrown. (futures.hpp)when_any no longer busy-polls at 1 ms** – exponential backoff (1 ms → 16 ms cap) and a randomized start index eliminate CPU waste and index bias. Empty input now throws std::invalid_argument instead of looping forever. (futures.hpp)ScheduledThreadPoolT::insert_task checks stop_** – scheduling a task after shutdown() now returns a pre-cancelled ScheduledTaskHandle instead of silently inserting a task that will never execute. (scheduled_pool.hpp)ChaosController uses actual thread priority** – priority jitter now reads the real scheduling priority via sched_getparam() on Linux instead of hardcoding ThreadPriority::normal(). (chaos.hpp)ErrorHandler::handle_error releases the lock before invoking callbacks** – callbacks are snapshot-copied under the mutex, then executed outside the critical section, eliminating deadlock risk when callbacks interact with the handler. (error_handler.hpp)PoolWithErrors documentation corrected** – the doc comment now says "implicitly movable" instead of the incorrect "non-movable". (thread_pool_with_errors.hpp)distribute_affinities_by_numa calls read_topology() once** – the previous implementation read sysfs O(n) times for n threads. New additive overloads affinity_for_node(CpuTopology const&, ...) and distribute_affinities_by_numa(CpuTopology const&, ...) accept a pre-read topology snapshot. (topology.hpp)InlinePool** – deterministic, single-threaded pool that executes every task synchronously on the calling thread. Same submit/post/try_submit API as ThreadPool, making it a drop-in for unit tests. (inline_pool.hpp)task_group<Pool>** – structured concurrency primitive. All submitted tasks are guaranteed to complete before wait() returns (or the destructor runs). First exception is captured and rethrown from wait(). (task_group.hpp)PoolWithErrors forwarding constructor** – new 2+ argument constructor forwards pool-specific arguments (e.g. deque_capacity for HighPerformancePool). (thread_pool_with_errors.hpp)apply_profile_detailed()** – new function returning a std::vector<std::error_code> with one entry per configuration step, unlike apply_profile() which aggregates into a single error code. (profiles.hpp)threadschedule.cppm: when_all, when_any, when_all_settled, ShutdownPolicy, IndefiniteWait, PollingWait, ThreadPoolBase, LightweightPoolT, LightweightPool, GlobalPool, PoolWithErrors, ScheduledLightweightPool, TaskStartCallback, TaskEndCallback, schedule_on, run_on, pool_executor, InlinePool, task_group, apply_profile_detailed.thread_pool_v2_test.cpp – try_submit, try_post, submit_batch, parallel_for_each, ShutdownPolicy, LightweightPool, GlobalPool, ScheduledThreadPool, stop-token tasks, InlinePool, task_group.futures_test.cpp – when_all, when_any, when_all_settled (typed and void variants, empty input, exception propagation).registry_query_test.cpp – chainable QueryView API: filter, map, for_each, find_if, any/all/none, take, skip.coroutine_pool_test.cpp – schedule_on, run_on, pool_executor, nested awaits, cross-pool hops, exception propagation (C++20 coroutines).sanitizers.yml workflow with:ThreadPool and FastThreadPool are now type aliases** for ThreadPoolBase<IndefiniteWait> and ThreadPoolBase<PollingWait>. Behavior is unchanged, but code that forward-declares or specializes on the concrete class name may need adjustment.configure_threads(), set_affinity(), distribute_across_cpus()** on ThreadPool and FastThreadPool now return expected<void, std::error_code> instead of bool. HighPerformancePool already used this return type. Migration: if (pool.configure_threads(...)) still compiles (expected has operator bool), but code that stores the result in a bool variable needs updating to auto or the expected type.ThreadPool::Statistics** now includes tasks_per_second and avg_task_time fields (previously only on FastThreadPool and HighPerformancePool).submit_range() removed** from ThreadPool. Use submit_batch() instead (consistent with FastThreadPool and HighPerformancePool). submit_batch() is also more efficient: it acquires the queue lock once for the entire batch instead of per-item.GlobalThreadPool::submit_range() removed**. Use GlobalThreadPool::submit_batch().HighPerformancePoolWithErrors, FastThreadPoolWithErrors, ThreadPoolWithErrors** are now type aliases for PoolWithErrors<Pool>. The public API is unchanged.GlobalThreadPool, GlobalHighPerformancePool** are now type aliases for GlobalPool<Pool>. The public API is unchanged.ErrorHandler::remove_callback(id) / has_callback(id)** – callbacks are now stored in a std::map with stable IDs. Individual callbacks can be removed without clearing all of them.try_submit() / try_submit_batch()** – non-throwing submission for all pool types, returning expected<std::future<T>, std::error_code> instead of throwing on shutdown.parallel_for_each – ThreadPoolBase now uses the same chunked work distribution as HighPerformancePool via a shared detail::parallel_for_each_chunked helper (one task per element is gone).PollingWait<IntervalMs>** – tunable polling interval (default 10 ms). FastThreadPool is ThreadPoolBase<PollingWait<>>.HighPerformancePool deque capacity** – configurable via constructor: HighPerformancePool(threads, deque_capacity).GlobalPool::init(n)** – pre-configure thread count before first use (std::call_once semantics).submit_batch(range), try_submit_batch(range), parallel_for_each(range, func) on all pool types and GlobalPool. Guarded by __cpp_lib_ranges.register_workers flag on both pool constructors. Workers register/unregister automatically via AutoRegisterCurrentThread RAII guard.set_on_task_start(callback) and set_on_task_end(callback) on both pool types. Callbacks receive timestamp, thread ID, and (for end) elapsed duration.submit(stop_token, F, Args...) and try_submit(stop_token, F, Args...) overloads. Tasks are skipped if stop is requested. Guarded by __cpp_lib_jthread.futures.hpp with when_all, when_any, when_all_settled (typed and void specializations).ShutdownPolicy::drain (default) and ShutdownPolicy::drop_pending. shutdown(policy) replaces the old no-argument shutdown(). shutdown_for(timeout) provides timed drain.schedule_on{pool} awaitable to hop to a pool thread, executor_base / pool_executor<Pool> type-erased executor for pool-aware tasks, run_on(pool, coro_fn) convenience returning std::future.LightweightPoolT<TaskSize>** – ultra-lightweight fire-and-forget pool using a custom detail::SboCallable<TaskSize> with configurable inline buffer (default 64 bytes = 1 cache line, 56 bytes usable). Zero heap allocations for typical lambdas. No futures, no packaged_task, no statistics, no tracing. Workers are ThreadWrapper so configure_threads/set_affinity still work. using LightweightPool = LightweightPoolT<> for the default.post() / try_post()** – fire-and-forget submission on all pool types (HighPerformancePool, ThreadPoolBase, GlobalPool). Same queue logic as submit() but skips packaged_task/shared_ptr/future overhead.ScheduledThreadPoolT now uses post()** internally instead of submit(), eliminating wasted future allocations for every scheduled task dispatch. New alias: ScheduledLightweightPool = ScheduledThreadPoolT<LightweightPool>.ThreadPoolBase<WaitPolicy> - parameterized single-queue thread pool.IndefiniteWait / PollingWait<IntervalMs> - wait policy types for ThreadPoolBase.PoolWithErrors<PoolType> - generic error-handling pool wrapper.GlobalPool<PoolType> - generic singleton pool accessor.ShutdownPolicy - enum controlling shutdown behavior (drain / drop_pending).TaskStartCallback / TaskEndCallback - tracing callback types.executor_base / pool_executor<Pool> - type-erased executor for coroutines.schedule_on<Pool> - awaitable for hopping to a pool thread.futures.hpp - future combinators (when_all, when_any, when_all_settled).LightweightPoolT<TaskSize> / LightweightPool - fire-and-forget pool with SBO.detail::SboCallable<TaskSize> - type-erased callable with inline storage.ScheduledLightweightPool - scheduled pool backed by LightweightPool.thread_pool.hpp, thread_pool_with_errors.hpp, thread_wrapper.hpp, thread_registry.hpp, pthread_wrapper.hpp, profiles.hpp, and scheduled_pool.hpp.detail::apply_priority(), detail::apply_scheduling_policy(), and detail::apply_affinity() free functions (overloaded for pthread_t, pid_t, and HANDLE). BaseThreadWrapper, ThreadControlBlock, PThreadWrapper, and ThreadByNameView now delegate to these shared implementations.apply_profile() overloads** refactored to use shared detail::apply_profile_to() and detail::apply_profile_to_pool() helpers.ScheduledThreadPoolT**: schedule_at() and schedule_periodic_after() now share a private insert_task() helper.configure_threads(), set_affinity(), distribute_across_cpus() in HighPerformancePool and ThreadPoolBase now delegate to shared detail::configure_worker_threads, detail::set_worker_affinity, detail::distribute_workers_across_cpus templates.set_name(), get_name(), get_affinity() across BaseThreadWrapper, PThreadWrapper, and ThreadControlBlock now delegate to detail::apply_name, detail::read_name, detail::read_affinity in scheduler_policy.hpp.FutureWithErrorHandler<void> specialization removed**: The primary template now handles both T and void via if constexpr, eliminating ~70 lines of duplicated code. No API change.CompositeThreadRegistry facade deduplicated**: The 12 query facade methods (filter, map, for_each, find_if, any, all, none, take, skip, count, empty, apply) are now inherited from detail::QueryFacadeMixin<Derived> CRTP base. No API change.ThreadRegistry inherits detail::QueryFacadeMixin**: The 12 facade methods (filter, map, for_each, find_if, any, all, none, take, skip, count, empty, apply) are now provided by the same CRTP mixin as CompositeThreadRegistry, eliminating the duplicate implementations.apply_priority and apply_scheduling_policy for both pthread_t and pid_t now share a common detail::apply_sched_params template, eliminating duplicated param validation and error handling.ThreadRegistry::register_current_thread consolidated**: Both overloads now delegate to a private try_register(RegisteredThreadInfo) method, removing the duplicated lock/emplace/callback logic.PoolWithErrors submit methods consolidated**: submit() and submit_with_description() now delegate to a private submit_impl with optional description parameter.TaskError::capture() factory**: New static factory method centralizes the repeated exception/thread_id/timestamp capture pattern. Used by ErrorHandledTask and PoolWithErrors.ThreadControlBlock native handle accessor**: Private native_handle() method replaces four identical #ifdef _WIN32 dispatch blocks in the set_affinity/set_priority/set_scheduling_policy/set_name methods.Full step-by-step guide: docs/MIGRATION_V2.md in v2.4.0.
Quick reference:
scheduled_pool.hpp, especially around queueing semantics and execution ordering guarantees for scheduled workloads.*WrapperReg types (ThreadWrapperReg, JThreadWrapperReg, PThreadWrapperReg) now have explicit move constructor and move assignment operator, enabling default-construct-then-assign patterns (e.g. JThreadWrapperReg t; t = JThreadWrapperReg(...);).*WrapperReg wrapping lambdas now use std::invoke, so member function pointers work as callables (e.g. JThreadWrapperReg("n", "c", &MyClass::run, this)).JThreadWrapperReg now correctly forwards std::stop_token to callables that accept it, while also supporting callables without stop_token - the previous auto&&... wrapper always claimed to accept a token, causing a compile error when the user's callable did not.AutoRegisterCurrentThread move constructor and move assignment now correctly transfer externalReg_, preventing unregister from the wrong registry after a move._MSVC_LANG) in thread_wrapper.hpp and concepts.hpp, matching the guard already used in registered_threads.hpp. Fixes compile errors on MSVC without /Zc:__cplusplus.apply_profile template can now be instantiated with ThreadWrapper, JThreadWrapper, ThreadWrapperView, JThreadWrapperView, and PThreadWrapper via new is_thread_like specialisations. Previously the template was constrained to std::thread/std::jthread which lack the required scheduling API.FastThreadPool::set_affinity() and FastThreadPool::wait_for_tasks() for API parity with ThreadPool and HighPerformancePool.WithErrors wrappers - HighPerformancePoolWithErrors::set_affinity(), FastThreadPoolWithErrors::set_affinity() and FastThreadPoolWithErrors::wait_for_tasks().JThreadWrapper / JThreadWrapperView jthread-specific methods now use trailing return types, [[nodiscard]], const, and noexcept consistently with the rest of the library.ThreadPriority factory methods are now [[nodiscard]] and noexcept; comparison operators are now constexpr noexcept.[[nodiscard]] to query methods across WorkStealingDeque, all pool classes, and ScheduledTaskHandle.thread_local std::random_device in HighPerformancePool::worker_function.task<T> (task.hpp) - a lazy single-value coroutine that starts execution only when co_awaited. Includes full task<void> specialisation and exception propagation.sync_wait(task<T>) / sync_wait(task<void>) - blocking bridge that runs a task on the calling thread and returns its result.generator<T> (generator.hpp) - a lazy multi-value coroutine producing elements via co_yield. Supports range-based for loops (begin() / end() with std::default_sentinel_t). Automatically aliases std::generator<T> when C++23 __cpp_lib_generator is available.threadschedule.cppm).BuildMode enum, build_mode(), build_mode_string()) to distinguish header-only from runtime builds at compile time and runtime.src/threadschedule.cppm) re-exporting the full public API..clang-format (IndentPPDirectives: AfterHash) for clearer preprocessor indentation.expected.hpp header detection - check <version> or <experimental/version> presence before including <expected>.expected.hpp for maintainability.ThreadScheduleRuntime now output libthreadscheduled.so instead of libthreadschedule.so to distinguish debug from release artifactsGetThreadGroupAffinity in include/threadschedule/thread_wrapper.hppintegration_tests/runtime_abi_compat to validate ABI compatibility (shared runtime) between current library and older tagsRUNTIME_ABI_OLD_REF or RUNTIME_ABI_OLD_OFFSETabi-compat.yml to run ABI tests on Linux and Windows for the last 3 tags; allowed failure only on major version bumps (or when explicitly enabled)integration_tests/README.md with usage for ABI compatibility scenarioapply_profile)ThreadControlBlock and RegisteredThreadInfoexpected class and error handlingThreadWrapperView, JThreadWrapperView)