cuda-api-wrappers
Thin C++-flavored wrappers for the CUDA Runtime API
current_context.hpp
Go to the documentation of this file.
1 
4 #pragma once
5 #ifndef CUDA_API_WRAPPERS_CURRENT_CONTEXT_HPP_
6 #define CUDA_API_WRAPPERS_CURRENT_CONTEXT_HPP_
7 
8 #include "error.hpp"
9 #include "constants.hpp"
10 #include "types.hpp"
11 
12 namespace cuda {
13 
15 class device_t;
16 class context_t;
17 namespace device {
18 class primary_context_t;
19 } // namespace device
21 
22 namespace context {
23 
24 namespace current {
25 
30 inline bool exists()
31 {
32  context::handle_t handle;
33  auto status = cuCtxGetCurrent(&handle);
34  if (status == cuda::status::not_yet_initialized) {
35  return false;
36  }
37  throw_if_error_lazy(status, "Failed obtaining the current context's handle");
38  return (handle != context::detail_::none);
39 }
40 
41 
42 namespace detail_ {
49 inline bool is_(handle_t handle)
50 {
51  handle_t current_context_handle;
52  auto status = cuCtxGetCurrent(&current_context_handle);
53  switch(status) {
54  case CUDA_ERROR_NOT_INITIALIZED:
55  case CUDA_ERROR_INVALID_CONTEXT:
56  return false;
57  case CUDA_SUCCESS:
58  return (handle == current_context_handle);
59  default:
60  throw cuda::runtime_error(status,
61  "Failed determining whether there's a current context, or what it is");
62  }
63 }
64 
65 struct status_and_handle_pair {
66  status_t status;
67  handle_t handle;
68 };
69 
78 inline status_and_handle_pair get_with_status()
79 {
80  handle_t handle;
81  auto status = cuCtxGetCurrent(&handle);
82  if (status == status::not_yet_initialized) {
83  handle = context::detail_::none;
84  }
85  return { status, handle };
86 }
87 
94 inline handle_t get_handle()
95 {
96  auto p = get_with_status();
97  throw_if_error_lazy(p.status, "Failed obtaining the current context's handle");
98  return p.handle;
99 }
100 
101 // Note: not calling this get_ since flags are read-only anyway
102 inline context::flags_t get_flags()
103 {
104  context::flags_t result;
105  auto status = cuCtxGetFlags(&result);
106  throw_if_error_lazy(status, "Failed obtaining the current context's flags");
107  // Note: Not sanitizing the flags from having CU_CTX_MAP_HOST set
108  return result;
109 }
110 
111 inline device::id_t get_device_id()
112 {
113  device::id_t device_id;
114  auto result = cuCtxGetDevice(&device_id);
115  throw_if_error_lazy(result, "Failed obtaining the current context's device");
116  return device_id;
117 }
118 
127 inline void push(handle_t context_handle)
128 {
129  auto status = cuCtxPushCurrent(context_handle);
130  throw_if_error_lazy(status, "Failed pushing to the top of the context stack: "
131  + context::detail_::identify(context_handle));
132 }
133 
146 inline bool push_if_not_on_top(handle_t context_handle)
147 {
148  if (get_handle() == context_handle) { return false; }
149  push(context_handle);
150  return true;
151 }
152 
153 inline context::handle_t pop()
154 {
155  handle_t popped_context_handle;
156  auto status = cuCtxPopCurrent(&popped_context_handle);
157  throw_if_error_lazy(status, "Failed popping the current CUDA context");
158  return popped_context_handle;
159 }
160 
161 inline void set(handle_t context_handle)
162 {
163  // Thought about doing this:
164  // if (detail_::get_handle() == context_handle_) { return; }
165  // ... but decided against it.
166  auto status = cuCtxSetCurrent(context_handle);
167  throw_if_error_lazy(status,
168  "Failed setting the current context to " + context::detail_::identify(context_handle));
169 }
170 
171 } // namespace detail_
172 
173 namespace detail_ {
177 class scoped_override_t {
178 public:
179  bool hold_primary_context_ref_unit_;
180  device::id_t device_id_or_0_;
181 
182  explicit scoped_override_t(handle_t context_handle) : scoped_override_t(false, 0, context_handle) {}
183  scoped_override_t(device::id_t device_for_which_context_is_primary, handle_t context_handle)
184  : scoped_override_t(true, device_for_which_context_is_primary, context_handle) {}
185  explicit scoped_override_t(bool hold_primary_context_ref_unit, device::id_t device_id, handle_t context_handle);
186  scoped_override_t(const scoped_override_t&) = delete;
188  scoped_override_t& operator=(const scoped_override_t&) = delete;
189  scoped_override_t& operator=(scoped_override_t&&) = delete;
190  ~scoped_override_t() noexcept(false);
191 };
192 
194 
195 /*
196  * This macro is intended for use inside the cuda-api-wrappers implementation, to
197  * save us some typing; it's quite usable on the outside, but you probably want to
198  * use the context_t objects, and for safety (e.g. w.r.t. primary device contexts),
199  * prefer @ref SET_CUDA_CONTEXT_FOR_THIS_SCOPE instead.
200  */
201 #define CAW_SET_SCOPE_CONTEXT(context_handle_expr_) \
202 const ::cuda::context::current::detail_::scoped_override_t caw_context_for_this_scope_(context_handle_expr_)
203 
210 class scoped_ensurer_t {
211 public:
212  bool context_was_pushed_on_construction;
213 
214  explicit scoped_ensurer_t(bool force_push, handle_t fallback_context_handle)
215  : context_was_pushed_on_construction(force_push)
216  {
217  if (force_push) { push(fallback_context_handle); }
218  }
219 
220  explicit scoped_ensurer_t(handle_t fallback_context_handle)
221  : scoped_ensurer_t(not exists(), fallback_context_handle)
222  {}
223 
224  scoped_ensurer_t(const scoped_ensurer_t&) = delete;
225  scoped_ensurer_t(scoped_ensurer_t&&) = delete;
226 
227  scoped_ensurer_t& operator=(scoped_ensurer_t&&) = delete;
228  scoped_ensurer_t& operator=(const scoped_ensurer_t&) = delete;
229 
230  ~scoped_ensurer_t() { if (context_was_pushed_on_construction) { pop(); } }
231 };
232 
233 } // namespace detail_
234 
248 class scoped_override_t : private detail_::scoped_override_t {
249 protected:
250  using parent = detail_::scoped_override_t;
251 public:
252 
253  explicit scoped_override_t(device::primary_context_t&& primary_context);
254  explicit scoped_override_t(const context_t& context);
255  explicit scoped_override_t(context_t&& context);
256  ~scoped_override_t() = default;
257 };
258 
259 
260 
267 #define CUDA_CONTEXT_FOR_THIS_SCOPE(_cuda_context) \
268 ::cuda::context::current::scoped_override_t set_context_for_this_scope{ _cuda_context }
269 
276 inline void synchronize()
277 {
278  auto status = cuCtxSynchronize();
279  if (not is_success(status)) {
280  throw cuda::runtime_error(status, "Failed synchronizing current context");
281  }
282 }
283 
284 namespace detail_ {
285 
286 // Just like context::current::synchronize(), but with an argument
287 // allowing for throwing a more informative exception on failure
288 inline void synchronize(context::handle_t current_context_handle)
289 {
290  auto status = cuCtxSynchronize();
291  if (not is_success(status)) {
292  throw cuda::runtime_error(status,"Failed synchronizing "
293  + context::detail_::identify(current_context_handle));
294  }
295 }
296 
297 // Just like context::current::synchronize(), but with arguments
298 // allowing for throwing a more informative exception on failure
299 inline void synchronize(
300  device::id_t current_context_device_id,
301  context::handle_t current_context_handle)
302 {
303  auto status = cuCtxSynchronize();
304  if (not is_success(status)) {
305  throw cuda::runtime_error(status, "Failed synchronizing "
306  + context::detail_::identify(current_context_handle, current_context_device_id));
307  }
308 }
309 
310 } // namespace detail
311 
312 } // namespace current
313 
314 } // namespace context
315 
316 } // namespace cuda
317 
318 #endif // CUDA_API_WRAPPERS_CURRENT_CONTEXT_HPP_
Wrapper class for a CUDA context.
Definition: context.hpp:244
Definitions and functionality wrapping CUDA APIs.
Definition: array.hpp:22
CUcontext handle_t
Raw CUDA driver handle for a context; see {context_t}.
Definition: types.hpp:878
A class for holding the primary context of a CUDA device.
Definition: primary_context.hpp:112
CUdevice id_t
Numeric ID of a CUDA device used by the CUDA Runtime API.
Definition: types.hpp:850
bool push_if_not_on_top(const context_t &context)
Push a (reference to a) context onto the top of the context stack - unless that context is already at...
Definition: context.hpp:887
context_t pop()
Pop the top off of the context stack.
Definition: context.hpp:910
A (base?) class for exceptions raised by CUDA code; these errors are thrown by essentially all CUDA R...
Definition: error.hpp:271
void synchronize(const context_t &context)
Waits for all previously-scheduled tasks on all streams (= queues) in a CUDA context to conclude...
Definition: context.hpp:968
A RAII-based mechanism for pushing a context onto the context stack for what remains of the current (...
Definition: current_context.hpp:248
void push(const context_t &context)
Push a (reference to a) context onto the top of the context stack.
Definition: context.hpp:899
#define throw_if_error_lazy(status__,...)
A macro for only throwing an error if we've failed - which also ensures no string is constructed unle...
Definition: error.hpp:316
Facilities for exception-based handling of Runtime and Driver API errors, including a basic exception...
Fundamental CUDA-related constants and enumerations, not dependent on any more complex abstractions...
bool exists()
Determine whether any CUDA context is current, or whether the context stack is empty/uninitialized.
Definition: current_context.hpp:30
Fundamental CUDA-related type definitions.
constexpr bool is_success(status_t status)
Determine whether the API call returning the specified status had succeeded.
Definition: error.hpp:203
CUresult status_t
Indicates either the result (success or error index) of a CUDA Runtime or Driver API call...
Definition: types.hpp:77