Coverage Report

Created: 2026-10-28 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/glib/glib/gbitlock.c
Line
Count
Source
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
g_futex_wait (const gint *address,
80
              gint        value)
81
0
{
82
0
  g_futex_simple (address, (gsize) FUTEX_WAIT_PRIVATE, (gsize) value, NULL);
83
0
}
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
g_futex_wake (const gint *address)
98
0
{
99
0
  g_futex_simple (address, (gsize) FUTEX_WAKE_PRIVATE, (gsize) 1, NULL);
100
0
}
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
g_futex_find_address (const gint *address)
114
{
115
  GSList *node;
116
117
  for (node = g_futex_address_list; node; node = node->next)
118
    {
119
      WaitAddress *waiter = node->data;
120
121
      if (waiter->address == address)
122
        return waiter;
123
    }
124
125
  return NULL;
126
}
127
128
static void
129
g_futex_wait (const gint *address,
130
              gint        value)
131
{
132
  g_mutex_lock (&g_futex_mutex);
133
  if G_LIKELY (g_atomic_int_get (address) == value)
134
    {
135
      WaitAddress *waiter;
136
137
      if ((waiter = g_futex_find_address (address)) == NULL)
138
        {
139
          waiter = g_slice_new (WaitAddress);
140
          waiter->address = address;
141
          g_cond_init (&waiter->wait_queue);
142
          waiter->ref_count = 0;
143
          g_futex_address_list =
144
            g_slist_prepend (g_futex_address_list, waiter);
145
        }
146
147
      waiter->ref_count++;
148
      g_cond_wait (&waiter->wait_queue, &g_futex_mutex);
149
150
      if (!--waiter->ref_count)
151
        {
152
          g_futex_address_list =
153
            g_slist_remove (g_futex_address_list, waiter);
154
          g_cond_clear (&waiter->wait_queue);
155
          g_slice_free (WaitAddress, waiter);
156
        }
157
    }
158
  g_mutex_unlock (&g_futex_mutex);
159
}
160
161
static void
162
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
  g_mutex_lock (&g_futex_mutex);
173
  if ((waiter = g_futex_find_address (address)))
174
    g_cond_signal (&waiter->wait_queue);
175
  g_mutex_unlock (&g_futex_mutex);
176
}
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
bit_lock_contended_class (gconstpointer address)
184
15.3k
{
185
15.3k
  return ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
186
15.3k
}
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
bit_lock_futex_wait (gconstpointer address, gboolean is_pointer_pointer, gint value)
199
0
{
200
0
  const guint CLASS = bit_lock_contended_class (address);
201
202
0
  g_atomic_int_add (&g_bit_lock_contended[CLASS], +1);
203
0
  if (is_pointer_pointer)
204
0
    address = g_futex_int_address (address);
205
0
  g_futex_wait (address, value);
206
0
  g_atomic_int_add (&g_bit_lock_contended[CLASS], -1);
207
0
}
208
209
G_ALWAYS_INLINE static inline void
210
bit_lock_futex_maybe_wake (gconstpointer address, gboolean is_pointer_pointer)
211
15.3k
{
212
15.3k
  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
15.3k
  if (g_atomic_int_get (&g_bit_lock_contended[CLASS]))
219
0
    {
220
0
      if (is_pointer_pointer)
221
0
        address = g_futex_int_address (address);
222
0
      g_futex_wake (address);
223
0
    }
224
15.3k
}
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
g_bit_lock_and_get (gint *address,
242
                    guint lock_bit,
243
                    gint *out_val)
244
0
{
245
0
  const guint MASK = 1u << lock_bit;
246
0
  guint v;
247
248
0
#ifdef G_ENABLE_DEBUG
249
0
  g_assert (lock_bit < 32u);
250
0
#endif
251
252
0
#ifdef USE_ASM_GOTO
253
0
  if (G_LIKELY (!out_val))
254
0
    {
255
0
      while (TRUE)
256
0
        {
257
0
          __asm__ volatile goto("lock bts %1, (%0)\n"
258
0
                                "jc %l[contended]"
259
0
                                : /* no output */
260
0
                                : "r"(address), "r"(lock_bit)
261
0
                                : "cc", "memory"
262
0
                                : contended);
263
#ifdef __SANITIZE_THREAD__
264
          __tsan_acquire (address);
265
#endif
266
0
          return;
267
268
0
        contended:
269
0
          {
270
0
            guint v;
271
272
0
            v = (guint) g_atomic_int_get (address);
273
0
            if (v & MASK)
274
0
              bit_lock_futex_wait (address, FALSE, (gint) v);
275
0
          }
276
0
        }
277
0
    }
278
0
#endif
279
280
0
retry:
281
0
  v = g_atomic_int_or ((guint *) address, MASK);
282
0
  if (v & MASK)
283
0
    {
284
0
      bit_lock_futex_wait (address, FALSE, (gint) v);
285
0
      goto retry;
286
0
    }
287
288
0
  if (out_val)
289
0
    *out_val = (gint) (v | MASK);
290
0
}
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
g_bit_lock (volatile gint *address,
316
            gint           lock_bit)
317
0
{
318
0
  g_bit_lock_and_get ((gint *) address, (guint) lock_bit, NULL);
319
0
}
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
g_bit_trylock (volatile gint *address,
346
               gint           lock_bit)
347
0
{
348
0
#ifdef USE_ASM_GOTO
349
0
  gboolean result;
350
351
0
  __asm__ volatile ("lock bts %2, (%1)\n"
352
0
                    "setnc %%al\n"
353
0
                    "movzx %%al, %0"
354
0
                    : "=r" (result)
355
0
                    : "r" (address), "r" (lock_bit)
356
0
                    : "cc", "memory");
357
#ifdef __SANITIZE_THREAD__
358
  int *address_nonvolatile = (int *) address;
359
  __tsan_acquire (address_nonvolatile);
360
#endif
361
0
  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
0
}
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
g_bit_unlock (volatile gint *address,
391
              gint           lock_bit)
392
0
{
393
0
  gint *address_nonvolatile = (gint *) address;
394
395
0
#ifdef USE_ASM_GOTO
396
#ifdef __SANITIZE_THREAD__
397
  __tsan_release (address_nonvolatile);
398
#endif
399
0
  __asm__ volatile ("lock btr %1, (%0)"
400
0
                    : /* no output */
401
0
                    : "r" (address), "r" (lock_bit)
402
0
                    : "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
0
  bit_lock_futex_maybe_wake (address_nonvolatile, FALSE);
413
0
}
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
g_bit_unlock_and_set (gint *address,
435
                      guint lock_bit,
436
                      gint val,
437
                      gint preserve_mask)
438
439
0
{
440
0
  const guint MASK = 1u << lock_bit;
441
442
0
#ifdef G_ENABLE_DEBUG
443
0
  g_assert (lock_bit < 32u);
444
0
#endif
445
446
0
  if (G_UNLIKELY (preserve_mask != 0))
447
0
    {
448
0
      guint old_val;
449
0
      guint new_val;
450
451
0
      old_val = (guint) g_atomic_int_get (address);
452
453
0
    again:
454
0
      new_val = ((old_val & ((guint) preserve_mask)) | (((guint) val) & ~((guint) preserve_mask))) & ~MASK;
455
0
      if (!g_atomic_int_compare_and_exchange_full (address, (gint) old_val, (gint) new_val, (gint *) &old_val))
456
0
        goto again;
457
0
    }
458
0
  else
459
0
    {
460
0
      g_atomic_int_set (address, (gint) (((guint) val) & ~MASK));
461
0
    }
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
0
  bit_lock_futex_maybe_wake (address, FALSE);
467
0
}
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
g_futex_int_address (const void *address)
495
0
{
496
0
  const gint *int_address = address;
497
498
  /* this implementation makes these (reasonable) assumptions: */
499
0
  G_STATIC_ASSERT (G_BYTE_ORDER == G_LITTLE_ENDIAN ||
500
0
      (G_BYTE_ORDER == G_BIG_ENDIAN &&
501
0
       sizeof (int) == 4 &&
502
0
       (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
0
  return int_address;
509
0
}
510
511
G_ALWAYS_INLINE static inline gpointer
512
pointer_bit_lock_mask_ptr (gpointer ptr, guint lock_bit, gboolean set, guintptr preserve_mask, gpointer preserve_ptr)
513
2.41k
{
514
2.41k
  guintptr x_ptr;
515
2.41k
  guintptr x_preserve_ptr;
516
2.41k
  guintptr lock_mask;
517
518
2.41k
  x_ptr = (guintptr) ptr;
519
520
2.41k
  if (preserve_mask != 0)
521
1.20k
    {
522
1.20k
      x_preserve_ptr = (guintptr) preserve_ptr;
523
1.20k
      x_ptr = (x_preserve_ptr & preserve_mask) | (x_ptr & ~preserve_mask);
524
1.20k
    }
525
526
2.41k
  if (lock_bit == G_MAXUINT)
527
0
    return (gpointer) x_ptr;
528
529
2.41k
  lock_mask = (guintptr) (1u << lock_bit);
530
2.41k
  if (set)
531
0
    return (gpointer) (x_ptr | lock_mask);
532
2.41k
  else
533
2.41k
    return (gpointer) (x_ptr & ~lock_mask);
534
2.41k
}
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
(g_pointer_bit_lock_and_get) (gpointer address,
555
                              guint lock_bit,
556
                              guintptr *out_ptr)
557
15.3k
{
558
15.3k
  guintptr mask;
559
15.3k
  guintptr v;
560
561
15.3k
  g_return_if_fail (lock_bit < 32);
562
563
15.3k
  mask = 1u << lock_bit;
564
565
15.3k
#ifdef USE_ASM_GOTO
566
15.3k
  if (G_LIKELY (!out_ptr))
567
0
    {
568
0
      while (TRUE)
569
0
        {
570
0
          __asm__ volatile goto ("lock bts %1, (%0)\n"
571
0
                                 "jc %l[contended]"
572
0
                                 : /* no output */
573
0
                                 : "r"(address), "r"((gsize) lock_bit)
574
0
                                 : "cc", "memory"
575
0
                                 : contended);
576
#ifdef __SANITIZE_THREAD__
577
          __tsan_acquire (address);
578
#endif
579
0
          return;
580
581
0
        contended:
582
0
          v = (guintptr) g_atomic_pointer_get ((gpointer *) address);
583
0
          if (v & mask)
584
0
            bit_lock_futex_wait (address, TRUE, (gint) v);
585
0
        }
586
0
    }
587
15.3k
#endif
588
589
15.3k
retry:
590
15.3k
  v = g_atomic_pointer_or ((gpointer *) address, mask);
591
15.3k
  if (v & mask)
592
0
    {
593
0
      bit_lock_futex_wait (address, TRUE, (gint) v);
594
0
      goto retry;
595
0
    }
596
597
15.3k
  if (out_ptr)
598
15.3k
    *out_ptr = (v | mask);
599
15.3k
}
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
(g_pointer_bit_lock) (volatile void *address,
619
                      gint lock_bit)
620
0
{
621
0
  g_pointer_bit_lock_and_get ((gpointer *) address, (guint) lock_bit, NULL);
622
0
}
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
(g_pointer_bit_trylock) (volatile void *address,
644
                         gint           lock_bit)
645
0
{
646
0
  g_return_val_if_fail (lock_bit < 32, FALSE);
647
648
0
  {
649
0
#ifdef USE_ASM_GOTO
650
0
    gboolean result;
651
652
0
    __asm__ volatile ("lock bts %2, (%1)\n"
653
0
                      "setnc %%al\n"
654
0
                      "movzx %%al, %0"
655
0
                      : "=r" (result)
656
0
                      : "r" (address), "r" ((gsize) lock_bit)
657
0
                      : "cc", "memory");
658
#ifdef __SANITIZE_THREAD__
659
    void *address_nonvolatile = (void *) address;
660
    __tsan_acquire (address_nonvolatile);
661
#endif
662
0
    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
0
  }
676
0
}
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
(g_pointer_bit_unlock) (volatile void *address,
696
                        gint           lock_bit)
697
14.1k
{
698
14.1k
  void *address_nonvolatile = (void *) address;
699
700
14.1k
  g_return_if_fail (lock_bit < 32);
701
702
14.1k
  {
703
14.1k
#ifdef USE_ASM_GOTO
704
#ifdef __SANITIZE_THREAD__
705
    __tsan_release (address_nonvolatile);
706
#endif
707
14.1k
    __asm__ volatile ("lock btr %1, (%0)"
708
14.1k
                      : /* no output */
709
14.1k
                      : "r" (address), "r" ((gsize) lock_bit)
710
14.1k
                      : "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
14.1k
    bit_lock_futex_maybe_wake (address_nonvolatile, TRUE);
722
14.1k
  }
723
14.1k
}
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
g_pointer_bit_lock_mask_ptr (gpointer ptr, guint lock_bit, gboolean set, guintptr preserve_mask, gpointer preserve_ptr)
749
0
{
750
0
  g_return_val_if_fail (lock_bit < 32u || lock_bit == G_MAXUINT, ptr);
751
752
0
  return pointer_bit_lock_mask_ptr (ptr, lock_bit, set, preserve_mask, preserve_ptr);
753
0
}
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
void (g_pointer_bit_unlock_and_set) (void *address,
776
                                     guint lock_bit,
777
                                     gpointer ptr,
778
                                     guintptr preserve_mask)
779
1.20k
{
780
1.20k
  gpointer *pointer_address = address;
781
1.20k
  gpointer ptr2;
782
783
1.20k
  g_return_if_fail (lock_bit < 32u);
784
785
1.20k
  if (preserve_mask != 0)
786
1.20k
    {
787
1.20k
      gpointer old_ptr = g_atomic_pointer_get ((gpointer *) address);
788
789
1.20k
    again:
790
1.20k
      ptr2 = pointer_bit_lock_mask_ptr (ptr, lock_bit, FALSE, preserve_mask, old_ptr);
791
1.20k
      if (!g_atomic_pointer_compare_and_exchange_full (pointer_address, old_ptr, ptr2, &old_ptr))
792
0
        goto again;
793
1.20k
    }
794
0
  else
795
0
    {
796
0
      ptr2 = pointer_bit_lock_mask_ptr (ptr, lock_bit, FALSE, 0, NULL);
797
0
      g_atomic_pointer_set (pointer_address, ptr2);
798
0
    }
799
800
1.20k
  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
1.20k
  g_return_if_fail (ptr == pointer_bit_lock_mask_ptr (ptr, lock_bit, FALSE, 0, NULL));
808
1.20k
}