Kokkos Core Kernels Package  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
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
20 static_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
52 KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_PUSH()
53 #endif
54 
55 namespace 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
60 template <typename ValueType, typename ExecutionSpace, typename QueueType>
61 class 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 
258 template <typename ValueType, typename Scheduler>
259 class 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
427 template <typename, typename ExecSpace = void>
428 struct KOKKOS_DEPRECATED is_future : public std::false_type {};
429 
430 template <typename ValueType, typename Scheduler, typename ExecSpace>
431 struct 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 
440 namespace Impl {
441 
442 template <class Arg1, class Arg2>
443 class 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 
477 template <class Arg1 = void, class Arg2 = void>
478 using Future KOKKOS_DEPRECATED =
479  typename Impl::ResolveFutureArgOrder<Arg1, Arg2>::type;
480 
481 } // namespace Kokkos
482 
483 #ifdef KOKKOS_ENABLE_DEPRECATION_WARNINGS
484 KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_POP()
485 #endif
486 
487 //----------------------------------------------------------------------------
488 //----------------------------------------------------------------------------
489 
490 #endif /* #if defined( KOKKOS_ENABLE_TASKDAG ) */
491 #endif /* #ifndef KOKKOS_FUTURE */
bool is_null(const boost::shared_ptr< T > &p)
Atomic functions.