/* This file is part of Fennix Kernel. Fennix Kernel is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fennix Kernel is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Fennix Kernel. If not, see . */ #pragma once #include #include #include namespace std { #define _atomic(T) T #define builtin_atomic_n(name) __atomic_##name##_n #define builtin_atomic(name) __atomic_##name /** * Specifies the memory ordering constraints for atomic operations. * * This enum specifies the possible values for the memory order * parameter of atomic operations. * * Possible values are: * * - memory_order_relaxed: There are no synchronization * or ordering constraints imposed on other reads or writes, * only this operation's atomicity is guaranteed. * * - memory_order_consume: A load operation with this * memory order performs a consume operation on the * affected memory location: no reads or writes in the * current thread dependent on the value currently loaded * can be reordered before this load. * * - memory_order_acquire: A load operation with this * memory order performs the acquire operation on the * affected memory location: no reads or writes in the * current thread can be reordered before this load. * * - memory_order_release: A store operation with this * memory order performs the release operation: no reads * or writes in the current thread can be reordered after * this store. * * - memory_order_acq_rel: A read-modify-write operation * with this memory order is both an acquire operation * and a release operation. * * - memory_order_seq_cst: A load operation with this * memory order performs an acquire operation, a store * performs a release operation, and read-modify-write * performs both an acquire operation and a release * operation, plus a single total order exists in which * all threads observe all modifications in the same order. */ enum class memory_order : int { relaxed = __ATOMIC_RELAXED, consume = __ATOMIC_CONSUME, acquire = __ATOMIC_ACQUIRE, release = __ATOMIC_RELEASE, acq_rel = __ATOMIC_ACQ_REL, seq_cst = __ATOMIC_SEQ_CST }; /** * Relaxed memory order * * No synchronization or ordering constraints * imposed on other reads or writes. * Only atomicity is guaranteed. */ inline constexpr memory_order memory_order_relaxed = memory_order::relaxed; /** * Consume memory order * * A load operation with this memory order * performs a consume operation on the affected * memory location. No reads or writes in the * current thread dependent on the value * currently loaded can be reordered before this * load. Writes to data-dependent variables in * other threads that release the same atomic * variable are visible in the current thread. */ inline constexpr memory_order memory_order_consume = memory_order::consume; /** Acquire memory order * * A load operation with this memory order * performs the acquire operation on the affected * memory location. No reads or writes in the * current thread can be reordered before this * load. All writes in other threads that release * the same atomic variable are visible in the * current thread. */ inline constexpr memory_order memory_order_acquire = memory_order::acquire; /** Release memory order * * A store operation with this memory order * performs the release operation. No reads or * writes in the current thread can be reordered * after this store. All writes in the current * thread are visible in other threads that acquire * the same atomic variable, and writes that carry * a dependency into the atomic variable become * visible in other threads that consume the same * atomic. */ inline constexpr memory_order memory_order_release = memory_order::release; /** Acquire-release memory order * * A read-modify-write operation with this memory * order is both an acquire operation and a release * operation. No memory reads or writes in the * current thread can be reordered before the load, * nor after the store. All writes in other threads * that release the same atomic variable are visible * before the modification, and the modification is * visible in other threads that acquire the same * atomic variable. */ inline constexpr memory_order memory_order_acq_rel = memory_order::acq_rel; /** Sequentially-consistent memory order * * A load operation with this memory order performs * an acquire operation, a store performs a release * operation, and read-modify-write performs both an * acquire operation and a release operation. * Additionally, a single total order exists in which * all threads observe all modifications in the same * order. */ inline constexpr memory_order memory_order_seq_cst = memory_order::seq_cst; template class atomic { _atomic(T) value; public: atomic() : value(0) {} atomic(T desired) : value(desired) {} // atomic(const atomic &) = delete; /** * Load the value of the atomic variable * * @note Order must be one of memory_order::relaxed, * memory_order::consume, memory_order::acquire or * memory_order::seq_cst * * @param order Memory order constraint to use * @return The value of the atomic variable */ inline __always_inline T load(memory_order order = memory_order::seq_cst) const { return builtin_atomic_n(load)(&this->value, static_cast(order)); } /** * @copydoc load() */ inline __always_inline T load(memory_order order = memory_order::seq_cst) const volatile { return builtin_atomic_n(load)(&this->value, static_cast(order)); } /** * Store the value of the atomic variable * * @note Order must be one of memory_order::relaxed, * memory_order::release or memory_order::seq_cst * * @param desired The value to store * @param order Memory order constraint to use */ inline __always_inline void store(T desired, memory_order order = memory_order::seq_cst) { builtin_atomic_n(store)(&this->value, desired, static_cast(order)); } /** * @copydoc store() */ inline __always_inline void store(T desired, memory_order order = memory_order::seq_cst) volatile { builtin_atomic_n(store)(&this->value, desired, static_cast(order)); } /** * Exchange the value of the atomic variable * * @param desired The value to exchange * @param order Memory order constraint to use * @return The value of the atomic variable before the exchange */ inline __always_inline T exchange(T desired, memory_order order = memory_order::seq_cst) { return builtin_atomic_n(exchange)(&this->value, desired, static_cast(order)); } /** * @copydoc exchange() */ inline __always_inline T exchange(T desired, memory_order order = memory_order::seq_cst) volatile { return builtin_atomic_n(exchange)(&this->value, desired, static_cast(order)); } /** * Compare and exchange the value of the atomic variable * * @param expected The expected value * @param desired The desired value * @param success Memory order constraint to use if the exchange succeeds * @param failure Memory order constraint to use if the exchange fails * @return True if the exchange succeeded, false otherwise */ inline __always_inline bool compare_exchange_weak(T &expected, T desired, memory_order success, memory_order failure) { return builtin_atomic(compare_exchange_weak)(&this->value, &expected, desired, false, success, failure); } /** * @copydoc compare_exchange_weak() */ inline __always_inline bool compare_exchange_weak(T &expected, T desired, memory_order success, memory_order failure) volatile { return builtin_atomic(compare_exchange_weak)(&this->value, &expected, desired, false, success, failure); } /** * Compare and exchange the value of the atomic variable * * @param expected The expected value * @param desired The desired value * @param order Memory order constraint to use * @return True if the exchange succeeded, false otherwise */ inline __always_inline bool compare_exchange_weak(T &expected, T desired, memory_order order = memory_order_seq_cst) { return builtin_atomic(compare_exchange_weak)(&this->value, &expected, desired, false, order, static_cast(order)); } /** * @copydoc compare_exchange_weak() */ inline __always_inline bool compare_exchange_weak(T &expected, T desired, memory_order order = memory_order_seq_cst) volatile { return builtin_atomic(compare_exchange_weak)(&this->value, &expected, desired, false, order, static_cast(order)); } /** * Compare and exchange the value of the atomic variable * * @param expected The expected value * @param desired The desired value * @param success Memory order constraint to use if the exchange succeeds * @param failure Memory order constraint to use if the exchange fails * @return True if the exchange succeeded, false otherwise */ inline __always_inline bool compare_exchange_strong(T &expected, T desired, memory_order success, memory_order failure) { return builtin_atomic(compare_exchange_strong)(&this->value, &expected, desired, true, success, failure); } /** * @copydoc compare_exchange_strong() */ inline __always_inline bool compare_exchange_strong(T &expected, T desired, memory_order success, memory_order failure) volatile { return builtin_atomic(compare_exchange_strong)(&this->value, &expected, desired, true, success, failure); } /** * Compare and exchange the value of the atomic variable * * @param expected The expected value * @param desired The desired value * @param order Memory order constraint to use * @return True if the exchange succeeded, false otherwise */ inline __always_inline bool compare_exchange_strong(T &expected, T desired, memory_order order = memory_order_seq_cst) { return builtin_atomic(compare_exchange_strong)(&this->value, &expected, desired, true, order, static_cast(order)); } /** * @copydoc compare_exchange_strong() */ inline __always_inline bool compare_exchange_strong(T &expected, T desired, memory_order order = memory_order_seq_cst) volatile { return builtin_atomic(compare_exchange_strong)(&this->value, &expected, desired, true, order, static_cast(order)); } /** * Fetch and add the value of the atomic variable * * @param arg The value to add * @param order Memory order constraint to use * @return The value of the atomic variable before the addition */ inline __always_inline T fetch_add(T arg, memory_order order = memory_order_seq_cst) { return builtin_atomic(fetch_add)(&this->value, arg, static_cast(order)); } /** * @copydoc fetch_add() */ inline __always_inline T fetch_add(T arg, memory_order order = memory_order_seq_cst) volatile { return builtin_atomic(fetch_add)(&this->value, arg, static_cast(order)); } /** * Fetch and subtract the value of the atomic variable * * @param arg The value to subtract * @param order Memory order constraint to use * @return The value of the atomic variable before the subtraction */ inline __always_inline T fetch_sub(T arg, memory_order order = memory_order_seq_cst) { return builtin_atomic(fetch_sub)(&this->value, arg, static_cast(order)); } /** * @copydoc fetch_sub() */ inline __always_inline T fetch_sub(T arg, memory_order order = memory_order_seq_cst) volatile { return builtin_atomic(fetch_sub)(&this->value, arg, static_cast(order)); } /** * Fetch and bitwise AND the value of the atomic variable * * @param arg The value to AND * @param order Memory order constraint to use * @return The value of the atomic variable before the AND */ inline __always_inline T fetch_and(T arg, memory_order order = memory_order_seq_cst) { return builtin_atomic(fetch_and)(&this->value, arg, static_cast(order)); } /** * @copydoc fetch_and() */ inline __always_inline T fetch_and(T arg, memory_order order = memory_order_seq_cst) volatile { return builtin_atomic(fetch_and)(&this->value, arg, static_cast(order)); } /** * Fetch and bitwise OR the value of the atomic variable * * @param arg The value to OR * @param order Memory order constraint to use * @return The value of the atomic variable before the OR */ inline __always_inline T fetch_or(T arg, memory_order order = memory_order_seq_cst) { return builtin_atomic(fetch_or)(&this->value, arg, static_cast(order)); } /** * @copydoc fetch_or() */ inline __always_inline T fetch_or(T arg, memory_order order = memory_order_seq_cst) volatile { return builtin_atomic(fetch_or)(&this->value, arg, static_cast(order)); } /** * Fetch and bitwise XOR the value of the atomic variable * * @param arg The value to XOR * @param order Memory order constraint to use * @return The value of the atomic variable before the XOR */ inline __always_inline T fetch_xor(T arg, memory_order order = memory_order_seq_cst) { return builtin_atomic(fetch_xor)(&this->value, arg, static_cast(order)); } /** * @copydoc fetch_xor() */ inline __always_inline T fetch_xor(T arg, memory_order order = memory_order_seq_cst) volatile { return builtin_atomic(fetch_xor)(&this->value, arg, static_cast(order)); } /** * Fetch and bitwise NAND the value of the atomic variable * * @param arg The value to NAND * @param order Memory order constraint to use * @return The value of the atomic variable before the NAND */ inline __always_inline T fetch_nand(T arg, memory_order order = memory_order_seq_cst) { return builtin_atomic(fetch_nand)(&this->value, arg, static_cast(order)); } /** * @copydoc fetch_nand() */ inline __always_inline T fetch_nand(T arg, memory_order order = memory_order_seq_cst) volatile { return builtin_atomic(fetch_nand)(&this->value, arg, static_cast(order)); } /** * Notify all threads waiting on this atomic variable */ void notify_all() { stub; } /** * @copydoc notify_all() */ void notify_all() volatile { stub; } /** * Notify one thread waiting on this atomic variable */ void notify_one() { stub; } /** * @copydoc notify_one() */ void notify_one() volatile { stub; } /** * Wait for the atomic variable to change * * @param old The value to wait for * @param order Memory order constraint to use */ void wait(T old, memory_order order = memory_order::seq_cst) const { while (this->load(order) == old) ; } /** * Check whether this atomic type is lock-free * @return True if this atomic type is lock-free */ bool is_lock_free() const { stub; return true; } /** * @copydoc is_lock_free() */ bool is_lock_free() const volatile { stub; return true; } /** * Equals true if this atomic type is always lock-free */ static constexpr bool is_always_lock_free = true; /************************************************/ T operator++() { return this->fetch_add(1) + 1; } // T operator++() volatile { return this->fetch_add(1) + 1; } T operator++(int) { return this->fetch_add(1); } // T operator++(int) volatile { return this->fetch_add(1); } /************************************************/ T operator--() { return this->fetch_sub(1) - 1; } // T operator--() volatile { return this->fetch_sub(1) - 1; } T operator--(int) { return this->fetch_sub(1); } // T operator--(int) volatile { return this->fetch_sub(1); } /************************************************/ T operator+=(T arg) { return this->fetch_add(arg) + arg; } // T operator+=(T arg) volatile { return this->fetch_add(arg) + arg; } // T operator+=(ptrdiff_t arg) { return this->fetch_add(arg) + arg; } // T operator+=(ptrdiff_t arg) volatile { return this->fetch_add(arg) + arg; } /************************************************/ T operator-=(T arg) { return this->fetch_sub(arg) - arg; } // T operator-=(T arg) volatile { return this->fetch_sub(arg) - arg; } // T operator-=(ptrdiff_t arg) { return this->fetch_sub(arg) - arg; } // T operator-=(ptrdiff_t arg) volatile { return this->fetch_sub(arg) - arg; } /************************************************/ T operator&=(T arg) { return this->fetch_and(arg) & arg; } // T operator&=(T arg) volatile { return this->fetch_and(arg) & arg; } T operator|=(T arg) { return this->fetch_or(arg) | arg; } // T operator|=(T arg) volatile { return this->fetch_or(arg) | arg; } T operator^=(T arg) { return this->fetch_xor(arg) ^ arg; } // T operator^=(T arg) volatile { return this->fetch_xor(arg) ^ arg; } /************************************************/ T operator=(T desired) { this->store(desired); return desired; } // T operator=(T desired) volatile // { // this->store(desired); // return desired; // } atomic &operator=(const atomic &other) = delete; atomic &operator=(const atomic &other) volatile = delete; /************************************************/ /* non standard functions */ T operator->() { return this->load(); } T operator~() { return this->fetch_nand(-1); } operator bool() { return this->load() != 0; } operator T() const { return this->load(); } bool operator==(const atomic &other) const { return this->load() == other.load(); } bool operator==(T other) const { return this->load() == other; } }; typedef atomic atomic_bool; typedef atomic atomic_char; typedef atomic atomic_schar; typedef atomic atomic_uchar; typedef atomic atomic_short; typedef atomic atomic_ushort; typedef atomic atomic_int; typedef atomic atomic_uint; typedef atomic atomic_long; typedef atomic atomic_ulong; typedef atomic atomic_llong; typedef atomic atomic_ullong; typedef atomic atomic_char16_t; typedef atomic atomic_char32_t; typedef atomic atomic_wchar_t; typedef atomic atomic_int8_t; typedef atomic atomic_uint8_t; typedef atomic atomic_int16_t; typedef atomic atomic_uint16_t; typedef atomic atomic_int32_t; typedef atomic atomic_uint32_t; typedef atomic atomic_int64_t; typedef atomic atomic_uint64_t; typedef atomic atomic_int_least8_t; typedef atomic atomic_uint_least8_t; typedef atomic atomic_int_least16_t; typedef atomic atomic_uint_least16_t; typedef atomic atomic_int_least32_t; typedef atomic atomic_uint_least32_t; typedef atomic atomic_int_least64_t; typedef atomic atomic_uint_least64_t; typedef atomic atomic_int_fast8_t; typedef atomic atomic_uint_fast8_t; typedef atomic atomic_int_fast16_t; typedef atomic atomic_uint_fast16_t; typedef atomic atomic_int_fast32_t; typedef atomic atomic_uint_fast32_t; typedef atomic atomic_int_fast64_t; typedef atomic atomic_uint_fast64_t; typedef atomic atomic_intptr_t; typedef atomic atomic_uintptr_t; typedef atomic atomic_size_t; typedef atomic atomic_ptrdiff_t; typedef atomic atomic_intmax_t; typedef atomic atomic_uintmax_t; } #undef builtin_atomic_n #undef builtin_atomic