Fix a C99ism
[platform/upstream/glib.git] / glib / gbitlock.c
1 /*
2  * Copyright © 2008 Ryan Lortie
3  * Copyright © 2010 Codethink Limited
4  *
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.
9  *
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.
14  *
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.
19  *
20  * Author: Ryan Lortie <desrt@desrt.ca>
21  */
22
23 #include "gbitlock.h"
24
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>
30
31 #include "gthreadprivate.h"
32 #include "config.h"
33
34 #undef HAVE_FUTEX
35 #ifdef G_BIT_LOCK_FORCE_FUTEX_EMULATION
36 #endif
37
38 #ifndef HAVE_FUTEX
39 static GMutex g_futex_mutex;
40 static GSList *g_futex_address_list = NULL;
41 #endif
42
43 #ifdef HAVE_FUTEX
44 /*
45  * We have headers for futex(2) on the build machine.  This does not
46  * imply that every system that ever runs the resulting glib will have
47  * kernel support for futex, but you'd have to have a pretty old
48  * kernel in order for that not to be the case.
49  *
50  * If anyone actually gets bit by this, please file a bug. :)
51  */
52 #include <linux/futex.h>
53 #include <sys/syscall.h>
54 #include <unistd.h>
55
56 /* < private >
57  * g_futex_wait:
58  * @address: a pointer to an integer
59  * @value: the value that should be at @address
60  *
61  * Atomically checks that the value stored at @address is equal to
62  * @value and then blocks.  If the value stored at @address is not
63  * equal to @value then this function returns immediately.
64  *
65  * To unblock, call g_futex_wake() on @address.
66  *
67  * This call may spuriously unblock (for example, in response to the
68  * process receiving a signal) but this is not guaranteed.  Unlike the
69  * Linux system call of a similar name, there is no guarantee that a
70  * waiting process will unblock due to a g_futex_wake() call in a
71  * separate process.
72  */
73 static void
74 g_futex_wait (const volatile gint *address,
75               gint                 value)
76 {
77   syscall (__NR_futex, address, (gsize) FUTEX_WAIT, (gsize) value, NULL);
78 }
79
80 /* < private >
81  * g_futex_wake:
82  * @address: a pointer to an integer
83  *
84  * Nominally, wakes one thread that is blocked in g_futex_wait() on
85  * @address (if any thread is currently waiting).
86  *
87  * As mentioned in the documention for g_futex_wait(), spurious
88  * wakeups may occur.  As such, this call may result in more than one
89  * thread being woken up.
90  */
91 static void
92 g_futex_wake (const volatile gint *address)
93 {
94   syscall (__NR_futex, address, (gsize) FUTEX_WAKE, (gsize) 1, NULL);
95 }
96
97 #else
98
99 /* emulate futex(2) */
100 typedef struct
101 {
102   const volatile gint *address;
103   gint                 ref_count;
104   GCond                wait_queue;
105 } WaitAddress;
106
107 static WaitAddress *
108 g_futex_find_address (const volatile gint *address)
109 {
110   GSList *node;
111
112   for (node = g_futex_address_list; node; node = node->next)
113     {
114       WaitAddress *waiter = node->data;
115
116       if (waiter->address == address)
117         return waiter;
118     }
119
120   return NULL;
121 }
122
123 static void
124 g_futex_wait (const volatile gint *address,
125               gint                 value)
126 {
127   g_mutex_lock (&g_futex_mutex);
128   if G_LIKELY (g_atomic_int_get (address) == value)
129     {
130       WaitAddress *waiter;
131
132       if ((waiter = g_futex_find_address (address)) == NULL)
133         {
134           waiter = g_slice_new (WaitAddress);
135           waiter->address = address;
136           g_cond_init (&waiter->wait_queue);
137           waiter->ref_count = 0;
138           g_futex_address_list =
139             g_slist_prepend (g_futex_address_list, waiter);
140         }
141
142       waiter->ref_count++;
143       g_cond_wait (&waiter->wait_queue, &g_futex_mutex);
144
145       if (!--waiter->ref_count)
146         {
147           g_futex_address_list =
148             g_slist_remove (g_futex_address_list, waiter);
149           g_cond_clear (&waiter->wait_queue);
150           g_slice_free (WaitAddress, waiter);
151         }
152     }
153   g_mutex_unlock (&g_futex_mutex);
154 }
155
156 static void
157 g_futex_wake (const volatile gint *address)
158 {
159   WaitAddress *waiter;
160
161   /* need to lock here for two reasons:
162    *   1) need to acquire/release lock to ensure waiter is not in
163    *      the process of registering a wait
164    *   2) need to -stay- locked until the end to ensure a wake()
165    *      in another thread doesn't cause 'waiter' to stop existing
166    */
167   g_mutex_lock (&g_futex_mutex);
168   if ((waiter = g_futex_find_address (address)))
169     g_cond_signal (&waiter->wait_queue);
170   g_mutex_unlock (&g_futex_mutex);
171 }
172 #endif
173
174 #define CONTENTION_CLASSES 11
175 static volatile gint g_bit_lock_contended[CONTENTION_CLASSES];
176
177 #if (defined (i386) || defined (__amd64__))
178   #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
179     #define USE_ASM_GOTO 1
180   #endif
181 #endif
182
183 /**
184  * g_bit_lock:
185  * @address: a pointer to an integer
186  * @lock_bit: a bit value between 0 and 31
187  *
188  * Sets the indicated @lock_bit in @address.  If the bit is already
189  * set, this call will block until g_bit_unlock() unsets the
190  * corresponding bit.
191  *
192  * Attempting to lock on two different bits within the same integer is
193  * not supported and will very probably cause deadlocks.
194  *
195  * The value of the bit that is set is (1u << @bit).  If @bit is not
196  * between 0 and 31 then the result is undefined.
197  *
198  * This function accesses @address atomically.  All other accesses to
199  * @address must be atomic in order for this function to work
200  * reliably.
201  *
202  * Since: 2.24
203  **/
204 void
205 g_bit_lock (volatile gint *address,
206             gint           lock_bit)
207 {
208 #ifdef USE_ASM_GOTO
209  retry:
210   asm volatile goto ("lock bts %1, (%0)\n"
211                      "jc %l[contended]"
212                      : /* no output */
213                      : "r" (address), "r" (lock_bit)
214                      : "cc", "memory"
215                      : contended);
216   return;
217
218  contended:
219   {
220     guint mask = 1u << lock_bit;
221     guint v;
222
223     v = g_atomic_int_get (address);
224     if (v & mask)
225       {
226         guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
227
228         g_atomic_int_add (&g_bit_lock_contended[class], +1);
229         g_futex_wait (address, v);
230         g_atomic_int_add (&g_bit_lock_contended[class], -1);
231       }
232   }
233   goto retry;
234 #else
235   guint mask = 1u << lock_bit;
236   guint v;
237
238  retry:
239   v = g_atomic_int_or (address, mask);
240   if (v & mask)
241     /* already locked */
242     {
243       guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
244
245       g_atomic_int_add (&g_bit_lock_contended[class], +1);
246       g_futex_wait (address, v);
247       g_atomic_int_add (&g_bit_lock_contended[class], -1);
248
249       goto retry;
250     }
251 #endif
252 }
253
254 /**
255  * g_bit_trylock:
256  * @address: a pointer to an integer
257  * @lock_bit: a bit value between 0 and 31
258  *
259  * Sets the indicated @lock_bit in @address, returning %TRUE if
260  * successful.  If the bit is already set, returns %FALSE immediately.
261  *
262  * Attempting to lock on two different bits within the same integer is
263  * not supported.
264  *
265  * The value of the bit that is set is (1u << @bit).  If @bit is not
266  * between 0 and 31 then the result is undefined.
267  *
268  * This function accesses @address atomically.  All other accesses to
269  * @address must be atomic in order for this function to work
270  * reliably.
271  *
272  * Returns: %TRUE if the lock was acquired
273  *
274  * Since: 2.24
275  **/
276 gboolean
277 g_bit_trylock (volatile gint *address,
278                gint           lock_bit)
279 {
280 #ifdef USE_ASM_GOTO
281   gboolean result;
282
283   asm volatile ("lock bts %2, (%1)\n"
284                 "setnc %%al\n"
285                 "movzx %%al, %0"
286                 : "=r" (result)
287                 : "r" (address), "r" (lock_bit)
288                 : "cc", "memory");
289
290   return result;
291 #else
292   guint mask = 1u << lock_bit;
293   guint v;
294
295   v = g_atomic_int_or (address, mask);
296
297   return ~v & mask;
298 #endif
299 }
300
301 /**
302  * g_bit_unlock:
303  * @address: a pointer to an integer
304  * @lock_bit: a bit value between 0 and 31
305  *
306  * Clears the indicated @lock_bit in @address.  If another thread is
307  * currently blocked in g_bit_lock() on this same bit then it will be
308  * woken up.
309  *
310  * This function accesses @address atomically.  All other accesses to
311  * @address must be atomic in order for this function to work
312  * reliably.
313  *
314  * Since: 2.24
315  **/
316 void
317 g_bit_unlock (volatile gint *address,
318               gint           lock_bit)
319 {
320 #ifdef USE_ASM_GOTO
321   asm volatile ("lock btr %1, (%0)"
322                 : /* no output */
323                 : "r" (address), "r" (lock_bit)
324                 : "cc", "memory");
325 #else
326   guint mask = 1u << lock_bit;
327
328   g_atomic_int_and (address, ~mask);
329 #endif
330
331   {
332     guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
333
334     if (g_atomic_int_get (&g_bit_lock_contended[class]))
335       g_futex_wake (address);
336   }
337 }
338
339
340 /* We emulate pointer-sized futex(2) because the kernel API only
341  * supports integers.
342  *
343  * We assume that the 'interesting' part is always the lower order bits.
344  * This assumption holds because pointer bitlocks are restricted to
345  * using the low order bits of the pointer as the lock.
346  *
347  * On 32 bits, there is nothing to do since the pointer size is equal to
348  * the integer size.  On little endian the lower-order bits don't move,
349  * so do nothing.  Only on 64bit big endian do we need to do a bit of
350  * pointer arithmetic: the low order bits are shifted by 4 bytes.  We
351  * have a helper function that always does the right thing here.
352  *
353  * Since we always consider the low-order bits of the integer value, a
354  * simple cast from (gsize) to (guint) always takes care of that.
355  *
356  * After that, pointer-sized futex becomes as simple as:
357  *
358  *   g_futex_wait (g_futex_int_address (address), (guint) value);
359  *
360  * and
361  *
362  *   g_futex_wake (g_futex_int_address (int_address));
363  */
364 static const volatile gint *
365 g_futex_int_address (const volatile void *address)
366 {
367   const volatile gint *int_address = address;
368
369   /* this implementation makes these (reasonable) assumptions: */
370   G_STATIC_ASSERT (G_BYTE_ORDER == G_LITTLE_ENDIAN ||
371       (G_BYTE_ORDER == G_BIG_ENDIAN &&
372        sizeof (int) == 4 &&
373        (sizeof (gpointer) == 4 || sizeof (gpointer) == 8)));
374
375 #if G_BYTE_ORDER == G_BIG_ENDIAN && GLIB_SIZEOF_VOID_P == 8
376   int_address++;
377 #endif
378
379   return int_address;
380 }
381
382 /**
383  * g_pointer_bit_lock:
384  * @address: a pointer to a #gpointer-sized value
385  * @lock_bit: a bit value between 0 and 31
386  *
387  * This is equivalent to g_bit_lock, but working on pointers (or other
388  * pointer-sized values).
389  *
390  * For portability reasons, you may only lock on the bottom 32 bits of
391  * the pointer.
392  *
393  * Since: 2.30
394  **/
395 void
396 (g_pointer_bit_lock) (volatile void *address,
397                       gint           lock_bit)
398 {
399   g_return_if_fail (lock_bit < 32);
400
401   {
402 #ifdef USE_ASM_GOTO
403  retry:
404     asm volatile goto ("lock bts %1, (%0)\n"
405                        "jc %l[contended]"
406                        : /* no output */
407                        : "r" (address), "r" ((gsize) lock_bit)
408                        : "cc", "memory"
409                        : contended);
410     return;
411
412  contended:
413     {
414       volatile gsize *pointer_address = address;
415       gsize mask = 1u << lock_bit;
416       gsize v;
417
418       v = (gsize) g_atomic_pointer_get (pointer_address);
419       if (v & mask)
420         {
421           guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
422
423           g_atomic_int_add (&g_bit_lock_contended[class], +1);
424           g_futex_wait (g_futex_int_address (address), v);
425           g_atomic_int_add (&g_bit_lock_contended[class], -1);
426         }
427     }
428     goto retry;
429 #else
430   volatile gsize *pointer_address = address;
431   gsize mask = 1u << lock_bit;
432   gsize v;
433
434  retry:
435   v = g_atomic_pointer_or (pointer_address, mask);
436   if (v & mask)
437     /* already locked */
438     {
439       guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
440
441       g_atomic_int_add (&g_bit_lock_contended[class], +1);
442       g_futex_wait (g_futex_int_address (address), (guint) v);
443       g_atomic_int_add (&g_bit_lock_contended[class], -1);
444
445       goto retry;
446     }
447 #endif
448   }
449 }
450
451 /**
452  * g_pointer_bit_trylock:
453  * @address: a pointer to a #gpointer-sized value
454  * @lock_bit: a bit value between 0 and 31
455  *
456  * This is equivalent to g_bit_trylock, but working on pointers (or
457  * other pointer-sized values).
458  *
459  * For portability reasons, you may only lock on the bottom 32 bits of
460  * the pointer.
461  *
462  * Returns: %TRUE if the lock was acquired
463  *
464  * Since: 2.30
465  **/
466 gboolean
467 (g_pointer_bit_trylock) (volatile void *address,
468                          gint           lock_bit)
469 {
470   g_return_val_if_fail (lock_bit < 32, FALSE);
471
472   {
473 #ifdef USE_ASM_GOTO
474     gboolean result;
475
476     asm volatile ("lock bts %2, (%1)\n"
477                   "setnc %%al\n"
478                   "movzx %%al, %0"
479                   : "=r" (result)
480                   : "r" (address), "r" ((gsize) lock_bit)
481                   : "cc", "memory");
482
483     return result;
484 #else
485     volatile gsize *pointer_address = address;
486     gsize mask = 1u << lock_bit;
487     gsize v;
488
489     g_return_val_if_fail (lock_bit < 32, FALSE);
490
491     v = g_atomic_pointer_or (pointer_address, mask);
492
493     return ~v & mask;
494 #endif
495   }
496 }
497
498 /**
499  * g_pointer_bit_unlock:
500  * @address: a pointer to a #gpointer-sized value
501  * @lock_bit: a bit value between 0 and 31
502  *
503  * This is equivalent to g_bit_unlock, but working on pointers (or other
504  * pointer-sized values).
505  *
506  * For portability reasons, you may only lock on the bottom 32 bits of
507  * the pointer.
508  *
509  * Since: 2.30
510  **/
511 void
512 (g_pointer_bit_unlock) (volatile void *address,
513                         gint           lock_bit)
514 {
515   g_return_if_fail (lock_bit < 32);
516
517   {
518 #ifdef USE_ASM_GOTO
519     asm volatile ("lock btr %1, (%0)"
520                   : /* no output */
521                   : "r" (address), "r" ((gsize) lock_bit)
522                   : "cc", "memory");
523 #else
524     volatile gsize *pointer_address = address;
525     gsize mask = 1u << lock_bit;
526
527     g_atomic_pointer_and (pointer_address, ~mask);
528 #endif
529
530     {
531       guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
532       if (g_atomic_int_get (&g_bit_lock_contended[class]))
533         g_futex_wake (g_futex_int_address (address));
534     }
535   }
536 }