2 * Copyright © 2008 Ryan Lortie
3 * Copyright © 2010 Codethink Limited
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 * Author: Ryan Lortie <desrt@desrt.ca>
25 #include <glib/gmessages.h>
26 #include <glib/gatomic.h>
27 #include <glib/gslist.h>
28 #include <glib/gthread.h>
29 #include <glib/gslice.h>
31 #include "gthreadprivate.h"
33 #ifdef G_BIT_LOCK_FORCE_FUTEX_EMULATION
38 static GMutex g_futex_mutex
;
39 static GSList
*g_futex_address_list
= NULL
;
44 * We have headers for futex(2) on the build machine. This does not
45 * imply that every system that ever runs the resulting glib will have
46 * kernel support for futex, but you'd have to have a pretty old
47 * kernel in order for that not to be the case.
49 * If anyone actually gets bit by this, please file a bug. :)
51 #include <linux/futex.h>
52 #include <sys/syscall.h>
55 #ifndef FUTEX_WAIT_PRIVATE
56 #define FUTEX_WAIT_PRIVATE FUTEX_WAIT
57 #define FUTEX_WAKE_PRIVATE FUTEX_WAKE
62 * @address: a pointer to an integer
63 * @value: the value that should be at @address
65 * Atomically checks that the value stored at @address is equal to
66 * @value and then blocks. If the value stored at @address is not
67 * equal to @value then this function returns immediately.
69 * To unblock, call g_futex_wake() on @address.
71 * This call may spuriously unblock (for example, in response to the
72 * process receiving a signal) but this is not guaranteed. Unlike the
73 * Linux system call of a similar name, there is no guarantee that a
74 * waiting process will unblock due to a g_futex_wake() call in a
78 g_futex_wait (const volatile gint
*address
,
81 syscall (__NR_futex
, address
, (gsize
) FUTEX_WAIT_PRIVATE
, (gsize
) value
, NULL
);
86 * @address: a pointer to an integer
88 * Nominally, wakes one thread that is blocked in g_futex_wait() on
89 * @address (if any thread is currently waiting).
91 * As mentioned in the documention for g_futex_wait(), spurious
92 * wakeups may occur. As such, this call may result in more than one
93 * thread being woken up.
96 g_futex_wake (const volatile gint
*address
)
98 syscall (__NR_futex
, address
, (gsize
) FUTEX_WAKE_PRIVATE
, (gsize
) 1, NULL
);
103 /* emulate futex(2) */
106 const volatile gint
*address
;
112 g_futex_find_address (const volatile gint
*address
)
116 for (node
= g_futex_address_list
; node
; node
= node
->next
)
118 WaitAddress
*waiter
= node
->data
;
120 if (waiter
->address
== address
)
128 g_futex_wait (const volatile gint
*address
,
131 g_mutex_lock (&g_futex_mutex
);
132 if G_LIKELY (g_atomic_int_get (address
) == value
)
136 if ((waiter
= g_futex_find_address (address
)) == NULL
)
138 waiter
= g_slice_new (WaitAddress
);
139 waiter
->address
= address
;
140 g_cond_init (&waiter
->wait_queue
);
141 waiter
->ref_count
= 0;
142 g_futex_address_list
=
143 g_slist_prepend (g_futex_address_list
, waiter
);
147 g_cond_wait (&waiter
->wait_queue
, &g_futex_mutex
);
149 if (!--waiter
->ref_count
)
151 g_futex_address_list
=
152 g_slist_remove (g_futex_address_list
, waiter
);
153 g_cond_clear (&waiter
->wait_queue
);
154 g_slice_free (WaitAddress
, waiter
);
157 g_mutex_unlock (&g_futex_mutex
);
161 g_futex_wake (const volatile gint
*address
)
165 /* need to lock here for two reasons:
166 * 1) need to acquire/release lock to ensure waiter is not in
167 * the process of registering a wait
168 * 2) need to -stay- locked until the end to ensure a wake()
169 * in another thread doesn't cause 'waiter' to stop existing
171 g_mutex_lock (&g_futex_mutex
);
172 if ((waiter
= g_futex_find_address (address
)))
173 g_cond_signal (&waiter
->wait_queue
);
174 g_mutex_unlock (&g_futex_mutex
);
178 #define CONTENTION_CLASSES 11
179 static volatile gint g_bit_lock_contended
[CONTENTION_CLASSES
];
181 #if (defined (i386) || defined (__amd64__))
182 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
183 #define USE_ASM_GOTO 1
189 * @address: a pointer to an integer
190 * @lock_bit: a bit value between 0 and 31
192 * Sets the indicated @lock_bit in @address. If the bit is already
193 * set, this call will block until g_bit_unlock() unsets the
196 * Attempting to lock on two different bits within the same integer is
197 * not supported and will very probably cause deadlocks.
199 * The value of the bit that is set is (1u << @bit). If @bit is not
200 * between 0 and 31 then the result is undefined.
202 * This function accesses @address atomically. All other accesses to
203 * @address must be atomic in order for this function to work
209 g_bit_lock (volatile gint
*address
,
214 __asm__
volatile goto ("lock bts %1, (%0)\n"
217 : "r" (address
), "r" (lock_bit
)
224 guint mask
= 1u << lock_bit
;
227 v
= g_atomic_int_get (address
);
230 guint
class = ((gsize
) address
) % G_N_ELEMENTS (g_bit_lock_contended
);
232 g_atomic_int_add (&g_bit_lock_contended
[class], +1);
233 g_futex_wait (address
, v
);
234 g_atomic_int_add (&g_bit_lock_contended
[class], -1);
239 guint mask
= 1u << lock_bit
;
243 v
= g_atomic_int_or (address
, mask
);
247 guint
class = ((gsize
) address
) % G_N_ELEMENTS (g_bit_lock_contended
);
249 g_atomic_int_add (&g_bit_lock_contended
[class], +1);
250 g_futex_wait (address
, v
);
251 g_atomic_int_add (&g_bit_lock_contended
[class], -1);
260 * @address: a pointer to an integer
261 * @lock_bit: a bit value between 0 and 31
263 * Sets the indicated @lock_bit in @address, returning %TRUE if
264 * successful. If the bit is already set, returns %FALSE immediately.
266 * Attempting to lock on two different bits within the same integer is
269 * The value of the bit that is set is (1u << @bit). If @bit is not
270 * between 0 and 31 then the result is undefined.
272 * This function accesses @address atomically. All other accesses to
273 * @address must be atomic in order for this function to work
276 * Returns: %TRUE if the lock was acquired
281 g_bit_trylock (volatile gint
*address
,
287 __asm__
volatile ("lock bts %2, (%1)\n"
291 : "r" (address
), "r" (lock_bit
)
296 guint mask
= 1u << lock_bit
;
299 v
= g_atomic_int_or (address
, mask
);
307 * @address: a pointer to an integer
308 * @lock_bit: a bit value between 0 and 31
310 * Clears the indicated @lock_bit in @address. If another thread is
311 * currently blocked in g_bit_lock() on this same bit then it will be
314 * This function accesses @address atomically. All other accesses to
315 * @address must be atomic in order for this function to work
321 g_bit_unlock (volatile gint
*address
,
325 asm volatile ("lock btr %1, (%0)"
327 : "r" (address
), "r" (lock_bit
)
330 guint mask
= 1u << lock_bit
;
332 g_atomic_int_and (address
, ~mask
);
336 guint
class = ((gsize
) address
) % G_N_ELEMENTS (g_bit_lock_contended
);
338 if (g_atomic_int_get (&g_bit_lock_contended
[class]))
339 g_futex_wake (address
);
344 /* We emulate pointer-sized futex(2) because the kernel API only
347 * We assume that the 'interesting' part is always the lower order bits.
348 * This assumption holds because pointer bitlocks are restricted to
349 * using the low order bits of the pointer as the lock.
351 * On 32 bits, there is nothing to do since the pointer size is equal to
352 * the integer size. On little endian the lower-order bits don't move,
353 * so do nothing. Only on 64bit big endian do we need to do a bit of
354 * pointer arithmetic: the low order bits are shifted by 4 bytes. We
355 * have a helper function that always does the right thing here.
357 * Since we always consider the low-order bits of the integer value, a
358 * simple cast from (gsize) to (guint) always takes care of that.
360 * After that, pointer-sized futex becomes as simple as:
362 * g_futex_wait (g_futex_int_address (address), (guint) value);
366 * g_futex_wake (g_futex_int_address (int_address));
368 static const volatile gint
*
369 g_futex_int_address (const volatile void *address
)
371 const volatile gint
*int_address
= address
;
373 /* this implementation makes these (reasonable) assumptions: */
374 G_STATIC_ASSERT (G_BYTE_ORDER
== G_LITTLE_ENDIAN
||
375 (G_BYTE_ORDER
== G_BIG_ENDIAN
&&
377 (sizeof (gpointer
) == 4 || sizeof (gpointer
) == 8)));
379 #if G_BYTE_ORDER == G_BIG_ENDIAN && GLIB_SIZEOF_VOID_P == 8
387 * g_pointer_bit_lock:
388 * @address: (not nullable): a pointer to a #gpointer-sized value
389 * @lock_bit: a bit value between 0 and 31
391 * This is equivalent to g_bit_lock, but working on pointers (or other
392 * pointer-sized values).
394 * For portability reasons, you may only lock on the bottom 32 bits of
400 (g_pointer_bit_lock
) (volatile void *address
,
403 g_return_if_fail (lock_bit
< 32);
408 asm volatile goto ("lock bts %1, (%0)\n"
411 : "r" (address
), "r" ((gsize
) lock_bit
)
418 volatile gsize
*pointer_address
= address
;
419 gsize mask
= 1u << lock_bit
;
422 v
= (gsize
) g_atomic_pointer_get (pointer_address
);
425 guint
class = ((gsize
) address
) % G_N_ELEMENTS (g_bit_lock_contended
);
427 g_atomic_int_add (&g_bit_lock_contended
[class], +1);
428 g_futex_wait (g_futex_int_address (address
), v
);
429 g_atomic_int_add (&g_bit_lock_contended
[class], -1);
434 volatile gsize
*pointer_address
= address
;
435 gsize mask
= 1u << lock_bit
;
439 v
= g_atomic_pointer_or (pointer_address
, mask
);
443 guint
class = ((gsize
) address
) % G_N_ELEMENTS (g_bit_lock_contended
);
445 g_atomic_int_add (&g_bit_lock_contended
[class], +1);
446 g_futex_wait (g_futex_int_address (address
), (guint
) v
);
447 g_atomic_int_add (&g_bit_lock_contended
[class], -1);
456 * g_pointer_bit_trylock:
457 * @address: (not nullable): a pointer to a #gpointer-sized value
458 * @lock_bit: a bit value between 0 and 31
460 * This is equivalent to g_bit_trylock, but working on pointers (or
461 * other pointer-sized values).
463 * For portability reasons, you may only lock on the bottom 32 bits of
466 * Returns: %TRUE if the lock was acquired
471 (g_pointer_bit_trylock
) (volatile void *address
,
474 g_return_val_if_fail (lock_bit
< 32, FALSE
);
480 asm volatile ("lock bts %2, (%1)\n"
484 : "r" (address
), "r" ((gsize
) lock_bit
)
489 volatile gsize
*pointer_address
= address
;
490 gsize mask
= 1u << lock_bit
;
493 g_return_val_if_fail (lock_bit
< 32, FALSE
);
495 v
= g_atomic_pointer_or (pointer_address
, mask
);
503 * g_pointer_bit_unlock:
504 * @address: (not nullable): a pointer to a #gpointer-sized value
505 * @lock_bit: a bit value between 0 and 31
507 * This is equivalent to g_bit_unlock, but working on pointers (or other
508 * pointer-sized values).
510 * For portability reasons, you may only lock on the bottom 32 bits of
516 (g_pointer_bit_unlock
) (volatile void *address
,
519 g_return_if_fail (lock_bit
< 32);
523 asm volatile ("lock btr %1, (%0)"
525 : "r" (address
), "r" ((gsize
) lock_bit
)
528 volatile gsize
*pointer_address
= address
;
529 gsize mask
= 1u << lock_bit
;
531 g_atomic_pointer_and (pointer_address
, ~mask
);
535 guint
class = ((gsize
) address
) % G_N_ELEMENTS (g_bit_lock_contended
);
536 if (g_atomic_int_get (&g_bit_lock_contended
[class]))
537 g_futex_wake (g_futex_int_address (address
));