Files
glib/glib/gatomic.c
Alex Richardson c762d51134 gatomic: Use g(u)intptr where appropriate
This is required for CHERI systems such as Arm Morello, where gsize is
not large enough to hold a pointer. For all other architectures this
should not result in any functional changes since gsize is already the
same size (and I believe for most architectures even the same type)
as guintptr.

This is fully ABI compatible with all currently supported architectures
and should also be fully API compatible except for some rather unlikely
examples such as someone happening to use something like
`decltype(g_atomic_pointer_add(&a, 0))` in a C++ context where it
changes name mangling of some function.

Helps: https://gitlab.gnome.org/GNOME/glib/-/issues/2842
2023-10-10 08:30:46 -07:00

1212 lines
31 KiB
C

/*
* Copyright © 2011 Ryan Lortie
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
#include "gatomic.h"
/**
* SECTION:atomic_operations
* @title: Atomic Operations
* @short_description: basic atomic integer and pointer operations
* @see_also: #GMutex
*
* The following is a collection of compiler macros to provide atomic
* access to integer and pointer-sized values.
*
* The macros that have 'int' in the name will operate on pointers to
* #gint and #guint. The macros with 'pointer' in the name will operate
* on pointers to any pointer-sized value, including #guintptr. There is
* no support for 64bit operations on platforms with 32bit pointers
* because it is not generally possible to perform these operations
* atomically.
*
* The get, set and exchange operations for integers and pointers
* nominally operate on #gint and #gpointer, respectively. Of the
* arithmetic operations, the 'add' operation operates on (and returns)
* signed integer values (#gint and #gssize) and the 'and', 'or', and
* 'xor' operations operate on (and return) unsigned integer values
* (#guint and #gsize).
*
* All of the operations act as a full compiler and (where appropriate)
* hardware memory barrier. Acquire and release or producer and
* consumer barrier semantics are not available through this API.
*
* It is very important that all accesses to a particular integer or
* pointer be performed using only this API and that different sizes of
* operation are not mixed or used on overlapping memory regions. Never
* read or assign directly from or to a value -- always use this API.
*
* For simple reference counting purposes you should use
* g_atomic_int_inc() and g_atomic_int_dec_and_test(). Other uses that
* fall outside of simple reference counting patterns are prone to
* subtle bugs and occasionally undefined behaviour. It is also worth
* noting that since all of these operations require global
* synchronisation of the entire machine, they can be quite slow. In
* the case of performing multiple atomic operations it can often be
* faster to simply acquire a mutex lock around the critical area,
* perform the operations normally and then release the lock.
**/
/**
* G_ATOMIC_LOCK_FREE:
*
* This macro is defined if the atomic operations of GLib are
* implemented using real hardware atomic operations. This means that
* the GLib atomic API can be used between processes and safely mixed
* with other (hardware) atomic APIs.
*
* If this macro is not defined, the atomic operations may be
* emulated using a mutex. In that case, the GLib atomic operations are
* only atomic relative to themselves and within a single process.
**/
/* NOTE CAREFULLY:
*
* This file is the lowest-level part of GLib.
*
* Other lowlevel parts of GLib (threads, slice allocator, g_malloc,
* messages, etc) call into these functions and macros to get work done.
*
* As such, these functions can not call back into any part of GLib
* without risking recursion.
*/
#ifdef G_ATOMIC_LOCK_FREE
/* if G_ATOMIC_LOCK_FREE was defined by `meson configure` then we MUST
* implement the atomic operations in a lock-free manner.
*/
#if defined (__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
/**
* g_atomic_int_get:
* @atomic: a pointer to a #gint or #guint
*
* Gets the current value of @atomic.
*
* This call acts as a full compiler and hardware
* memory barrier (before the get).
*
* While @atomic has a `volatile` qualifier, this is a historical artifact and
* the pointer passed to it should not be `volatile`.
*
* Returns: the value of the integer
*
* Since: 2.4
**/
gint
(g_atomic_int_get) (const volatile gint *atomic)
{
return g_atomic_int_get (atomic);
}
/**
* g_atomic_int_set:
* @atomic: a pointer to a #gint or #guint
* @newval: a new value to store
*
* Sets the value of @atomic to @newval.
*
* This call acts as a full compiler and hardware
* memory barrier (after the set).
*
* While @atomic has a `volatile` qualifier, this is a historical artifact and
* the pointer passed to it should not be `volatile`.
*
* Since: 2.4
*/
void
(g_atomic_int_set) (volatile gint *atomic,
gint newval)
{
g_atomic_int_set (atomic, newval);
}
/**
* g_atomic_int_inc:
* @atomic: a pointer to a #gint or #guint
*
* Increments the value of @atomic by 1.
*
* Think of this operation as an atomic version of `{ *atomic += 1; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* While @atomic has a `volatile` qualifier, this is a historical artifact and
* the pointer passed to it should not be `volatile`.
*
* Since: 2.4
**/
void
(g_atomic_int_inc) (volatile gint *atomic)
{
g_atomic_int_inc (atomic);
}
/**
* g_atomic_int_dec_and_test:
* @atomic: a pointer to a #gint or #guint
*
* Decrements the value of @atomic by 1.
*
* Think of this operation as an atomic version of
* `{ *atomic -= 1; return (*atomic == 0); }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* While @atomic has a `volatile` qualifier, this is a historical artifact and
* the pointer passed to it should not be `volatile`.
*
* Returns: %TRUE if the resultant value is zero
*
* Since: 2.4
**/
gboolean
(g_atomic_int_dec_and_test) (volatile gint *atomic)
{
return g_atomic_int_dec_and_test (atomic);
}
/**
* g_atomic_int_compare_and_exchange:
* @atomic: a pointer to a #gint or #guint
* @oldval: the value to compare with
* @newval: the value to conditionally replace with
*
* Compares @atomic to @oldval and, if equal, sets it to @newval.
* If @atomic was not equal to @oldval then no change occurs.
*
* This compare and exchange is done atomically.
*
* Think of this operation as an atomic version of
* `{ if (*atomic == oldval) { *atomic = newval; return TRUE; } else return FALSE; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* While @atomic has a `volatile` qualifier, this is a historical artifact and
* the pointer passed to it should not be `volatile`.
*
* Returns: %TRUE if the exchange took place
*
* Since: 2.4
**/
gboolean
(g_atomic_int_compare_and_exchange) (volatile gint *atomic,
gint oldval,
gint newval)
{
return g_atomic_int_compare_and_exchange (atomic, oldval, newval);
}
/**
* g_atomic_int_compare_and_exchange_full:
* @atomic: a pointer to a #gint or #guint
* @oldval: the value to compare with
* @newval: the value to conditionally replace with
* @preval: (out): the contents of @atomic before this operation
*
* Compares @atomic to @oldval and, if equal, sets it to @newval.
* If @atomic was not equal to @oldval then no change occurs.
* In any case the value of @atomic before this operation is stored in @preval.
*
* This compare and exchange is done atomically.
*
* Think of this operation as an atomic version of
* `{ *preval = *atomic; if (*atomic == oldval) { *atomic = newval; return TRUE; } else return FALSE; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* See also g_atomic_int_compare_and_exchange()
*
* Returns: %TRUE if the exchange took place
*
* Since: 2.74
**/
gboolean
(g_atomic_int_compare_and_exchange_full) (gint *atomic,
gint oldval,
gint newval,
gint *preval)
{
return g_atomic_int_compare_and_exchange_full (atomic, oldval, newval, preval);
}
/**
* g_atomic_int_exchange:
* @atomic: a pointer to a #gint or #guint
* @newval: the value to replace with
*
* Sets the @atomic to @newval and returns the old value from @atomic.
*
* This exchange is done atomically.
*
* Think of this operation as an atomic version of
* `{ tmp = *atomic; *atomic = val; return tmp; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* Returns: the value of @atomic before the exchange, signed
*
* Since: 2.74
**/
gint
(g_atomic_int_exchange) (gint *atomic,
gint newval)
{
return g_atomic_int_exchange (atomic, newval);
}
/**
* g_atomic_int_add:
* @atomic: a pointer to a #gint or #guint
* @val: the value to add
*
* Atomically adds @val to the value of @atomic.
*
* Think of this operation as an atomic version of
* `{ tmp = *atomic; *atomic += val; return tmp; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* Before version 2.30, this function did not return a value
* (but g_atomic_int_exchange_and_add() did, and had the same meaning).
*
* While @atomic has a `volatile` qualifier, this is a historical artifact and
* the pointer passed to it should not be `volatile`.
*
* Returns: the value of @atomic before the add, signed
*
* Since: 2.4
**/
gint
(g_atomic_int_add) (volatile gint *atomic,
gint val)
{
return g_atomic_int_add (atomic, val);
}
/**
* g_atomic_int_and:
* @atomic: a pointer to a #gint or #guint
* @val: the value to 'and'
*
* Performs an atomic bitwise 'and' of the value of @atomic and @val,
* storing the result back in @atomic.
*
* This call acts as a full compiler and hardware memory barrier.
*
* Think of this operation as an atomic version of
* `{ tmp = *atomic; *atomic &= val; return tmp; }`.
*
* While @atomic has a `volatile` qualifier, this is a historical artifact and
* the pointer passed to it should not be `volatile`.
*
* Returns: the value of @atomic before the operation, unsigned
*
* Since: 2.30
**/
guint
(g_atomic_int_and) (volatile guint *atomic,
guint val)
{
return g_atomic_int_and (atomic, val);
}
/**
* g_atomic_int_or:
* @atomic: a pointer to a #gint or #guint
* @val: the value to 'or'
*
* Performs an atomic bitwise 'or' of the value of @atomic and @val,
* storing the result back in @atomic.
*
* Think of this operation as an atomic version of
* `{ tmp = *atomic; *atomic |= val; return tmp; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* While @atomic has a `volatile` qualifier, this is a historical artifact and
* the pointer passed to it should not be `volatile`.
*
* Returns: the value of @atomic before the operation, unsigned
*
* Since: 2.30
**/
guint
(g_atomic_int_or) (volatile guint *atomic,
guint val)
{
return g_atomic_int_or (atomic, val);
}
/**
* g_atomic_int_xor:
* @atomic: a pointer to a #gint or #guint
* @val: the value to 'xor'
*
* Performs an atomic bitwise 'xor' of the value of @atomic and @val,
* storing the result back in @atomic.
*
* Think of this operation as an atomic version of
* `{ tmp = *atomic; *atomic ^= val; return tmp; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* While @atomic has a `volatile` qualifier, this is a historical artifact and
* the pointer passed to it should not be `volatile`.
*
* Returns: the value of @atomic before the operation, unsigned
*
* Since: 2.30
**/
guint
(g_atomic_int_xor) (volatile guint *atomic,
guint val)
{
return g_atomic_int_xor (atomic, val);
}
/**
* g_atomic_pointer_get:
* @atomic: (not nullable): a pointer to a #gpointer-sized value
*
* Gets the current value of @atomic.
*
* This call acts as a full compiler and hardware
* memory barrier (before the get).
*
* While @atomic has a `volatile` qualifier, this is a historical artifact and
* the pointer passed to it should not be `volatile`.
*
* Returns: the value of the pointer
*
* Since: 2.4
**/
gpointer
(g_atomic_pointer_get) (const volatile void *atomic)
{
return g_atomic_pointer_get ((gpointer *) atomic);
}
/**
* g_atomic_pointer_set:
* @atomic: (not nullable): a pointer to a #gpointer-sized value
* @newval: a new value to store
*
* Sets the value of @atomic to @newval.
*
* This call acts as a full compiler and hardware
* memory barrier (after the set).
*
* While @atomic has a `volatile` qualifier, this is a historical artifact and
* the pointer passed to it should not be `volatile`.
*
* Since: 2.4
**/
void
(g_atomic_pointer_set) (volatile void *atomic,
gpointer newval)
{
g_atomic_pointer_set ((gpointer *) atomic, newval);
}
/**
* g_atomic_pointer_compare_and_exchange:
* @atomic: (not nullable): a pointer to a #gpointer-sized value
* @oldval: the value to compare with
* @newval: the value to conditionally replace with
*
* Compares @atomic to @oldval and, if equal, sets it to @newval.
* If @atomic was not equal to @oldval then no change occurs.
*
* This compare and exchange is done atomically.
*
* Think of this operation as an atomic version of
* `{ if (*atomic == oldval) { *atomic = newval; return TRUE; } else return FALSE; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* While @atomic has a `volatile` qualifier, this is a historical artifact and
* the pointer passed to it should not be `volatile`.
*
* Returns: %TRUE if the exchange took place
*
* Since: 2.4
**/
gboolean
(g_atomic_pointer_compare_and_exchange) (volatile void *atomic,
gpointer oldval,
gpointer newval)
{
return g_atomic_pointer_compare_and_exchange ((gpointer *) atomic,
oldval, newval);
}
/**
* g_atomic_pointer_compare_and_exchange_full:
* @atomic: (not nullable): a pointer to a #gpointer-sized value
* @oldval: the value to compare with
* @newval: the value to conditionally replace with
* @preval: (not nullable) (out): the contents of @atomic before this operation
*
* Compares @atomic to @oldval and, if equal, sets it to @newval.
* If @atomic was not equal to @oldval then no change occurs.
* In any case the value of @atomic before this operation is stored in @preval.
*
* This compare and exchange is done atomically.
*
* Think of this operation as an atomic version of
* `{ *preval = *atomic; if (*atomic == oldval) { *atomic = newval; return TRUE; } else return FALSE; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* See also g_atomic_pointer_compare_and_exchange()
*
* Returns: %TRUE if the exchange took place
*
* Since: 2.74
**/
gboolean
(g_atomic_pointer_compare_and_exchange_full) (void *atomic,
gpointer oldval,
gpointer newval,
void *preval)
{
return g_atomic_pointer_compare_and_exchange_full ((gpointer *) atomic,
oldval, newval,
(gpointer *) preval);
}
/**
* g_atomic_pointer_exchange:
* @atomic: a pointer to a #gpointer-sized value
* @newval: the value to replace with
*
* Sets the @atomic to @newval and returns the old value from @atomic.
*
* This exchange is done atomically.
*
* Think of this operation as an atomic version of
* `{ tmp = *atomic; *atomic = val; return tmp; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* Returns: the value of @atomic before the exchange
*
* Since: 2.74
**/
gpointer
(g_atomic_pointer_exchange) (void *atomic,
gpointer newval)
{
return g_atomic_pointer_exchange ((gpointer *) atomic, newval);
}
/**
* g_atomic_pointer_add:
* @atomic: (not nullable): a pointer to a #gpointer-sized value
* @val: the value to add
*
* Atomically adds @val to the value of @atomic.
*
* Think of this operation as an atomic version of
* `{ tmp = *atomic; *atomic += val; return tmp; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* While @atomic has a `volatile` qualifier, this is a historical artifact and
* the pointer passed to it should not be `volatile`.
*
* In GLib 2.80, the return type was changed from #gssize to #gintptr to add
* support for platforms with 128-bit pointers. This should not affect existing
* code.
*
* Returns: the value of @atomic before the add, signed
*
* Since: 2.30
**/
gintptr
(g_atomic_pointer_add) (volatile void *atomic,
gssize val)
{
return g_atomic_pointer_add ((gpointer *) atomic, val);
}
/**
* g_atomic_pointer_and:
* @atomic: (not nullable): a pointer to a #gpointer-sized value
* @val: the value to 'and'
*
* Performs an atomic bitwise 'and' of the value of @atomic and @val,
* storing the result back in @atomic.
*
* Think of this operation as an atomic version of
* `{ tmp = *atomic; *atomic &= val; return tmp; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* While @atomic has a `volatile` qualifier, this is a historical artifact and
* the pointer passed to it should not be `volatile`.
*
* In GLib 2.80, the return type was changed from #gsize to #guintptr to add
* support for platforms with 128-bit pointers. This should not affect existing
* code.
*
* Returns: the value of @atomic before the operation, unsigned
*
* Since: 2.30
**/
guintptr
(g_atomic_pointer_and) (volatile void *atomic,
gsize val)
{
return g_atomic_pointer_and ((gpointer *) atomic, val);
}
/**
* g_atomic_pointer_or:
* @atomic: (not nullable): a pointer to a #gpointer-sized value
* @val: the value to 'or'
*
* Performs an atomic bitwise 'or' of the value of @atomic and @val,
* storing the result back in @atomic.
*
* Think of this operation as an atomic version of
* `{ tmp = *atomic; *atomic |= val; return tmp; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* While @atomic has a `volatile` qualifier, this is a historical artifact and
* the pointer passed to it should not be `volatile`.
*
* In GLib 2.80, the return type was changed from #gsize to #guintptr to add
* support for platforms with 128-bit pointers. This should not affect existing
* code.
*
* Returns: the value of @atomic before the operation, unsigned
*
* Since: 2.30
**/
guintptr
(g_atomic_pointer_or) (volatile void *atomic,
gsize val)
{
return g_atomic_pointer_or ((gpointer *) atomic, val);
}
/**
* g_atomic_pointer_xor:
* @atomic: (not nullable): a pointer to a #gpointer-sized value
* @val: the value to 'xor'
*
* Performs an atomic bitwise 'xor' of the value of @atomic and @val,
* storing the result back in @atomic.
*
* Think of this operation as an atomic version of
* `{ tmp = *atomic; *atomic ^= val; return tmp; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* While @atomic has a `volatile` qualifier, this is a historical artifact and
* the pointer passed to it should not be `volatile`.
*
* In GLib 2.80, the return type was changed from #gsize to #guintptr to add
* support for platforms with 128-bit pointers. This should not affect existing
* code.
*
* Returns: the value of @atomic before the operation, unsigned
*
* Since: 2.30
**/
guintptr
(g_atomic_pointer_xor) (volatile void *atomic,
gsize val)
{
return g_atomic_pointer_xor ((gpointer *) atomic, val);
}
#elif defined (G_PLATFORM_WIN32)
#include <windows.h>
#if !defined(_M_AMD64) && !defined (_M_IA64) && !defined(_M_X64) && !(defined _MSC_VER && _MSC_VER <= 1200)
#define InterlockedAnd _InterlockedAnd
#define InterlockedOr _InterlockedOr
#define InterlockedXor _InterlockedXor
#endif
#if !defined (_MSC_VER) || _MSC_VER <= 1200
#include "gmessages.h"
/* Inlined versions for older compiler */
static LONG
_gInterlockedAnd (volatile guint *atomic,
guint val)
{
LONG i, j;
j = *atomic;
do {
i = j;
j = InterlockedCompareExchange(atomic, i & val, i);
} while (i != j);
return j;
}
#define InterlockedAnd(a,b) _gInterlockedAnd(a,b)
static LONG
_gInterlockedOr (volatile guint *atomic,
guint val)
{
LONG i, j;
j = *atomic;
do {
i = j;
j = InterlockedCompareExchange(atomic, i | val, i);
} while (i != j);
return j;
}
#define InterlockedOr(a,b) _gInterlockedOr(a,b)
static LONG
_gInterlockedXor (volatile guint *atomic,
guint val)
{
LONG i, j;
j = *atomic;
do {
i = j;
j = InterlockedCompareExchange(atomic, i ^ val, i);
} while (i != j);
return j;
}
#define InterlockedXor(a,b) _gInterlockedXor(a,b)
#endif
/*
* http://msdn.microsoft.com/en-us/library/ms684122(v=vs.85).aspx
*/
gint
(g_atomic_int_get) (const volatile gint *atomic)
{
MemoryBarrier ();
return *atomic;
}
void
(g_atomic_int_set) (volatile gint *atomic,
gint newval)
{
*atomic = newval;
MemoryBarrier ();
}
void
(g_atomic_int_inc) (volatile gint *atomic)
{
InterlockedIncrement (atomic);
}
gboolean
(g_atomic_int_dec_and_test) (volatile gint *atomic)
{
return InterlockedDecrement (atomic) == 0;
}
gboolean
(g_atomic_int_compare_and_exchange) (volatile gint *atomic,
gint oldval,
gint newval)
{
return InterlockedCompareExchange (atomic, newval, oldval) == oldval;
}
gboolean
(g_atomic_int_compare_and_exchange_full) (gint *atomic,
gint oldval,
gint newval,
gint *preval)
{
*preval = InterlockedCompareExchange (atomic, newval, oldval);
return *preval == oldval;
}
gint
(g_atomic_int_exchange) (gint *atomic,
gint newval)
{
return InterlockedExchange (atomic, newval);
}
gint
(g_atomic_int_add) (volatile gint *atomic,
gint val)
{
return InterlockedExchangeAdd (atomic, val);
}
guint
(g_atomic_int_and) (volatile guint *atomic,
guint val)
{
return InterlockedAnd (atomic, val);
}
guint
(g_atomic_int_or) (volatile guint *atomic,
guint val)
{
return InterlockedOr (atomic, val);
}
guint
(g_atomic_int_xor) (volatile guint *atomic,
guint val)
{
return InterlockedXor (atomic, val);
}
gpointer
(g_atomic_pointer_get) (const volatile void *atomic)
{
const gpointer *ptr = atomic;
MemoryBarrier ();
return *ptr;
}
void
(g_atomic_pointer_set) (volatile void *atomic,
gpointer newval)
{
gpointer *ptr = atomic;
*ptr = newval;
MemoryBarrier ();
}
gboolean
(g_atomic_pointer_compare_and_exchange) (volatile void *atomic,
gpointer oldval,
gpointer newval)
{
return InterlockedCompareExchangePointer (atomic, newval, oldval) == oldval;
}
gboolean
(g_atomic_pointer_compare_and_exchange_full) (void *atomic,
gpointer oldval,
gpointer newval,
void *preval)
{
gpointer *pre = preval;
*pre = InterlockedCompareExchangePointer (atomic, newval, oldval);
return *pre == oldval;
}
gpointer
(g_atomic_pointer_exchange) (void *atomic,
gpointer newval)
{
return InterlockedExchangePointer (atomic, newval);
}
gintptr
(g_atomic_pointer_add) (volatile void *atomic,
gssize val)
{
#if GLIB_SIZEOF_VOID_P == 8
return InterlockedExchangeAdd64 (atomic, val);
#else
return InterlockedExchangeAdd (atomic, val);
#endif
}
guintptr
(g_atomic_pointer_and) (volatile void *atomic,
gsize val)
{
#if GLIB_SIZEOF_VOID_P == 8
return InterlockedAnd64 (atomic, val);
#else
return InterlockedAnd (atomic, val);
#endif
}
guintptr
(g_atomic_pointer_or) (volatile void *atomic,
gsize val)
{
#if GLIB_SIZEOF_VOID_P == 8
return InterlockedOr64 (atomic, val);
#else
return InterlockedOr (atomic, val);
#endif
}
guintptr
(g_atomic_pointer_xor) (volatile void *atomic,
gsize val)
{
#if GLIB_SIZEOF_VOID_P == 8
return InterlockedXor64 (atomic, val);
#else
return InterlockedXor (atomic, val);
#endif
}
#else
/* This error occurs when `meson configure` decided that we should be capable
* of lock-free atomics but we find at compile-time that we are not.
*/
#error G_ATOMIC_LOCK_FREE defined, but incapable of lock-free atomics.
#endif /* defined (__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) */
#else /* G_ATOMIC_LOCK_FREE */
/* We are not permitted to call into any GLib functions from here, so we
* can not use GMutex.
*
* Fortunately, we already take care of the Windows case above, and all
* non-Windows platforms on which glib runs have pthreads. Use those.
*/
#include <pthread.h>
static pthread_mutex_t g_atomic_lock = PTHREAD_MUTEX_INITIALIZER;
gint
(g_atomic_int_get) (const volatile gint *atomic)
{
gint value;
pthread_mutex_lock (&g_atomic_lock);
value = *atomic;
pthread_mutex_unlock (&g_atomic_lock);
return value;
}
void
(g_atomic_int_set) (volatile gint *atomic,
gint value)
{
pthread_mutex_lock (&g_atomic_lock);
*atomic = value;
pthread_mutex_unlock (&g_atomic_lock);
}
void
(g_atomic_int_inc) (volatile gint *atomic)
{
pthread_mutex_lock (&g_atomic_lock);
(*atomic)++;
pthread_mutex_unlock (&g_atomic_lock);
}
gboolean
(g_atomic_int_dec_and_test) (volatile gint *atomic)
{
gboolean is_zero;
pthread_mutex_lock (&g_atomic_lock);
is_zero = --(*atomic) == 0;
pthread_mutex_unlock (&g_atomic_lock);
return is_zero;
}
gboolean
(g_atomic_int_compare_and_exchange) (volatile gint *atomic,
gint oldval,
gint newval)
{
gboolean success;
pthread_mutex_lock (&g_atomic_lock);
if ((success = (*atomic == oldval)))
*atomic = newval;
pthread_mutex_unlock (&g_atomic_lock);
return success;
}
gboolean
(g_atomic_int_compare_and_exchange_full) (gint *atomic,
gint oldval,
gint newval,
gint *preval)
{
gboolean success;
pthread_mutex_lock (&g_atomic_lock);
*preval = *atomic;
if ((success = (*atomic == oldval)))
*atomic = newval;
pthread_mutex_unlock (&g_atomic_lock);
return success;
}
gint
(g_atomic_int_exchange) (gint *atomic,
gint newval)
{
gint *ptr = atomic;
gint oldval;
pthread_mutex_lock (&g_atomic_lock);
oldval = *ptr;
*ptr = newval;
pthread_mutex_unlock (&g_atomic_lock);
return oldval;
}
gint
(g_atomic_int_add) (volatile gint *atomic,
gint val)
{
gint oldval;
pthread_mutex_lock (&g_atomic_lock);
oldval = *atomic;
*atomic = oldval + val;
pthread_mutex_unlock (&g_atomic_lock);
return oldval;
}
guint
(g_atomic_int_and) (volatile guint *atomic,
guint val)
{
guint oldval;
pthread_mutex_lock (&g_atomic_lock);
oldval = *atomic;
*atomic = oldval & val;
pthread_mutex_unlock (&g_atomic_lock);
return oldval;
}
guint
(g_atomic_int_or) (volatile guint *atomic,
guint val)
{
guint oldval;
pthread_mutex_lock (&g_atomic_lock);
oldval = *atomic;
*atomic = oldval | val;
pthread_mutex_unlock (&g_atomic_lock);
return oldval;
}
guint
(g_atomic_int_xor) (volatile guint *atomic,
guint val)
{
guint oldval;
pthread_mutex_lock (&g_atomic_lock);
oldval = *atomic;
*atomic = oldval ^ val;
pthread_mutex_unlock (&g_atomic_lock);
return oldval;
}
gpointer
(g_atomic_pointer_get) (const volatile void *atomic)
{
const gpointer *ptr = atomic;
gpointer value;
pthread_mutex_lock (&g_atomic_lock);
value = *ptr;
pthread_mutex_unlock (&g_atomic_lock);
return value;
}
void
(g_atomic_pointer_set) (volatile void *atomic,
gpointer newval)
{
gpointer *ptr = atomic;
pthread_mutex_lock (&g_atomic_lock);
*ptr = newval;
pthread_mutex_unlock (&g_atomic_lock);
}
gboolean
(g_atomic_pointer_compare_and_exchange) (volatile void *atomic,
gpointer oldval,
gpointer newval)
{
gpointer *ptr = atomic;
gboolean success;
pthread_mutex_lock (&g_atomic_lock);
if ((success = (*ptr == oldval)))
*ptr = newval;
pthread_mutex_unlock (&g_atomic_lock);
return success;
}
gboolean
(g_atomic_pointer_compare_and_exchange_full) (void *atomic,
gpointer oldval,
gpointer newval,
void *preval)
{
gpointer *ptr = atomic;
gpointer *pre = preval;
gboolean success;
pthread_mutex_lock (&g_atomic_lock);
*pre = *ptr;
if ((success = (*ptr == oldval)))
*ptr = newval;
pthread_mutex_unlock (&g_atomic_lock);
return success;
}
gpointer
(g_atomic_pointer_exchange) (void *atomic,
gpointer newval)
{
gpointer *ptr = atomic;
gpointer oldval;
pthread_mutex_lock (&g_atomic_lock);
oldval = *ptr;
*ptr = newval;
pthread_mutex_unlock (&g_atomic_lock);
return oldval;
}
gintptr
(g_atomic_pointer_add) (volatile void *atomic,
gssize val)
{
gintptr *ptr = atomic;
gintptr oldval;
pthread_mutex_lock (&g_atomic_lock);
oldval = *ptr;
*ptr = oldval + val;
pthread_mutex_unlock (&g_atomic_lock);
return oldval;
}
guintptr
(g_atomic_pointer_and) (volatile void *atomic,
gsize val)
{
guintptr *ptr = atomic;
guintptr oldval;
pthread_mutex_lock (&g_atomic_lock);
oldval = *ptr;
*ptr = oldval & val;
pthread_mutex_unlock (&g_atomic_lock);
return oldval;
}
guintptr
(g_atomic_pointer_or) (volatile void *atomic,
gsize val)
{
guintptr *ptr = atomic;
guintptr oldval;
pthread_mutex_lock (&g_atomic_lock);
oldval = *ptr;
*ptr = oldval | val;
pthread_mutex_unlock (&g_atomic_lock);
return oldval;
}
guintptr
(g_atomic_pointer_xor) (volatile void *atomic,
gsize val)
{
guintptr *ptr = atomic;
guintptr oldval;
pthread_mutex_lock (&g_atomic_lock);
oldval = *ptr;
*ptr = oldval ^ val;
pthread_mutex_unlock (&g_atomic_lock);
return oldval;
}
#endif
/**
* g_atomic_int_exchange_and_add:
* @atomic: a pointer to a #gint
* @val: the value to add
*
* This function existed before g_atomic_int_add() returned the prior
* value of the integer (which it now does). It is retained only for
* compatibility reasons. Don't use this function in new code.
*
* Returns: the value of @atomic before the add, signed
* Since: 2.4
* Deprecated: 2.30: Use g_atomic_int_add() instead.
**/
gint
g_atomic_int_exchange_and_add (volatile gint *atomic,
gint val)
{
return (g_atomic_int_add) ((gint *) atomic, val);
}