Blame glib/gbitlock.c

Packit ae235b
/*
Packit ae235b
 * Copyright © 2008 Ryan Lortie
Packit ae235b
 * Copyright © 2010 Codethink Limited
Packit ae235b
 *
Packit ae235b
 * This library is free software; you can redistribute it and/or
Packit ae235b
 * modify it under the terms of the GNU Lesser General Public
Packit ae235b
 * License as published by the Free Software Foundation; either
Packit ae235b
 * version 2.1 of the License, or (at your option) any later version.
Packit ae235b
 *
Packit ae235b
 * This library is distributed in the hope that it will be useful,
Packit ae235b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit ae235b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit ae235b
 * Lesser General Public License for more details.
Packit ae235b
 *
Packit ae235b
 * You should have received a copy of the GNU Lesser General Public
Packit ae235b
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit ae235b
 *
Packit ae235b
 * Author: Ryan Lortie <desrt@desrt.ca>
Packit ae235b
 */
Packit ae235b
Packit ae235b
#include "config.h"
Packit ae235b
Packit ae235b
#include "gbitlock.h"
Packit ae235b
Packit ae235b
#include <glib/gmessages.h>
Packit ae235b
#include <glib/gatomic.h>
Packit ae235b
#include <glib/gslist.h>
Packit ae235b
#include <glib/gthread.h>
Packit ae235b
#include <glib/gslice.h>
Packit ae235b
Packit ae235b
#include "gthreadprivate.h"
Packit ae235b
Packit ae235b
#ifdef G_BIT_LOCK_FORCE_FUTEX_EMULATION
Packit ae235b
#undef HAVE_FUTEX
Packit ae235b
#endif
Packit ae235b
Packit ae235b
#ifndef HAVE_FUTEX
Packit ae235b
static GMutex g_futex_mutex;
Packit ae235b
static GSList *g_futex_address_list = NULL;
Packit ae235b
#endif
Packit ae235b
Packit ae235b
#ifdef HAVE_FUTEX
Packit ae235b
/*
Packit ae235b
 * We have headers for futex(2) on the build machine.  This does not
Packit ae235b
 * imply that every system that ever runs the resulting glib will have
Packit ae235b
 * kernel support for futex, but you'd have to have a pretty old
Packit ae235b
 * kernel in order for that not to be the case.
Packit ae235b
 *
Packit ae235b
 * If anyone actually gets bit by this, please file a bug. :)
Packit ae235b
 */
Packit ae235b
#include <linux/futex.h>
Packit ae235b
#include <sys/syscall.h>
Packit ae235b
#include <unistd.h>
Packit ae235b
Packit ae235b
#ifndef FUTEX_WAIT_PRIVATE
Packit ae235b
#define FUTEX_WAIT_PRIVATE FUTEX_WAIT
Packit ae235b
#define FUTEX_WAKE_PRIVATE FUTEX_WAKE
Packit ae235b
#endif
Packit ae235b
Packit ae235b
/* < private >
Packit ae235b
 * g_futex_wait:
Packit ae235b
 * @address: a pointer to an integer
Packit ae235b
 * @value: the value that should be at @address
Packit ae235b
 *
Packit ae235b
 * Atomically checks that the value stored at @address is equal to
Packit ae235b
 * @value and then blocks.  If the value stored at @address is not
Packit ae235b
 * equal to @value then this function returns immediately.
Packit ae235b
 *
Packit ae235b
 * To unblock, call g_futex_wake() on @address.
Packit ae235b
 *
Packit ae235b
 * This call may spuriously unblock (for example, in response to the
Packit ae235b
 * process receiving a signal) but this is not guaranteed.  Unlike the
Packit ae235b
 * Linux system call of a similar name, there is no guarantee that a
Packit ae235b
 * waiting process will unblock due to a g_futex_wake() call in a
Packit ae235b
 * separate process.
Packit ae235b
 */
Packit ae235b
static void
Packit ae235b
g_futex_wait (const volatile gint *address,
Packit ae235b
              gint                 value)
Packit ae235b
{
Packit ae235b
  syscall (__NR_futex, address, (gsize) FUTEX_WAIT_PRIVATE, (gsize) value, NULL);
Packit ae235b
}
Packit ae235b
Packit ae235b
/* < private >
Packit ae235b
 * g_futex_wake:
Packit ae235b
 * @address: a pointer to an integer
Packit ae235b
 *
Packit ae235b
 * Nominally, wakes one thread that is blocked in g_futex_wait() on
Packit ae235b
 * @address (if any thread is currently waiting).
Packit ae235b
 *
Packit ae235b
 * As mentioned in the documention for g_futex_wait(), spurious
Packit ae235b
 * wakeups may occur.  As such, this call may result in more than one
Packit ae235b
 * thread being woken up.
Packit ae235b
 */
Packit ae235b
static void
Packit ae235b
g_futex_wake (const volatile gint *address)
Packit ae235b
{
Packit ae235b
  syscall (__NR_futex, address, (gsize) FUTEX_WAKE_PRIVATE, (gsize) 1, NULL);
Packit ae235b
}
Packit ae235b
Packit ae235b
#else
Packit ae235b
Packit ae235b
/* emulate futex(2) */
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  const volatile gint *address;
Packit ae235b
  gint                 ref_count;
Packit ae235b
  GCond                wait_queue;
Packit ae235b
} WaitAddress;
Packit ae235b
Packit ae235b
static WaitAddress *
Packit ae235b
g_futex_find_address (const volatile gint *address)
Packit ae235b
{
Packit ae235b
  GSList *node;
Packit ae235b
Packit ae235b
  for (node = g_futex_address_list; node; node = node->next)
Packit ae235b
    {
Packit ae235b
      WaitAddress *waiter = node->data;
Packit ae235b
Packit ae235b
      if (waiter->address == address)
Packit ae235b
        return waiter;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return NULL;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
g_futex_wait (const volatile gint *address,
Packit ae235b
              gint                 value)
Packit ae235b
{
Packit ae235b
  g_mutex_lock (&g_futex_mutex);
Packit ae235b
  if G_LIKELY (g_atomic_int_get (address) == value)
Packit ae235b
    {
Packit ae235b
      WaitAddress *waiter;
Packit ae235b
Packit ae235b
      if ((waiter = g_futex_find_address (address)) == NULL)
Packit ae235b
        {
Packit ae235b
          waiter = g_slice_new (WaitAddress);
Packit ae235b
          waiter->address = address;
Packit ae235b
          g_cond_init (&waiter->wait_queue);
Packit ae235b
          waiter->ref_count = 0;
Packit ae235b
          g_futex_address_list =
Packit ae235b
            g_slist_prepend (g_futex_address_list, waiter);
Packit ae235b
        }
Packit ae235b
Packit ae235b
      waiter->ref_count++;
Packit ae235b
      g_cond_wait (&waiter->wait_queue, &g_futex_mutex);
Packit ae235b
Packit ae235b
      if (!--waiter->ref_count)
Packit ae235b
        {
Packit ae235b
          g_futex_address_list =
Packit ae235b
            g_slist_remove (g_futex_address_list, waiter);
Packit ae235b
          g_cond_clear (&waiter->wait_queue);
Packit ae235b
          g_slice_free (WaitAddress, waiter);
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
  g_mutex_unlock (&g_futex_mutex);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
g_futex_wake (const volatile gint *address)
Packit ae235b
{
Packit ae235b
  WaitAddress *waiter;
Packit ae235b
Packit ae235b
  /* need to lock here for two reasons:
Packit ae235b
   *   1) need to acquire/release lock to ensure waiter is not in
Packit ae235b
   *      the process of registering a wait
Packit ae235b
   *   2) need to -stay- locked until the end to ensure a wake()
Packit ae235b
   *      in another thread doesn't cause 'waiter' to stop existing
Packit ae235b
   */
Packit ae235b
  g_mutex_lock (&g_futex_mutex);
Packit ae235b
  if ((waiter = g_futex_find_address (address)))
Packit ae235b
    g_cond_signal (&waiter->wait_queue);
Packit ae235b
  g_mutex_unlock (&g_futex_mutex);
Packit ae235b
}
Packit ae235b
#endif
Packit ae235b
Packit ae235b
#define CONTENTION_CLASSES 11
Packit ae235b
static volatile gint g_bit_lock_contended[CONTENTION_CLASSES];
Packit ae235b
Packit ae235b
#if (defined (i386) || defined (__amd64__))
Packit ae235b
  #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
Packit ae235b
    #define USE_ASM_GOTO 1
Packit ae235b
  #endif
Packit ae235b
#endif
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_bit_lock:
Packit ae235b
 * @address: a pointer to an integer
Packit ae235b
 * @lock_bit: a bit value between 0 and 31
Packit ae235b
 *
Packit ae235b
 * Sets the indicated @lock_bit in @address.  If the bit is already
Packit ae235b
 * set, this call will block until g_bit_unlock() unsets the
Packit ae235b
 * corresponding bit.
Packit ae235b
 *
Packit ae235b
 * Attempting to lock on two different bits within the same integer is
Packit ae235b
 * not supported and will very probably cause deadlocks.
Packit ae235b
 *
Packit ae235b
 * The value of the bit that is set is (1u << @bit).  If @bit is not
Packit ae235b
 * between 0 and 31 then the result is undefined.
Packit ae235b
 *
Packit ae235b
 * This function accesses @address atomically.  All other accesses to
Packit ae235b
 * @address must be atomic in order for this function to work
Packit ae235b
 * reliably.
Packit ae235b
 *
Packit ae235b
 * Since: 2.24
Packit ae235b
 **/
Packit ae235b
void
Packit ae235b
g_bit_lock (volatile gint *address,
Packit ae235b
            gint           lock_bit)
Packit ae235b
{
Packit ae235b
#ifdef USE_ASM_GOTO
Packit ae235b
 retry:
Packit ae235b
  __asm__ volatile goto ("lock bts %1, (%0)\n"
Packit ae235b
                         "jc %l[contended]"
Packit ae235b
                         : /* no output */
Packit ae235b
                         : "r" (address), "r" (lock_bit)
Packit ae235b
                         : "cc", "memory"
Packit ae235b
                         : contended);
Packit ae235b
  return;
Packit ae235b
Packit ae235b
 contended:
Packit ae235b
  {
Packit ae235b
    guint mask = 1u << lock_bit;
Packit ae235b
    guint v;
Packit ae235b
Packit ae235b
    v = g_atomic_int_get (address);
Packit ae235b
    if (v & mask)
Packit ae235b
      {
Packit ae235b
        guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
Packit ae235b
Packit ae235b
        g_atomic_int_add (&g_bit_lock_contended[class], +1);
Packit ae235b
        g_futex_wait (address, v);
Packit ae235b
        g_atomic_int_add (&g_bit_lock_contended[class], -1);
Packit ae235b
      }
Packit ae235b
  }
Packit ae235b
  goto retry;
Packit ae235b
#else
Packit ae235b
  guint mask = 1u << lock_bit;
Packit ae235b
  guint v;
Packit ae235b
Packit ae235b
 retry:
Packit ae235b
  v = g_atomic_int_or (address, mask);
Packit ae235b
  if (v & mask)
Packit ae235b
    /* already locked */
Packit ae235b
    {
Packit ae235b
      guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
Packit ae235b
Packit ae235b
      g_atomic_int_add (&g_bit_lock_contended[class], +1);
Packit ae235b
      g_futex_wait (address, v);
Packit ae235b
      g_atomic_int_add (&g_bit_lock_contended[class], -1);
Packit ae235b
Packit ae235b
      goto retry;
Packit ae235b
    }
Packit ae235b
#endif
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_bit_trylock:
Packit ae235b
 * @address: a pointer to an integer
Packit ae235b
 * @lock_bit: a bit value between 0 and 31
Packit ae235b
 *
Packit ae235b
 * Sets the indicated @lock_bit in @address, returning %TRUE if
Packit ae235b
 * successful.  If the bit is already set, returns %FALSE immediately.
Packit ae235b
 *
Packit ae235b
 * Attempting to lock on two different bits within the same integer is
Packit ae235b
 * not supported.
Packit ae235b
 *
Packit ae235b
 * The value of the bit that is set is (1u << @bit).  If @bit is not
Packit ae235b
 * between 0 and 31 then the result is undefined.
Packit ae235b
 *
Packit ae235b
 * This function accesses @address atomically.  All other accesses to
Packit ae235b
 * @address must be atomic in order for this function to work
Packit ae235b
 * reliably.
Packit ae235b
 *
Packit ae235b
 * Returns: %TRUE if the lock was acquired
Packit ae235b
 *
Packit ae235b
 * Since: 2.24
Packit ae235b
 **/
Packit ae235b
gboolean
Packit ae235b
g_bit_trylock (volatile gint *address,
Packit ae235b
               gint           lock_bit)
Packit ae235b
{
Packit ae235b
#ifdef USE_ASM_GOTO
Packit ae235b
  gboolean result;
Packit ae235b
Packit ae235b
  __asm__ volatile ("lock bts %2, (%1)\n"
Packit ae235b
                    "setnc %%al\n"
Packit ae235b
                    "movzx %%al, %0"
Packit ae235b
                    : "=r" (result)
Packit ae235b
                    : "r" (address), "r" (lock_bit)
Packit ae235b
                    : "cc", "memory");
Packit ae235b
Packit ae235b
  return result;
Packit ae235b
#else
Packit ae235b
  guint mask = 1u << lock_bit;
Packit ae235b
  guint v;
Packit ae235b
Packit ae235b
  v = g_atomic_int_or (address, mask);
Packit ae235b
Packit ae235b
  return ~v & mask;
Packit ae235b
#endif
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_bit_unlock:
Packit ae235b
 * @address: a pointer to an integer
Packit ae235b
 * @lock_bit: a bit value between 0 and 31
Packit ae235b
 *
Packit ae235b
 * Clears the indicated @lock_bit in @address.  If another thread is
Packit ae235b
 * currently blocked in g_bit_lock() on this same bit then it will be
Packit ae235b
 * woken up.
Packit ae235b
 *
Packit ae235b
 * This function accesses @address atomically.  All other accesses to
Packit ae235b
 * @address must be atomic in order for this function to work
Packit ae235b
 * reliably.
Packit ae235b
 *
Packit ae235b
 * Since: 2.24
Packit ae235b
 **/
Packit ae235b
void
Packit ae235b
g_bit_unlock (volatile gint *address,
Packit ae235b
              gint           lock_bit)
Packit ae235b
{
Packit ae235b
#ifdef USE_ASM_GOTO
Packit ae235b
  asm volatile ("lock btr %1, (%0)"
Packit ae235b
                : /* no output */
Packit ae235b
                : "r" (address), "r" (lock_bit)
Packit ae235b
                : "cc", "memory");
Packit ae235b
#else
Packit ae235b
  guint mask = 1u << lock_bit;
Packit ae235b
Packit ae235b
  g_atomic_int_and (address, ~mask);
Packit ae235b
#endif
Packit ae235b
Packit ae235b
  {
Packit ae235b
    guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
Packit ae235b
Packit ae235b
    if (g_atomic_int_get (&g_bit_lock_contended[class]))
Packit ae235b
      g_futex_wake (address);
Packit ae235b
  }
Packit ae235b
}
Packit ae235b
Packit ae235b
Packit ae235b
/* We emulate pointer-sized futex(2) because the kernel API only
Packit ae235b
 * supports integers.
Packit ae235b
 *
Packit ae235b
 * We assume that the 'interesting' part is always the lower order bits.
Packit ae235b
 * This assumption holds because pointer bitlocks are restricted to
Packit ae235b
 * using the low order bits of the pointer as the lock.
Packit ae235b
 *
Packit ae235b
 * On 32 bits, there is nothing to do since the pointer size is equal to
Packit ae235b
 * the integer size.  On little endian the lower-order bits don't move,
Packit ae235b
 * so do nothing.  Only on 64bit big endian do we need to do a bit of
Packit ae235b
 * pointer arithmetic: the low order bits are shifted by 4 bytes.  We
Packit ae235b
 * have a helper function that always does the right thing here.
Packit ae235b
 *
Packit ae235b
 * Since we always consider the low-order bits of the integer value, a
Packit ae235b
 * simple cast from (gsize) to (guint) always takes care of that.
Packit ae235b
 *
Packit ae235b
 * After that, pointer-sized futex becomes as simple as:
Packit ae235b
 *
Packit ae235b
 *   g_futex_wait (g_futex_int_address (address), (guint) value);
Packit ae235b
 *
Packit ae235b
 * and
Packit ae235b
 *
Packit ae235b
 *   g_futex_wake (g_futex_int_address (int_address));
Packit ae235b
 */
Packit ae235b
static const volatile gint *
Packit ae235b
g_futex_int_address (const volatile void *address)
Packit ae235b
{
Packit ae235b
  const volatile gint *int_address = address;
Packit ae235b
Packit ae235b
  /* this implementation makes these (reasonable) assumptions: */
Packit ae235b
  G_STATIC_ASSERT (G_BYTE_ORDER == G_LITTLE_ENDIAN ||
Packit ae235b
      (G_BYTE_ORDER == G_BIG_ENDIAN &&
Packit ae235b
       sizeof (int) == 4 &&
Packit ae235b
       (sizeof (gpointer) == 4 || sizeof (gpointer) == 8)));
Packit ae235b
Packit ae235b
#if G_BYTE_ORDER == G_BIG_ENDIAN && GLIB_SIZEOF_VOID_P == 8
Packit ae235b
  int_address++;
Packit ae235b
#endif
Packit ae235b
Packit ae235b
  return int_address;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_pointer_bit_lock:
Packit ae235b
 * @address: (not nullable): a pointer to a #gpointer-sized value
Packit ae235b
 * @lock_bit: a bit value between 0 and 31
Packit ae235b
 *
Packit ae235b
 * This is equivalent to g_bit_lock, but working on pointers (or other
Packit ae235b
 * pointer-sized values).
Packit ae235b
 *
Packit ae235b
 * For portability reasons, you may only lock on the bottom 32 bits of
Packit ae235b
 * the pointer.
Packit ae235b
 *
Packit ae235b
 * Since: 2.30
Packit ae235b
 **/
Packit ae235b
void
Packit ae235b
(g_pointer_bit_lock) (volatile void *address,
Packit ae235b
                      gint           lock_bit)
Packit ae235b
{
Packit ae235b
  g_return_if_fail (lock_bit < 32);
Packit ae235b
Packit ae235b
  {
Packit ae235b
#ifdef USE_ASM_GOTO
Packit ae235b
 retry:
Packit ae235b
    asm volatile goto ("lock bts %1, (%0)\n"
Packit ae235b
                       "jc %l[contended]"
Packit ae235b
                       : /* no output */
Packit ae235b
                       : "r" (address), "r" ((gsize) lock_bit)
Packit ae235b
                       : "cc", "memory"
Packit ae235b
                       : contended);
Packit ae235b
    return;
Packit ae235b
Packit ae235b
 contended:
Packit ae235b
    {
Packit ae235b
      volatile gsize *pointer_address = address;
Packit ae235b
      gsize mask = 1u << lock_bit;
Packit ae235b
      gsize v;
Packit ae235b
Packit ae235b
      v = (gsize) g_atomic_pointer_get (pointer_address);
Packit ae235b
      if (v & mask)
Packit ae235b
        {
Packit ae235b
          guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
Packit ae235b
Packit ae235b
          g_atomic_int_add (&g_bit_lock_contended[class], +1);
Packit ae235b
          g_futex_wait (g_futex_int_address (address), v);
Packit ae235b
          g_atomic_int_add (&g_bit_lock_contended[class], -1);
Packit ae235b
        }
Packit ae235b
    }
Packit ae235b
    goto retry;
Packit ae235b
#else
Packit ae235b
  volatile gsize *pointer_address = address;
Packit ae235b
  gsize mask = 1u << lock_bit;
Packit ae235b
  gsize v;
Packit ae235b
Packit ae235b
 retry:
Packit ae235b
  v = g_atomic_pointer_or (pointer_address, mask);
Packit ae235b
  if (v & mask)
Packit ae235b
    /* already locked */
Packit ae235b
    {
Packit ae235b
      guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
Packit ae235b
Packit ae235b
      g_atomic_int_add (&g_bit_lock_contended[class], +1);
Packit ae235b
      g_futex_wait (g_futex_int_address (address), (guint) v);
Packit ae235b
      g_atomic_int_add (&g_bit_lock_contended[class], -1);
Packit ae235b
Packit ae235b
      goto retry;
Packit ae235b
    }
Packit ae235b
#endif
Packit ae235b
  }
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_pointer_bit_trylock:
Packit ae235b
 * @address: (not nullable): a pointer to a #gpointer-sized value
Packit ae235b
 * @lock_bit: a bit value between 0 and 31
Packit ae235b
 *
Packit ae235b
 * This is equivalent to g_bit_trylock, but working on pointers (or
Packit ae235b
 * other pointer-sized values).
Packit ae235b
 *
Packit ae235b
 * For portability reasons, you may only lock on the bottom 32 bits of
Packit ae235b
 * the pointer.
Packit ae235b
 *
Packit ae235b
 * Returns: %TRUE if the lock was acquired
Packit ae235b
 *
Packit ae235b
 * Since: 2.30
Packit ae235b
 **/
Packit ae235b
gboolean
Packit ae235b
(g_pointer_bit_trylock) (volatile void *address,
Packit ae235b
                         gint           lock_bit)
Packit ae235b
{
Packit ae235b
  g_return_val_if_fail (lock_bit < 32, FALSE);
Packit ae235b
Packit ae235b
  {
Packit ae235b
#ifdef USE_ASM_GOTO
Packit ae235b
    gboolean result;
Packit ae235b
Packit ae235b
    asm volatile ("lock bts %2, (%1)\n"
Packit ae235b
                  "setnc %%al\n"
Packit ae235b
                  "movzx %%al, %0"
Packit ae235b
                  : "=r" (result)
Packit ae235b
                  : "r" (address), "r" ((gsize) lock_bit)
Packit ae235b
                  : "cc", "memory");
Packit ae235b
Packit ae235b
    return result;
Packit ae235b
#else
Packit ae235b
    volatile gsize *pointer_address = address;
Packit ae235b
    gsize mask = 1u << lock_bit;
Packit ae235b
    gsize v;
Packit ae235b
Packit ae235b
    g_return_val_if_fail (lock_bit < 32, FALSE);
Packit ae235b
Packit ae235b
    v = g_atomic_pointer_or (pointer_address, mask);
Packit ae235b
Packit ae235b
    return ~v & mask;
Packit ae235b
#endif
Packit ae235b
  }
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_pointer_bit_unlock:
Packit ae235b
 * @address: (not nullable): a pointer to a #gpointer-sized value
Packit ae235b
 * @lock_bit: a bit value between 0 and 31
Packit ae235b
 *
Packit ae235b
 * This is equivalent to g_bit_unlock, but working on pointers (or other
Packit ae235b
 * pointer-sized values).
Packit ae235b
 *
Packit ae235b
 * For portability reasons, you may only lock on the bottom 32 bits of
Packit ae235b
 * the pointer.
Packit ae235b
 *
Packit ae235b
 * Since: 2.30
Packit ae235b
 **/
Packit ae235b
void
Packit ae235b
(g_pointer_bit_unlock) (volatile void *address,
Packit ae235b
                        gint           lock_bit)
Packit ae235b
{
Packit ae235b
  g_return_if_fail (lock_bit < 32);
Packit ae235b
Packit ae235b
  {
Packit ae235b
#ifdef USE_ASM_GOTO
Packit ae235b
    asm volatile ("lock btr %1, (%0)"
Packit ae235b
                  : /* no output */
Packit ae235b
                  : "r" (address), "r" ((gsize) lock_bit)
Packit ae235b
                  : "cc", "memory");
Packit ae235b
#else
Packit ae235b
    volatile gsize *pointer_address = address;
Packit ae235b
    gsize mask = 1u << lock_bit;
Packit ae235b
Packit ae235b
    g_atomic_pointer_and (pointer_address, ~mask);
Packit ae235b
#endif
Packit ae235b
Packit ae235b
    {
Packit ae235b
      guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
Packit ae235b
      if (g_atomic_int_get (&g_bit_lock_contended[class]))
Packit ae235b
        g_futex_wake (g_futex_int_address (address));
Packit ae235b
    }
Packit ae235b
  }
Packit ae235b
}