Change order of GFormatSizeFlags
[platform/upstream/glib.git] / glib / gbitlock.c
index 33cbb2a..a9f7a5e 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "gbitlock.h"
 
+#include <glib/gmessages.h>
 #include <glib/gatomic.h>
 #include <glib/gslist.h>
 #include <glib/gthread.h>
@@ -29,7 +30,6 @@
 #include "gthreadprivate.h"
 #include "config.h"
 
-#include "galias.h"
 
 #ifdef G_BIT_LOCK_FORCE_FUTEX_EMULATION
 #undef HAVE_FUTEX
@@ -57,7 +57,7 @@ _g_futex_thread_init (void) {
  * If anyone actually gets bit by this, please file a bug. :)
  */
 #include <linux/futex.h>
-#include <syscall.h>
+#include <sys/syscall.h>
 #include <unistd.h>
 
 /* < private >
@@ -81,7 +81,7 @@ static void
 g_futex_wait (const volatile gint *address,
               gint                 value)
 {
-  syscall (SYS_futex, address, (gsize) FUTEX_WAIT, (gsize) value, NULL);
+  syscall (__NR_futex, address, (gsize) FUTEX_WAIT, (gsize) value, NULL);
 }
 
 /* < private >
@@ -98,7 +98,7 @@ g_futex_wait (const volatile gint *address,
 static void
 g_futex_wake (const volatile gint *address)
 {
-  syscall (SYS_futex, address, (gsize) FUTEX_WAKE, (gsize) 1, NULL);
+  syscall (__NR_futex, address, (gsize) FUTEX_WAKE, (gsize) 1, NULL);
 }
 
 #else
@@ -111,9 +111,6 @@ typedef struct
   GCond               *wait_queue;
 } WaitAddress;
 
-static GSList *g_futex_address_list;
-static GMutex *g_futex_mutex;
-
 static WaitAddress *
 g_futex_find_address (const volatile gint *address)
 {
@@ -184,6 +181,12 @@ g_futex_wake (const volatile gint *address)
 #define CONTENTION_CLASSES 11
 static volatile gint g_bit_lock_contended[CONTENTION_CLASSES];
 
+#if (defined (i386) || defined (__amd64__))
+  #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+    #define USE_ASM_GOTO 1
+  #endif
+#endif
+
 /**
  * g_bit_lock:
  * @address: a pointer to an integer
@@ -209,11 +212,39 @@ void
 g_bit_lock (volatile gint *address,
             gint           lock_bit)
 {
+#ifdef USE_ASM_GOTO
+ retry:
+  asm volatile goto ("lock bts %1, (%0)\n"
+                     "jc %l[contended]"
+                     : /* no output */
+                     : "r" (address), "r" (lock_bit)
+                     : "cc", "memory"
+                     : contended);
+  return;
+
+ contended:
+  {
+    guint mask = 1u << lock_bit;
+    guint v;
+
+    v = g_atomic_int_get (address);
+    if (v & mask)
+      {
+        guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
+
+        g_atomic_int_add (&g_bit_lock_contended[class], +1);
+        g_futex_wait (address, v);
+        g_atomic_int_add (&g_bit_lock_contended[class], -1);
+      }
+  }
+  goto retry;
+#else
+  guint mask = 1u << lock_bit;
   guint v;
 
  retry:
-  v = g_atomic_int_get (address);
-  if (v & (1u << lock_bit))
+  v = g_atomic_int_or (address, mask);
+  if (v & mask)
     /* already locked */
     {
       guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
@@ -224,15 +255,14 @@ g_bit_lock (volatile gint *address,
 
       goto retry;
     }
-
-  if (!g_atomic_int_compare_and_exchange (address, v, v | (1u << lock_bit)))
-    goto retry;
+#endif
 }
 
 /**
  * g_bit_trylock:
  * @address: a pointer to an integer
  * @lock_bit: a bit value between 0 and 31
+ * @returns: %TRUE if the lock was acquired
  *
  * Sets the indicated @lock_bit in @address, returning %TRUE if
  * successful.  If the bit is already set, returns %FALSE immediately.
@@ -253,18 +283,25 @@ gboolean
 g_bit_trylock (volatile gint *address,
                gint           lock_bit)
 {
-  guint v;
+#ifdef USE_ASM_GOTO
+  gboolean result;
 
- retry:
-  v = g_atomic_int_get (address);
-  if (v & (1u << lock_bit))
-    /* already locked */
-    return FALSE;
+  asm volatile ("lock bts %2, (%1)\n"
+                "setnc %%al\n"
+                "movzx %%al, %0"
+                : "=r" (result)
+                : "r" (address), "r" (lock_bit)
+                : "cc", "memory");
 
-  if (!g_atomic_int_compare_and_exchange (address, v, v | (1u << lock_bit)))
-    goto retry;
+  return result;
+#else
+  guint mask = 1u << lock_bit;
+  guint v;
+
+  v = g_atomic_int_or (address, mask);
 
-  return TRUE;
+  return ~v & mask;
+#endif
 }
 
 /**
@@ -286,17 +323,213 @@ void
 g_bit_unlock (volatile gint *address,
               gint           lock_bit)
 {
-  guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
-  guint v;
+#ifdef USE_ASM_GOTO
+  asm volatile ("lock btr %1, (%0)"
+                : /* no output */
+                : "r" (address), "r" (lock_bit)
+                : "cc", "memory");
+#else
+  guint mask = 1u << lock_bit;
+
+  g_atomic_int_and (address, ~mask);
+#endif
+
+  {
+    guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
+
+    if (g_atomic_int_get (&g_bit_lock_contended[class]))
+      g_futex_wake (address);
+  }
+}
+
+
+/* We emulate pointer-sized futex(2) because the kernel API only
+ * supports integers.
+ *
+ * We assume that the 'interesting' part is always the lower order bits.
+ * This assumption holds because pointer bitlocks are restricted to
+ * using the low order bits of the pointer as the lock.
+ *
+ * On 32 bits, there is nothing to do since the pointer size is equal to
+ * the integer size.  On little endian the lower-order bits don't move,
+ * so do nothing.  Only on 64bit big endian do we need to do a bit of
+ * pointer arithmetic: the low order bits are shifted by 4 bytes.  We
+ * have a helper function that always does the right thing here.
+ *
+ * Since we always consider the low-order bits of the integer value, a
+ * simple cast from (gsize) to (guint) always takes care of that.
+ *
+ * After that, pointer-sized futex becomes as simple as:
+ *
+ *   g_futex_wait (g_futex_int_address (address), (guint) value);
+ *
+ * and
+ *
+ *   g_futex_wake (g_futex_int_address (int_address));
+ */
+static const volatile gint *
+g_futex_int_address (const volatile void *address)
+{
+  const volatile gint *int_address = address;
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN && GLIB_SIZEOF_VOID_P == 8
+  int_address++;
+#endif
+
+  return int_address;
+}
 
+/**
+ * g_pointer_bit_lock:
+ * @address: a pointer to a #gpointer-sized value
+ * @lock_bit: a bit value between 0 and 31
+ *
+ * This is equivalent to g_bit_lock, but working on pointers (or other
+ * pointer-sized values).
+ *
+ * For portability reasons, you may only lock on the bottom 32 bits of
+ * the pointer.
+ *
+ * Since: 2.30
+ **/
+void
+(g_pointer_bit_lock) (volatile void *address,
+                      gint           lock_bit)
+{
+  g_return_if_fail (lock_bit < 32);
+
+  {
+#ifdef USE_ASM_GOTO
  retry:
-  v = g_atomic_int_get (address);
-  if (!g_atomic_int_compare_and_exchange (address, v, v & ~(1u << lock_bit)))
+    asm volatile goto ("lock bts %1, (%0)\n"
+                       "jc %l[contended]"
+                       : /* no output */
+                       : "r" (address), "r" ((gsize) lock_bit)
+                       : "cc", "memory"
+                       : contended);
+    return;
+
+ contended:
+    {
+      volatile gsize *pointer_address = address;
+      gsize mask = 1u << lock_bit;
+      gsize v;
+
+      v = (gsize) g_atomic_pointer_get (pointer_address);
+      if (v & mask)
+        {
+          guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
+
+          g_atomic_int_add (&g_bit_lock_contended[class], +1);
+          g_futex_wait (g_futex_int_address (address), v);
+          g_atomic_int_add (&g_bit_lock_contended[class], -1);
+        }
+    }
     goto retry;
+#else
+  volatile gsize *pointer_address = address;
+  gsize mask = 1u << lock_bit;
+  gsize v;
 
-  if (g_atomic_int_get (&g_bit_lock_contended[class]))
-    g_futex_wake (address);
+ retry:
+  v = g_atomic_pointer_or (pointer_address, mask);
+  if (v & mask)
+    /* already locked */
+    {
+      guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
+
+      g_atomic_int_add (&g_bit_lock_contended[class], +1);
+      g_futex_wait (g_futex_int_address (address), (guint) v);
+      g_atomic_int_add (&g_bit_lock_contended[class], -1);
+
+      goto retry;
+    }
+#endif
+  }
 }
 
-#define __G_BITLOCK_C__
-#include "galiasdef.c"
+/**
+ * g_pointer_bit_trylock:
+ * @address: a pointer to a #gpointer-sized value
+ * @lock_bit: a bit value between 0 and 31
+ * @returns: %TRUE if the lock was acquired
+ *
+ * This is equivalent to g_bit_trylock, but working on pointers (or
+ * other pointer-sized values).
+ *
+ * For portability reasons, you may only lock on the bottom 32 bits of
+ * the pointer.
+ *
+ * Since: 2.30
+ **/
+gboolean
+(g_pointer_bit_trylock) (volatile void *address,
+                         gint           lock_bit)
+{
+  g_return_val_if_fail (lock_bit < 32, FALSE);
+
+  {
+#ifdef USE_ASM_GOTO
+    gboolean result;
+
+    asm volatile ("lock bts %2, (%1)\n"
+                  "setnc %%al\n"
+                  "movzx %%al, %0"
+                  : "=r" (result)
+                  : "r" (address), "r" ((gsize) lock_bit)
+                  : "cc", "memory");
+
+    return result;
+#else
+    volatile gsize *pointer_address = address;
+    gsize mask = 1u << lock_bit;
+    gsize v;
+
+    g_return_val_if_fail (lock_bit < 32, FALSE);
+
+    v = g_atomic_pointer_or (pointer_address, mask);
+
+    return ~v & mask;
+#endif
+  }
+}
+
+/**
+ * g_pointer_bit_unlock:
+ * @address: a pointer to a #gpointer-sized value
+ * @lock_bit: a bit value between 0 and 31
+ *
+ * This is equivalent to g_bit_unlock, but working on pointers (or other
+ * pointer-sized values).
+ *
+ * For portability reasons, you may only lock on the bottom 32 bits of
+ * the pointer.
+ *
+ * Since: 2.30
+ **/
+void
+(g_pointer_bit_unlock) (volatile void *address,
+                        gint           lock_bit)
+{
+  g_return_if_fail (lock_bit < 32);
+
+  {
+#ifdef USE_ASM_GOTO
+    asm volatile ("lock btr %1, (%0)"
+                  : /* no output */
+                  : "r" (address), "r" ((gsize) lock_bit)
+                  : "cc", "memory");
+#else
+    volatile gsize *pointer_address = address;
+    gsize mask = 1u << lock_bit;
+
+    g_atomic_pointer_and (pointer_address, ~mask);
+#endif
+
+    {
+      guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
+      if (g_atomic_int_get (&g_bit_lock_contended[class]))
+        g_futex_wake (g_futex_int_address (address));
+    }
+  }
+}