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>
25 #include <glib/gatomic.h>
26 #include <glib/gslist.h>
27 #include <glib/gthread.h>
29 #include "gthreadprivate.h"
33 #ifdef G_BIT_LOCK_FORCE_FUTEX_EMULATION
38 static GSList
*g_futex_address_list
= NULL
;
39 static GMutex
*g_futex_mutex
= NULL
;
43 _g_futex_thread_init (void) {
45 g_futex_mutex
= g_mutex_new ();
51 * We have headers for futex(2) on the build machine. This does not
52 * imply that every system that ever runs the resulting glib will have
53 * kernel support for futex, but you'd have to have a pretty old
54 * kernel in order for that not to be the case.
56 * If anyone actually gets bit by this, please file a bug. :)
58 #include <linux/futex.h>
64 * @address: a pointer to an integer
65 * @value: the value that should be at @address
67 * Atomically checks that the value stored at @address is equal to
68 * @value and then blocks. If the value stored at @address is not
69 * equal to @value then this function returns immediately.
71 * To unblock, call g_futex_wake() on @address.
73 * This call may spuriously unblock (for example, in response to the
74 * process receiving a signal) but this is not guaranteed. Unlike the
75 * Linux system call of a similar name, there is no guarantee that a
76 * waiting process will unblock due to a g_futex_wake() call in a
80 g_futex_wait (const volatile gint
*address
,
83 syscall (SYS_futex
, address
, (gsize
) FUTEX_WAIT
, (gsize
) value
, NULL
);
88 * @address: a pointer to an integer
90 * Nominally, wakes one thread that is blocked in g_futex_wait() on
91 * @address (if any thread is currently waiting).
93 * As mentioned in the documention for g_futex_wait(), spurious
94 * wakeups may occur. As such, this call may result in more than one
95 * thread being woken up.
98 g_futex_wake (const volatile gint
*address
)
100 syscall (SYS_futex
, address
, (gsize
) FUTEX_WAKE
, (gsize
) 1, NULL
);
105 /* emulate futex(2) */
108 const volatile gint
*address
;
114 g_futex_find_address (const volatile gint
*address
)
118 for (node
= g_futex_address_list
; node
; node
= node
->next
)
120 WaitAddress
*waiter
= node
->data
;
122 if (waiter
->address
== address
)
130 g_futex_wait (const volatile gint
*address
,
133 g_mutex_lock (g_futex_mutex
);
134 if G_LIKELY (g_atomic_int_get (address
) == value
)
138 if ((waiter
= g_futex_find_address (address
)) == NULL
)
140 waiter
= g_slice_new (WaitAddress
);
141 waiter
->address
= address
;
142 waiter
->wait_queue
= g_cond_new ();
143 waiter
->ref_count
= 0;
144 g_futex_address_list
=
145 g_slist_prepend (g_futex_address_list
, waiter
);
149 g_cond_wait (waiter
->wait_queue
, g_futex_mutex
);
151 if (!--waiter
->ref_count
)
153 g_futex_address_list
=
154 g_slist_remove (g_futex_address_list
, waiter
);
155 g_cond_free (waiter
->wait_queue
);
156 g_slice_free (WaitAddress
, waiter
);
159 g_mutex_unlock (g_futex_mutex
);
163 g_futex_wake (const volatile gint
*address
)
167 /* need to lock here for two reasons:
168 * 1) need to acquire/release lock to ensure waiter is not in
169 * the process of registering a wait
170 * 2) need to -stay- locked until the end to ensure a wake()
171 * in another thread doesn't cause 'waiter' to stop existing
173 g_mutex_lock (g_futex_mutex
);
174 if ((waiter
= g_futex_find_address (address
)))
175 g_cond_signal (waiter
->wait_queue
);
176 g_mutex_unlock (g_futex_mutex
);
180 #define CONTENTION_CLASSES 11
181 static volatile gint g_bit_lock_contended
[CONTENTION_CLASSES
];
185 * @address: a pointer to an integer
186 * @lock_bit: a bit value between 0 and 31
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
192 * Attempting to lock on two different bits within the same integer is
193 * not supported and will very probably cause deadlocks.
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.
198 * This function accesses @address atomically. All other accesses to
199 * @address must be atomic in order for this function to work
205 g_bit_lock (volatile gint
*address
,
211 v
= g_atomic_int_get (address
);
212 if (v
& (1u << lock_bit
))
215 guint
class = ((gsize
) address
) % G_N_ELEMENTS (g_bit_lock_contended
);
217 g_atomic_int_add (&g_bit_lock_contended
[class], +1);
218 g_futex_wait (address
, v
);
219 g_atomic_int_add (&g_bit_lock_contended
[class], -1);
224 if (!g_atomic_int_compare_and_exchange (address
, v
, v
| (1u << lock_bit
)))
230 * @address: a pointer to an integer
231 * @lock_bit: a bit value between 0 and 31
232 * @returns: %TRUE if the lock was acquired
234 * Sets the indicated @lock_bit in @address, returning %TRUE if
235 * successful. If the bit is already set, returns %FALSE immediately.
237 * Attempting to lock on two different bits within the same integer is
240 * The value of the bit that is set is (1u << @bit). If @bit is not
241 * between 0 and 31 then the result is undefined.
243 * This function accesses @address atomically. All other accesses to
244 * @address must be atomic in order for this function to work
250 g_bit_trylock (volatile gint
*address
,
256 v
= g_atomic_int_get (address
);
257 if (v
& (1u << lock_bit
))
261 if (!g_atomic_int_compare_and_exchange (address
, v
, v
| (1u << lock_bit
)))
269 * @address: a pointer to an integer
270 * @lock_bit: a bit value between 0 and 31
272 * Clears the indicated @lock_bit in @address. If another thread is
273 * currently blocked in g_bit_lock() on this same bit then it will be
276 * This function accesses @address atomically. All other accesses to
277 * @address must be atomic in order for this function to work
283 g_bit_unlock (volatile gint
*address
,
286 guint
class = ((gsize
) address
) % G_N_ELEMENTS (g_bit_lock_contended
);
290 v
= g_atomic_int_get (address
);
291 if (!g_atomic_int_compare_and_exchange (address
, v
, v
& ~(1u << lock_bit
)))
294 if (g_atomic_int_get (&g_bit_lock_contended
[class]))
295 g_futex_wake (address
);