Branch data Line data Source code
1 : : /*
2 : : * Copyright © 2008 Ryan Lortie
3 : : * Copyright © 2010 Codethink Limited
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General Public
18 : : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * Author: Ryan Lortie <desrt@desrt.ca>
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include "gbitlock.h"
26 : :
27 : : #include <glib/gmacros.h>
28 : : #include <glib/gmessages.h>
29 : : #include <glib/gatomic.h>
30 : : #include <glib/gslist.h>
31 : : #include <glib/gthread.h>
32 : : #include <glib/gslice.h>
33 : :
34 : : #include "gtestutils.h"
35 : : #include "gthreadprivate.h"
36 : :
37 : : #ifdef __SANITIZE_THREAD__
38 : : #include <sanitizer/tsan_interface.h>
39 : : #endif
40 : :
41 : : #ifdef G_BIT_LOCK_FORCE_FUTEX_EMULATION
42 : : #undef HAVE_FUTEX
43 : : #undef HAVE_FUTEX_TIME64
44 : : #endif
45 : :
46 : : #ifndef HAVE_FUTEX
47 : : static GMutex g_futex_mutex;
48 : : static GSList *g_futex_address_list = NULL;
49 : : #endif
50 : :
51 : : #if defined(HAVE_FUTEX) || defined(HAVE_FUTEX_TIME64)
52 : : /*
53 : : * We have headers for futex(2) on the build machine. This does not
54 : : * imply that every system that ever runs the resulting glib will have
55 : : * kernel support for futex, but you'd have to have a pretty old
56 : : * kernel in order for that not to be the case.
57 : : *
58 : : * If anyone actually gets bit by this, please file a bug. :)
59 : : */
60 : :
61 : : /* < private >
62 : : * g_futex_wait:
63 : : * @address: (type gpointer): a pointer to an integer
64 : : * @value: the value that should be at @address
65 : : *
66 : : * Atomically checks that the value stored at @address is equal to
67 : : * @value and then blocks. If the value stored at @address is not
68 : : * equal to @value then this function returns immediately.
69 : : *
70 : : * To unblock, call g_futex_wake() on @address.
71 : : *
72 : : * This call may spuriously unblock (for example, in response to the
73 : : * process receiving a signal) but this is not guaranteed. Unlike the
74 : : * Linux system call of a similar name, there is no guarantee that a
75 : : * waiting process will unblock due to a g_futex_wake() call in a
76 : : * separate process.
77 : : */
78 : : static void
79 : 6820685 : g_futex_wait (const gint *address,
80 : : gint value)
81 : : {
82 : 6820685 : g_futex_simple (address, (gsize) FUTEX_WAIT_PRIVATE, (gsize) value, NULL);
83 : 6820685 : }
84 : :
85 : : /* < private >
86 : : * g_futex_wake:
87 : : * @address: (type gpointer): a pointer to an integer
88 : : *
89 : : * Nominally, wakes one thread that is blocked in g_futex_wait() on
90 : : * @address (if any thread is currently waiting).
91 : : *
92 : : * As mentioned in the documentation for g_futex_wait(), spurious
93 : : * wakeups may occur. As such, this call may result in more than one
94 : : * thread being woken up.
95 : : */
96 : : static void
97 : 13927866 : g_futex_wake (const gint *address)
98 : : {
99 : 13927866 : g_futex_simple (address, (gsize) FUTEX_WAKE_PRIVATE, (gsize) 1, NULL);
100 : 13927866 : }
101 : :
102 : : #else
103 : :
104 : : /* emulate futex(2) */
105 : : typedef struct
106 : : {
107 : : const gint *address;
108 : : gint ref_count;
109 : : GCond wait_queue;
110 : : } WaitAddress;
111 : :
112 : : static WaitAddress *
113 : 25063382 : g_futex_find_address (const gint *address)
114 : : {
115 : : GSList *node;
116 : :
117 : 138678575 : for (node = g_futex_address_list; node; node = node->next)
118 : : {
119 : 126789748 : WaitAddress *waiter = node->data;
120 : :
121 : 126789748 : if (waiter->address == address)
122 : 13174555 : return waiter;
123 : 103978741 : }
124 : :
125 : 11888827 : return NULL;
126 : 23536987 : }
127 : :
128 : : static void
129 : 10083516 : g_futex_wait (const gint *address,
130 : : gint value)
131 : : {
132 : 10083516 : g_mutex_lock (&g_futex_mutex);
133 : 10083516 : if G_LIKELY (g_atomic_int_get (address) == value)
134 : : {
135 : : WaitAddress *waiter;
136 : :
137 : 4808218 : if ((waiter = g_futex_find_address (address)) == NULL)
138 : : {
139 : 2390578 : waiter = g_slice_new (WaitAddress);
140 : 2390578 : waiter->address = address;
141 : 2390578 : g_cond_init (&waiter->wait_queue);
142 : 2390578 : waiter->ref_count = 0;
143 : 2390578 : g_futex_address_list =
144 : 2390578 : g_slist_prepend (g_futex_address_list, waiter);
145 : 2333772 : }
146 : :
147 : 4808218 : waiter->ref_count++;
148 : 4808218 : g_cond_wait (&waiter->wait_queue, &g_futex_mutex);
149 : :
150 : 4808218 : if (!--waiter->ref_count)
151 : : {
152 : 2390578 : g_futex_address_list =
153 : 2390578 : g_slist_remove (g_futex_address_list, waiter);
154 : 2390578 : g_cond_clear (&waiter->wait_queue);
155 : 2390578 : g_slice_free (WaitAddress, waiter);
156 : 2333772 : }
157 : 4690713 : }
158 : 10084334 : g_mutex_unlock (&g_futex_mutex);
159 : 10084334 : }
160 : :
161 : : static void
162 : 20121028 : g_futex_wake (const gint *address)
163 : : {
164 : : WaitAddress *waiter;
165 : :
166 : : /* need to lock here for two reasons:
167 : : * 1) need to acquire/release lock to ensure waiter is not in
168 : : * the process of registering a wait
169 : : * 2) need to -stay- locked until the end to ensure a wake()
170 : : * in another thread doesn't cause 'waiter' to stop existing
171 : : */
172 : 20121028 : g_mutex_lock (&g_futex_mutex);
173 : 20121028 : if ((waiter = g_futex_find_address (address)))
174 : 10756915 : g_cond_signal (&waiter->wait_queue);
175 : 20121028 : g_mutex_unlock (&g_futex_mutex);
176 : 20121028 : }
177 : : #endif
178 : :
179 : : #define CONTENTION_CLASSES 11
180 : : static gint g_bit_lock_contended[CONTENTION_CLASSES]; /* (atomic) */
181 : :
182 : : G_ALWAYS_INLINE static inline guint
183 : 382199397 : bit_lock_contended_class (gconstpointer address)
184 : : {
185 : 689491673 : return ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
186 : : }
187 : :
188 : : #if (defined (i386) || defined (__amd64__))
189 : : #if G_GNUC_CHECK_VERSION(4, 5) || \
190 : : (defined (__clang_major__) && __clang_major__ >= 9)
191 : : #define USE_ASM_GOTO 1
192 : : #endif
193 : : #endif
194 : :
195 : : static const gint *g_futex_int_address (const void *address);
196 : :
197 : : G_ALWAYS_INLINE static inline void
198 : 9901220 : bit_lock_futex_wait (gconstpointer address, gboolean is_pointer_pointer, gint value)
199 : : {
200 : 16909119 : const guint CLASS = bit_lock_contended_class (address);
201 : :
202 : 16909119 : g_atomic_int_add (&g_bit_lock_contended[CLASS], +1);
203 : 16909119 : if (is_pointer_pointer)
204 : 15117803 : address = g_futex_int_address (address);
205 : 16909119 : g_futex_wait (address, value);
206 : 16909119 : g_atomic_int_add (&g_bit_lock_contended[CLASS], -1);
207 : 16909119 : }
208 : :
209 : : G_ALWAYS_INLINE static inline void
210 : 373794560 : bit_lock_futex_maybe_wake (gconstpointer address, gboolean is_pointer_pointer)
211 : : {
212 : 681086836 : const guint CLASS = bit_lock_contended_class (address);
213 : :
214 : : /* Warning: unlocking may allow another thread to proceed and destroy the
215 : : * memory that @address points to. We thus must not dereference it anymore.
216 : : */
217 : :
218 : 681086836 : if (g_atomic_int_get (&g_bit_lock_contended[CLASS]))
219 : : {
220 : 34079690 : if (is_pointer_pointer)
221 : 30553160 : address = g_futex_int_address (address);
222 : 34079690 : g_futex_wake (address);
223 : 18742934 : }
224 : 681086836 : }
225 : :
226 : : /**
227 : : * g_bit_lock_and_get:
228 : : * @address: (type gpointer): a pointer to an integer
229 : : * @lock_bit: a bit value between 0 and 31
230 : : * @out_val: (out) (optional): return location for the new value of the integer
231 : : *
232 : : * Sets the indicated @lock_bit in @address and atomically returns the new value.
233 : : *
234 : : * This is like [func@GLib.bit_lock], except it can atomically return the new value at
235 : : * @address (right after obtaining the lock). Thus the value returned in @out_val
236 : : * always has the @lock_bit set.
237 : : *
238 : : * Since: 2.86
239 : : **/
240 : : void
241 : 6672877 : g_bit_lock_and_get (gint *address,
242 : : guint lock_bit,
243 : : gint *out_val)
244 : : {
245 : 6672877 : const guint MASK = 1u << lock_bit;
246 : : guint v;
247 : :
248 : : #ifdef G_ENABLE_DEBUG
249 : 6672877 : g_assert (lock_bit < 32u);
250 : : #endif
251 : :
252 : : #ifdef USE_ASM_GOTO
253 : 6672877 : if (G_LIKELY (!out_val))
254 : : {
255 : 4882940 : while (TRUE)
256 : : {
257 : 8542277 : __asm__ volatile goto("lock bts %1, (%0)\n"
258 : : "jc %l[contended]"
259 : : : /* no output */
260 : 4882940 : : "r"(address), "r"(lock_bit)
261 : : : "cc", "memory"
262 : : : contended);
263 : : #ifdef __SANITIZE_THREAD__
264 : : __tsan_acquire (address);
265 : : #endif
266 : 6666467 : return;
267 : :
268 : 344862 : contended:
269 : : {
270 : : guint v;
271 : :
272 : 1875810 : v = (guint) g_atomic_int_get (address);
273 : 1875810 : if (v & MASK)
274 : 1866731 : bit_lock_futex_wait (address, FALSE, (gint) v);
275 : : }
276 : : }
277 : : }
278 : : #endif
279 : :
280 : 3062 : retry:
281 : 10805 : v = g_atomic_int_or ((guint *) address, MASK);
282 : 10805 : if (v & MASK)
283 : : {
284 : 3327 : bit_lock_futex_wait (address, FALSE, (gint) v);
285 : 3327 : goto retry;
286 : : }
287 : :
288 : 7478 : if (out_val)
289 : 7479 : *out_val = (gint) (v | MASK);
290 : 3356410 : }
291 : :
292 : : /**
293 : : * g_bit_lock:
294 : : * @address: (type gpointer): a pointer to an integer
295 : : * @lock_bit: a bit value between 0 and 31
296 : : *
297 : : * Sets the indicated @lock_bit in @address. If the bit is already
298 : : * set, this call will block until g_bit_unlock() unsets the
299 : : * corresponding bit.
300 : : *
301 : : * Attempting to lock on two different bits within the same integer is
302 : : * not supported and will very probably cause deadlocks.
303 : : *
304 : : * The value of the bit that is set is (1u << @bit). If @bit is not
305 : : * between 0 and 31 then the result is undefined.
306 : : *
307 : : * This function accesses @address atomically. All other accesses to
308 : : * @address must be atomic in order for this function to work
309 : : * reliably. While @address has a `volatile` qualifier, this is a historical
310 : : * artifact and the argument passed to it should not be `volatile`.
311 : : *
312 : : * Since: 2.24
313 : : **/
314 : : void
315 : 6665459 : g_bit_lock (volatile gint *address,
316 : : gint lock_bit)
317 : : {
318 : 6665459 : g_bit_lock_and_get ((gint *) address, (guint) lock_bit, NULL);
319 : 6665459 : }
320 : :
321 : : /**
322 : : * g_bit_trylock:
323 : : * @address: (type gpointer): a pointer to an integer
324 : : * @lock_bit: a bit value between 0 and 31
325 : : *
326 : : * Sets the indicated @lock_bit in @address, returning %TRUE if
327 : : * successful. If the bit is already set, returns %FALSE immediately.
328 : : *
329 : : * Attempting to lock on two different bits within the same integer is
330 : : * not supported.
331 : : *
332 : : * The value of the bit that is set is (1u << @bit). If @bit is not
333 : : * between 0 and 31 then the result is undefined.
334 : : *
335 : : * This function accesses @address atomically. All other accesses to
336 : : * @address must be atomic in order for this function to work
337 : : * reliably. While @address has a `volatile` qualifier, this is a historical
338 : : * artifact and the argument passed to it should not be `volatile`.
339 : : *
340 : : * Returns: %TRUE if the lock was acquired
341 : : *
342 : : * Since: 2.24
343 : : **/
344 : : gboolean
345 : 3989620 : g_bit_trylock (volatile gint *address,
346 : : gint lock_bit)
347 : : {
348 : : #ifdef USE_ASM_GOTO
349 : : gboolean result;
350 : :
351 : 3989620 : __asm__ volatile ("lock bts %2, (%1)\n"
352 : : "setnc %%al\n"
353 : : "movzx %%al, %0"
354 : : : "=r" (result)
355 : 1989620 : : "r" (address), "r" (lock_bit)
356 : : : "cc", "memory");
357 : : #ifdef __SANITIZE_THREAD__
358 : : int *address_nonvolatile = (int *) address;
359 : : __tsan_acquire (address_nonvolatile);
360 : : #endif
361 : 3989620 : return result;
362 : : #else
363 : : gint *address_nonvolatile = (gint *) address;
364 : : guint mask = 1u << lock_bit;
365 : : guint v;
366 : :
367 : : v = g_atomic_int_or (address_nonvolatile, mask);
368 : :
369 : : return ~v & mask;
370 : : #endif
371 : : }
372 : :
373 : : /**
374 : : * g_bit_unlock:
375 : : * @address: (type gpointer): a pointer to an integer
376 : : * @lock_bit: a bit value between 0 and 31
377 : : *
378 : : * Clears the indicated @lock_bit in @address. If another thread is
379 : : * currently blocked in g_bit_lock() on this same bit then it will be
380 : : * woken up.
381 : : *
382 : : * This function accesses @address atomically. All other accesses to
383 : : * @address must be atomic in order for this function to work
384 : : * reliably. While @address has a `volatile` qualifier, this is a historical
385 : : * artifact and the argument passed to it should not be `volatile`.
386 : : *
387 : : * Since: 2.24
388 : : **/
389 : : void
390 : 8860302 : g_bit_unlock (volatile gint *address,
391 : : gint lock_bit)
392 : : {
393 : 8860302 : gint *address_nonvolatile = (gint *) address;
394 : :
395 : : #ifdef USE_ASM_GOTO
396 : : #ifdef __SANITIZE_THREAD__
397 : : __tsan_release (address_nonvolatile);
398 : : #endif
399 : 8860302 : __asm__ volatile ("lock btr %1, (%0)"
400 : : : /* no output */
401 : 3842236 : : "r" (address), "r" (lock_bit)
402 : : : "cc", "memory");
403 : : #else
404 : : guint mask = 1u << lock_bit;
405 : :
406 : : g_atomic_int_and (address_nonvolatile, ~mask);
407 : : #endif
408 : :
409 : : /* Warning: unlocking may allow another thread to proceed and destroy the
410 : : * memory that @address points to. We thus must not dereference it anymore.
411 : : */
412 : 3842236 : bit_lock_futex_maybe_wake (address_nonvolatile, FALSE);
413 : 8860302 : }
414 : :
415 : : /**
416 : : * g_bit_unlock_and_set:
417 : : * @address: (type gpointer): a pointer to an integer
418 : : * @lock_bit: a bit value between 0 and 31
419 : : * @new_val: the new value to set
420 : : * @preserve_mask: mask for bits from @address to preserve
421 : : *
422 : : * This is like [func@GLib.bit_unlock] but also atomically sets @address to
423 : : * @val.
424 : : *
425 : : * If @preserve_mask is not zero, then the @preserve_mask bits will be
426 : : * preserved in @address and are not set to @val.
427 : : *
428 : : * Note that the @lock_bit bit will always be unset regardless of
429 : : * @val, @preserve_mask and the currently set value in @address.
430 : : *
431 : : * Since: 2.86
432 : : **/
433 : : void
434 : 12006 : g_bit_unlock_and_set (gint *address,
435 : : guint lock_bit,
436 : : gint val,
437 : : gint preserve_mask)
438 : :
439 : : {
440 : 12006 : const guint MASK = 1u << lock_bit;
441 : :
442 : : #ifdef G_ENABLE_DEBUG
443 : 12006 : g_assert (lock_bit < 32u);
444 : : #endif
445 : :
446 : 12006 : if (G_UNLIKELY (preserve_mask != 0))
447 : : {
448 : : guint old_val;
449 : : guint new_val;
450 : :
451 : 9061 : old_val = (guint) g_atomic_int_get (address);
452 : :
453 : 3770 : again:
454 : 9067 : new_val = ((old_val & ((guint) preserve_mask)) | (((guint) val) & ~((guint) preserve_mask))) & ~MASK;
455 : 9067 : if (!g_atomic_int_compare_and_exchange_full (address, (gint) old_val, (gint) new_val, (gint *) &old_val))
456 : 6 : goto again;
457 : 5297 : }
458 : : else
459 : : {
460 : 2945 : g_atomic_int_set (address, (gint) (((guint) val) & ~MASK));
461 : : }
462 : :
463 : : /* Warning: unlocking may allow another thread to proceed and destroy the
464 : : * memory that @address points to. We thus must not dereference it anymore.
465 : : */
466 : 7001 : bit_lock_futex_maybe_wake (address, FALSE);
467 : 12006 : }
468 : :
469 : : /* We emulate pointer-sized futex(2) because the kernel API only
470 : : * supports integers.
471 : : *
472 : : * We assume that the 'interesting' part is always the lower order bits.
473 : : * This assumption holds because pointer bitlocks are restricted to
474 : : * using the low order bits of the pointer as the lock.
475 : : *
476 : : * On 32 bits, there is nothing to do since the pointer size is equal to
477 : : * the integer size. On little endian the lower-order bits don't move,
478 : : * so do nothing. Only on 64bit big endian do we need to do a bit of
479 : : * pointer arithmetic: the low order bits are shifted by 4 bytes. We
480 : : * have a helper function that always does the right thing here.
481 : : *
482 : : * Since we always consider the low-order bits of the integer value, a
483 : : * simple cast from (gsize) to (guint) always takes care of that.
484 : : *
485 : : * After that, pointer-sized futex becomes as simple as:
486 : : *
487 : : * g_futex_wait (g_futex_int_address (address), (guint) value);
488 : : *
489 : : * and
490 : : *
491 : : * g_futex_wake (g_futex_int_address (int_address));
492 : : */
493 : : G_ALWAYS_INLINE static inline const gint *
494 : 23848165 : g_futex_int_address (const void *address)
495 : : {
496 : 44258468 : const gint *int_address = address;
497 : :
498 : : /* this implementation makes these (reasonable) assumptions: */
499 : : G_STATIC_ASSERT (G_BYTE_ORDER == G_LITTLE_ENDIAN ||
500 : : (G_BYTE_ORDER == G_BIG_ENDIAN &&
501 : : sizeof (int) == 4 &&
502 : : (sizeof (gpointer) == 4 || sizeof (gpointer) == 8)));
503 : :
504 : : #if G_BYTE_ORDER == G_BIG_ENDIAN && GLIB_SIZEOF_VOID_P == 8
505 : : int_address++;
506 : : #endif
507 : :
508 : 44258468 : return int_address;
509 : : }
510 : :
511 : : G_ALWAYS_INLINE static inline gpointer
512 : 39493357 : pointer_bit_lock_mask_ptr (gpointer ptr, guint lock_bit, gboolean set, guintptr preserve_mask, gpointer preserve_ptr)
513 : : {
514 : : guintptr x_ptr;
515 : : guintptr x_preserve_ptr;
516 : : guintptr lock_mask;
517 : :
518 : 81793515 : x_ptr = (guintptr) ptr;
519 : :
520 : 60712574 : if (preserve_mask != 0)
521 : : {
522 : 40826560 : x_preserve_ptr = (guintptr) preserve_ptr;
523 : 40826560 : x_ptr = (x_preserve_ptr & preserve_mask) | (x_ptr & ~preserve_mask);
524 : 19745611 : }
525 : :
526 : 81793515 : if (lock_bit == G_MAXUINT)
527 : 0 : return (gpointer) x_ptr;
528 : :
529 : 81793515 : lock_mask = (guintptr) (1u << lock_bit);
530 : 81793515 : if (set)
531 : 4 : return (gpointer) (x_ptr | lock_mask);
532 : : else
533 : 81793511 : return (gpointer) (x_ptr & ~lock_mask);
534 : 39493357 : }
535 : :
536 : : /**
537 : : * g_pointer_bit_lock_and_get:
538 : : * @address: (not nullable): a pointer to a #gpointer-sized value
539 : : * @lock_bit: a bit value between 0 and 31
540 : : * @out_ptr: (out) (optional): returns the set pointer atomically.
541 : : * This is the value after setting the lock, it thus always has the
542 : : * lock bit set, while previously @address had the lockbit unset.
543 : : * You may also use g_pointer_bit_lock_mask_ptr() to clear the lock bit.
544 : : *
545 : : * This is equivalent to g_bit_lock, but working on pointers (or other
546 : : * pointer-sized values).
547 : : *
548 : : * For portability reasons, you may only lock on the bottom 32 bits of
549 : : * the pointer.
550 : : *
551 : : * Since: 2.80
552 : : **/
553 : : void
554 : 669614175 : (g_pointer_bit_lock_and_get) (gpointer address,
555 : : guint lock_bit,
556 : : guintptr *out_ptr)
557 : : {
558 : : guintptr mask;
559 : : guintptr v;
560 : :
561 : 669614175 : g_return_if_fail (lock_bit < 32);
562 : :
563 : 669614175 : mask = 1u << lock_bit;
564 : :
565 : : #ifdef USE_ASM_GOTO
566 : 669614175 : if (G_LIKELY (!out_ptr))
567 : : {
568 : 302394559 : while (TRUE)
569 : : {
570 : 533926391 : __asm__ volatile goto ("lock bts %1, (%0)\n"
571 : : "jc %l[contended]"
572 : : : /* no output */
573 : 533926391 : : "r"(address), "r"((gsize) lock_bit)
574 : : : "cc", "memory"
575 : : : contended);
576 : : #ifdef __SANITIZE_THREAD__
577 : : __tsan_acquire (address);
578 : : #endif
579 : 530599789 : return;
580 : :
581 : 1124466 : contended:
582 : 3326602 : v = (guintptr) g_atomic_pointer_get ((gpointer *) address);
583 : 3326602 : if (v & mask)
584 : 3017504 : bit_lock_futex_wait (address, TRUE, (gint) v);
585 : : }
586 : : }
587 : : #endif
588 : :
589 : 70171321 : retry:
590 : 151069428 : v = g_atomic_pointer_or ((gpointer *) address, mask);
591 : 151069428 : if (v & mask)
592 : : {
593 : 12055042 : bit_lock_futex_wait (address, TRUE, (gint) v);
594 : 12055042 : goto retry;
595 : : }
596 : :
597 : 139014386 : if (out_ptr)
598 : 140111721 : *out_ptr = (v | mask);
599 : 371230158 : }
600 : :
601 : : /**
602 : : * g_pointer_bit_lock:
603 : : * @address: (not nullable): a pointer to a #gpointer-sized value
604 : : * @lock_bit: a bit value between 0 and 31
605 : : *
606 : : * This is equivalent to g_bit_lock, but working on pointers (or other
607 : : * pointer-sized values).
608 : : *
609 : : * For portability reasons, you may only lock on the bottom 32 bits of
610 : : * the pointer.
611 : : *
612 : : * While @address has a `volatile` qualifier, this is a historical
613 : : * artifact and the argument passed to it should not be `volatile`.
614 : : *
615 : : * Since: 2.30
616 : : **/
617 : : void
618 : 529496195 : (g_pointer_bit_lock) (volatile void *address,
619 : : gint lock_bit)
620 : : {
621 : 529496195 : g_pointer_bit_lock_and_get ((gpointer *) address, (guint) lock_bit, NULL);
622 : 529496195 : }
623 : :
624 : : /**
625 : : * g_pointer_bit_trylock:
626 : : * @address: (not nullable): a pointer to a #gpointer-sized value
627 : : * @lock_bit: a bit value between 0 and 31
628 : : *
629 : : * This is equivalent to g_bit_trylock(), but working on pointers (or
630 : : * other pointer-sized values).
631 : : *
632 : : * For portability reasons, you may only lock on the bottom 32 bits of
633 : : * the pointer.
634 : : *
635 : : * While @address has a `volatile` qualifier, this is a historical
636 : : * artifact and the argument passed to it should not be `volatile`.
637 : : *
638 : : * Returns: %TRUE if the lock was acquired
639 : : *
640 : : * Since: 2.30
641 : : **/
642 : : gboolean
643 : 3991224 : (g_pointer_bit_trylock) (volatile void *address,
644 : : gint lock_bit)
645 : : {
646 : 3991224 : g_return_val_if_fail (lock_bit < 32, FALSE);
647 : :
648 : : {
649 : : #ifdef USE_ASM_GOTO
650 : : gboolean result;
651 : :
652 : 3991224 : __asm__ volatile ("lock bts %2, (%1)\n"
653 : : "setnc %%al\n"
654 : : "movzx %%al, %0"
655 : : : "=r" (result)
656 : 3991224 : : "r" (address), "r" ((gsize) lock_bit)
657 : : : "cc", "memory");
658 : : #ifdef __SANITIZE_THREAD__
659 : : void *address_nonvolatile = (void *) address;
660 : : __tsan_acquire (address_nonvolatile);
661 : : #endif
662 : 3991224 : return result;
663 : : #else
664 : : void *address_nonvolatile = (void *) address;
665 : : gpointer *pointer_address = address_nonvolatile;
666 : : gsize mask = 1u << lock_bit;
667 : : guintptr v;
668 : :
669 : : g_return_val_if_fail (lock_bit < 32, FALSE);
670 : :
671 : : v = g_atomic_pointer_or (pointer_address, mask);
672 : :
673 : : return (~(gsize) v & mask) != 0;
674 : : #endif
675 : : }
676 : 1991224 : }
677 : :
678 : : /**
679 : : * g_pointer_bit_unlock:
680 : : * @address: (not nullable): a pointer to a #gpointer-sized value
681 : : * @lock_bit: a bit value between 0 and 31
682 : : *
683 : : * This is equivalent to g_bit_unlock, but working on pointers (or other
684 : : * pointer-sized values).
685 : : *
686 : : * For portability reasons, you may only lock on the bottom 32 bits of
687 : : * the pointer.
688 : : *
689 : : * While @address has a `volatile` qualifier, this is a historical
690 : : * artifact and the argument passed to it should not be `volatile`.
691 : : *
692 : : * Since: 2.30
693 : : **/
694 : : void
695 : 631787341 : (g_pointer_bit_unlock) (volatile void *address,
696 : : gint lock_bit)
697 : : {
698 : 631787341 : void *address_nonvolatile = (void *) address;
699 : :
700 : 631787341 : g_return_if_fail (lock_bit < 32);
701 : :
702 : : {
703 : : #ifdef USE_ASM_GOTO
704 : : #ifdef __SANITIZE_THREAD__
705 : : __tsan_release (address_nonvolatile);
706 : : #endif
707 : 631787341 : __asm__ volatile ("lock btr %1, (%0)"
708 : : : /* no output */
709 : 631787341 : : "r" (address), "r" ((gsize) lock_bit)
710 : : : "cc", "memory");
711 : : #else
712 : : gpointer *pointer_address = address_nonvolatile;
713 : : gsize mask = 1u << lock_bit;
714 : :
715 : : g_atomic_pointer_and (pointer_address, ~mask);
716 : : #endif
717 : :
718 : : /* Warning: unlocking may allow another thread to proceed and destroy the
719 : : * memory that @address points to. We thus must not dereference it anymore.
720 : : */
721 : 350613447 : bit_lock_futex_maybe_wake (address_nonvolatile, TRUE);
722 : : }
723 : 350613447 : }
724 : :
725 : : /**
726 : : * g_pointer_bit_lock_mask_ptr:
727 : : * @ptr: (nullable): the pointer to mask
728 : : * @lock_bit: the bit to set/clear. If set to `G_MAXUINT`, the
729 : : * lockbit is taken from @preserve_ptr or @ptr (depending on @preserve_mask).
730 : : * @set: whether to set (lock) the bit or unset (unlock). This
731 : : * has no effect, if @lock_bit is set to `G_MAXUINT`.
732 : : * @preserve_mask: if non-zero, a bit-mask for @preserve_ptr. The
733 : : * @preserve_mask bits from @preserve_ptr are set in the result.
734 : : * Note that the @lock_bit bit will be always set according to @set,
735 : : * regardless of @preserve_mask and @preserve_ptr (unless @lock_bit is
736 : : * `G_MAXUINT`).
737 : : * @preserve_ptr: (nullable): if @preserve_mask is non-zero, the bits
738 : : * from this pointer are set in the result.
739 : : *
740 : : * This mangles @ptr as g_pointer_bit_lock() and g_pointer_bit_unlock()
741 : : * do.
742 : : *
743 : : * Returns: the mangled pointer.
744 : : *
745 : : * Since: 2.80
746 : : **/
747 : : gpointer
748 : 141429 : g_pointer_bit_lock_mask_ptr (gpointer ptr, guint lock_bit, gboolean set, guintptr preserve_mask, gpointer preserve_ptr)
749 : : {
750 : 141429 : g_return_val_if_fail (lock_bit < 32u || lock_bit == G_MAXUINT, ptr);
751 : :
752 : 141429 : return pointer_bit_lock_mask_ptr (ptr, lock_bit, set, preserve_mask, preserve_ptr);
753 : 31893 : }
754 : :
755 : : /**
756 : : * g_pointer_bit_unlock_and_set:
757 : : * @address: (not nullable): a pointer to a #gpointer-sized value
758 : : * @lock_bit: a bit value between 0 and 31
759 : : * @ptr: the new pointer value to set
760 : : * @preserve_mask: if non-zero, those bits of the current pointer in @address
761 : : * are preserved.
762 : : * Note that the @lock_bit bit will be always unset regardless of
763 : : * @ptr, @preserve_mask and the currently set value in @address.
764 : : *
765 : : * This is equivalent to g_pointer_bit_unlock() and atomically setting
766 : : * the pointer value.
767 : : *
768 : : * Note that the lock bit will be cleared from the pointer. If the unlocked
769 : : * pointer that was set is not identical to @ptr, an assertion fails. In other
770 : : * words, @ptr must have @lock_bit unset. This also means, you usually can
771 : : * only use this on the lowest bits.
772 : : *
773 : : * Since: 2.80
774 : : **/
775 : 40849404 : void (g_pointer_bit_unlock_and_set) (void *address,
776 : : guint lock_bit,
777 : : gpointer ptr,
778 : : guintptr preserve_mask)
779 : : {
780 : 40849404 : gpointer *pointer_address = address;
781 : : gpointer ptr2;
782 : :
783 : 40849404 : g_return_if_fail (lock_bit < 32u);
784 : :
785 : 40849404 : if (preserve_mask != 0)
786 : : {
787 : 40829980 : gpointer old_ptr = g_atomic_pointer_get ((gpointer *) address);
788 : :
789 : 21080941 : again:
790 : 40829980 : ptr2 = pointer_bit_lock_mask_ptr (ptr, lock_bit, FALSE, preserve_mask, old_ptr);
791 : 40829980 : if (!g_atomic_pointer_compare_and_exchange_full (pointer_address, old_ptr, ptr2, &old_ptr))
792 : 0 : goto again;
793 : 19749039 : }
794 : : else
795 : : {
796 : 19424 : ptr2 = pointer_bit_lock_mask_ptr (ptr, lock_bit, FALSE, 0, NULL);
797 : 19424 : g_atomic_pointer_set (pointer_address, ptr2);
798 : : }
799 : :
800 : 19754093 : bit_lock_futex_maybe_wake (address, TRUE);
801 : :
802 : : /* It makes no sense, if unlocking mangles the pointer. Assert against
803 : : * that.
804 : : *
805 : : * Note that based on @preserve_mask, the pointer also gets mangled, which
806 : : * can make sense for the caller. We don't assert for that. */
807 : 40849404 : g_return_if_fail (ptr == pointer_bit_lock_mask_ptr (ptr, lock_bit, FALSE, 0, NULL));
808 : 19754093 : }
|