GDelayedSettingsBackend: another mandatory fixup
[glib.git] / glib / gbitlock.c
blob915aa7cb659251bd3fdc24306702305f655858bc
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 "gbitlock.h"
25 #include <glib/gatomic.h>
26 #include <glib/gslist.h>
27 #include <glib/gthread.h>
29 #include "gthreadprivate.h"
30 #include "config.h"
32 #include "galias.h"
34 #ifdef G_BIT_LOCK_FORCE_FUTEX_EMULATION
35 #undef HAVE_FUTEX
36 #endif
38 #ifndef HAVE_FUTEX
39 static GSList *g_futex_address_list = NULL;
40 static GMutex *g_futex_mutex = NULL;
41 #endif
43 void
44 _g_futex_thread_init (void) {
45 #ifndef HAVE_FUTEX
46 g_futex_mutex = g_mutex_new ();
47 #endif
50 #ifdef HAVE_FUTEX
52 * We have headers for futex(2) on the build machine. This does not
53 * imply that every system that ever runs the resulting glib will have
54 * kernel support for futex, but you'd have to have a pretty old
55 * kernel in order for that not to be the case.
57 * If anyone actually gets bit by this, please file a bug. :)
59 #include <linux/futex.h>
60 #include <syscall.h>
61 #include <unistd.h>
63 /* < private >
64 * g_futex_wait:
65 * @address: a pointer to an integer
66 * @value: the value that should be at @address
68 * Atomically checks that the value stored at @address is equal to
69 * @value and then blocks. If the value stored at @address is not
70 * equal to @value then this function returns immediately.
72 * To unblock, call g_futex_wake() on @address.
74 * This call may spuriously unblock (for example, in response to the
75 * process receiving a signal) but this is not guaranteed. Unlike the
76 * Linux system call of a similar name, there is no guarantee that a
77 * waiting process will unblock due to a g_futex_wake() call in a
78 * separate process.
80 static void
81 g_futex_wait (const volatile gint *address,
82 gint value)
84 syscall (SYS_futex, address, (gsize) FUTEX_WAIT, (gsize) value, NULL);
87 /* < private >
88 * g_futex_wake:
89 * @address: a pointer to an integer
91 * Nominally, wakes one thread that is blocked in g_futex_wait() on
92 * @address (if any thread is currently waiting).
94 * As mentioned in the documention for g_futex_wait(), spurious
95 * wakeups may occur. As such, this call may result in more than one
96 * thread being woken up.
98 static void
99 g_futex_wake (const volatile gint *address)
101 syscall (SYS_futex, address, (gsize) FUTEX_WAKE, (gsize) 1, NULL);
104 #else
106 /* emulate futex(2) */
107 typedef struct
109 const volatile gint *address;
110 gint ref_count;
111 GCond *wait_queue;
112 } WaitAddress;
114 static WaitAddress *
115 g_futex_find_address (const volatile gint *address)
117 GSList *node;
119 for (node = g_futex_address_list; node; node = node->next)
121 WaitAddress *waiter = node->data;
123 if (waiter->address == address)
124 return waiter;
127 return NULL;
130 static void
131 g_futex_wait (const volatile gint *address,
132 gint value)
134 g_mutex_lock (g_futex_mutex);
135 if G_LIKELY (g_atomic_int_get (address) == value)
137 WaitAddress *waiter;
139 if ((waiter = g_futex_find_address (address)) == NULL)
141 waiter = g_slice_new (WaitAddress);
142 waiter->address = address;
143 waiter->wait_queue = g_cond_new ();
144 waiter->ref_count = 0;
145 g_futex_address_list =
146 g_slist_prepend (g_futex_address_list, waiter);
149 waiter->ref_count++;
150 g_cond_wait (waiter->wait_queue, g_futex_mutex);
152 if (!--waiter->ref_count)
154 g_futex_address_list =
155 g_slist_remove (g_futex_address_list, waiter);
156 g_cond_free (waiter->wait_queue);
157 g_slice_free (WaitAddress, waiter);
160 g_mutex_unlock (g_futex_mutex);
163 static void
164 g_futex_wake (const volatile gint *address)
166 WaitAddress *waiter;
168 /* need to lock here for two reasons:
169 * 1) need to acquire/release lock to ensure waiter is not in
170 * the process of registering a wait
171 * 2) need to -stay- locked until the end to ensure a wake()
172 * in another thread doesn't cause 'waiter' to stop existing
174 g_mutex_lock (g_futex_mutex);
175 if ((waiter = g_futex_find_address (address)))
176 g_cond_signal (waiter->wait_queue);
177 g_mutex_unlock (g_futex_mutex);
179 #endif
181 #define CONTENTION_CLASSES 11
182 static volatile gint g_bit_lock_contended[CONTENTION_CLASSES];
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 guint v;
211 retry:
212 v = g_atomic_int_get (address);
213 if (v & (1u << lock_bit))
214 /* already locked */
216 guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
218 g_atomic_int_add (&g_bit_lock_contended[class], +1);
219 g_futex_wait (address, v);
220 g_atomic_int_add (&g_bit_lock_contended[class], -1);
222 goto retry;
225 if (!g_atomic_int_compare_and_exchange (address, v, v | (1u << lock_bit)))
226 goto retry;
230 * g_bit_trylock:
231 * @address: a pointer to an integer
232 * @lock_bit: a bit value between 0 and 31
233 * @returns: %TRUE if the lock was acquired
235 * Sets the indicated @lock_bit in @address, returning %TRUE if
236 * successful. If the bit is already set, returns %FALSE immediately.
238 * Attempting to lock on two different bits within the same integer is
239 * not supported.
241 * The value of the bit that is set is (1u << @bit). If @bit is not
242 * between 0 and 31 then the result is undefined.
244 * This function accesses @address atomically. All other accesses to
245 * @address must be atomic in order for this function to work
246 * reliably.
248 * Since: 2.24
250 gboolean
251 g_bit_trylock (volatile gint *address,
252 gint lock_bit)
254 guint v;
256 retry:
257 v = g_atomic_int_get (address);
258 if (v & (1u << lock_bit))
259 /* already locked */
260 return FALSE;
262 if (!g_atomic_int_compare_and_exchange (address, v, v | (1u << lock_bit)))
263 goto retry;
265 return TRUE;
269 * g_bit_unlock:
270 * @address: a pointer to an integer
271 * @lock_bit: a bit value between 0 and 31
273 * Clears the indicated @lock_bit in @address. If another thread is
274 * currently blocked in g_bit_lock() on this same bit then it will be
275 * woken up.
277 * This function accesses @address atomically. All other accesses to
278 * @address must be atomic in order for this function to work
279 * reliably.
281 * Since: 2.24
283 void
284 g_bit_unlock (volatile gint *address,
285 gint lock_bit)
287 guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
288 guint v;
290 retry:
291 v = g_atomic_int_get (address);
292 if (!g_atomic_int_compare_and_exchange (address, v, v & ~(1u << lock_bit)))
293 goto retry;
295 if (g_atomic_int_get (&g_bit_lock_contended[class]))
296 g_futex_wake (address);
299 #define __G_BITLOCK_C__
300 #include "galiasdef.c"