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(
148  std::is_void_v<scheduler_type> || std::is_same_v<scheduler_type, S>,
149  "Moved Futures must have the same scheduler");
150 
151  static_assert(std::is_void_v<value_type> || std::is_same_v<value_type, T>,
152  "Moved Futures must have the same value_type");
153 
154  // reference counts are unchanged, since this is a move
155  rhs.m_task = nullptr;
156  }
157 
158  template <class T, class S>
159  KOKKOS_INLINE_FUNCTION BasicFuture(
160  BasicFuture<T, S> const& rhs) // NOLINT(google-explicit-constructor)
161  //: m_task(rhs.m_task)
162  : m_task(nullptr) {
163  static_assert(
164  std::is_void_v<scheduler_type> || std::is_same_v<scheduler_type, S>,
165  "Copied Futures must have the same scheduler");
166 
167  static_assert(std::is_void_v<value_type> || std::is_same_v<value_type, T>,
168  "Copied Futures must have the same value_type");
169 
170  *static_cast<task_base_type* volatile*>(&m_task) = rhs.m_task;
171  if (m_task) m_task->increment_reference_count();
172  }
173 
174  template <class T, class S>
175  KOKKOS_INLINE_FUNCTION BasicFuture& operator=(BasicFuture<T, S> const& rhs) {
176  static_assert(
177  std::is_void_v<scheduler_type> || std::is_same_v<scheduler_type, S>,
178  "Assigned Futures must have the same scheduler");
179 
180  static_assert(std::is_void_v<value_type> || std::is_same_v<value_type, T>,
181  "Assigned Futures must have the same value_type");
182 
183  if (m_task != rhs.m_task) {
184  clear();
185  // m_task = rhs.m_task;
186  *static_cast<task_base_type* volatile*>(&m_task) = rhs.m_task;
187  if (m_task != nullptr) {
188  m_task->increment_reference_count();
189  }
190  }
191  return *this;
192  }
193 
194  template <class T, class S>
195  KOKKOS_INLINE_FUNCTION BasicFuture& operator=(BasicFuture<T, S>&& rhs) {
196  static_assert(
197  std::is_void_v<scheduler_type> || std::is_same_v<scheduler_type, S>,
198  "Assigned Futures must have the same scheduler");
199 
200  static_assert(std::is_void_v<value_type> || std::is_same_v<value_type, T>,
201  "Assigned Futures must have the same value_type");
202 
203  if (m_task != rhs.m_task) {
204  clear();
205  // m_task = std::move(rhs.m_task);
206  *static_cast<task_base_type* volatile*>(&m_task) = rhs.m_task;
207  // rhs.m_task reference count is unchanged, since this is a move
208  } else {
209  // They're the same, but this is a move, so 1 fewer references now
210  rhs.clear();
211  }
212  rhs.m_task = nullptr;
213  return *this;
214  }
215 
216  KOKKOS_INLINE_FUNCTION
217  ~BasicFuture() noexcept { clear(); }
218 
219  //----------------------------------------
220 
221  KOKKOS_INLINE_FUNCTION
222  void clear() noexcept {
223  if (m_task) {
224  bool should_delete = m_task->decrement_and_check_reference_count();
225  if (should_delete) {
226  static_cast<task_queue_type*>(m_task->ready_queue_base_ptr())
227  ->deallocate(std::move(*m_task));
228  }
229  }
230  // m_task = nullptr;
231  *static_cast<task_base_type* volatile*>(&m_task) = nullptr;
232  }
233 
234  KOKKOS_INLINE_FUNCTION
235  bool is_null() const noexcept { return m_task == nullptr; }
236 
237  KOKKOS_INLINE_FUNCTION
238  bool is_ready() const noexcept {
239  return (m_task == nullptr) || m_task->wait_queue_is_consumed();
240  }
241 
242  KOKKOS_INLINE_FUNCTION
243  const typename Impl::TaskResult<ValueType>::reference_type get() const {
244  KOKKOS_EXPECTS(is_ready());
245  return static_cast<result_storage_type*>(m_task)->value_reference();
246  // return Impl::TaskResult<ValueType>::get(m_task);
247  }
248 };
249 
251 // OLD CODE
253 
254 template <typename ValueType, typename Scheduler>
255 class KOKKOS_DEPRECATED BasicFuture {
256  private:
257  template <typename, typename>
258  friend class BasicTaskScheduler;
259  template <typename, typename>
260  friend class BasicFuture;
261  friend class Impl::TaskBase;
262  template <typename, typename, typename>
263  friend class Impl::Task;
264 
265  //----------------------------------------
266 
267  public:
268  //----------------------------------------
269 
270  using scheduler_type = Scheduler;
271  using queue_type = typename scheduler_type::queue_type;
272  using execution_space = typename scheduler_type::execution_space;
273  using value_type = ValueType;
274 
275  //----------------------------------------
276 
277  private:
278  //----------------------------------------
279 
280  using task_base = Impl::TaskBase;
281 
282  task_base* m_task;
283 
284  KOKKOS_INLINE_FUNCTION explicit BasicFuture(task_base* task)
285  : m_task(nullptr) {
286  if (task) queue_type::assign(&m_task, task);
287  }
288 
289  //----------------------------------------
290 
291  public:
292  //----------------------------------------
293 
294  KOKKOS_INLINE_FUNCTION
295  bool is_null() const { return nullptr == m_task; }
296 
297  KOKKOS_INLINE_FUNCTION
298  int reference_count() const {
299  return nullptr != m_task ? m_task->reference_count() : 0;
300  }
301 
302  //----------------------------------------
303 
304  KOKKOS_INLINE_FUNCTION
305  void clear() {
306  if (m_task) queue_type::assign(&m_task, nullptr);
307  }
308 
309  //----------------------------------------
310 
311  KOKKOS_INLINE_FUNCTION
312  ~BasicFuture() { clear(); }
313 
314  //----------------------------------------
315 
316  KOKKOS_INLINE_FUNCTION
317  BasicFuture() noexcept : m_task(nullptr) {}
318 
319  KOKKOS_INLINE_FUNCTION
320  BasicFuture(BasicFuture&& rhs) noexcept : m_task(rhs.m_task) {
321  rhs.m_task = nullptr;
322  }
323 
324  KOKKOS_INLINE_FUNCTION
325  BasicFuture(const BasicFuture& rhs) : m_task(nullptr) {
326  if (rhs.m_task) queue_type::assign(&m_task, rhs.m_task);
327  }
328 
329  KOKKOS_INLINE_FUNCTION
330  BasicFuture& operator=(BasicFuture&& rhs) noexcept {
331  clear();
332  m_task = rhs.m_task;
333  rhs.m_task = nullptr;
334  return *this;
335  }
336 
337  KOKKOS_INLINE_FUNCTION
338  BasicFuture& operator=(BasicFuture const& rhs) {
339  if (&rhs == this) return *this;
340  if (m_task || rhs.m_task) queue_type::assign(&m_task, rhs.m_task);
341  return *this;
342  }
343 
344  //----------------------------------------
345 
346  template <class T, class S>
347  KOKKOS_INLINE_FUNCTION BasicFuture(
348  BasicFuture<T, S>&& rhs) noexcept // NOLINT(google-explicit-constructor)
349  : m_task(rhs.m_task) {
350  static_assert(
351  std::is_void_v<scheduler_type> || std::is_same_v<scheduler_type, S>,
352  "Assigned Futures must have the same scheduler");
353 
354  static_assert(std::is_void_v<value_type> || std::is_same_v<value_type, T>,
355  "Assigned Futures must have the same value_type");
356 
357  rhs.m_task = 0;
358  }
359 
360  template <class T, class S>
361  KOKKOS_INLINE_FUNCTION BasicFuture(
362  BasicFuture<T, S> const& rhs) // NOLINT(google-explicit-constructor)
363  : m_task(nullptr) {
364  static_assert(
365  std::is_void_v<scheduler_type> || std::is_same_v<scheduler_type, S>,
366  "Assigned Futures must have the same scheduler");
367 
368  static_assert(std::is_void_v<value_type> || std::is_same_v<value_type, T>,
369  "Assigned Futures must have the same value_type");
370 
371  if (rhs.m_task) queue_type::assign(&m_task, rhs.m_task);
372  }
373 
374  template <class T, class S>
375  KOKKOS_INLINE_FUNCTION BasicFuture& operator=(BasicFuture<T, S> const& rhs) {
376  static_assert(
377  std::is_void_v<scheduler_type> || std::is_same_v<scheduler_type, S>,
378  "Assigned Futures must have the same scheduler");
379 
380  static_assert(std::is_void_v<value_type> || std::is_same_v<value_type, T>,
381  "Assigned Futures must have the same value_type");
382 
383  if (m_task || rhs.m_task) queue_type::assign(&m_task, rhs.m_task);
384  return *this;
385  }
386 
387  template <class T, class S>
388  KOKKOS_INLINE_FUNCTION BasicFuture& operator=(BasicFuture<T, S>&& rhs) {
389  static_assert(
390  std::is_void_v<scheduler_type> || std::is_same_v<scheduler_type, S>,
391  "Assigned Futures must have the same scheduler");
392 
393  static_assert(std::is_void_v<value_type> || std::is_same_v<value_type, T>,
394  "Assigned Futures must have the same value_type");
395 
396  clear();
397  m_task = rhs.m_task;
398  rhs.m_task = 0;
399  return *this;
400  }
401 
402  //----------------------------------------
403 
404  KOKKOS_INLINE_FUNCTION
405  int is_ready() const noexcept {
406  return (nullptr == m_task) ||
407  (reinterpret_cast<task_base*>(task_base::LockTag) == m_task->m_wait);
408  }
409 
410  KOKKOS_INLINE_FUNCTION
411  const typename Impl::TaskResult<ValueType>::reference_type get() const {
412  if (nullptr == m_task) {
413  Kokkos::abort("Kokkos:::Future::get ERROR: is_null()");
414  }
415  return Impl::TaskResult<ValueType>::get(m_task);
416  }
417 };
418 
419 // Is a Future with the given execution space
420 template <typename, typename ExecSpace = void>
421 struct KOKKOS_DEPRECATED is_future : public std::false_type {};
422 
423 template <typename ValueType, typename Scheduler, typename ExecSpace>
424 struct KOKKOS_DEPRECATED is_future<BasicFuture<ValueType, Scheduler>, ExecSpace>
425  : std::bool_constant<
426  std::is_same_v<ExecSpace, typename Scheduler::execution_space> ||
427  std::is_void_v<ExecSpace>> {};
428 
430 // END OLD CODE
432 
433 namespace Impl {
434 
435 template <class Arg1, class Arg2>
436 class ResolveFutureArgOrder {
437  private:
438  enum { Arg1_is_space = Kokkos::is_space<Arg1>::value };
439  enum { Arg2_is_space = Kokkos::is_space<Arg2>::value };
440  enum { Arg1_is_value = !Arg1_is_space && !std::is_void_v<Arg1> };
441  enum { Arg2_is_value = !Arg2_is_space && !std::is_void_v<Arg2> };
442 
443  static_assert(!(Arg1_is_space && Arg2_is_space),
444  "Future cannot be given two spaces");
445 
446  static_assert(!(Arg1_is_value && Arg2_is_value),
447  "Future cannot be given two value types");
448 
449  using value_type =
450  std::conditional_t<Arg1_is_value, Arg1,
451  std::conditional_t<Arg2_is_value, Arg2, void>>;
452 
453  using execution_space = typename std::conditional_t<
454  Arg1_is_space, Arg1,
455  std::conditional_t<Arg2_is_space, Arg2, void>>::execution_space;
456 
457  public:
458  using type = BasicFuture<value_type, TaskScheduler<execution_space>>;
459 };
460 
461 } // end namespace Impl
462 
470 template <class Arg1 = void, class Arg2 = void>
471 using Future KOKKOS_DEPRECATED =
472  typename Impl::ResolveFutureArgOrder<Arg1, Arg2>::type;
473 
474 } // namespace Kokkos
475 
476 #ifdef KOKKOS_ENABLE_DEPRECATION_WARNINGS
477 KOKKOS_IMPL_DISABLE_DEPRECATED_WARNINGS_POP()
478 #endif
479 
480 //----------------------------------------------------------------------------
481 //----------------------------------------------------------------------------
482 
483 #endif /* #if defined( KOKKOS_ENABLE_TASKDAG ) */
484 #endif /* #ifndef KOKKOS_FUTURE */
bool is_null(const boost::shared_ptr< T > &p)