Files
MentOS/mentos/inc/klib/stdatomic.h
T
Enrico Fraccaroli (Galfurian) 1b2bc49d41 Update license and remove unused files.
2022-01-27 15:12:36 -05:00

165 lines
5.4 KiB
C

/// @file stdatomic.h
/// @brief
/// @copyright (c) 2014-2022 This file is distributed under the MIT License.
/// See LICENSE.md for details.
#pragma once
#include "stdint.h"
#include "stdbool.h"
#include "klib/compiler.h"
/// @brief Standard structure for atomic operations (see below
/// for volatile explanation).
typedef volatile unsigned atomic_t;
/// @brief The prefix used to lock.
#define LOCK_PREFIX "\n\tlock; "
/// @brief Compile read-write barrier.
#define barrier() __asm__ __volatile__("" \
: \
: \
: "memory")
/// @brief Pause instruction to prevent excess processor bus usage.
#define cpu_relax() __asm__ __volatile__("pause\n" \
: \
: \
: "memory")
/// @brief Atomically sets the value of ptr with the value of x.
///
/// @param ptr Pointer to the atomic variable to set.
/// @param x The value to set.
/// @return The final value of the atomic variable.
inline static int32_t atomic_set_and_test(atomic_t *ptr, int32_t x)
{
__asm__ __volatile__("xchgl %0,%1" // instruction
: "=r"(x) // outputs
: "m"(*ptr), "0"(x) // inputs
: "memory"); // side effects
return x;
}
/// @brief Atomically set ptr equal to i.
inline static void atomic_set(atomic_t *ptr, int32_t i)
{
atomic_set_and_test(ptr, i);
}
/// @brief Atomically read the integer value of ptr.
inline static int32_t atomic_read(const atomic_t *ptr)
{
return READ_ONCE(*ptr);
}
/// @brief Atomically add i to ptr.
inline static int32_t atomic_add(atomic_t const *ptr, int32_t i)
{
int result = i;
__asm__ __volatile__(LOCK_PREFIX "xaddl %0, %1"
: "=r"(i)
: "m"(ptr), "0"(i)
: "memory", "cc");
return result + i;
}
/// @brief Atomically subtract i from ptr.
inline static int32_t atomic_sub(atomic_t *ptr, int32_t i)
{
return atomic_add(ptr, -i);
}
/// @brief Atomically add one to ptr.
inline static int32_t atomic_inc(atomic_t *ptr)
{
return atomic_add(ptr, 1);
}
/// @brief Atomically subtract one from ptr.
inline static int32_t atomic_dec(atomic_t *ptr)
{
return atomic_add(ptr, -1);
}
/// @brief Atomically add i to ptr and return true if the result is negative;
/// otherwise false.
inline static bool_t atomic_add_negative(atomic_t *ptr, int32_t i)
{
return (bool_t)(atomic_add(ptr, i) < 0);
}
/// @brief Atomically subtract i from ptr and return true if the result is
/// zero; otherwise false.
inline static bool_t atomic_sub_and_test(atomic_t *ptr, int32_t i)
{
return (bool_t)(atomic_sub(ptr, i) == 0);
}
/// @brief Atomically increment ptr by one and return true if the result is
/// zero; false otherwise.
inline static int32_t atomic_inc_and_test(atomic_t *ptr)
{
return (bool_t)(atomic_inc(ptr) == 0);
}
/// @brief Atomically decrement ptr by one and return true if zero; false
/// otherwise.
inline static int32_t atomic_dec_and_test(atomic_t *ptr)
{
return (bool_t)(atomic_dec(ptr) == 0);
}
/// @brief Atomically sets a bit in memory, using Bit Test And Set (bts).
/// @param offset The offset to the bit.
/// @param base The base address.
static inline void set_bit(int offset, volatile unsigned long *base)
{
__asm__ __volatile__("btsl %[offset],%[base]"
: [base] "=m"(*(volatile long *)base)
: [offset] "Ir"(offset));
}
/// @brief Atomically clears a bit in memory.
/// @param offset The offset to the bit.
/// @param base The base address.
static inline void clear_bit(int offset, volatile unsigned long *base)
{
__asm__ __volatile__("btrl %[offset],%[base]"
: [base] "=m"(*(volatile long *)base)
: [offset] "Ir"(offset));
}
/// @brief Atomically tests a bit in memory.
/// @param offset The offset to the bit.
/// @param base The base address.
/// @return 1 if the bit is set, 0 otherwise.
static inline int test_bit(int offset, volatile unsigned long *base)
{
int old = 0;
__asm__ __volatile__("btl %[offset],%[base]\n" // Bit Test
"sbbl %[old],%[old]\n" // Return the previous value.
: [old] "=r"(old)
: [base] "m"(*(volatile long *)base),
[offset] "Ir"(offset));
return old;
}
// == Volatile Variable =======================================================
// In C, and consequently C++, the volatile keyword was intended to:
// - allow access to memory-mapped I/O devices
// - allow uses of variables between setjmp and longjmp
// - allow uses of sig_atomic_t variables in signal handlers.
//
// Operations on volatile variables are not atomic, nor do they establish
// a proper happens-before relationship for threading like with the
// `__asm__` inline blocks.
// This is specified in the relevant standards (C, C++, POSIX, WIN32), and
// volatile variables are not thread-safe in the vast majority of current
// implementations.
// Thus, the usage of volatile keyword as a portable synchronization mechanism
// is discouraged by many C/C++ groups.
// == xchg/xchgl ==============================================================
//