* 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, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Ryan Lortie <desrt@desrt.ca>
*/
* hardware memory barrier. Acquire and release or producer and
* consumer barrier semantics are not available through this API.
*
- * On GCC, these macros are implemented using GCC intrinsic operations.
- * On non-GCC compilers they will evaluate to function calls to
- * functions implemented by GLib.
- *
- * If GLib itself was compiled with GCC then these functions will again
- * be implemented by the GCC intrinsics. On Windows without GCC, the
- * interlocked API is used to implement the functions.
- *
- * With non-GCC compilers on non-Windows systems, the functions are
- * currently incapable of implementing true atomic operations --
- * instead, they fallback to holding a global lock while performing the
- * operation. This provides atomicity between the threads of one
- * process, but not between separate processes. For this reason, one
- * should exercise caution when attempting to use these options on
- * shared memory regions.
- *
* 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
* 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
+ * 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.
* without risking recursion.
*/
-#ifdef G_ATOMIC_OP_USE_GCC_BUILTINS
+#ifdef G_ATOMIC_LOCK_FREE
-#ifndef __GNUC__
-#error Using GCC builtin atomic ops, but not compiling with GCC?
-#endif
+/* if G_ATOMIC_LOCK_FREE was defined by ./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
* Since: 2.4
**/
gint
-(g_atomic_int_get) (volatile gint *atomic)
+(g_atomic_int_get) (const volatile gint *atomic)
{
return g_atomic_int_get (atomic);
}
*
* Increments the value of @atomic by 1.
*
- * Think of this operation as an atomic version of
- * <literal>{ *@atomic += 1; }</literal>
+ * Think of this operation as an atomic version of `{ *atomic += 1; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* Decrements the value of @atomic by 1.
*
* Think of this operation as an atomic version of
- * <literal>{ *@atomic -= 1; return (*@atomic == 0); }</literal>
+ * `{ *atomic -= 1; return (*atomic == 0); }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* This compare and exchange is done atomically.
*
* Think of this operation as an atomic version of
- * <literal>{ if (*@atomic == @oldval) { *@atomic = @newval; return TRUE; } else return FALSE; }</literal>
+ * `{ if (*atomic == oldval) { *atomic = newval; return TRUE; } else return FALSE; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* Atomically adds @val to the value of @atomic.
*
* Think of this operation as an atomic version of
- * <literal>{ tmp = *atomic; *@atomic += @val; return tmp; }</literal>
+ * `{ tmp = *atomic; *atomic += val; return tmp; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* This call acts as a full compiler and hardware memory barrier.
*
* Think of this operation as an atomic version of
- * <literal>{ tmp = *atomic; *@atomic &= @val; return tmp; }</literal>
+ * `{ tmp = *atomic; *atomic &= val; return tmp; }`.
*
* Returns: the value of @atomic before the operation, unsigned
*
* storing the result back in @atomic.
*
* Think of this operation as an atomic version of
- * <literal>{ tmp = *atomic; *@atomic |= @val; return tmp; }</literal>
+ * `{ tmp = *atomic; *atomic |= val; return tmp; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* storing the result back in @atomic.
*
* Think of this operation as an atomic version of
- * <literal>{ tmp = *atomic; *@atomic ^= @val; return tmp; }</literal>
+ * `{ tmp = *atomic; *atomic ^= val; return tmp; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* Since: 2.4
**/
gpointer
-(g_atomic_pointer_get) (volatile void *atomic)
+(g_atomic_pointer_get) (const volatile void *atomic)
{
- return g_atomic_pointer_get ((volatile gpointer *) atomic);
+ return g_atomic_pointer_get ((const volatile gpointer *) atomic);
}
/**
* This compare and exchange is done atomically.
*
* Think of this operation as an atomic version of
- * <literal>{ if (*@atomic == @oldval) { *@atomic = @newval; return TRUE; } else return FALSE; }</literal>
+ * `{ if (*atomic == oldval) { *atomic = newval; return TRUE; } else return FALSE; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* Atomically adds @val to the value of @atomic.
*
* Think of this operation as an atomic version of
- * <literal>{ tmp = *atomic; *@atomic += @val; return tmp; }</literal>
+ * `{ tmp = *atomic; *atomic += val; return tmp; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* storing the result back in @atomic.
*
* Think of this operation as an atomic version of
- * <literal>{ tmp = *atomic; *@atomic &= @val; return tmp; }</literal>
+ * `{ tmp = *atomic; *atomic &= val; return tmp; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* storing the result back in @atomic.
*
* Think of this operation as an atomic version of
- * <literal>{ tmp = *atomic; *@atomic |= @val; return tmp; }</literal>
+ * `{ tmp = *atomic; *atomic |= val; return tmp; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
* storing the result back in @atomic.
*
* Think of this operation as an atomic version of
- * <literal>{ tmp = *atomic; *@atomic ^= @val; return tmp; }</literal>
+ * `{ tmp = *atomic; *atomic ^= val; return tmp; }`.
*
* This call acts as a full compiler and hardware memory barrier.
*
#elif defined (G_PLATFORM_WIN32)
#include <windows.h>
-#if !defined(_M_AMD64) && !defined (_M_IA64) && !defined(_M_X64)
+#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) (volatile gint *atomic)
+(g_atomic_int_get) (const volatile gint *atomic)
{
MemoryBarrier ();
return *atomic;
gpointer
-(g_atomic_pointer_get) (volatile void *atomic)
+(g_atomic_pointer_get) (const volatile void *atomic)
{
- volatile gpointer *ptr = atomic;
+ const volatile gpointer *ptr = atomic;
MemoryBarrier ();
return *ptr;
return InterlockedXor (atomic, val);
#endif
}
-
#else
+/* This error occurs when ./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.
*
static pthread_mutex_t g_atomic_lock = PTHREAD_MUTEX_INITIALIZER;
gint
-(g_atomic_int_get) (volatile gint *atomic)
+(g_atomic_int_get) (const volatile gint *atomic)
{
gint value;
gpointer
-(g_atomic_pointer_get) (volatile void *atomic)
+(g_atomic_pointer_get) (const volatile void *atomic)
{
- volatile gpointer *ptr = atomic;
+ const volatile gpointer *ptr = atomic;
gpointer value;
pthread_mutex_lock (&g_atomic_lock);