Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
cuda/task_awaiters.hpp
Go to the documentation of this file.
1
7#pragma once
8
12#include <uni20/config.hpp>
13
14#if !UNI20_BACKEND_CUDA
15#error "CUDA task awaiters require the CUDA backend"
16#endif
17
18#include "cuda_error.hpp"
19
20#include <cuda_runtime_api.h>
21
22#include <atomic>
23
24namespace uni20::async
25{
26
28template <class Resource>
29class CudaResourceAcquireAwaiter : public CudaTaskAwaiterTag, private cuda::detail::ResourceWaiter<Resource> {
30 public:
32 : cuda::detail::ResourceWaiter<Resource>(&CudaResourceAcquireAwaiter::resource_available), pool_(&pool)
33 {}
34
39
40 [[nodiscard]] bool await_ready()
41 {
42 lease_ = pool_->try_acquire();
43 return lease_.has_value();
44 }
45
46 bool await_suspend(std::coroutine_handle<CudaTaskPromise> handle) noexcept
47 {
48 auto current = TaskHandle::from(handle);
49 state_.store(registration_state::registering, std::memory_order_relaxed);
50 task_ = handle.promise().take_ownership();
52
53 auto lease = pool_->acquire_or_enqueue(*this);
54 if (lease)
55 {
56 lease_ = std::move(*lease);
57 this->publish_ready();
58 }
59
60 auto const prior = state_.exchange(registration_state::suspended, std::memory_order_acq_rel);
61 if (prior == registration_state::ready) BasicTask::reschedule(std::move(task_));
62 return true;
63 }
64
66 {
67 CHECK(lease_.has_value());
68 return std::move(*lease_);
69 }
70
71 private:
72 static void resource_available(cuda::detail::ResourceWaiter<Resource>& waiter,
73 cuda::ResourceLease<Resource> lease) noexcept
74 {
75 auto& self = static_cast<CudaResourceAcquireAwaiter&>(waiter);
76 self.lease_ = std::move(lease);
77 self.publish_ready();
78 }
79
80 enum class registration_state
81 {
82 registering,
83 suspended,
84 ready
85 };
86
87 void publish_ready() noexcept
88 {
89 auto const prior = state_.exchange(registration_state::ready, std::memory_order_acq_rel);
90 if (prior == registration_state::suspended) BasicTask::reschedule(std::move(task_));
91 }
92
93 cuda::ResourcePool<Resource>* pool_;
94 std::optional<cuda::ResourceLease<Resource>> lease_;
95 BasicTask task_;
96 std::atomic<registration_state> state_{registration_state::registering};
97};
98
100class CudaStreamAcquireAwaiter : public CudaTaskAwaiterTag, private cuda::detail::StreamWaiter {
101 public:
103 : cuda::detail::StreamWaiter(&CudaStreamAcquireAwaiter::stream_available), pool_(&pool)
104 {}
105
110
111 [[nodiscard]] bool await_ready()
112 {
113 stream_ = pool_->try_acquire();
114 return stream_.has_value();
115 }
116
117 bool await_suspend(std::coroutine_handle<CudaTaskPromise> handle) noexcept
118 {
119 auto current = TaskHandle::from(handle);
120 state_.store(registration_state::registering, std::memory_order_relaxed);
121 task_ = handle.promise().take_ownership();
123
124 auto stream = pool_->acquire_or_enqueue(*this);
125 if (stream)
126 {
127 stream_ = std::move(*stream);
128 this->publish_ready();
129 }
130
131 auto const prior = state_.exchange(registration_state::suspended, std::memory_order_acq_rel);
132 if (prior == registration_state::ready) BasicTask::reschedule(std::move(task_));
133 return true;
134 }
135
137 {
138 CHECK(stream_.has_value());
139 int current_device = -1;
140 cuda::check(cudaGetDevice(&current_device), "cudaGetDevice after stream acquisition");
141 CHECK_EQUAL(current_device, stream_->device(), "CUDA task resumed outside the acquired stream's device");
142 return std::move(*stream_);
143 }
144
145 private:
146 static void stream_available(cuda::detail::StreamWaiter& waiter, cuda::Stream stream) noexcept
147 {
148 auto& self = static_cast<CudaStreamAcquireAwaiter&>(waiter);
149 self.stream_ = std::move(stream);
150 self.publish_ready();
151 }
152
153 enum class registration_state
154 {
155 registering,
156 suspended,
157 ready
158 };
159
160 void publish_ready() noexcept
161 {
162 auto const prior = state_.exchange(registration_state::ready, std::memory_order_acq_rel);
163 if (prior == registration_state::suspended) BasicTask::reschedule(std::move(task_));
164 }
165
166 cuda::StreamPool* pool_;
167 std::optional<cuda::Stream> stream_;
168 BasicTask task_;
169 std::atomic<registration_state> state_{registration_state::registering};
170};
171
178 public:
179 explicit constexpr CudaDeviceAwaiter(int device) noexcept : device_(device) {}
180
181 [[nodiscard]] constexpr bool await_ready() const noexcept { return false; }
182
183 bool await_suspend(std::coroutine_handle<CudaTaskPromise> handle) const
184 {
185 int current_device = -1;
186 cuda::check(cudaGetDevice(&current_device), "cudaGetDevice for CUDA task device selection");
187
188 auto current = TaskHandle::from(handle);
189 auto& promise = handle.promise();
190 promise.select_device(device_);
191 if (current_device == device_) return false;
192
194 BasicTask::reschedule(promise.take_ownership());
195 return true;
196 }
197
198 constexpr void await_resume() const noexcept {}
199
200 private:
201 int device_;
202};
203
204} // namespace uni20::async
205
206namespace uni20::cuda
207{
208
210template <class Resource>
211[[nodiscard]] inline async::CudaResourceAcquireAwaiter<Resource> acquire_resource(ResourcePool<Resource>& pool)
212{
214}
215
217[[nodiscard]] inline async::CudaStreamAcquireAwaiter acquire_stream(StreamPool& pool)
218{
220}
221
228[[nodiscard]] inline async::CudaDeviceAwaiter set_device(int device) noexcept
229{
230 return async::CudaDeviceAwaiter(device);
231}
232
237template <typename DeviceLike>
238 requires requires(DeviceLike const& device) {
239 { device.ordinal() } -> std::convertible_to<int>;
240 }
241[[nodiscard]] inline async::CudaDeviceAwaiter set_device(DeviceLike const& device) noexcept
242{
243 return set_device(static_cast<int>(device.ordinal()));
244}
245
246} // namespace uni20::cuda
static void reschedule(BasicTask task)
Reschedule a task when it becomes the sole ownership claimant.
Definition async_task_impl.hpp:11
Awaiter that establishes or changes a CUDA task's device affinity.
Definition cuda/task_awaiters.hpp:177
constexpr void await_resume() const noexcept
Definition cuda/task_awaiters.hpp:198
constexpr bool await_ready() const noexcept
Definition cuda/task_awaiters.hpp:181
bool await_suspend(std::coroutine_handle< CudaTaskPromise > handle) const
Definition cuda/task_awaiters.hpp:183
constexpr CudaDeviceAwaiter(int device) noexcept
Definition cuda/task_awaiters.hpp:179
CUDA-task awaiter for one provider resource from a fixed-capacity pool.
Definition cuda/task_awaiters.hpp:29
cuda::ResourceLease< Resource > await_resume()
Definition cuda/task_awaiters.hpp:65
bool await_ready()
Definition cuda/task_awaiters.hpp:40
CudaResourceAcquireAwaiter & operator=(CudaResourceAcquireAwaiter &&)=delete
CudaResourceAcquireAwaiter(CudaResourceAcquireAwaiter const &)=delete
bool await_suspend(std::coroutine_handle< CudaTaskPromise > handle) noexcept
Definition cuda/task_awaiters.hpp:46
CudaResourceAcquireAwaiter(CudaResourceAcquireAwaiter &&)=delete
CudaResourceAcquireAwaiter(cuda::ResourcePool< Resource > &pool) noexcept
Definition cuda/task_awaiters.hpp:31
CudaResourceAcquireAwaiter & operator=(CudaResourceAcquireAwaiter const &)=delete
CUDA-task awaiter for an actually-idle stream-pool lease.
Definition cuda/task_awaiters.hpp:100
cuda::Stream await_resume()
Definition cuda/task_awaiters.hpp:136
CudaStreamAcquireAwaiter(CudaStreamAcquireAwaiter &&)=delete
CudaStreamAcquireAwaiter(CudaStreamAcquireAwaiter const &)=delete
CudaStreamAcquireAwaiter & operator=(CudaStreamAcquireAwaiter &&)=delete
bool await_suspend(std::coroutine_handle< CudaTaskPromise > handle) noexcept
Definition cuda/task_awaiters.hpp:117
bool await_ready()
Definition cuda/task_awaiters.hpp:111
CudaStreamAcquireAwaiter & operator=(CudaStreamAcquireAwaiter const &)=delete
CudaStreamAcquireAwaiter(cuda::StreamPool &pool) noexcept
Definition cuda/task_awaiters.hpp:102
static TaskHandle from(std::coroutine_handle< Promise > handle) noexcept
Definition async_task_promise.hpp:991
static void note_suspended(TaskHandle handle) noexcept
Record that a coroutine has suspended and is waiting for resumption.
Definition async_task_promise.hpp:370
Move-only exclusive lease of one provider-resource pool slot.
Definition resource_pool.hpp:51
Fixed-capacity pool of preconstructed provider resources.
Definition resource_pool.hpp:96
Device-local pool whose streams become available only after their queued work completes.
Definition runtime.hpp:163
std::optional< Stream > acquire_or_enqueue(detail::StreamWaiter &waiter) noexcept
Register an internal awaiter unless a stream is immediately available.
Definition runtime.cpp:608
std::optional< Stream > try_acquire()
Try to lease one actually idle stream without waiting.
Definition runtime.cpp:576
Reference-counted lease of one CUDA stream-pool slot.
Definition runtime.hpp:65
cuda::ResourceLease< HandleSlot > handle
Definition cublas/execution.cpp:33
Structured CUDA runtime failures and checked-call helpers.
Defines the CUDA task admission type and scheduler interface.
This represents the reverse-mode adjoint for functions of complex variables, assuming a real-valued s...
Definition async/async.hpp:26
Definition cublas/execution.hpp:18
void check(cudaError_t status, std::string_view operation, std::optional< int > device, std::source_location where)
Raise when a CUDA runtime operation did not return cudaSuccess.
Definition cuda_error.cpp:44
Fixed-capacity pools and move-only leases for CUDA provider resources.
CUDA device, stream, resource, and scoped runtime primitives.
Marker base for awaiters that require a CudaTaskPromise handle.
Definition async_task_promise.hpp:29
#define CHECK(cond,...)
Definition trace.hpp:299
#define CHECK_EQUAL(a, b,...)
Definition trace.hpp:309