mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 15:06:14 +01:00
Bug 548967 - 1 bit mutex lock
Add support for a mutex lock that consumes only one bit of storage inside of an integer on systems that support futexes. Futex is emulated (at a higher cost) on systems that don't have it -- but only in the contended case.
This commit is contained in:
parent
595cad2531
commit
669b0f2d6b
26
configure.in
26
configure.in
@ -2544,6 +2544,32 @@ fi
|
||||
AM_CONDITIONAL(HAVE_GCC_BUILTINS_FOR_ATOMIC_OPERATIONS,
|
||||
[test $glib_cv_gcc_has_builtin_atomic_operations = yes])
|
||||
|
||||
dnl ************************
|
||||
dnl ** Check for futex(2) **
|
||||
dnl ************************
|
||||
AC_MSG_CHECKING([for futex(2) system call])
|
||||
AC_COMPILE_IFELSE([
|
||||
#include <linux/futex.h>
|
||||
#include <syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
/* it's not like this actually runs or anything... */
|
||||
syscall (SYS_futex, NULL, FUTEX_WAKE, FUTEX_WAIT);
|
||||
return 0;
|
||||
}
|
||||
],
|
||||
[
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(HAVE_FUTEX, [test "$have_futex" = "yes"],
|
||||
[we have the futex(2) system call])
|
||||
],
|
||||
[
|
||||
AC_MSG_RESULT(no)
|
||||
])
|
||||
|
||||
dnl ****************************************
|
||||
dnl *** GLib POLL* compatibility defines ***
|
||||
dnl ****************************************
|
||||
|
@ -660,6 +660,11 @@ g_once
|
||||
g_once_init_enter
|
||||
g_once_init_leave
|
||||
|
||||
<SUBSECTION>
|
||||
g_bit_lock
|
||||
g_bit_trylock
|
||||
g_bit_unlock
|
||||
|
||||
<SUBSECTION Private>
|
||||
G_THREAD_ECF
|
||||
G_THREAD_CF
|
||||
|
@ -109,6 +109,7 @@ libglib_2_0_la_SOURCES = \
|
||||
$(gatomic_c) \
|
||||
gbacktrace.c \
|
||||
gbase64.c \
|
||||
gbitlock.c \
|
||||
gbookmarkfile.c \
|
||||
gbsearcharray.h \
|
||||
gcache.c \
|
||||
@ -196,6 +197,7 @@ glibsubinclude_HEADERS = \
|
||||
gatomic.h \
|
||||
gbacktrace.h \
|
||||
gbase64.h \
|
||||
gbitlock.h \
|
||||
gbookmarkfile.h \
|
||||
gcache.h \
|
||||
gchecksum.h \
|
||||
|
292
glib/gbitlock.c
Normal file
292
glib/gbitlock.c
Normal file
@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright © 2008 Ryan Lortie
|
||||
* Copyright © 2010 Codethink Limited
|
||||
*
|
||||
* 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 of the licence, 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, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#include "gbitlock.h"
|
||||
|
||||
#include <glib/gatomic.h>
|
||||
#include <glib/gslist.h>
|
||||
#include <glib/gthread.h>
|
||||
|
||||
#include "gthreadprivate.h"
|
||||
#include "config.h"
|
||||
|
||||
#include "galias.h"
|
||||
|
||||
#ifndef HAVE_FUTEX
|
||||
static GSList *g_futex_address_list = NULL;
|
||||
static GMutex *g_futex_mutex = NULL;
|
||||
#endif
|
||||
|
||||
void
|
||||
_g_futex_thread_init (void) {
|
||||
#ifndef HAVE_FUTEX
|
||||
g_futex_mutex = g_mutex_new ();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_FUTEX
|
||||
/*
|
||||
* We have headers for futex(2) on the build machine. This does not
|
||||
* imply that every system that ever runs the resulting glib will have
|
||||
* kernel support for futex, but you'd have to have a pretty old
|
||||
* kernel in order for that not to be the case.
|
||||
*
|
||||
* If anyone actually gets bit by this, please file a bug. :)
|
||||
*/
|
||||
#include <linux/futex.h>
|
||||
#include <syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* < private >
|
||||
* g_futex_wait:
|
||||
* @address: a pointer to an integer
|
||||
* @value: the value that should be at @address
|
||||
*
|
||||
* Atomically checks that the value stored at @address is equal to
|
||||
* @value and then blocks. If the value stored at @address is not
|
||||
* equal to @value then this function returns immediately.
|
||||
*
|
||||
* To unblock, call g_futex_wake() on @address.
|
||||
*
|
||||
* This call may spuriously unblock (for example, in response to the
|
||||
* process receiving a signal) but this is not guaranteed. Unlike the
|
||||
* Linux system call of a similar name, there is no guarantee that a
|
||||
* waiting process will unblock due to a g_futex_wake() call in a
|
||||
* separate process.
|
||||
*/
|
||||
static void
|
||||
g_futex_wait (const volatile gint *address,
|
||||
gint value)
|
||||
{
|
||||
syscall (SYS_futex, address, (gsize) FUTEX_WAIT, (gsize) value, NULL);
|
||||
}
|
||||
|
||||
/* < private >
|
||||
* g_futex_wake:
|
||||
* @address: a pointer to an integer
|
||||
*
|
||||
* Nominally, wakes one thread that is blocked in g_futex_wait() on
|
||||
* @address (if any thread is currently waiting).
|
||||
*
|
||||
* As mentioned in the documention for g_futex_wait(), spurious
|
||||
* wakeups may occur. As such, this call may result in more than one
|
||||
* thread being woken up.
|
||||
*/
|
||||
static void
|
||||
g_futex_wake (const volatile gint *address)
|
||||
{
|
||||
syscall (SYS_futex, address, (gsize) FUTEX_WAKE, (gsize) 1, NULL);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* emulate futex(2) */
|
||||
typedef struct
|
||||
{
|
||||
const volatile gint *address;
|
||||
gint ref_count;
|
||||
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)
|
||||
{
|
||||
GSList *node;
|
||||
|
||||
for (node = g_futex_address_list; node; node = node->next)
|
||||
{
|
||||
WaitAddress *waiter = node->data;
|
||||
|
||||
if (waiter->address == address)
|
||||
return waiter;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
g_futex_wait (const volatile gint *address,
|
||||
gint value)
|
||||
{
|
||||
g_mutex_lock (g_futex_mutex);
|
||||
if G_LIKELY (g_atomic_int_get (address) == value)
|
||||
{
|
||||
WaitAddress *waiter;
|
||||
|
||||
if ((waiter = g_futex_find_address (address)) == NULL)
|
||||
{
|
||||
waiter = g_slice_new (WaitAddress);
|
||||
waiter->address = address;
|
||||
waiter->wait_queue = g_cond_new ();
|
||||
waiter->ref_count = 0;
|
||||
g_futex_address_list =
|
||||
g_slist_prepend (g_futex_address_list, waiter);
|
||||
}
|
||||
|
||||
waiter->ref_count++;
|
||||
g_cond_wait (waiter->wait_queue, g_futex_mutex);
|
||||
|
||||
if (!--waiter->ref_count)
|
||||
{
|
||||
g_futex_address_list =
|
||||
g_slist_remove (g_futex_address_list, waiter);
|
||||
g_cond_free (waiter->wait_queue);
|
||||
g_slice_free (WaitAddress, waiter);
|
||||
}
|
||||
}
|
||||
g_mutex_unlock (g_futex_mutex);
|
||||
}
|
||||
|
||||
static void
|
||||
g_futex_wake (const volatile gint *address)
|
||||
{
|
||||
WaitAddress *waiter;
|
||||
|
||||
/* need to lock here for two reasons:
|
||||
* 1) need to acquire/release lock to ensure waiter is not in
|
||||
* the process of registering a wait
|
||||
* 2) need to -stay- locked until the end to ensure a wake()
|
||||
* in another thread doesn't cause 'waiter' to stop existing
|
||||
*/
|
||||
g_mutex_lock (g_futex_mutex);
|
||||
if ((waiter = g_futex_find_address (address)))
|
||||
g_cond_signal (waiter->wait_queue);
|
||||
g_mutex_unlock (g_futex_mutex);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define CONTENTION_CLASSES 11
|
||||
static volatile gint g_bit_lock_contended[CONTENTION_CLASSES];
|
||||
|
||||
/**
|
||||
* g_bit_lock:
|
||||
* @address: a pointer to an integer
|
||||
* @lock_bit: a bit value between 0 and 31
|
||||
*
|
||||
* Sets the indicated @lock_bit in @address. If the bit is already
|
||||
* set, this call will block until g_bit_unlock() unsets the
|
||||
* corresponding bit.
|
||||
*
|
||||
* Attempting to lock on two different bits within the same integer is
|
||||
* not supported and will very probably cause deadlocks.
|
||||
*
|
||||
* The value of the bit that is set is (1u << @bit). If @bit is not
|
||||
* between 0 and 31 then the result is undefined.
|
||||
*
|
||||
* This function accesses @address atomically. All other accesses to
|
||||
* @address must be atomic in order for this function to work
|
||||
* reliably.
|
||||
**/
|
||||
void
|
||||
g_bit_lock (volatile gint *address,
|
||||
gint lock_bit)
|
||||
{
|
||||
guint v;
|
||||
|
||||
retry:
|
||||
v = g_atomic_int_get (address);
|
||||
if (v & (1u << lock_bit))
|
||||
/* 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 (address, v);
|
||||
g_atomic_int_add (&g_bit_lock_contended[class], -1);
|
||||
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (!g_atomic_int_compare_and_exchange (address, v, v | (1u << lock_bit)))
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_bit_trylock:
|
||||
* @address: a pointer to an integer
|
||||
* @lock_bit: a bit value between 0 and 31
|
||||
*
|
||||
* Sets the indicated @lock_bit in @address, returning %TRUE if
|
||||
* successful. If the bit is already set, returns %FALSE immediately.
|
||||
*
|
||||
* Attempting to lock on two different bits within the same integer is
|
||||
* not supported.
|
||||
*
|
||||
* The value of the bit that is set is (1u << @bit). If @bit is not
|
||||
* between 0 and 31 then the result is undefined.
|
||||
*
|
||||
* This function accesses @address atomically. All other accesses to
|
||||
* @address must be atomic in order for this function to work
|
||||
* reliably.
|
||||
**/
|
||||
gboolean
|
||||
g_bit_trylock (volatile gint *address,
|
||||
gint lock_bit)
|
||||
{
|
||||
guint v;
|
||||
|
||||
retry:
|
||||
v = g_atomic_int_get (address);
|
||||
if (v & (1u << lock_bit))
|
||||
/* already locked */
|
||||
return FALSE;
|
||||
|
||||
if (!g_atomic_int_compare_and_exchange (address, v, v | (1u << lock_bit)))
|
||||
goto retry;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_bit_unlock:
|
||||
* @address: a pointer to an integer
|
||||
* @lock_bit: a bit value between 0 and 31
|
||||
*
|
||||
* Clears the indicated @lock_bit in @address. If another thread is
|
||||
* currently blocked in g_bit_lock() on this same bit then it will be
|
||||
* woken up.
|
||||
*
|
||||
* This function accesses @address atomically. All other accesses to
|
||||
* @address must be atomic in order for this function to work
|
||||
* reliably.
|
||||
**/
|
||||
void
|
||||
g_bit_unlock (volatile gint *address,
|
||||
gint lock_bit)
|
||||
{
|
||||
guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
|
||||
guint v;
|
||||
|
||||
retry:
|
||||
v = g_atomic_int_get (address);
|
||||
if (!g_atomic_int_compare_and_exchange (address, v, v & ~(1u << lock_bit)))
|
||||
goto retry;
|
||||
|
||||
if (g_atomic_int_get (&g_bit_lock_contended[class]))
|
||||
g_futex_wake (address);
|
||||
}
|
||||
|
||||
#define __G_BITLOCK_C__
|
||||
#include "galiasdef.c"
|
43
glib/gbitlock.h
Normal file
43
glib/gbitlock.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright © 2008 Ryan Lortie
|
||||
* Copyright © 2010 Codethink Limited
|
||||
*
|
||||
* 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 of the licence, 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, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||||
*/
|
||||
|
||||
#ifndef __G_BITLOCK_H__
|
||||
#define __G_BITLOCK_H__
|
||||
|
||||
#include <glib/gtypes.h>
|
||||
|
||||
#if defined(G_DISABLE_SINGLE_INCLUDES) && !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
|
||||
#error "Only <glib.h> can be included directly."
|
||||
#endif
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void g_bit_lock (volatile gint *lock,
|
||||
gint lock_bit);
|
||||
gboolean g_bit_trylock (volatile gint *lock,
|
||||
gint lock_bit);
|
||||
void g_bit_unlock (volatile gint *lock,
|
||||
gint lock_bit);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_BITLOCK_H_ */
|
@ -35,6 +35,7 @@
|
||||
#include <glib/gatomic.h>
|
||||
#include <glib/gbacktrace.h>
|
||||
#include <glib/gbase64.h>
|
||||
#include <glib/gbitlock.h>
|
||||
#include <glib/gbookmarkfile.h>
|
||||
#include <glib/gcache.h>
|
||||
#include <glib/gchecksum.h>
|
||||
|
@ -1264,6 +1264,14 @@ g_str_hash
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if IN_HEADER(__G_BITLOCK_H__)
|
||||
#if IN_FILE(__G_BITLOCK_C__)
|
||||
g_bit_lock
|
||||
g_bit_trylock
|
||||
g_bit_unlock
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if IN_HEADER(__G_THREAD_H__)
|
||||
#if IN_FILE(__G_THREAD_C__)
|
||||
g_once_impl
|
||||
|
@ -166,6 +166,7 @@ g_thread_init_glib (void)
|
||||
_g_rand_thread_init ();
|
||||
_g_main_thread_init ();
|
||||
_g_utils_thread_init ();
|
||||
_g_futex_thread_init ();
|
||||
#ifdef G_OS_WIN32
|
||||
_g_win32_thread_init ();
|
||||
#endif
|
||||
|
@ -58,6 +58,7 @@ G_GNUC_INTERNAL void _g_rand_thread_init (void);
|
||||
G_GNUC_INTERNAL void _g_main_thread_init (void);
|
||||
G_GNUC_INTERNAL void _g_atomic_thread_init (void);
|
||||
G_GNUC_INTERNAL void _g_utils_thread_init (void);
|
||||
G_GNUC_INTERNAL void _g_futex_thread_init (void);
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
G_GNUC_INTERNAL void _g_win32_thread_init (void);
|
||||
|
Loading…
Reference in New Issue
Block a user