DPC++ Runtime
Runtime libraries for oneAPI DPC++
atomic.hpp
Go to the documentation of this file.
1 /***************************************************************************
2  *
3  * Copyright (C) Codeplay Software Ltd.
4  *
5  * Part of the LLVM Project, under the Apache License v2.0 with LLVM
6  * Exceptions. See https://llvm.org/LICENSE.txt for license information.
7  * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  *
15  * SYCL compatibility extension
16  *
17  * atomic.hpp
18  *
19  * Description:
20  * Atomic functionality for the SYCL compatibility extension
21  **************************************************************************/
22 
23 // The original source was under the license below:
24 //==---- atomic.hpp -------------------------------*- C++ -*----------------==//
25 //
26 // Copyright (C) Intel Corporation
27 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
28 // See https://llvm.org/LICENSE.txt for license information.
29 //
30 //===----------------------------------------------------------------------===//
31 
32 #pragma once
33 
34 #include <cassert>
35 
36 #include <sycl/access/access.hpp>
37 #include <sycl/atomic_ref.hpp>
38 #include <sycl/memory_enums.hpp>
39 #include <sycl/multi_ptr.hpp>
40 
41 #include <syclcompat/traits.hpp>
42 
43 namespace syclcompat {
44 
51 template <sycl::access::address_space addressSpace =
52  sycl::access::address_space::generic_space,
53  sycl::memory_order memoryOrder = sycl::memory_order::relaxed,
55  typename T>
56 inline T atomic_fetch_add(T *addr, arith_t<T> operand) {
57  auto atm =
59  return atm.fetch_add(operand);
60 }
61 
68 template <sycl::access::address_space addressSpace =
69  sycl::access::address_space::generic_space,
70  sycl::memory_order memoryOrder = sycl::memory_order::relaxed,
72  typename T>
73 inline T atomic_fetch_sub(T *addr, arith_t<T> operand) {
74  auto atm =
76  return atm.fetch_sub(operand);
77 }
78 
86 template <sycl::access::address_space addressSpace =
87  sycl::access::address_space::generic_space,
88  sycl::memory_order memoryOrder = sycl::memory_order::relaxed,
90  typename T>
91 inline T atomic_fetch_and(T *addr, type_identity_t<T> operand) {
92  auto atm =
94  return atm.fetch_and(operand);
95 }
96 
104 template <sycl::access::address_space addressSpace =
105  sycl::access::address_space::generic_space,
106  sycl::memory_order memoryOrder = sycl::memory_order::relaxed,
108  typename T>
109 inline T atomic_fetch_or(T *addr, type_identity_t<T> operand) {
110  auto atm =
112  return atm.fetch_or(operand);
113 }
114 
122 template <sycl::access::address_space addressSpace =
123  sycl::access::address_space::generic_space,
124  sycl::memory_order memoryOrder = sycl::memory_order::relaxed,
126  typename T>
127 inline T atomic_fetch_xor(T *addr, type_identity_t<T> operand) {
128  auto atm =
130  return atm.fetch_xor(operand);
131 }
132 
138 template <sycl::access::address_space addressSpace =
139  sycl::access::address_space::generic_space,
140  sycl::memory_order memoryOrder = sycl::memory_order::relaxed,
142  typename T>
143 inline T atomic_fetch_min(T *addr, type_identity_t<T> operand) {
144  auto atm =
146  return atm.fetch_min(operand);
147 }
148 
155 template <sycl::access::address_space addressSpace =
156  sycl::access::address_space::generic_space,
157  sycl::memory_order memoryOrder = sycl::memory_order::relaxed,
159  typename T>
160 inline T atomic_fetch_max(T *addr, type_identity_t<T> operand) {
161  auto atm =
163  return atm.fetch_max(operand);
164 }
165 
172 template <sycl::access::address_space addressSpace =
173  sycl::access::address_space::generic_space,
174  sycl::memory_order memoryOrder = sycl::memory_order::relaxed,
176 unsigned int atomic_fetch_compare_dec(unsigned int *addr,
177  unsigned int operand) {
178  auto atm =
180  addr[0]);
181  unsigned int old;
182 
183  while (true) {
184  old = atm.load();
185  if (old == 0 || old > operand) {
186  if (atm.compare_exchange_strong(old, operand))
187  break;
188  } else if (atm.compare_exchange_strong(old, old - 1))
189  break;
190  }
191 
192  return old;
193 }
194 
201 template <sycl::access::address_space addressSpace =
202  sycl::access::address_space::generic_space,
203  sycl::memory_order memoryOrder = sycl::memory_order::relaxed,
205 inline unsigned int atomic_fetch_compare_inc(unsigned int *addr,
206  unsigned int operand) {
207  auto atm =
209  addr[0]);
210  unsigned int old;
211  while (true) {
212  old = atm.load();
213  if (old >= operand) {
214  if (atm.compare_exchange_strong(old, 0))
215  break;
216  } else if (atm.compare_exchange_strong(old, old + 1))
217  break;
218  }
219  return old;
220 }
221 
227 template <sycl::access::address_space addressSpace =
228  sycl::access::address_space::generic_space,
229  sycl::memory_order memoryOrder = sycl::memory_order::relaxed,
231  typename T>
232 inline T atomic_exchange(T *addr, type_identity_t<T> operand) {
233  auto atm =
235  return atm.exchange(operand);
236 }
237 
248 template <sycl::access::address_space addressSpace =
249  sycl::access::address_space::generic_space,
250  sycl::memory_order memoryOrder = sycl::memory_order::relaxed,
252  typename T>
255  type_identity_t<T> expected, type_identity_t<T> desired,
256  sycl::memory_order success = sycl::memory_order::relaxed,
257  sycl::memory_order fail = sycl::memory_order::relaxed) {
259 
260  atm.compare_exchange_strong(expected, desired, success, fail);
261  return expected;
262 }
263 
274 template <sycl::access::address_space addressSpace =
275  sycl::access::address_space::generic_space,
276  sycl::memory_order memoryOrder = sycl::memory_order::relaxed,
278  typename T>
280  T *addr, type_identity_t<T> expected, type_identity_t<T> desired,
281  sycl::memory_order success = sycl::memory_order::relaxed,
282  sycl::memory_order fail = sycl::memory_order::relaxed) {
283  auto atm =
285  atm.compare_exchange_strong(expected, desired, success, fail);
286  return expected;
287 }
288 
290 namespace detail {
291 template <typename T> struct IsValidAtomicType {
292  static constexpr bool value =
293  (std::is_same<T, int>::value || std::is_same<T, unsigned int>::value ||
294  std::is_same<T, long>::value || std::is_same<T, unsigned long>::value ||
295  std::is_same<T, long long>::value ||
296  std::is_same<T, unsigned long long>::value ||
297  std::is_same<T, float>::value || std::is_same<T, double>::value ||
298  std::is_pointer<T>::value);
299 };
300 } // namespace detail
301 
302 template <typename T,
303  sycl::memory_scope DefaultScope = sycl::memory_scope::system,
304  sycl::memory_order DefaultOrder = sycl::memory_order::seq_cst,
306  sycl::access::address_space::generic_space>
307 class atomic {
308  static_assert(
310  "Invalid atomic type. Valid types are int, unsigned int, long, "
311  "unsigned long, long long, unsigned long long, float, double "
312  "and pointer types");
313  T __d;
314 
315 public:
318  sycl::atomic_ref<T, DefaultOrder, DefaultScope,
319  Space>::default_read_order;
321  sycl::atomic_ref<T, DefaultOrder, DefaultScope,
322  Space>::default_write_order;
323  static constexpr sycl::memory_scope default_scope = DefaultScope;
325  DefaultOrder;
326 
328  constexpr atomic() noexcept = default;
330  constexpr atomic(T d) noexcept : __d(d){};
331 
337  void store(T operand, sycl::memory_order memoryOrder = default_write_order,
338  sycl::memory_scope memoryScope = default_scope) noexcept {
340  atm.store(operand, memoryOrder, memoryScope);
341  }
342 
348  sycl::memory_scope memoryScope = default_scope) const noexcept {
350  const_cast<T &>(__d));
351  return atm.load(memoryOrder, memoryScope);
352  }
353 
360  T exchange(T operand,
362  sycl::memory_scope memoryScope = default_scope) noexcept {
363 
365  return atm.exchange(operand, memoryOrder, memoryScope);
366  }
367 
380  T &expected, T desired, sycl::memory_order success,
381  sycl::memory_order failure,
382  sycl::memory_scope memoryScope = default_scope) noexcept {
384  return atm.compare_exchange_weak(expected, desired, success, failure,
385  memoryScope);
386  }
397  T &expected, T desired,
399  sycl::memory_scope memoryScope = default_scope) noexcept {
401  return atm.compare_exchange_weak(expected, desired, memoryOrder,
402  memoryScope);
403  }
404 
418  T &expected, T desired, sycl::memory_order success,
419  sycl::memory_order failure,
420  sycl::memory_scope memoryScope = default_scope) noexcept {
421 
423  return atm.compare_exchange_strong(expected, desired, success, failure,
424  memoryScope);
425  }
436  T &expected, T desired,
438  sycl::memory_scope memoryScope = default_scope) noexcept {
440  return atm.compare_exchange_strong(expected, desired, memoryOrder,
441  memoryScope);
442  }
443 
452  sycl::memory_scope memoryScope = default_scope) noexcept {
453 
455  return atm.fetch_add(operand, memoryOrder, memoryScope);
456  }
457 
466  sycl::memory_scope memoryScope = default_scope) noexcept {
467 
469  return atm.fetch_sub(operand, memoryOrder, memoryScope);
470  }
471 };
472 
473 } // namespace syclcompat
bool compare_exchange_strong(T &expected, T desired, memory_order success, memory_order failure, memory_scope scope=default_scope) const noexcept
Definition: atomic_ref.hpp:203
T load(memory_order order=default_read_order, memory_scope scope=default_scope) const noexcept
Definition: atomic_ref.hpp:180
void store(T operand, memory_order order=default_write_order, memory_scope scope=default_scope) const noexcept
Definition: atomic_ref.hpp:165
bool compare_exchange_weak(T &expected, T desired, memory_order success, memory_order failure, memory_scope scope=default_scope) const noexcept
Definition: atomic_ref.hpp:230
T exchange(T operand, memory_order order=default_read_modify_write_order, memory_scope scope=default_scope) const noexcept
Definition: atomic_ref.hpp:192
bool compare_exchange_strong(T &expected, T desired, sycl::memory_order memoryOrder=default_read_modify_write_order, sycl::memory_scope memoryScope=default_scope) noexcept
Definition: atomic.hpp:435
static constexpr sycl::memory_order default_read_modify_write_order
Definition: atomic.hpp:324
static constexpr sycl::memory_order default_read_order
default memory synchronization order
Definition: atomic.hpp:317
bool compare_exchange_weak(T &expected, T desired, sycl::memory_order memoryOrder=default_read_modify_write_order, sycl::memory_scope memoryScope=default_scope) noexcept
Definition: atomic.hpp:396
T fetch_sub(arith_t< T > operand, sycl::memory_order memoryOrder=default_read_modify_write_order, sycl::memory_scope memoryScope=default_scope) noexcept
atomically subtracts the argument from the value stored in the atomic object and obtains the value he...
Definition: atomic.hpp:464
T load(sycl::memory_order memoryOrder=default_read_order, sycl::memory_scope memoryScope=default_scope) const noexcept
atomically obtains the value of the referenced object
Definition: atomic.hpp:347
constexpr atomic() noexcept=default
Default constructor.
T fetch_add(arith_t< T > operand, sycl::memory_order memoryOrder=default_read_modify_write_order, sycl::memory_scope memoryScope=default_scope) noexcept
atomically adds the argument to the value stored in the atomic object and obtains the value held prev...
Definition: atomic.hpp:450
static constexpr sycl::memory_order default_write_order
Definition: atomic.hpp:320
static constexpr sycl::memory_scope default_scope
Definition: atomic.hpp:323
void store(T operand, sycl::memory_order memoryOrder=default_write_order, sycl::memory_scope memoryScope=default_scope) noexcept
atomically replaces the value of the referenced object with a non-atomic argument
Definition: atomic.hpp:337
bool compare_exchange_strong(T &expected, T desired, sycl::memory_order success, sycl::memory_order failure, sycl::memory_scope memoryScope=default_scope) noexcept
atomically compares the value of the referenced object with non-atomic argument and performs atomic e...
Definition: atomic.hpp:417
T exchange(T operand, sycl::memory_order memoryOrder=default_read_modify_write_order, sycl::memory_scope memoryScope=default_scope) noexcept
atomically replaces the value of the referenced object and obtains the value held previously
Definition: atomic.hpp:360
bool compare_exchange_weak(T &expected, T desired, sycl::memory_order success, sycl::memory_order failure, sycl::memory_scope memoryScope=default_scope) noexcept
atomically compares the value of the referenced object with non-atomic argument and performs atomic e...
Definition: atomic.hpp:379
T atomic_fetch_and(T *addr, type_identity_t< T > operand)
Atomically perform a bitwise AND between the value operand and the value at the addr and assign the r...
Definition: atomic.hpp:91
T atomic_compare_exchange_strong(sycl::multi_ptr< T, sycl::access::address_space::generic_space > addr, type_identity_t< T > expected, type_identity_t< T > desired, sycl::memory_order success=sycl::memory_order::relaxed, sycl::memory_order fail=sycl::memory_order::relaxed)
Atomically compare the value at addr to the value expected and exchange with the value desired if the...
Definition: atomic.hpp:253
unsigned int atomic_fetch_compare_inc(unsigned int *addr, unsigned int operand)
Atomically increment the value stored in addr if old value stored in addr is less than operand,...
Definition: atomic.hpp:205
T atomic_fetch_sub(T *addr, arith_t< T > operand)
Atomically subtract the value operand from the value at the addr and assign the result to the value a...
Definition: atomic.hpp:73
typename type_identity< T >::type type_identity_t
Definition: traits.hpp:35
T atomic_fetch_max(T *addr, type_identity_t< T > operand)
Atomically calculate the maximum of the value at addr and the value operand and assign the result to ...
Definition: atomic.hpp:160
T atomic_fetch_min(T *addr, type_identity_t< T > operand)
Atomically calculate the minimum of the value at addr and the value operand and assign the result to ...
Definition: atomic.hpp:143
T atomic_fetch_xor(T *addr, type_identity_t< T > operand)
Atomically xor the value at the addr with the value operand, and assign the result to the value at ad...
Definition: atomic.hpp:127
unsigned int atomic_fetch_compare_dec(unsigned int *addr, unsigned int operand)
Atomically set operand to the value stored in addr, if old value stored in addr is equal to zero or g...
Definition: atomic.hpp:176
typename arith< T >::type arith_t
Definition: traits.hpp:42
T atomic_fetch_or(T *addr, type_identity_t< T > operand)
Atomically or the value at the addr with the value operand, and assign the result to the value at add...
Definition: atomic.hpp:109
T atomic_exchange(T *addr, type_identity_t< T > operand)
Atomically exchange the value at the address addr with the value operand.
Definition: atomic.hpp:232
T atomic_fetch_add(T *addr, arith_t< T > operand)
Atomically add the value operand to the value at the addr and assign the result to the value at addr.
Definition: atomic.hpp:56
_Abi const simd< _Tp, _Abi > & noexcept
Definition: simd.hpp:1324
static constexpr bool value
Definition: atomic.hpp:292