Add basic atomic operations for refcounting, etc.
authorRyan C. Gordon <icculus@icculus.org>
Sun, 06 Aug 2017 09:21:38 -0400
changeset 1538 81c73124d651
parent 1537 274c95a7cc49
child 1539 e965964f054a
Add basic atomic operations for refcounting, etc.
src/physfs.c
src/physfs_internal.h
--- a/src/physfs.c	Sun Aug 06 01:34:18 2017 -0400
+++ b/src/physfs.c	Sun Aug 06 09:21:38 2017 -0400
@@ -96,6 +96,30 @@
 PHYSFS_Allocator allocator;
 
 
+#ifdef PHYSFS_NEED_ATOMIC_OP_FALLBACK
+static inline int __PHYSFS_atomicAdd(int *ptrval, const int val)
+{
+    int retval;
+    __PHYSFS_platformGrabMutex(stateLock);
+    retval = *ptrval;
+    *ptrval = retval + val;
+    __PHYSFS_platformReleaseMutex(stateLock);
+    return retval;
+} /* __PHYSFS_atomicAdd */
+
+int __PHYSFS_ATOMIC_INCR(int *ptrval)
+{
+    return __PHYSFS_atomicAdd(ptrval, 1);
+} /* __PHYSFS_ATOMIC_INCR */
+
+int __PHYSFS_ATOMIC_DECR(int *ptrval)
+{
+    return __PHYSFS_atomicAdd(ptrval, -1);
+} /* __PHYSFS_ATOMIC_DECR */
+#endif
+
+
+
 /* PHYSFS_Io implementation for i/o to physical filesystem... */
 
 /* !!! FIXME: maybe refcount the paths in a string pool? */
@@ -292,10 +316,7 @@
         BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
     } /* if */
 
-    /* !!! FIXME: want lockless atomic increment. */
-    __PHYSFS_platformGrabMutex(stateLock);
-    info->refcount++;
-    __PHYSFS_platformReleaseMutex(stateLock);
+    __PHYSFS_ATOMIC_INCR(&info->refcount);
 
     memset(newinfo, '\0', sizeof (*info));
     newinfo->buf = info->buf;
@@ -316,7 +337,6 @@
 {
     MemoryIoInfo *info = (MemoryIoInfo *) io->opaque;
     PHYSFS_Io *parent = info->parent;
-    int should_die = 0;
 
     if (parent != NULL)
     {
@@ -333,13 +353,7 @@
     /* we _are_ the parent. */
     assert(info->refcount > 0);  /* even in a race, we hold a reference. */
 
-    /* !!! FIXME: want lockless atomic decrement. */
-    __PHYSFS_platformGrabMutex(stateLock);
-    info->refcount--;
-    should_die = (info->refcount == 0);
-    __PHYSFS_platformReleaseMutex(stateLock);
-
-    if (should_die)
+    if (__PHYSFS_ATOMIC_DECR(&info->refcount) == 0)
     {
         void (*destruct)(void *) = info->destruct;
         void *buf = (void *) info->buf;
--- a/src/physfs_internal.h	Sun Aug 06 01:34:18 2017 -0400
+++ b/src/physfs_internal.h	Sun Aug 06 09:21:38 2017 -0400
@@ -104,6 +104,22 @@
 const void *__PHYSFS_winrtCalcPrefDir(void);
 #endif
 
+/* atomic operations. */
+#if defined(_MSC_VER) && (_MSC_VER >= 1500)
+#include <intrin.h>
+PHYSFS_COMPILE_TIME_ASSERT(LongEqualsInt, sizeof (int) == sizeof (long));
+#define __PHYSFS_ATOMIC_INCR(ptrval) _InterlockedIncrement((long*)ptrval)
+#define __PHYSFS_ATOMIC_INCR(ptrval) _InterlockedDecrement((long*)ptrval)
+#elif defined(__clang__) || (defined(__GNUC__) && (((__GNUC__ * 10000) + (__GNUC_MINOR__ * 100)) >= 40100))
+#define __PHYSFS_ATOMIC_INCR(ptrval) __sync_fetch_and_add(ptrval, 1)
+#define __PHYSFS_ATOMIC_DECR(ptrval) __sync_fetch_and_add(ptrval, -1)
+#else
+#define PHYSFS_NEED_ATOMIC_OP_FALLBACK 1
+int __PHYSFS_ATOMIC_INCR(int *ptrval);
+int __PHYSFS_ATOMIC_DECR(int *ptrval);
+#endif
+
+
 /*
  * Interface for small allocations. If you need a little scratch space for
  *  a throwaway buffer or string, use this. It will make small allocations