Kokkos Core Kernels Package Version of the Day
Kokkos_Future.hpp
1/*
2//@HEADER
3// ************************************************************************
4//
5// Kokkos v. 3.0
6// Copyright (2020) National Technology & Engineering
7// Solutions of Sandia, LLC (NTESS).
8//
9// Under the terms of Contract DE-NA0003525 with NTESS,
10// the U.S. Government retains certain rights in this software.
11//
12// Redistribution and use in source and binary forms, with or without
13// modification, are permitted provided that the following conditions are
14// met:
15//
16// 1. Redistributions of source code must retain the above copyright
17// notice, this list of conditions and the following disclaimer.
18//
19// 2. Redistributions in binary form must reproduce the above copyright
20// notice, this list of conditions and the following disclaimer in the
21// documentation and/or other materials provided with the distribution.
22//
23// 3. Neither the name of the Corporation nor the names of the
24// contributors may be used to endorse or promote products derived from
25// this software without specific prior written permission.
26//
27// THIS SOFTWARE IS PROVIDED BY NTESS "AS IS" AND ANY
28// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NTESS OR THE
31// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
32// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
33// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
34// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
35// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
36// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
37// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38//
39// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
40//
41// ************************************************************************
42//@HEADER
43*/
44
45#ifndef KOKKOS_FUTURE_HPP
46#define KOKKOS_FUTURE_HPP
47
48//----------------------------------------------------------------------------
49
50#include <Kokkos_Macros.hpp>
51#if defined(KOKKOS_ENABLE_TASKDAG)
52
53#include <Kokkos_Core_fwd.hpp>
54#include <Kokkos_TaskScheduler_fwd.hpp>
55//----------------------------------------------------------------------------
56
57#include <impl/Kokkos_TaskQueue.hpp>
58#include <impl/Kokkos_TaskResult.hpp>
59#include <impl/Kokkos_TaskBase.hpp>
60#include <Kokkos_Atomic.hpp>
61
62#include <Kokkos_Concepts.hpp> // is_space
63
64//----------------------------------------------------------------------------
65//----------------------------------------------------------------------------
66
67namespace Kokkos {
68
69// For now, hack this in as a partial specialization
70// TODO @tasking @cleanup Make this the "normal" class template and make the old
71// code the specialization
72template <typename ValueType, typename ExecutionSpace, typename QueueType>
73class BasicFuture<ValueType, SimpleTaskScheduler<ExecutionSpace, QueueType>> {
74 public:
75 using value_type = ValueType;
76 using execution_space = ExecutionSpace;
77 using scheduler_type = SimpleTaskScheduler<ExecutionSpace, QueueType>;
78 using queue_type = typename scheduler_type::task_queue_type;
79
80 private:
81 template <class, class>
82 friend class SimpleTaskScheduler;
83 template <class, class>
84 friend class BasicFuture;
85
86 using task_base_type = typename scheduler_type::task_base_type;
87 using task_queue_type = typename scheduler_type::task_queue_type;
88
89 using task_queue_traits = typename scheduler_type::task_queue_traits;
90 using task_scheduling_info_type =
91 typename scheduler_type::task_scheduling_info_type;
92
93 using result_storage_type = Impl::TaskResultStorage<
94 ValueType,
95 Impl::SchedulingInfoStorage<Impl::RunnableTaskBase<task_queue_traits>,
96 task_scheduling_info_type>>;
97
98 OwningRawPtr<task_base_type> m_task = nullptr;
99
100 KOKKOS_INLINE_FUNCTION
101 explicit BasicFuture(task_base_type* task) : m_task(task) {
102 // Note: reference count starts at 2 to account for initial increment
103 // TODO @tasking @minor DSH verify reference count here and/or encapsulate
104 // starting reference count closer to here
105 }
106
107 public:
108 KOKKOS_INLINE_FUNCTION
109 BasicFuture() noexcept : m_task(nullptr) {}
110
111 KOKKOS_INLINE_FUNCTION
112 BasicFuture(BasicFuture&& rhs) noexcept : m_task(std::move(rhs.m_task)) {
113 rhs.m_task = nullptr;
114 }
115
116 KOKKOS_INLINE_FUNCTION
117 BasicFuture(BasicFuture const& rhs)
118 // : m_task(rhs.m_task)
119 : m_task(nullptr) {
120 *static_cast<task_base_type* volatile*>(&m_task) = rhs.m_task;
121 if (m_task) m_task->increment_reference_count();
122 }
123
124 KOKKOS_INLINE_FUNCTION
125 BasicFuture& operator=(BasicFuture&& rhs) noexcept {
126 if (m_task != rhs.m_task) {
127 clear();
128 // m_task = std::move(rhs.m_task);
129 *static_cast<task_base_type* volatile*>(&m_task) = rhs.m_task;
130 // rhs.m_task reference count is unchanged, since this is a move
131 } else {
132 // They're the same, but this is a move, so 1 fewer references now
133 rhs.clear();
134 }
135 rhs.m_task = nullptr;
136 return *this;
137 }
138
139 KOKKOS_INLINE_FUNCTION
140 BasicFuture& operator=(BasicFuture const& rhs) {
141 if (m_task != rhs.m_task) {
142 clear();
143 // m_task = rhs.m_task;
144 *static_cast<task_base_type* volatile*>(&m_task) = rhs.m_task;
145 }
146 if (m_task != nullptr) {
147 m_task->increment_reference_count();
148 }
149 return *this;
150 }
151
152 //----------------------------------------
153
154 template <class T, class S>
155 KOKKOS_INLINE_FUNCTION BasicFuture(
156 BasicFuture<T, S>&& rhs) noexcept // NOLINT(google-explicit-constructor)
157 : m_task(std::move(rhs.m_task)) {
158 static_assert(std::is_same<scheduler_type, void>::value ||
159 std::is_same<scheduler_type, S>::value,
160 "Moved Futures must have the same scheduler");
161
162 static_assert(std::is_same<value_type, void>::value ||
163 std::is_same<value_type, T>::value,
164 "Moved Futures must have the same value_type");
165
166 // reference counts are unchanged, since this is a move
167 rhs.m_task = nullptr;
168 }
169
170 template <class T, class S>
171 KOKKOS_INLINE_FUNCTION BasicFuture(
172 BasicFuture<T, S> const& rhs) // NOLINT(google-explicit-constructor)
173 //: m_task(rhs.m_task)
174 : m_task(nullptr) {
175 static_assert(std::is_same<scheduler_type, void>::value ||
176 std::is_same<scheduler_type, S>::value,
177 "Copied Futures must have the same scheduler");
178
179 static_assert(std::is_same<value_type, void>::value ||
180 std::is_same<value_type, T>::value,
181 "Copied Futures must have the same value_type");
182
183 *static_cast<task_base_type* volatile*>(&m_task) = rhs.m_task;
184 if (m_task) m_task->increment_reference_count();
185 }
186
187 template <class T, class S>
188 KOKKOS_INLINE_FUNCTION BasicFuture& operator=(BasicFuture<T, S> const& rhs) {
189 static_assert(std::is_same<scheduler_type, void>::value ||
190 std::is_same<scheduler_type, S>::value,
191 "Assigned Futures must have the same scheduler");
192
193 static_assert(std::is_same<value_type, void>::value ||
194 std::is_same<value_type, T>::value,
195 "Assigned Futures must have the same value_type");
196
197 if (m_task != rhs.m_task) {
198 clear();
199 // m_task = rhs.m_task;
200 *static_cast<task_base_type* volatile*>(&m_task) = rhs.m_task;
201 if (m_task != nullptr) {
202 m_task->increment_reference_count();
203 }
204 }
205 return *this;
206 }
207
208 template <class T, class S>
209 KOKKOS_INLINE_FUNCTION BasicFuture& operator=(BasicFuture<T, S>&& rhs) {
210 static_assert(std::is_same<scheduler_type, void>::value ||
211 std::is_same<scheduler_type, S>::value,
212 "Assigned Futures must have the same scheduler");
213
214 static_assert(std::is_same<value_type, void>::value ||
215 std::is_same<value_type, T>::value,
216 "Assigned Futures must have the same value_type");
217
218 if (m_task != rhs.m_task) {
219 clear();
220 // m_task = std::move(rhs.m_task);
221 *static_cast<task_base_type* volatile*>(&m_task) = rhs.m_task;
222 // rhs.m_task reference count is unchanged, since this is a move
223 } else {
224 // They're the same, but this is a move, so 1 fewer references now
225 rhs.clear();
226 }
227 rhs.m_task = nullptr;
228 return *this;
229 }
230
231 KOKKOS_INLINE_FUNCTION
232 ~BasicFuture() noexcept { clear(); }
233
234 //----------------------------------------
235
236 KOKKOS_INLINE_FUNCTION
237 void clear() noexcept {
238 if (m_task) {
239 bool should_delete = m_task->decrement_and_check_reference_count();
240 if (should_delete) {
241 static_cast<task_queue_type*>(m_task->ready_queue_base_ptr())
242 ->deallocate(std::move(*m_task));
243 }
244 }
245 // m_task = nullptr;
246 *static_cast<task_base_type* volatile*>(&m_task) = nullptr;
247 }
248
249 KOKKOS_INLINE_FUNCTION
250 bool is_null() const noexcept { return m_task == nullptr; }
251
252 KOKKOS_INLINE_FUNCTION
253 bool is_ready() const noexcept {
254 return (m_task == nullptr) || m_task->wait_queue_is_consumed();
255 }
256
257 KOKKOS_INLINE_FUNCTION
258 const typename Impl::TaskResult<ValueType>::reference_type get() const {
259 KOKKOS_EXPECTS(is_ready());
260 return static_cast<result_storage_type*>(m_task)->value_reference();
261 // return Impl::TaskResult<ValueType>::get(m_task);
262 }
263};
264
266// OLD CODE
268
269template <typename ValueType, typename Scheduler>
270class BasicFuture {
271 private:
272 template <typename, typename>
273 friend class BasicTaskScheduler;
274 template <typename, typename>
275 friend class BasicFuture;
276 friend class Impl::TaskBase;
277 template <typename, typename, typename>
278 friend class Impl::Task;
279
280 //----------------------------------------
281
282 public:
283 //----------------------------------------
284
285 using scheduler_type = Scheduler;
286 using queue_type = typename scheduler_type::queue_type;
287 using execution_space = typename scheduler_type::execution_space;
288 using value_type = ValueType;
289
290 //----------------------------------------
291
292 private:
293 //----------------------------------------
294
295 using task_base = Impl::TaskBase;
296
297 task_base* m_task;
298
299 KOKKOS_INLINE_FUNCTION explicit BasicFuture(task_base* task)
300 : m_task(nullptr) {
301 if (task) queue_type::assign(&m_task, task);
302 }
303
304 //----------------------------------------
305
306 public:
307 //----------------------------------------
308
309 KOKKOS_INLINE_FUNCTION
310 bool is_null() const { return nullptr == m_task; }
311
312 KOKKOS_INLINE_FUNCTION
313 int reference_count() const {
314 return nullptr != m_task ? m_task->reference_count() : 0;
315 }
316
317 //----------------------------------------
318
319 KOKKOS_INLINE_FUNCTION
320 void clear() {
321 if (m_task) queue_type::assign(&m_task, nullptr);
322 }
323
324 //----------------------------------------
325
326 KOKKOS_INLINE_FUNCTION
327 ~BasicFuture() { clear(); }
328
329 //----------------------------------------
330
331 KOKKOS_INLINE_FUNCTION
332 BasicFuture() noexcept : m_task(nullptr) {}
333
334 KOKKOS_INLINE_FUNCTION
335 BasicFuture(BasicFuture&& rhs) noexcept : m_task(rhs.m_task) {
336 rhs.m_task = nullptr;
337 }
338
339 KOKKOS_INLINE_FUNCTION
340 BasicFuture(const BasicFuture& rhs) : m_task(nullptr) {
341 if (rhs.m_task) queue_type::assign(&m_task, rhs.m_task);
342 }
343
344 KOKKOS_INLINE_FUNCTION
345 BasicFuture& operator=(BasicFuture&& rhs) noexcept {
346 clear();
347 m_task = rhs.m_task;
348 rhs.m_task = nullptr;
349 return *this;
350 }
351
352 KOKKOS_INLINE_FUNCTION
353 BasicFuture& operator=(BasicFuture const& rhs) {
354 if (m_task || rhs.m_task) queue_type::assign(&m_task, rhs.m_task);
355 return *this;
356 }
357
358 //----------------------------------------
359
360 template <class T, class S>
361 KOKKOS_INLINE_FUNCTION BasicFuture(
362 BasicFuture<T, S>&& rhs) noexcept // NOLINT(google-explicit-constructor)
363 : m_task(rhs.m_task) {
364 static_assert(std::is_same<scheduler_type, void>::value ||
365 std::is_same<scheduler_type, S>::value,
366 "Assigned Futures must have the same scheduler");
367
368 static_assert(std::is_same<value_type, void>::value ||
369 std::is_same<value_type, T>::value,
370 "Assigned Futures must have the same value_type");
371
372 rhs.m_task = 0;
373 }
374
375 template <class T, class S>
376 KOKKOS_INLINE_FUNCTION BasicFuture(
377 BasicFuture<T, S> const& rhs) // NOLINT(google-explicit-constructor)
378 : m_task(nullptr) {
379 static_assert(std::is_same<scheduler_type, void>::value ||
380 std::is_same<scheduler_type, S>::value,
381 "Assigned Futures must have the same scheduler");
382
383 static_assert(std::is_same<value_type, void>::value ||
384 std::is_same<value_type, T>::value,
385 "Assigned Futures must have the same value_type");
386
387 if (rhs.m_task) queue_type::assign(&m_task, rhs.m_task);
388 }
389
390 template <class T, class S>
391 KOKKOS_INLINE_FUNCTION BasicFuture& operator=(BasicFuture<T, S> const& rhs) {
392 static_assert(std::is_same<scheduler_type, void>::value ||
393 std::is_same<scheduler_type, S>::value,
394 "Assigned Futures must have the same scheduler");
395
396 static_assert(std::is_same<value_type, void>::value ||
397 std::is_same<value_type, T>::value,
398 "Assigned Futures must have the same value_type");
399
400 if (m_task || rhs.m_task) queue_type::assign(&m_task, rhs.m_task);
401 return *this;
402 }
403
404 template <class T, class S>
405 KOKKOS_INLINE_FUNCTION BasicFuture& operator=(BasicFuture<T, S>&& rhs) {
406 static_assert(std::is_same<scheduler_type, void>::value ||
407 std::is_same<scheduler_type, S>::value,
408 "Assigned Futures must have the same scheduler");
409
410 static_assert(std::is_same<value_type, void>::value ||
411 std::is_same<value_type, T>::value,
412 "Assigned Futures must have the same value_type");
413
414 clear();
415 m_task = rhs.m_task;
416 rhs.m_task = 0;
417 return *this;
418 }
419
420 //----------------------------------------
421
422 KOKKOS_INLINE_FUNCTION
423 int is_ready() const noexcept {
424 return (nullptr == m_task) ||
425 (((task_base*)task_base::LockTag) == m_task->m_wait);
426 }
427
428 KOKKOS_INLINE_FUNCTION
429 const typename Impl::TaskResult<ValueType>::reference_type get() const {
430 if (nullptr == m_task) {
431 Kokkos::abort("Kokkos:::Future::get ERROR: is_null()");
432 }
433 return Impl::TaskResult<ValueType>::get(m_task);
434 }
435};
436
437// Is a Future with the given execution space
438template <typename, typename ExecSpace = void>
439struct is_future : public std::false_type {};
440
441template <typename ValueType, typename Scheduler, typename ExecSpace>
442struct is_future<BasicFuture<ValueType, Scheduler>, ExecSpace>
443 : std::integral_constant<
444 bool,
445 std::is_same<ExecSpace, typename Scheduler::execution_space>::value ||
446 std::is_void<ExecSpace>::value> {};
447
449// END OLD CODE
451
452namespace Impl {
453
454template <class Arg1, class Arg2>
455class ResolveFutureArgOrder {
456 private:
457 enum { Arg1_is_space = Kokkos::is_space<Arg1>::value };
458 enum { Arg2_is_space = Kokkos::is_space<Arg2>::value };
459 enum { Arg1_is_value = !Arg1_is_space && !std::is_same<Arg1, void>::value };
460 enum { Arg2_is_value = !Arg2_is_space && !std::is_same<Arg2, void>::value };
461
462 static_assert(!(Arg1_is_space && Arg2_is_space),
463 "Future cannot be given two spaces");
464
465 static_assert(!(Arg1_is_value && Arg2_is_value),
466 "Future cannot be given two value types");
467
468 using value_type = typename std::conditional<
469 Arg1_is_value, Arg1,
470 typename std::conditional<Arg2_is_value, Arg2, void>::type>::type;
471
472 using execution_space = typename std::conditional<
473 Arg1_is_space, Arg1,
474 typename std::conditional<Arg2_is_space, Arg2,
475 void>::type>::type::execution_space;
476
477 public:
478 using type = BasicFuture<value_type, TaskScheduler<execution_space>>;
479};
480
481} // end namespace Impl
482
490template <class Arg1 = void, class Arg2 = void>
491using Future = typename Impl::ResolveFutureArgOrder<Arg1, Arg2>::type;
492
493} // namespace Kokkos
494
495//----------------------------------------------------------------------------
496//----------------------------------------------------------------------------
497
498#endif /* #if defined( KOKKOS_ENABLE_TASKDAG ) */
499#endif /* #ifndef KOKKOS_FUTURE */
Atomic functions.