add 2.36 index to gobject docs
[glib.git] / glib / gbitlock.c
blob89e0071c566010d5ffa5667f15ac6cdd0cb5a58e
1 /*
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 of the licence, 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, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Author: Ryan Lortie <desrt@desrt.ca>
23 #include "config.h"
25 #include "gbitlock.h"
27 #include <glib/gmessages.h>
28 #include <glib/gatomic.h>
29 #include <glib/gslist.h>
30 #include <glib/gthread.h>
31 #include <glib/gslice.h>
33 #include "gthreadprivate.h"
35 #undef HAVE_FUTEX
36 #ifdef G_BIT_LOCK_FORCE_FUTEX_EMULATION
37 #endif
39 #ifndef HAVE_FUTEX
40 static GMutex g_futex_mutex;
41 static GSList *g_futex_address_list = NULL;
42 #endif
44 #ifdef HAVE_FUTEX
46 * We have headers for futex(2) on the build machine. This does not
47 * imply that every system that ever runs the resulting glib will have
48 * kernel support for futex, but you'd have to have a pretty old
49 * kernel in order for that not to be the case.
51 * If anyone actually gets bit by this, please file a bug. :)
53 #include <linux/futex.h>
54 #include <sys/syscall.h>
55 #include <unistd.h>
57 /* < private >
58 * g_futex_wait:
59 * @address: a pointer to an integer
60 * @value: the value that should be at @address
62 * Atomically checks that the value stored at @address is equal to
63 * @value and then blocks. If the value stored at @address is not
64 * equal to @value then this function returns immediately.
66 * To unblock, call g_futex_wake() on @address.
68 * This call may spuriously unblock (for example, in response to the
69 * process receiving a signal) but this is not guaranteed. Unlike the
70 * Linux system call of a similar name, there is no guarantee that a
71 * waiting process will unblock due to a g_futex_wake() call in a
72 * separate process.
74 static void
75 g_futex_wait (const volatile gint *address,
76 gint value)
78 syscall (__NR_futex, address, (gsize) FUTEX_WAIT, (gsize) value, NULL);
81 /* < private >
82 * g_futex_wake:
83 * @address: a pointer to an integer
85 * Nominally, wakes one thread that is blocked in g_futex_wait() on
86 * @address (if any thread is currently waiting).
88 * As mentioned in the documention for g_futex_wait(), spurious
89 * wakeups may occur. As such, this call may result in more than one
90 * thread being woken up.
92 static void
93 g_futex_wake (const volatile gint *address)
95 syscall (__NR_futex, address, (gsize) FUTEX_WAKE, (gsize) 1, NULL);
98 #else
100 /* emulate futex(2) */
101 typedef struct
103 const volatile gint *address;
104 gint ref_count;
105 GCond wait_queue;
106 } WaitAddress;
108 static WaitAddress *
109 g_futex_find_address (const volatile gint *address)
111 GSList *node;
113 for (node = g_futex_address_list; node; node = node->next)
115 WaitAddress *waiter = node->data;
117 if (waiter->address == address)
118 return waiter;
121 return NULL;
124 static void
125 g_futex_wait (const volatile gint *address,
126 gint value)
128 g_mutex_lock (&g_futex_mutex);
129 if G_LIKELY (g_atomic_int_get (address) == value)
131 WaitAddress *waiter;
133 if ((waiter = g_futex_find_address (address)) == NULL)
135 waiter = g_slice_new (WaitAddress);
136 waiter->address = address;
137 g_cond_init (&waiter->wait_queue);
138 waiter->ref_count = 0;
139 g_futex_address_list =
140 g_slist_prepend (g_futex_address_list, waiter);
143 waiter->ref_count++;
144 g_cond_wait (&waiter->wait_queue, &g_futex_mutex);
146 if (!--waiter->ref_count)
148 g_futex_address_list =
149 g_slist_remove (g_futex_address_list, waiter);
150 g_cond_clear (&waiter->wait_queue);
151 g_slice_free (WaitAddress, waiter);
154 g_mutex_unlock (&g_futex_mutex);
157 static void
158 g_futex_wake (const volatile gint *address)
160 WaitAddress *waiter;
162 /* need to lock here for two reasons:
163 * 1) need to acquire/release lock to ensure waiter is not in
164 * the process of registering a wait
165 * 2) need to -stay- locked until the end to ensure a wake()
166 * in another thread doesn't cause 'waiter' to stop existing
168 g_mutex_lock (&g_futex_mutex);
169 if ((waiter = g_futex_find_address (address)))
170 g_cond_signal (&waiter->wait_queue);
171 g_mutex_unlock (&g_futex_mutex);
173 #endif
175 #define CONTENTION_CLASSES 11
176 static volatile gint g_bit_lock_contended[CONTENTION_CLASSES];
178 #if (defined (i386) || defined (__amd64__))
179 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
180 #define USE_ASM_GOTO 1
181 #endif
182 #endif
185 * g_bit_lock:
186 * @address: a pointer to an integer
187 * @lock_bit: a bit value between 0 and 31
189 * Sets the indicated @lock_bit in @address. If the bit is already
190 * set, this call will block until g_bit_unlock() unsets the
191 * corresponding bit.
193 * Attempting to lock on two different bits within the same integer is
194 * not supported and will very probably cause deadlocks.
196 * The value of the bit that is set is (1u << @bit). If @bit is not
197 * between 0 and 31 then the result is undefined.
199 * This function accesses @address atomically. All other accesses to
200 * @address must be atomic in order for this function to work
201 * reliably.
203 * Since: 2.24
205 void
206 g_bit_lock (volatile gint *address,
207 gint lock_bit)
209 #ifdef USE_ASM_GOTO
210 retry:
211 asm volatile goto ("lock bts %1, (%0)\n"
212 "jc %l[contended]"
213 : /* no output */
214 : "r" (address), "r" (lock_bit)
215 : "cc", "memory"
216 : contended);
217 return;
219 contended:
221 guint mask = 1u << lock_bit;
222 guint v;
224 v = g_atomic_int_get (address);
225 if (v & mask)
227 guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
229 g_atomic_int_add (&g_bit_lock_contended[class], +1);
230 g_futex_wait (address, v);
231 g_atomic_int_add (&g_bit_lock_contended[class], -1);
234 goto retry;
235 #else
236 guint mask = 1u << lock_bit;
237 guint v;
239 retry:
240 v = g_atomic_int_or (address, mask);
241 if (v & mask)
242 /* already locked */
244 guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
246 g_atomic_int_add (&g_bit_lock_contended[class], +1);
247 g_futex_wait (address, v);
248 g_atomic_int_add (&g_bit_lock_contended[class], -1);
250 goto retry;
252 #endif
256 * g_bit_trylock:
257 * @address: a pointer to an integer
258 * @lock_bit: a bit value between 0 and 31
260 * Sets the indicated @lock_bit in @address, returning %TRUE if
261 * successful. If the bit is already set, returns %FALSE immediately.
263 * Attempting to lock on two different bits within the same integer is
264 * not supported.
266 * The value of the bit that is set is (1u << @bit). If @bit is not
267 * between 0 and 31 then the result is undefined.
269 * This function accesses @address atomically. All other accesses to
270 * @address must be atomic in order for this function to work
271 * reliably.
273 * Returns: %TRUE if the lock was acquired
275 * Since: 2.24
277 gboolean
278 g_bit_trylock (volatile gint *address,
279 gint lock_bit)
281 #ifdef USE_ASM_GOTO
282 gboolean result;
284 asm volatile ("lock bts %2, (%1)\n"
285 "setnc %%al\n"
286 "movzx %%al, %0"
287 : "=r" (result)
288 : "r" (address), "r" (lock_bit)
289 : "cc", "memory");
291 return result;
292 #else
293 guint mask = 1u << lock_bit;
294 guint v;
296 v = g_atomic_int_or (address, mask);
298 return ~v & mask;
299 #endif
303 * g_bit_unlock:
304 * @address: a pointer to an integer
305 * @lock_bit: a bit value between 0 and 31
307 * Clears the indicated @lock_bit in @address. If another thread is
308 * currently blocked in g_bit_lock() on this same bit then it will be
309 * woken up.
311 * This function accesses @address atomically. All other accesses to
312 * @address must be atomic in order for this function to work
313 * reliably.
315 * Since: 2.24
317 void
318 g_bit_unlock (volatile gint *address,
319 gint lock_bit)
321 #ifdef USE_ASM_GOTO
322 asm volatile ("lock btr %1, (%0)"
323 : /* no output */
324 : "r" (address), "r" (lock_bit)
325 : "cc", "memory");
326 #else
327 guint mask = 1u << lock_bit;
329 g_atomic_int_and (address, ~mask);
330 #endif
333 guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
335 if (g_atomic_int_get (&g_bit_lock_contended[class]))
336 g_futex_wake (address);
341 /* We emulate pointer-sized futex(2) because the kernel API only
342 * supports integers.
344 * We assume that the 'interesting' part is always the lower order bits.
345 * This assumption holds because pointer bitlocks are restricted to
346 * using the low order bits of the pointer as the lock.
348 * On 32 bits, there is nothing to do since the pointer size is equal to
349 * the integer size. On little endian the lower-order bits don't move,
350 * so do nothing. Only on 64bit big endian do we need to do a bit of
351 * pointer arithmetic: the low order bits are shifted by 4 bytes. We
352 * have a helper function that always does the right thing here.
354 * Since we always consider the low-order bits of the integer value, a
355 * simple cast from (gsize) to (guint) always takes care of that.
357 * After that, pointer-sized futex becomes as simple as:
359 * g_futex_wait (g_futex_int_address (address), (guint) value);
361 * and
363 * g_futex_wake (g_futex_int_address (int_address));
365 static const volatile gint *
366 g_futex_int_address (const volatile void *address)
368 const volatile gint *int_address = address;
370 /* this implementation makes these (reasonable) assumptions: */
371 G_STATIC_ASSERT (G_BYTE_ORDER == G_LITTLE_ENDIAN ||
372 (G_BYTE_ORDER == G_BIG_ENDIAN &&
373 sizeof (int) == 4 &&
374 (sizeof (gpointer) == 4 || sizeof (gpointer) == 8)));
376 #if G_BYTE_ORDER == G_BIG_ENDIAN && GLIB_SIZEOF_VOID_P == 8
377 int_address++;
378 #endif
380 return int_address;
384 * g_pointer_bit_lock:
385 * @address: a pointer to a #gpointer-sized value
386 * @lock_bit: a bit value between 0 and 31
388 * This is equivalent to g_bit_lock, but working on pointers (or other
389 * pointer-sized values).
391 * For portability reasons, you may only lock on the bottom 32 bits of
392 * the pointer.
394 * Since: 2.30
396 void
397 (g_pointer_bit_lock) (volatile void *address,
398 gint lock_bit)
400 g_return_if_fail (lock_bit < 32);
403 #ifdef USE_ASM_GOTO
404 retry:
405 asm volatile goto ("lock bts %1, (%0)\n"
406 "jc %l[contended]"
407 : /* no output */
408 : "r" (address), "r" ((gsize) lock_bit)
409 : "cc", "memory"
410 : contended);
411 return;
413 contended:
415 volatile gsize *pointer_address = address;
416 gsize mask = 1u << lock_bit;
417 gsize v;
419 v = (gsize) g_atomic_pointer_get (pointer_address);
420 if (v & mask)
422 guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
424 g_atomic_int_add (&g_bit_lock_contended[class], +1);
425 g_futex_wait (g_futex_int_address (address), v);
426 g_atomic_int_add (&g_bit_lock_contended[class], -1);
429 goto retry;
430 #else
431 volatile gsize *pointer_address = address;
432 gsize mask = 1u << lock_bit;
433 gsize v;
435 retry:
436 v = g_atomic_pointer_or (pointer_address, mask);
437 if (v & mask)
438 /* already locked */
440 guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
442 g_atomic_int_add (&g_bit_lock_contended[class], +1);
443 g_futex_wait (g_futex_int_address (address), (guint) v);
444 g_atomic_int_add (&g_bit_lock_contended[class], -1);
446 goto retry;
448 #endif
453 * g_pointer_bit_trylock:
454 * @address: a pointer to a #gpointer-sized value
455 * @lock_bit: a bit value between 0 and 31
457 * This is equivalent to g_bit_trylock, but working on pointers (or
458 * other pointer-sized values).
460 * For portability reasons, you may only lock on the bottom 32 bits of
461 * the pointer.
463 * Returns: %TRUE if the lock was acquired
465 * Since: 2.30
467 gboolean
468 (g_pointer_bit_trylock) (volatile void *address,
469 gint lock_bit)
471 g_return_val_if_fail (lock_bit < 32, FALSE);
474 #ifdef USE_ASM_GOTO
475 gboolean result;
477 asm volatile ("lock bts %2, (%1)\n"
478 "setnc %%al\n"
479 "movzx %%al, %0"
480 : "=r" (result)
481 : "r" (address), "r" ((gsize) lock_bit)
482 : "cc", "memory");
484 return result;
485 #else
486 volatile gsize *pointer_address = address;
487 gsize mask = 1u << lock_bit;
488 gsize v;
490 g_return_val_if_fail (lock_bit < 32, FALSE);
492 v = g_atomic_pointer_or (pointer_address, mask);
494 return ~v & mask;
495 #endif
500 * g_pointer_bit_unlock:
501 * @address: a pointer to a #gpointer-sized value
502 * @lock_bit: a bit value between 0 and 31
504 * This is equivalent to g_bit_unlock, but working on pointers (or other
505 * pointer-sized values).
507 * For portability reasons, you may only lock on the bottom 32 bits of
508 * the pointer.
510 * Since: 2.30
512 void
513 (g_pointer_bit_unlock) (volatile void *address,
514 gint lock_bit)
516 g_return_if_fail (lock_bit < 32);
519 #ifdef USE_ASM_GOTO
520 asm volatile ("lock btr %1, (%0)"
521 : /* no output */
522 : "r" (address), "r" ((gsize) lock_bit)
523 : "cc", "memory");
524 #else
525 volatile gsize *pointer_address = address;
526 gsize mask = 1u << lock_bit;
528 g_atomic_pointer_and (pointer_address, ~mask);
529 #endif
532 guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
533 if (g_atomic_int_get (&g_bit_lock_contended[class]))
534 g_futex_wake (g_futex_int_address (address));