diff --git a/include/exec/libdispatch_queue.hpp b/include/exec/libdispatch_queue.hpp index 5f717cacf..1c9bb6065 100644 --- a/include/exec/libdispatch_queue.hpp +++ b/include/exec/libdispatch_queue.hpp @@ -117,6 +117,8 @@ namespace experimental::execution struct libdispatch_scheduler { + using scheduler_concept = STDEXEC::scheduler_tag; + bool operator==(libdispatch_scheduler const &) const = default; struct domain diff --git a/include/exec/linux/io_uring_context.hpp b/include/exec/linux/io_uring_context.hpp index 011cf1fb6..1ebc197d5 100644 --- a/include/exec/linux/io_uring_context.hpp +++ b/include/exec/linux/io_uring_context.hpp @@ -1226,6 +1226,8 @@ namespace experimental::execution class __scheduler { public: + using scheduler_concept = STDEXEC::scheduler_tag; + __context* __context_; auto operator==(__scheduler const &) const -> bool = default; diff --git a/include/exec/reschedule.hpp b/include/exec/reschedule.hpp index 51b30d3c6..736e90f01 100644 --- a/include/exec/reschedule.hpp +++ b/include/exec/reschedule.hpp @@ -48,6 +48,8 @@ namespace experimental::execution struct __scheduler { + using scheduler_concept = scheduler_tag; + struct __sender { using sender_concept = sender_tag; diff --git a/include/exec/static_thread_pool.hpp b/include/exec/static_thread_pool.hpp index 1dcee0aa0..8946b59bc 100644 --- a/include/exec/static_thread_pool.hpp +++ b/include/exec/static_thread_pool.hpp @@ -341,6 +341,8 @@ namespace experimental::execution struct scheduler { + using scheduler_concept = scheduler_tag; + private: template friend struct _opstate; diff --git a/include/exec/thread_pool_base.hpp b/include/exec/thread_pool_base.hpp index 47a892204..09c278d46 100644 --- a/include/exec/thread_pool_base.hpp +++ b/include/exec/thread_pool_base.hpp @@ -138,6 +138,8 @@ namespace experimental::execution struct scheduler { + using scheduler_concept = STDEXEC::scheduler_tag; + private: template friend struct _pool_::opstate; diff --git a/include/exec/timed_thread_scheduler.hpp b/include/exec/timed_thread_scheduler.hpp index b84f1d8f4..45a8914d8 100644 --- a/include/exec/timed_thread_scheduler.hpp +++ b/include/exec/timed_thread_scheduler.hpp @@ -365,8 +365,9 @@ namespace experimental::execution class timed_thread_scheduler { public: - using time_point = std::chrono::steady_clock::time_point; - using duration = std::chrono::steady_clock::duration; + using scheduler_concept = STDEXEC::scheduler_tag; + using time_point = std::chrono::steady_clock::time_point; + using duration = std::chrono::steady_clock::duration; class schedule_at_sender { diff --git a/include/exec/trampoline_scheduler.hpp b/include/exec/trampoline_scheduler.hpp index c11a8eb63..449385ab1 100644 --- a/include/exec/trampoline_scheduler.hpp +++ b/include/exec/trampoline_scheduler.hpp @@ -111,6 +111,8 @@ namespace experimental::execution std::size_t __max_recursion_size_; public: + using scheduler_concept = scheduler_tag; + constexpr __scheduler() noexcept : __attrs{16} , __max_recursion_size_(4096) diff --git a/include/exec/windows/windows_thread_pool.hpp b/include/exec/windows/windows_thread_pool.hpp index 0ca885c68..f41776d52 100644 --- a/include/exec/windows/windows_thread_pool.hpp +++ b/include/exec/windows/windows_thread_pool.hpp @@ -891,7 +891,8 @@ namespace experimental::execution::__win32 class windows_thread_pool::scheduler { public: - using time_point = filetime_clock::time_point; + using scheduler_concept = STDEXEC::scheduler_tag; + using time_point = filetime_clock::time_point; [[nodiscard]] auto schedule() const noexcept -> schedule_sender diff --git a/include/nvexec/multi_gpu_context.cuh b/include/nvexec/multi_gpu_context.cuh index 77a452245..3f5940693 100644 --- a/include/nvexec/multi_gpu_context.cuh +++ b/include/nvexec/multi_gpu_context.cuh @@ -31,6 +31,8 @@ namespace nv::execution { struct multi_gpu_stream_scheduler : private stream_scheduler_env { + using scheduler_concept = STDEXEC::scheduler_tag; + multi_gpu_stream_scheduler(int num_devices, context ctx) : num_devices_(num_devices) , ctx_(ctx) diff --git a/include/nvexec/stream_context.cuh b/include/nvexec/stream_context.cuh index 5d5b60342..deb5fc4ef 100644 --- a/include/nvexec/stream_context.cuh +++ b/include/nvexec/stream_context.cuh @@ -69,6 +69,8 @@ namespace nv::execution struct stream_scheduler : private stream_scheduler_env { + using scheduler_concept = STDEXEC::scheduler_tag; + explicit stream_scheduler(context ctx) noexcept : ctx_(ctx) {} diff --git a/include/stdexec/__detail/__parallel_scheduler.hpp b/include/stdexec/__detail/__parallel_scheduler.hpp index 954e9d074..821e439f7 100644 --- a/include/stdexec/__detail/__parallel_scheduler.hpp +++ b/include/stdexec/__detail/__parallel_scheduler.hpp @@ -293,6 +293,8 @@ namespace STDEXEC class parallel_scheduler { public: + using scheduler_concept = scheduler_tag; + parallel_scheduler() = delete; /// Returns `true` iff `*this` refers to the same scheduler as the argument. diff --git a/test/exec/test_task.cpp b/test/exec/test_task.cpp index dfc1e2d0e..97f036f59 100644 --- a/test/exec/test_task.cpp +++ b/test/exec/test_task.cpp @@ -21,7 +21,9 @@ # include # include # include +# include # include +# include # include @@ -370,6 +372,42 @@ namespace CHECK(count == 3); } # endif // !STDEXEC_NO_STDCPP_EXCEPTIONS() + + // Regression tests for https://github.com/NVIDIA/stdexec/issues/2134: the + // schedulers of exec::static_thread_pool and exec::timed_thread_context used + // to be missing the `scheduler_concept` nested alias required by the + // scheduler concept, which made exec::task's internal __any_scheduler + // conversion fail to compile when such a task was co_started on them. + TEST_CASE("task - can be started on a static_thread_pool scheduler", "[types][task]") + { + exec::static_thread_pool pool{2}; + bool ran = false; + // Keep the closure a temporary inside the full expression: the task is + // lazy, so the closure (and its by-reference capture) must outlive the + // point at which the pool thread resumes the coroutine. + auto op_state = STDEXEC::sync_wait(STDEXEC::starts_on(pool.get_scheduler(), + [&]() -> exec::task + { + ran = true; + co_return; + }())); + REQUIRE(op_state); + CHECK(ran); + } + + TEST_CASE("task - can be started on a timed_thread_context scheduler", "[types][task]") + { + exec::timed_thread_context ctx; + bool ran = false; + auto op_state = STDEXEC::sync_wait(STDEXEC::starts_on(ctx.get_scheduler(), + [&]() -> exec::task + { + ran = true; + co_return; + }())); + REQUIRE(op_state); + CHECK(ran); + } } // namespace #endif