Kokkos Core Kernels Package Version of the Day
Loading...
Searching...
No Matches
Kokkos_Future.hpp
1//@HEADER
2// ************************************************************************
3//
4// Kokkos v. 4.0
5// Copyright (2022) National Technology & Engineering
6// Solutions of Sandia, LLC (NTESS).
7//
8// Under the terms of Contract DE-NA0003525 with NTESS,
9// the U.S. Government retains certain rights in this software.
10//
11// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions.
12// See https://kokkos.org/LICENSE for license information.
13// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
14//
15//@HEADER
16
17#include <Kokkos_Macros.hpp>
18
19#ifndef KOKKOS_IMPL_PUBLIC_INCLUDE
20static_assert(false,
21 "Including non-public Kokkos header files is not allowed.");
22#endif
23
24#ifndef KOKKOS_ENABLE_DEPRECATED_CODE_4
25#error "The tasking framework is deprecated"
26#endif
27
28#ifndef KOKKOS_FUTURE_HPP
29#define KOKKOS_FUTURE_HPP
30
31//----------------------------------------------------------------------------
32
33#include <Kokkos_Macros.hpp>
34#if defined(KOKKOS_ENABLE_TASKDAG)
35
36#include <Kokkos_Core_fwd.hpp>
37#include <Kokkos_TaskScheduler_fwd.hpp>
38//----------------------------------------------------------------------------
39
40#include <impl/Kokkos_TaskQueue.hpp>
41#include <impl/Kokkos_TaskResult.hpp>
42#include <impl/Kokkos_TaskBase.hpp>
43#include <Kokkos_Atomic.hpp>
44
45#include <Kokkos_Concepts.hpp> // is_space
46
47//----------------------------------------------------------------------------
48//----------------------------------------------------------------------------
49
50#ifdef KOKKOS_ENABLE_DEPRECATION_WARNINGS
51// We allow using deprecated classes in this file
52KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_PUSH()
53#endif
54
55namespace Kokkos {
56
57// For now, hack this in as a partial specialization
58// TODO @tasking @cleanup Make this the "normal" class template and make the old
59// code the specialization
60template <typename ValueType, typename ExecutionSpace, typename QueueType>
61class KOKKOS_DEPRECATED
62 BasicFuture<ValueType, SimpleTaskScheduler<ExecutionSpace, QueueType>> {
63 public:
64 using value_type = ValueType;
65 using execution_space = ExecutionSpace;
66 using scheduler_type = SimpleTaskScheduler<ExecutionSpace, QueueType>;
67 using queue_type = typename scheduler_type::task_queue_type;
68
69 private:
70 template <class, class>
71 friend class SimpleTaskScheduler;
72 template <class, class>
73 friend class BasicFuture;
74
75 using task_base_type = typename scheduler_type::task_base_type;
76 using task_queue_type = typename scheduler_type::task_queue_type;
77
78 using task_queue_traits = typename scheduler_type::task_queue_traits;
79 using task_scheduling_info_type =
80 typename scheduler_type::task_scheduling_info_type;
81
82 using result_storage_type = Impl::TaskResultStorage<
83 ValueType,
84 Impl::SchedulingInfoStorage<Impl::RunnableTaskBase<task_queue_traits>,
85 task_scheduling_info_type>>;
86
87 OwningRawPtr<task_base_type> m_task = nullptr;
88
89 KOKKOS_INLINE_FUNCTION
90 explicit BasicFuture(task_base_type* task) : m_task(task) {
91 // Note: reference count starts at 2 to account for initial increment
92 // TODO @tasking @minor DSH verify reference count here and/or encapsulate
93 // starting reference count closer to here
94 }
95
96 public:
97 KOKKOS_INLINE_FUNCTION
98 BasicFuture() noexcept : m_task(nullptr) {}
99
100 KOKKOS_INLINE_FUNCTION
101 BasicFuture(BasicFuture&& rhs) noexcept : m_task(std::move(rhs.m_task)) {
102 rhs.m_task = nullptr;
103 }
104
105 KOKKOS_INLINE_FUNCTION
106 BasicFuture(BasicFuture const& rhs)
107 // : m_task(rhs.m_task)
108 : m_task(nullptr) {
109 *static_cast<task_base_type* volatile*>(&m_task) = rhs.m_task;
110 if (m_task) m_task->increment_reference_count();
111 }
112
113 KOKKOS_INLINE_FUNCTION
114 BasicFuture& operator=(BasicFuture&& rhs) noexcept {
115 if (m_task != rhs.m_task) {
116 clear();
117 // m_task = std::move(rhs.m_task);
118 *static_cast<task_base_type* volatile*>(&m_task) = rhs.m_task;
119 // rhs.m_task reference count is unchanged, since this is a move
120 } else {
121 // They're the same, but this is a move, so 1 fewer references now
122 rhs.clear();
123 }
124 rhs.m_task = nullptr;
125 return *this;
126 }
127
128 KOKKOS_INLINE_FUNCTION
129 BasicFuture& operator=(BasicFuture const& rhs) {
130 if (m_task != rhs.m_task) {
131 clear();
132 // m_task = rhs.m_task;
133 *static_cast<task_base_type* volatile*>(&m_task) = rhs.m_task;
134 }
135 if (m_task != nullptr) {
136 m_task->increment_reference_count();
137 }
138 return *this;
139 }
140
141 //----------------------------------------
142
143 template <class T, class S>
144 KOKKOS_INLINE_FUNCTION BasicFuture(
145 BasicFuture<T, S>&& rhs) noexcept // NOLINT(google-explicit-constructor)
146 : m_task(std::move(rhs.m_task)) {
147 static_assert(std::is_void<scheduler_type>::value ||
148 std::is_same<scheduler_type, S>::value,
149 "Moved Futures must have the same scheduler");
150
151 static_assert(
152 std::is_void<value_type>::value || std::is_same<value_type, T>::value,
153 "Moved Futures must have the same value_type");
154
155 // reference counts are unchanged, since this is a move
156 rhs.m_task = nullptr;
157 }
158
159 template <class T, class S>
160 KOKKOS_INLINE_FUNCTION BasicFuture(
161 BasicFuture<T, S> const& rhs) // NOLINT(google-explicit-constructor)
162 //: m_task(rhs.m_task)
163 : m_task(nullptr) {
164 static_assert(std::is_void<scheduler_type>::value ||
165 std::is_same<scheduler_type, S>::value,
166 "Copied Futures must have the same scheduler");
167
168 static_assert(
169 std::is_void<value_type>::value || std::is_same<value_type, T>::value,
170 "Copied Futures must have the same value_type");
171
172 *static_cast<task_base_type* volatile*>(&m_task) = rhs.m_task;
173 if (m_task) m_task->increment_reference_count();
174 }
175
176 template <class T, class S>
177 KOKKOS_INLINE_FUNCTION BasicFuture& operator=(BasicFuture<T, S> const& rhs) {
178 static_assert(std::is_void<scheduler_type>::value ||
179 std::is_same<scheduler_type, S>::value,
180 "Assigned Futures must have the same scheduler");
181
182 static_assert(
183 std::is_void<value_type>::value || std::is_same<value_type, T>::value,
184 "Assigned Futures must have the same value_type");
185
186 if (m_task != rhs.m_task) {
187 clear();
188 // m_task = rhs.m_task;
189 *static_cast<task_base_type* volatile*>(&m_task) = rhs.m_task;
190 if (m_task != nullptr) {
191 m_task->increment_reference_count();
192 }
193 }
194 return *this;
195 }
196
197 template <class T, class S>
198 KOKKOS_INLINE_FUNCTION BasicFuture& operator=(BasicFuture<T, S>&& rhs) {
199 static_assert(std::is_void<scheduler_type>::value ||
200 std::is_same<scheduler_type, S>::value,
201 "Assigned Futures must have the same scheduler");
202
203 static_assert(
204 std::is_void<value_type>::value || std::is_same<value_type, T>::value,
205 "Assigned Futures must have the same value_type");
206
207 if (m_task != rhs.m_task) {
208 clear();
209 // m_task = std::move(rhs.m_task);
210 *static_cast<task_base_type* volatile*>(&m_task) = rhs.m_task;
211 // rhs.m_task reference count is unchanged, since this is a move
212 } else {
213 // They're the same, but this is a move, so 1 fewer references now
214 rhs.clear();
215 }
216 rhs.m_task = nullptr;
217 return *this;
218 }
219
220 KOKKOS_INLINE_FUNCTION
221 ~BasicFuture() noexcept { clear(); }
222
223 //----------------------------------------
224
225 KOKKOS_INLINE_FUNCTION
226 void clear() noexcept {
227 if (m_task) {
228 bool should_delete = m_task->decrement_and_check_reference_count();
229 if (should_delete) {
230 static_cast<task_queue_type*>(m_task->ready_queue_base_ptr())
231 ->deallocate(std::move(*m_task));
232 }
233 }
234 // m_task = nullptr;
235 *static_cast<task_base_type* volatile*>(&m_task) = nullptr;
236 }
237
238 KOKKOS_INLINE_FUNCTION
239 bool is_null() const noexcept { return m_task == nullptr; }
240
241 KOKKOS_INLINE_FUNCTION
242 bool is_ready() const noexcept {
243 return (m_task == nullptr) || m_task->wait_queue_is_consumed();
244 }
245
246 KOKKOS_INLINE_FUNCTION
247 const typename Impl::TaskResult<ValueType>::reference_type get() const {
248 KOKKOS_EXPECTS(is_ready());
249 return static_cast<result_storage_type*>(m_task)->value_reference();
250 // return Impl::TaskResult<ValueType>::get(m_task);
251 }
252};
253
255// OLD CODE
257
258template <typename ValueType, typename Scheduler>
259class KOKKOS_DEPRECATED BasicFuture {
260 private:
261 template <typename, typename>
262 friend class BasicTaskScheduler;
263 template <typename, typename>
264 friend class BasicFuture;
265 friend class Impl::TaskBase;
266 template <typename, typename, typename>
267 friend class Impl::Task;
268
269 //----------------------------------------
270
271 public:
272 //----------------------------------------
273
274 using scheduler_type = Scheduler;
275 using queue_type = typename scheduler_type::queue_type;
276 using execution_space = typename scheduler_type::execution_space;
277 using value_type = ValueType;
278
279 //----------------------------------------
280
281 private:
282 //----------------------------------------
283
284 using task_base = Impl::TaskBase;
285
286 task_base* m_task;
287
288 KOKKOS_INLINE_FUNCTION explicit BasicFuture(task_base* task)
289 : m_task(nullptr) {
290 if (task) queue_type::assign(&m_task, task);
291 }
292
293 //----------------------------------------
294
295 public:
296 //----------------------------------------
297
298 KOKKOS_INLINE_FUNCTION
299 bool is_null() const { return nullptr == m_task; }
300
301 KOKKOS_INLINE_FUNCTION
302 int reference_count() const {
303 return nullptr != m_task ? m_task->reference_count() : 0;
304 }
305
306 //----------------------------------------
307
308 KOKKOS_INLINE_FUNCTION
309 void clear() {
310 if (m_task) queue_type::assign(&m_task, nullptr);
311 }
312
313 //----------------------------------------
314
315 KOKKOS_INLINE_FUNCTION
316 ~BasicFuture() { clear(); }
317
318 //----------------------------------------
319
320 KOKKOS_INLINE_FUNCTION
321 BasicFuture() noexcept : m_task(nullptr) {}
322
323 KOKKOS_INLINE_FUNCTION
324 BasicFuture(BasicFuture&& rhs) noexcept : m_task(rhs.m_task) {
325 rhs.m_task = nullptr;
326 }
327
328 KOKKOS_INLINE_FUNCTION
329 BasicFuture(const BasicFuture& rhs) : m_task(nullptr) {
330 if (rhs.m_task) queue_type::assign(&m_task, rhs.m_task);
331 }
332
333 KOKKOS_INLINE_FUNCTION
334 BasicFuture& operator=(BasicFuture&& rhs) noexcept {
335 clear();
336 m_task = rhs.m_task;
337 rhs.m_task = nullptr;
338 return *this;
339 }
340
341 KOKKOS_INLINE_FUNCTION
342 BasicFuture& operator=(BasicFuture const& rhs) {
343 if (m_task || rhs.m_task) queue_type::assign(&m_task, rhs.m_task);
344 return *this;
345 }
346
347 //----------------------------------------
348
349 template <class T, class S>
350 KOKKOS_INLINE_FUNCTION BasicFuture(
351 BasicFuture<T, S>&& rhs) noexcept // NOLINT(google-explicit-constructor)
352 : m_task(rhs.m_task) {
353 static_assert(std::is_void<scheduler_type>::value ||
354 std::is_same<scheduler_type, S>::value,
355 "Assigned Futures must have the same scheduler");
356
357 static_assert(
358 std::is_void<value_type>::value || std::is_same<value_type, T>::value,
359 "Assigned Futures must have the same value_type");
360
361 rhs.m_task = 0;
362 }
363
364 template <class T, class S>
365 KOKKOS_INLINE_FUNCTION BasicFuture(
366 BasicFuture<T, S> const& rhs) // NOLINT(google-explicit-constructor)
367 : m_task(nullptr) {
368 static_assert(std::is_void<scheduler_type>::value ||
369 std::is_same<scheduler_type, S>::value,
370 "Assigned Futures must have the same scheduler");
371
372 static_assert(
373 std::is_void<value_type>::value || std::is_same<value_type, T>::value,
374 "Assigned Futures must have the same value_type");
375
376 if (rhs.m_task) queue_type::assign(&m_task, rhs.m_task);
377 }
378
379 template <class T, class S>
380 KOKKOS_INLINE_FUNCTION BasicFuture& operator=(BasicFuture<T, S> const& rhs) {
381 static_assert(std::is_void<scheduler_type>::value ||
382 std::is_same<scheduler_type, S>::value,
383 "Assigned Futures must have the same scheduler");
384
385 static_assert(
386 std::is_void<value_type>::value || std::is_same<value_type, T>::value,
387 "Assigned Futures must have the same value_type");
388
389 if (m_task || rhs.m_task) queue_type::assign(&m_task, rhs.m_task);
390 return *this;
391 }
392
393 template <class T, class S>
394 KOKKOS_INLINE_FUNCTION BasicFuture& operator=(BasicFuture<T, S>&& rhs) {
395 static_assert(std::is_void<scheduler_type>::value ||
396 std::is_same<scheduler_type, S>::value,
397 "Assigned Futures must have the same scheduler");
398
399 static_assert(
400 std::is_void<value_type>::value || std::is_same<value_type, T>::value,
401 "Assigned Futures must have the same value_type");
402
403 clear();
404 m_task = rhs.m_task;
405 rhs.m_task = 0;
406 return *this;
407 }
408
409 //----------------------------------------
410
411 KOKKOS_INLINE_FUNCTION
412 int is_ready() const noexcept {
413 return (nullptr == m_task) ||
414 (reinterpret_cast<task_base*>(task_base::LockTag) == m_task->m_wait);
415 }
416
417 KOKKOS_INLINE_FUNCTION
418 const typename Impl::TaskResult<ValueType>::reference_type get() const {
419 if (nullptr == m_task) {
420 Kokkos::abort("Kokkos:::Future::get ERROR: is_null()");
421 }
422 return Impl::TaskResult<ValueType>::get(m_task);
423 }
424};
425
426// Is a Future with the given execution space
427template <typename, typename ExecSpace = void>
428struct KOKKOS_DEPRECATED is_future : public std::false_type {};
429
430template <typename ValueType, typename Scheduler, typename ExecSpace>
431struct KOKKOS_DEPRECATED is_future<BasicFuture<ValueType, Scheduler>, ExecSpace>
432 : std::bool_constant<
433 std::is_same_v<ExecSpace, typename Scheduler::execution_space> ||
434 std::is_void_v<ExecSpace>> {};
435
437// END OLD CODE
439
440namespace Impl {
441
442template <class Arg1, class Arg2>
443class ResolveFutureArgOrder {
444 private:
445 enum { Arg1_is_space = Kokkos::is_space<Arg1>::value };
446 enum { Arg2_is_space = Kokkos::is_space<Arg2>::value };
447 enum { Arg1_is_value = !Arg1_is_space && !std::is_void_v<Arg1> };
448 enum { Arg2_is_value = !Arg2_is_space && !std::is_void_v<Arg2> };
449
450 static_assert(!(Arg1_is_space && Arg2_is_space),
451 "Future cannot be given two spaces");
452
453 static_assert(!(Arg1_is_value && Arg2_is_value),
454 "Future cannot be given two value types");
455
456 using value_type =
457 std::conditional_t<Arg1_is_value, Arg1,
458 std::conditional_t<Arg2_is_value, Arg2, void>>;
459
460 using execution_space = typename std::conditional_t<
461 Arg1_is_space, Arg1,
462 std::conditional_t<Arg2_is_space, Arg2, void>>::execution_space;
463
464 public:
465 using type = BasicFuture<value_type, TaskScheduler<execution_space>>;
466};
467
468} // end namespace Impl
469
477template <class Arg1 = void, class Arg2 = void>
478using Future KOKKOS_DEPRECATED =
479 typename Impl::ResolveFutureArgOrder<Arg1, Arg2>::type;
480
481} // namespace Kokkos
482
483#ifdef KOKKOS_ENABLE_DEPRECATION_WARNINGS
484KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_POP()
485#endif
486
487//----------------------------------------------------------------------------
488//----------------------------------------------------------------------------
489
490#endif /* #if defined( KOKKOS_ENABLE_TASKDAG ) */
491#endif /* #ifndef KOKKOS_FUTURE */
Atomic functions.
ScopeGuard Some user scope issues have been identified with some Kokkos::finalize calls; ScopeGuard a...