gthread-posix.c 40.3 KB
Newer Older
1
2
3
4
5
6
7
/* GLIB - Library of useful routines for C programming
 * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * gthread.c: posix thread system implementation
 * Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe
 *
 * 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
13
14
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * 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
 */

21
/*
22
 * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
23
24
 * file for a list of people on the GLib Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
25
 * GLib at ftp://ftp.gtk.org/pub/gtk/.
26
27
 */

Allison Karlitskaya's avatar
Allison Karlitskaya committed
28
29
/* The GMutex, GCond and GPrivate implementations in this file are some
 * of the lowest-level code in GLib.  All other parts of GLib (messages,
30
31
32
33
34
35
36
37
 * memory, slices, etc) assume that they can freely use these facilities
 * without risking recursion.
 *
 * As such, these functions are NOT permitted to call any other part of
 * GLib.
 *
 * The thread manipulation functions (create, exit, join, etc.) have
 * more freedom -- they can do as they please.
38
39
 */

40
#include "config.h"
41

42
#include "gthread.h"
43

44
#include "gmain.h"
45
#include "gmessages.h"
46
#include "gslice.h"
47
#include "gstrfuncs.h"
48
49
#include "gtestutils.h"
#include "gthreadprivate.h"
50
#include "gutils.h"
51
52

#include <stdlib.h>
53
#include <stdio.h>
54
55
#include <string.h>
#include <errno.h>
56
57
#include <pthread.h>

58
59
60
#include <sys/time.h>
#include <unistd.h>

61
62
63
#ifdef HAVE_PTHREAD_SET_NAME_NP
#include <pthread_np.h>
#endif
64
65
66
#ifdef HAVE_SCHED_H
#include <sched.h>
#endif
67
68
69
#ifdef G_OS_WIN32
#include <windows.h>
#endif
70

71
#if defined(HAVE_SYS_SCHED_GETATTR)
72
73
74
#include <sys/syscall.h>
#endif

75
76
#if defined(HAVE_FUTEX) && \
    (defined(HAVE_STDATOMIC_H) || defined(__ATOMIC_SEQ_CST))
77
78
79
#define USE_NATIVE_MUTEX
#endif

80
81
82
83
84
static void
g_thread_abort (gint         status,
                const gchar *function)
{
  fprintf (stderr, "GLib (gthread-posix.c): Unexpected error from C library during '%s': %s.  Aborting.\n",
85
           function, strerror (status));
86
  g_abort ();
87
88
89
}

/* {{{1 GMutex */
Matthias Clasen's avatar
Matthias Clasen committed
90

91
92
#if !defined(USE_NATIVE_MUTEX)

93
94
95
96
97
98
static pthread_mutex_t *
g_mutex_impl_new (void)
{
  pthread_mutexattr_t *pattr = NULL;
  pthread_mutex_t *mutex;
  gint status;
99
100
101
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
  pthread_mutexattr_t attr;
#endif
102
103
104
105
106
107

  mutex = malloc (sizeof (pthread_mutex_t));
  if G_UNLIKELY (mutex == NULL)
    g_thread_abort (errno, "malloc");

#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
108
109
110
  pthread_mutexattr_init (&attr);
  pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
  pattr = &attr;
111
112
113
114
115
#endif

  if G_UNLIKELY ((status = pthread_mutex_init (mutex, pattr)) != 0)
    g_thread_abort (status, "pthread_mutex_init");

116
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
117
118
119
120
121
122
123
124
125
126
127
128
129
  pthread_mutexattr_destroy (&attr);
#endif

  return mutex;
}

static void
g_mutex_impl_free (pthread_mutex_t *mutex)
{
  pthread_mutex_destroy (mutex);
  free (mutex);
}

130
static inline pthread_mutex_t *
131
132
g_mutex_get_impl (GMutex *mutex)
{
133
  pthread_mutex_t *impl = g_atomic_pointer_get (&mutex->p);
134
135
136
137

  if G_UNLIKELY (impl == NULL)
    {
      impl = g_mutex_impl_new ();
138
      if (!g_atomic_pointer_compare_and_exchange (&mutex->p, NULL, impl))
139
        g_mutex_impl_free (impl);
140
      impl = mutex->p;
141
142
143
144
145
146
    }

  return impl;
}


Matthias Clasen's avatar
Matthias Clasen committed
147
148
149
150
151
152
153
154
155
/**
 * g_mutex_init:
 * @mutex: an uninitialized #GMutex
 *
 * Initializes a #GMutex so that it can be used.
 *
 * This function is useful to initialize a mutex that has been
 * allocated on the stack, or as part of a larger structure.
 * It is not necessary to initialize a mutex that has been
Matthias Clasen's avatar
Matthias Clasen committed
156
 * statically allocated.
Matthias Clasen's avatar
Matthias Clasen committed
157
 *
158
 * |[<!-- language="C" --> 
Matthias Clasen's avatar
Matthias Clasen committed
159
160
 *   typedef struct {
 *     GMutex m;
Matthias Clasen's avatar
Matthias Clasen committed
161
 *     ...
Matthias Clasen's avatar
Matthias Clasen committed
162
163
164
165
166
167
168
169
170
171
172
 *   } Blob;
 *
 * Blob *b;
 *
 * b = g_new (Blob, 1);
 * g_mutex_init (&b->m);
 * ]|
 *
 * To undo the effect of g_mutex_init() when a mutex is no longer
 * needed, use g_mutex_clear().
 *
Matthias Clasen's avatar
Matthias Clasen committed
173
174
175
 * Calling g_mutex_init() on an already initialized #GMutex leads
 * to undefined behaviour.
 *
Matthias Clasen's avatar
Matthias Clasen committed
176
177
 * Since: 2.32
 */
178
179
180
void
g_mutex_init (GMutex *mutex)
{
181
  mutex->p = g_mutex_impl_new ();
182
183
}

Matthias Clasen's avatar
Matthias Clasen committed
184
185
186
187
188
189
/**
 * g_mutex_clear:
 * @mutex: an initialized #GMutex
 *
 * Frees the resources allocated to a mutex with g_mutex_init().
 *
190
191
 * This function should not be used with a #GMutex that has been
 * statically allocated.
Matthias Clasen's avatar
Matthias Clasen committed
192
 *
Matthias Clasen's avatar
Matthias Clasen committed
193
194
195
 * Calling g_mutex_clear() on a locked mutex leads to undefined
 * behaviour.
 *
196
 * Sine: 2.32
Matthias Clasen's avatar
Matthias Clasen committed
197
 */
198
199
200
void
g_mutex_clear (GMutex *mutex)
{
201
  g_mutex_impl_free (mutex->p);
202
203
}

Matthias Clasen's avatar
Matthias Clasen committed
204
205
206
207
208
209
210
211
/**
 * g_mutex_lock:
 * @mutex: a #GMutex
 *
 * Locks @mutex. If @mutex is already locked by another thread, the
 * current thread will block until @mutex is unlocked by the other
 * thread.
 *
Matthias Clasen's avatar
Matthias Clasen committed
212
 * #GMutex is neither guaranteed to be recursive nor to be
213
214
 * non-recursive.  As such, calling g_mutex_lock() on a #GMutex that has
 * already been locked by the same thread results in undefined behaviour
Matthias Clasen's avatar
Matthias Clasen committed
215
 * (including but not limited to deadlocks).
Matthias Clasen's avatar
Matthias Clasen committed
216
 */
217
218
219
220
221
void
g_mutex_lock (GMutex *mutex)
{
  gint status;

222
  if G_UNLIKELY ((status = pthread_mutex_lock (g_mutex_get_impl (mutex))) != 0)
223
224
225
    g_thread_abort (status, "pthread_mutex_lock");
}

Matthias Clasen's avatar
Matthias Clasen committed
226
227
228
229
230
/**
 * g_mutex_unlock:
 * @mutex: a #GMutex
 *
 * Unlocks @mutex. If another thread is blocked in a g_mutex_lock()
Matthias Clasen's avatar
Matthias Clasen committed
231
232
233
234
 * call for @mutex, it will become unblocked and can lock @mutex itself.
 *
 * Calling g_mutex_unlock() on a mutex that is not locked by the
 * current thread leads to undefined behaviour.
Matthias Clasen's avatar
Matthias Clasen committed
235
 */
236
237
238
239
240
void
g_mutex_unlock (GMutex *mutex)
{
  gint status;

241
  if G_UNLIKELY ((status = pthread_mutex_unlock (g_mutex_get_impl (mutex))) != 0)
242
    g_thread_abort (status, "pthread_mutex_unlock");
243
244
}

Matthias Clasen's avatar
Matthias Clasen committed
245
246
247
248
249
250
251
252
/**
 * g_mutex_trylock:
 * @mutex: a #GMutex
 *
 * Tries to lock @mutex. If @mutex is already locked by another thread,
 * it immediately returns %FALSE. Otherwise it locks @mutex and returns
 * %TRUE.
 *
Matthias Clasen's avatar
Matthias Clasen committed
253
 * #GMutex is neither guaranteed to be recursive nor to be
254
255
256
 * non-recursive.  As such, calling g_mutex_lock() on a #GMutex that has
 * already been locked by the same thread results in undefined behaviour
 * (including but not limited to deadlocks or arbitrary return values).
257
 *
Matthias Clasen's avatar
Matthias Clasen committed
258
 * Returns: %TRUE if @mutex could be locked
Matthias Clasen's avatar
Matthias Clasen committed
259
 */
260
261
262
263
264
gboolean
g_mutex_trylock (GMutex *mutex)
{
  gint status;

265
  if G_LIKELY ((status = pthread_mutex_trylock (g_mutex_get_impl (mutex))) == 0)
266
267
268
269
270
271
272
273
    return TRUE;

  if G_UNLIKELY (status != EBUSY)
    g_thread_abort (status, "pthread_mutex_trylock");

  return FALSE;
}

274
275
#endif /* !defined(USE_NATIVE_MUTEX) */

276
277
278
279
280
281
282
283
/* {{{1 GRecMutex */

static pthread_mutex_t *
g_rec_mutex_impl_new (void)
{
  pthread_mutexattr_t attr;
  pthread_mutex_t *mutex;

284
285
286
287
  mutex = malloc (sizeof (pthread_mutex_t));
  if G_UNLIKELY (mutex == NULL)
    g_thread_abort (errno, "malloc");

288
289
290
291
292
293
294
295
296
297
298
299
  pthread_mutexattr_init (&attr);
  pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
  pthread_mutex_init (mutex, &attr);
  pthread_mutexattr_destroy (&attr);

  return mutex;
}

static void
g_rec_mutex_impl_free (pthread_mutex_t *mutex)
{
  pthread_mutex_destroy (mutex);
300
  free (mutex);
301
302
}

303
static inline pthread_mutex_t *
304
g_rec_mutex_get_impl (GRecMutex *rec_mutex)
305
{
306
  pthread_mutex_t *impl = g_atomic_pointer_get (&rec_mutex->p);
307

308
  if G_UNLIKELY (impl == NULL)
309
310
    {
      impl = g_rec_mutex_impl_new ();
311
      if (!g_atomic_pointer_compare_and_exchange (&rec_mutex->p, NULL, impl))
312
        g_rec_mutex_impl_free (impl);
313
      impl = rec_mutex->p;
314
315
316
317
318
    }

  return impl;
}

Matthias Clasen's avatar
Matthias Clasen committed
319
320
321
322
323
324
325
326
327
/**
 * g_rec_mutex_init:
 * @rec_mutex: an uninitialized #GRecMutex
 *
 * Initializes a #GRecMutex so that it can be used.
 *
 * This function is useful to initialize a recursive mutex
 * that has been allocated on the stack, or as part of a larger
 * structure.
328
329
330
 *
 * It is not necessary to initialise a recursive mutex that has been
 * statically allocated.
Matthias Clasen's avatar
Matthias Clasen committed
331
 *
332
 * |[<!-- language="C" --> 
Matthias Clasen's avatar
Matthias Clasen committed
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
 *   typedef struct {
 *     GRecMutex m;
 *     ...
 *   } Blob;
 *
 * Blob *b;
 *
 * b = g_new (Blob, 1);
 * g_rec_mutex_init (&b->m);
 * ]|
 *
 * Calling g_rec_mutex_init() on an already initialized #GRecMutex
 * leads to undefined behaviour.
 *
 * To undo the effect of g_rec_mutex_init() when a recursive mutex
 * is no longer needed, use g_rec_mutex_clear().
 *
 * Since: 2.32
 */
352
void
Matthias Clasen's avatar
Matthias Clasen committed
353
g_rec_mutex_init (GRecMutex *rec_mutex)
354
{
355
  rec_mutex->p = g_rec_mutex_impl_new ();
356
357
}

Matthias Clasen's avatar
Matthias Clasen committed
358
359
360
361
362
363
364
/**
 * g_rec_mutex_clear:
 * @rec_mutex: an initialized #GRecMutex
 *
 * Frees the resources allocated to a recursive mutex with
 * g_rec_mutex_init().
 *
365
366
 * This function should not be used with a #GRecMutex that has been
 * statically allocated.
Matthias Clasen's avatar
Matthias Clasen committed
367
368
369
370
371
372
 *
 * Calling g_rec_mutex_clear() on a locked recursive mutex leads
 * to undefined behaviour.
 *
 * Sine: 2.32
 */
373
void
Matthias Clasen's avatar
Matthias Clasen committed
374
g_rec_mutex_clear (GRecMutex *rec_mutex)
375
{
376
  g_rec_mutex_impl_free (rec_mutex->p);
377
378
}

Matthias Clasen's avatar
Matthias Clasen committed
379
380
381
382
383
384
385
386
387
388
389
390
391
/**
 * g_rec_mutex_lock:
 * @rec_mutex: a #GRecMutex
 *
 * Locks @rec_mutex. If @rec_mutex is already locked by another
 * thread, the current thread will block until @rec_mutex is
 * unlocked by the other thread. If @rec_mutex is already locked
 * by the current thread, the 'lock count' of @rec_mutex is increased.
 * The mutex will only become available again when it is unlocked
 * as many times as it has been locked.
 *
 * Since: 2.32
 */
392
393
394
395
396
397
void
g_rec_mutex_lock (GRecMutex *mutex)
{
  pthread_mutex_lock (g_rec_mutex_get_impl (mutex));
}

Matthias Clasen's avatar
Matthias Clasen committed
398
399
/**
 * g_rec_mutex_unlock:
Stef Walter's avatar
Stef Walter committed
400
 * @rec_mutex: a #GRecMutex
Matthias Clasen's avatar
Matthias Clasen committed
401
402
403
404
405
406
407
408
409
410
 *
 * Unlocks @rec_mutex. If another thread is blocked in a
 * g_rec_mutex_lock() call for @rec_mutex, it will become unblocked
 * and can lock @rec_mutex itself.
 *
 * Calling g_rec_mutex_unlock() on a recursive mutex that is not
 * locked by the current thread leads to undefined behaviour.
 *
 * Since: 2.32
 */
411
void
Matthias Clasen's avatar
Matthias Clasen committed
412
g_rec_mutex_unlock (GRecMutex *rec_mutex)
413
{
414
  pthread_mutex_unlock (rec_mutex->p);
415
416
}

Matthias Clasen's avatar
Matthias Clasen committed
417
418
419
420
421
422
423
424
425
426
427
428
/**
 * g_rec_mutex_trylock:
 * @rec_mutex: a #GRecMutex
 *
 * Tries to lock @rec_mutex. If @rec_mutex is already locked
 * by another thread, it immediately returns %FALSE. Otherwise
 * it locks @rec_mutex and returns %TRUE.
 *
 * Returns: %TRUE if @rec_mutex could be locked
 *
 * Since: 2.32
 */
429
gboolean
Matthias Clasen's avatar
Matthias Clasen committed
430
g_rec_mutex_trylock (GRecMutex *rec_mutex)
431
{
Matthias Clasen's avatar
Matthias Clasen committed
432
  if (pthread_mutex_trylock (g_rec_mutex_get_impl (rec_mutex)) != 0)
Matthias Clasen's avatar
Matthias Clasen committed
433
434
435
    return FALSE;

  return TRUE;
436
437
}

Allison Karlitskaya's avatar
Allison Karlitskaya committed
438
439
/* {{{1 GRWLock */

440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
static pthread_rwlock_t *
g_rw_lock_impl_new (void)
{
  pthread_rwlock_t *rwlock;
  gint status;

  rwlock = malloc (sizeof (pthread_rwlock_t));
  if G_UNLIKELY (rwlock == NULL)
    g_thread_abort (errno, "malloc");

  if G_UNLIKELY ((status = pthread_rwlock_init (rwlock, NULL)) != 0)
    g_thread_abort (status, "pthread_rwlock_init");

  return rwlock;
}

static void
g_rw_lock_impl_free (pthread_rwlock_t *rwlock)
{
  pthread_rwlock_destroy (rwlock);
  free (rwlock);
}

463
static inline pthread_rwlock_t *
464
465
g_rw_lock_get_impl (GRWLock *lock)
{
466
  pthread_rwlock_t *impl = g_atomic_pointer_get (&lock->p);
467
468
469
470

  if G_UNLIKELY (impl == NULL)
    {
      impl = g_rw_lock_impl_new ();
471
      if (!g_atomic_pointer_compare_and_exchange (&lock->p, NULL, impl))
472
        g_rw_lock_impl_free (impl);
473
      impl = lock->p;
474
475
476
477
478
    }

  return impl;
}

Matthias Clasen's avatar
Matthias Clasen committed
479
480
/**
 * g_rw_lock_init:
481
 * @rw_lock: an uninitialized #GRWLock
Matthias Clasen's avatar
Matthias Clasen committed
482
483
484
485
 *
 * Initializes a #GRWLock so that it can be used.
 *
 * This function is useful to initialize a lock that has been
Allison Karlitskaya's avatar
Allison Karlitskaya committed
486
487
488
 * allocated on the stack, or as part of a larger structure.  It is not
 * necessary to initialise a reader-writer lock that has been statically
 * allocated.
Matthias Clasen's avatar
Matthias Clasen committed
489
 *
490
 * |[<!-- language="C" --> 
Matthias Clasen's avatar
Matthias Clasen committed
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
 *   typedef struct {
 *     GRWLock l;
 *     ...
 *   } Blob;
 *
 * Blob *b;
 *
 * b = g_new (Blob, 1);
 * g_rw_lock_init (&b->l);
 * ]|
 *
 * To undo the effect of g_rw_lock_init() when a lock is no longer
 * needed, use g_rw_lock_clear().
 *
 * Calling g_rw_lock_init() on an already initialized #GRWLock leads
 * to undefined behaviour.
 *
 * Since: 2.32
 */
Allison Karlitskaya's avatar
Allison Karlitskaya committed
510
void
511
g_rw_lock_init (GRWLock *rw_lock)
Allison Karlitskaya's avatar
Allison Karlitskaya committed
512
{
513
  rw_lock->p = g_rw_lock_impl_new ();
Allison Karlitskaya's avatar
Allison Karlitskaya committed
514
515
}

Matthias Clasen's avatar
Matthias Clasen committed
516
517
/**
 * g_rw_lock_clear:
518
 * @rw_lock: an initialized #GRWLock
Matthias Clasen's avatar
Matthias Clasen committed
519
520
521
 *
 * Frees the resources allocated to a lock with g_rw_lock_init().
 *
522
523
524
 * This function should not be used with a #GRWLock that has been
 * statically allocated.
 *
Matthias Clasen's avatar
Matthias Clasen committed
525
526
527
528
529
 * Calling g_rw_lock_clear() when any thread holds the lock
 * leads to undefined behaviour.
 *
 * Sine: 2.32
 */
Allison Karlitskaya's avatar
Allison Karlitskaya committed
530
void
531
g_rw_lock_clear (GRWLock *rw_lock)
Allison Karlitskaya's avatar
Allison Karlitskaya committed
532
{
533
  g_rw_lock_impl_free (rw_lock->p);
Allison Karlitskaya's avatar
Allison Karlitskaya committed
534
535
}

Matthias Clasen's avatar
Matthias Clasen committed
536
537
/**
 * g_rw_lock_writer_lock:
538
 * @rw_lock: a #GRWLock
Matthias Clasen's avatar
Matthias Clasen committed
539
 *
540
541
542
 * Obtain a write lock on @rw_lock. If any thread already holds
 * a read or write lock on @rw_lock, the current thread will block
 * until all other threads have dropped their locks on @rw_lock.
Matthias Clasen's avatar
Matthias Clasen committed
543
544
545
 *
 * Since: 2.32
 */
Allison Karlitskaya's avatar
Allison Karlitskaya committed
546
void
547
g_rw_lock_writer_lock (GRWLock *rw_lock)
Allison Karlitskaya's avatar
Allison Karlitskaya committed
548
{
549
550
551
552
  int retval = pthread_rwlock_wrlock (g_rw_lock_get_impl (rw_lock));

  if (retval != 0)
    g_critical ("Failed to get RW lock %p: %s", rw_lock, g_strerror (retval));
Allison Karlitskaya's avatar
Allison Karlitskaya committed
553
554
}

Matthias Clasen's avatar
Matthias Clasen committed
555
556
/**
 * g_rw_lock_writer_trylock:
557
 * @rw_lock: a #GRWLock
Matthias Clasen's avatar
Matthias Clasen committed
558
 *
559
560
561
 * Tries to obtain a write lock on @rw_lock. If any other thread holds
 * a read or write lock on @rw_lock, it immediately returns %FALSE.
 * Otherwise it locks @rw_lock and returns %TRUE.
Matthias Clasen's avatar
Matthias Clasen committed
562
 *
563
 * Returns: %TRUE if @rw_lock could be locked
Matthias Clasen's avatar
Matthias Clasen committed
564
565
566
 *
 * Since: 2.32
 */
Allison Karlitskaya's avatar
Allison Karlitskaya committed
567
gboolean
568
g_rw_lock_writer_trylock (GRWLock *rw_lock)
Allison Karlitskaya's avatar
Allison Karlitskaya committed
569
{
570
  if (pthread_rwlock_trywrlock (g_rw_lock_get_impl (rw_lock)) != 0)
571
572
573
    return FALSE;

  return TRUE;
Allison Karlitskaya's avatar
Allison Karlitskaya committed
574
575
}

Matthias Clasen's avatar
Matthias Clasen committed
576
577
/**
 * g_rw_lock_writer_unlock:
578
 * @rw_lock: a #GRWLock
Matthias Clasen's avatar
Matthias Clasen committed
579
 *
580
 * Release a write lock on @rw_lock.
Matthias Clasen's avatar
Matthias Clasen committed
581
582
583
584
585
586
 *
 * Calling g_rw_lock_writer_unlock() on a lock that is not held
 * by the current thread leads to undefined behaviour.
 *
 * Since: 2.32
 */
Allison Karlitskaya's avatar
Allison Karlitskaya committed
587
void
588
g_rw_lock_writer_unlock (GRWLock *rw_lock)
Allison Karlitskaya's avatar
Allison Karlitskaya committed
589
{
590
  pthread_rwlock_unlock (g_rw_lock_get_impl (rw_lock));
Allison Karlitskaya's avatar
Allison Karlitskaya committed
591
592
}

Matthias Clasen's avatar
Matthias Clasen committed
593
594
/**
 * g_rw_lock_reader_lock:
595
 * @rw_lock: a #GRWLock
Matthias Clasen's avatar
Matthias Clasen committed
596
 *
597
 * Obtain a read lock on @rw_lock. If another thread currently holds
598
599
600
601
 * the write lock on @rw_lock, the current thread will block. If another thread
 * does not hold the write lock, but is waiting for it, it is implementation
 * defined whether the reader or writer will block. Read locks can be taken
 * recursively.
Matthias Clasen's avatar
Matthias Clasen committed
602
603
 *
 * It is implementation-defined how many threads are allowed to
604
605
 * hold read locks on the same lock simultaneously. If the limit is hit,
 * or if a deadlock is detected, a critical warning will be emitted.
Matthias Clasen's avatar
Matthias Clasen committed
606
607
608
 *
 * Since: 2.32
 */
Allison Karlitskaya's avatar
Allison Karlitskaya committed
609
void
610
g_rw_lock_reader_lock (GRWLock *rw_lock)
Allison Karlitskaya's avatar
Allison Karlitskaya committed
611
{
612
613
614
615
  int retval = pthread_rwlock_rdlock (g_rw_lock_get_impl (rw_lock));

  if (retval != 0)
    g_critical ("Failed to get RW lock %p: %s", rw_lock, g_strerror (retval));
Allison Karlitskaya's avatar
Allison Karlitskaya committed
616
617
}

Matthias Clasen's avatar
Matthias Clasen committed
618
619
/**
 * g_rw_lock_reader_trylock:
620
 * @rw_lock: a #GRWLock
Matthias Clasen's avatar
Matthias Clasen committed
621
 *
622
 * Tries to obtain a read lock on @rw_lock and returns %TRUE if
Matthias Clasen's avatar
Matthias Clasen committed
623
624
625
 * the read lock was successfully obtained. Otherwise it
 * returns %FALSE.
 *
626
 * Returns: %TRUE if @rw_lock could be locked
Matthias Clasen's avatar
Matthias Clasen committed
627
628
629
 *
 * Since: 2.32
 */
Allison Karlitskaya's avatar
Allison Karlitskaya committed
630
gboolean
631
g_rw_lock_reader_trylock (GRWLock *rw_lock)
Allison Karlitskaya's avatar
Allison Karlitskaya committed
632
{
633
  if (pthread_rwlock_tryrdlock (g_rw_lock_get_impl (rw_lock)) != 0)
634
635
636
    return FALSE;

  return TRUE;
Allison Karlitskaya's avatar
Allison Karlitskaya committed
637
638
}

Matthias Clasen's avatar
Matthias Clasen committed
639
640
/**
 * g_rw_lock_reader_unlock:
641
 * @rw_lock: a #GRWLock
Matthias Clasen's avatar
Matthias Clasen committed
642
 *
643
 * Release a read lock on @rw_lock.
Matthias Clasen's avatar
Matthias Clasen committed
644
645
646
647
648
649
 *
 * Calling g_rw_lock_reader_unlock() on a lock that is not held
 * by the current thread leads to undefined behaviour.
 *
 * Since: 2.32
 */
Allison Karlitskaya's avatar
Allison Karlitskaya committed
650
void
651
g_rw_lock_reader_unlock (GRWLock *rw_lock)
Allison Karlitskaya's avatar
Allison Karlitskaya committed
652
{
653
  pthread_rwlock_unlock (g_rw_lock_get_impl (rw_lock));
Allison Karlitskaya's avatar
Allison Karlitskaya committed
654
655
}

656
657
/* {{{1 GCond */

658
659
#if !defined(USE_NATIVE_MUTEX)

660
661
662
static pthread_cond_t *
g_cond_impl_new (void)
{
663
  pthread_condattr_t attr;
664
665
666
  pthread_cond_t *cond;
  gint status;

667
  pthread_condattr_init (&attr);
668
669
670
671
672
673
674

#ifdef HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP
#elif defined (HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined (CLOCK_MONOTONIC)
  if G_UNLIKELY ((status = pthread_condattr_setclock (&attr, CLOCK_MONOTONIC)) != 0)
    g_thread_abort (status, "pthread_condattr_setclock");
#else
#error Cannot support GCond on your platform.
675
676
#endif

677
678
679
680
  cond = malloc (sizeof (pthread_cond_t));
  if G_UNLIKELY (cond == NULL)
    g_thread_abort (errno, "malloc");

681
  if G_UNLIKELY ((status = pthread_cond_init (cond, &attr)) != 0)
682
683
    g_thread_abort (status, "pthread_cond_init");

684
685
  pthread_condattr_destroy (&attr);

686
687
688
689
690
691
692
693
694
695
  return cond;
}

static void
g_cond_impl_free (pthread_cond_t *cond)
{
  pthread_cond_destroy (cond);
  free (cond);
}

696
static inline pthread_cond_t *
697
698
g_cond_get_impl (GCond *cond)
{
699
  pthread_cond_t *impl = g_atomic_pointer_get (&cond->p);
700
701
702
703

  if G_UNLIKELY (impl == NULL)
    {
      impl = g_cond_impl_new ();
704
      if (!g_atomic_pointer_compare_and_exchange (&cond->p, NULL, impl))
705
        g_cond_impl_free (impl);
706
      impl = cond->p;
707
708
709
710
711
    }

  return impl;
}

Matthias Clasen's avatar
Matthias Clasen committed
712
713
714
715
/**
 * g_cond_init:
 * @cond: an uninitialized #GCond
 *
716
 * Initialises a #GCond so that it can be used.
Matthias Clasen's avatar
Matthias Clasen committed
717
 *
718
719
720
 * This function is useful to initialise a #GCond that has been
 * allocated as part of a larger structure.  It is not necessary to
 * initialise a #GCond that has been statically allocated.
Matthias Clasen's avatar
Matthias Clasen committed
721
722
723
724
 *
 * To undo the effect of g_cond_init() when a #GCond is no longer
 * needed, use g_cond_clear().
 *
725
 * Calling g_cond_init() on an already-initialised #GCond leads
Matthias Clasen's avatar
Matthias Clasen committed
726
 * to undefined behaviour.
Matthias Clasen's avatar
Matthias Clasen committed
727
728
729
 *
 * Since: 2.32
 */
730
731
732
void
g_cond_init (GCond *cond)
{
733
  cond->p = g_cond_impl_new ();
734
735
}

Matthias Clasen's avatar
Matthias Clasen committed
736
737
/**
 * g_cond_clear:
738
 * @cond: an initialised #GCond
Matthias Clasen's avatar
Matthias Clasen committed
739
 *
Matthias Clasen's avatar
Matthias Clasen committed
740
 * Frees the resources allocated to a #GCond with g_cond_init().
Matthias Clasen's avatar
Matthias Clasen committed
741
 *
742
743
 * This function should not be used with a #GCond that has been
 * statically allocated.
Matthias Clasen's avatar
Matthias Clasen committed
744
 *
Matthias Clasen's avatar
Matthias Clasen committed
745
746
747
 * Calling g_cond_clear() for a #GCond on which threads are
 * blocking leads to undefined behaviour.
 *
Matthias Clasen's avatar
Matthias Clasen committed
748
749
 * Since: 2.32
 */
750
751
752
void
g_cond_clear (GCond *cond)
{
753
  g_cond_impl_free (cond->p);
754
755
}

Matthias Clasen's avatar
Matthias Clasen committed
756
757
758
759
760
/**
 * g_cond_wait:
 * @cond: a #GCond
 * @mutex: a #GMutex that is currently locked
 *
761
 * Atomically releases @mutex and waits until @cond is signalled.
Matthias Clasen's avatar
Matthias Clasen committed
762
763
 * When this function returns, @mutex is locked again and owned by the
 * calling thread.
Matthias Clasen's avatar
Matthias Clasen committed
764
 *
765
766
767
768
769
770
771
772
773
774
775
 * When using condition variables, it is possible that a spurious wakeup
 * may occur (ie: g_cond_wait() returns even though g_cond_signal() was
 * not called).  It's also possible that a stolen wakeup may occur.
 * This is when g_cond_signal() is called, but another thread acquires
 * @mutex before this thread and modifies the state of the program in
 * such a way that when g_cond_wait() is able to return, the expected
 * condition is no longer met.
 *
 * For this reason, g_cond_wait() must always be used in a loop.  See
 * the documentation for #GCond for a complete example.
 **/
776
777
778
779
780
781
void
g_cond_wait (GCond  *cond,
             GMutex *mutex)
{
  gint status;

782
  if G_UNLIKELY ((status = pthread_cond_wait (g_cond_get_impl (cond), g_mutex_get_impl (mutex))) != 0)
783
784
785
    g_thread_abort (status, "pthread_cond_wait");
}

Matthias Clasen's avatar
Matthias Clasen committed
786
787
788
789
/**
 * g_cond_signal:
 * @cond: a #GCond
 *
Matthias Clasen's avatar
Matthias Clasen committed
790
791
 * If threads are waiting for @cond, at least one of them is unblocked.
 * If no threads are waiting for @cond, this function has no effect.
Matthias Clasen's avatar
Matthias Clasen committed
792
793
794
 * It is good practice to hold the same lock as the waiting thread
 * while calling this function, though not required.
 */
795
796
797
798
799
void
g_cond_signal (GCond *cond)
{
  gint status;

800
  if G_UNLIKELY ((status = pthread_cond_signal (g_cond_get_impl (cond))) != 0)
801
802
803
    g_thread_abort (status, "pthread_cond_signal");
}

Matthias Clasen's avatar
Matthias Clasen committed
804
805
806
807
/**
 * g_cond_broadcast:
 * @cond: a #GCond
 *
Matthias Clasen's avatar
Matthias Clasen committed
808
809
 * If threads are waiting for @cond, all of them are unblocked.
 * If no threads are waiting for @cond, this function has no effect.
Matthias Clasen's avatar
Matthias Clasen committed
810
811
812
 * It is good practice to lock the same mutex as the waiting threads
 * while calling this function, though not required.
 */
813
814
815
816
817
void
g_cond_broadcast (GCond *cond)
{
  gint status;

818
  if G_UNLIKELY ((status = pthread_cond_broadcast (g_cond_get_impl (cond))) != 0)
819
820
821
    g_thread_abort (status, "pthread_cond_broadcast");
}

Matthias Clasen's avatar
Matthias Clasen committed
822
/**
823
 * g_cond_wait_until:
Matthias Clasen's avatar
Matthias Clasen committed
824
825
 * @cond: a #GCond
 * @mutex: a #GMutex that is currently locked
826
 * @end_time: the monotonic time to wait until
Matthias Clasen's avatar
Matthias Clasen committed
827
 *
828
 * Waits until either @cond is signalled or @end_time has passed.
Matthias Clasen's avatar
Matthias Clasen committed
829
 *
830
831
832
 * As with g_cond_wait() it is possible that a spurious or stolen wakeup
 * could occur.  For that reason, waiting on a condition variable should
 * always be in a loop, based on an explicitly-checked predicate.
Matthias Clasen's avatar
Matthias Clasen committed
833
 *
834
835
836
 * %TRUE is returned if the condition variable was signalled (or in the
 * case of a spurious wakeup).  %FALSE is returned if @end_time has
 * passed.
Matthias Clasen's avatar
Matthias Clasen committed
837
 *
838
 * The following code shows how to correctly perform a timed wait on a
839
 * condition variable (extending the example presented in the
840
 * documentation for #GCond):
Matthias Clasen's avatar
Matthias Clasen committed
841
 *
842
 * |[<!-- language="C" --> 
843
844
845
846
847
848
849
850
851
852
853
854
 * gpointer
 * pop_data_timed (void)
 * {
 *   gint64 end_time;
 *   gpointer data;
 *
 *   g_mutex_lock (&data_mutex);
 *
 *   end_time = g_get_monotonic_time () + 5 * G_TIME_SPAN_SECOND;
 *   while (!current_data)
 *     if (!g_cond_wait_until (&data_cond, &data_mutex, end_time))
 *       {
Matthias Clasen's avatar
Matthias Clasen committed
855
 *         // timeout has passed.
856
857
858
859
 *         g_mutex_unlock (&data_mutex);
 *         return NULL;
 *       }
 *
Matthias Clasen's avatar
Matthias Clasen committed
860
 *   // there is data for us
861
862
863
864
865
866
867
868
 *   data = current_data;
 *   current_data = NULL;
 *
 *   g_mutex_unlock (&data_mutex);
 *
 *   return data;
 * }
 * ]|
Matthias Clasen's avatar
Matthias Clasen committed
869
 *
870
871
872
 * Notice that the end time is calculated once, before entering the
 * loop and reused.  This is the motivation behind the use of absolute
 * time on this API -- if a relative time of 5 seconds were passed
873
 * directly to the call and a spurious wakeup occurred, the program would
874
875
 * have to start over waiting again (which would lead to a total wait
 * time of more than 5 seconds).
Matthias Clasen's avatar
Matthias Clasen committed
876
 *
877
 * Returns: %TRUE on a signal, %FALSE on a timeout
Matthias Clasen's avatar
Matthias Clasen committed
878
 * Since: 2.32
879
 **/
880
gboolean
881
882
883
g_cond_wait_until (GCond  *cond,
                   GMutex *mutex,
                   gint64  end_time)
884
{
885
  struct timespec ts;
886
887
  gint status;

888
889
890
891
892
893
894
895
896
#ifdef HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP
  /* end_time is given relative to the monotonic clock as returned by
   * g_get_monotonic_time().
   *
   * Since this pthreads wants the relative time, convert it back again.
   */
  {
    gint64 now = g_get_monotonic_time ();
    gint64 relative;
897

898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
    if (end_time <= now)
      return FALSE;

    relative = end_time - now;

    ts.tv_sec = relative / 1000000;
    ts.tv_nsec = (relative % 1000000) * 1000;

    if ((status = pthread_cond_timedwait_relative_np (g_cond_get_impl (cond), g_mutex_get_impl (mutex), &ts)) == 0)
      return TRUE;
  }
#elif defined (HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined (CLOCK_MONOTONIC)
  /* This is the exact check we used during init to set the clock to
   * monotonic, so if we're in this branch, timedwait() will already be
   * expecting a monotonic clock.
   */
  {
    ts.tv_sec = end_time / 1000000;
    ts.tv_nsec = (end_time % 1000000) * 1000;

    if ((status = pthread_cond_timedwait (g_cond_get_impl (cond), g_mutex_get_impl (mutex), &ts)) == 0)
      return TRUE;
  }
921
#else
922
#error Cannot support GCond on your platform.
923
#endif
924
925
926
927
928
929
930

  if G_UNLIKELY (status != ETIMEDOUT)
    g_thread_abort (status, "pthread_cond_timedwait");

  return FALSE;
}

931
932
#endif /* defined(USE_NATIVE_MUTEX) */

933
934
/* {{{1 GPrivate */

935
936
937
938
/**
 * GPrivate:
 *
 * The #GPrivate struct is an opaque data structure to represent a
Matthias Clasen's avatar
Matthias Clasen committed
939
940
941
 * thread-local data key. It is approximately equivalent to the
 * pthread_setspecific()/pthread_getspecific() APIs on POSIX and to
 * TlsSetValue()/TlsGetValue() on Windows.
942
 *
Matthias Clasen's avatar
Matthias Clasen committed
943
944
 * If you don't already know why you might want this functionality,
 * then you probably don't need it.
945
946
 *
 * #GPrivate is a very limited resource (as far as 128 per program,
Matthias Clasen's avatar
Matthias Clasen committed
947
 * shared between all libraries). It is also not possible to destroy a
948
949
950
951
952
953
 * #GPrivate after it has been used. As such, it is only ever acceptable
 * to use #GPrivate in static scope, and even then sparingly so.
 *
 * See G_PRIVATE_INIT() for a couple of examples.
 *
 * The #GPrivate structure should be considered opaque.  It should only
954
 * be accessed via the g_private_ functions.
955
956
957
958
959
960
961
962
963
 */

/**
 * G_PRIVATE_INIT:
 * @notify: a #GDestroyNotify
 *
 * A macro to assist with the static initialisation of a #GPrivate.
 *
 * This macro is useful for the case that a #GDestroyNotify function
964
 * should be associated with the key.  This is needed when the key will be
965
966
967
968
969
970
971
972
973
974
975
 * used to point at memory that should be deallocated when the thread
 * exits.
 *
 * Additionally, the #GDestroyNotify will also be called on the previous
 * value stored in the key when g_private_replace() is used.
 *
 * If no #GDestroyNotify is needed, then use of this macro is not
 * required -- if the #GPrivate is declared in static scope then it will
 * be properly initialised by default (ie: to all zeros).  See the
 * examples below.
 *
976
 * |[<!-- language="C" --> 
977
978
 * static GPrivate name_key = G_PRIVATE_INIT (g_free);
 *
Matthias Clasen's avatar
Matthias Clasen committed
979
 * // return value should not be freed
980
981
982
983
984
985
986
987
988
989
990
991
992
 * const gchar *
 * get_local_name (void)
 * {
 *   return g_private_get (&name_key);
 * }
 *
 * void
 * set_local_name (const gchar *name)
 * {
 *   g_private_replace (&name_key, g_strdup (name));
 * }
 *
 *
Matthias Clasen's avatar
Matthias Clasen committed
993
 * static GPrivate count_key;   // no free function
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
 *
 * gint
 * get_local_count (void)
 * {
 *   return GPOINTER_TO_INT (g_private_get (&count_key));
 * }
 *
 * void
 * set_local_count (gint count)
 * {
 *   g_private_set (&count_key, GINT_TO_POINTER (count));
 * }
 * ]|
 *
 * Since: 2.32
 **/

static pthread_key_t *
g_private_impl_new (GDestroyNotify notify)
Allison Karlitskaya's avatar
Allison Karlitskaya committed
1013
{
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
  pthread_key_t *key;
  gint status;

  key = malloc (sizeof (pthread_key_t));
  if G_UNLIKELY (key == NULL)
    g_thread_abort (errno, "malloc");
  status = pthread_key_create (key, notify);
  if G_UNLIKELY (status != 0)
    g_thread_abort (status, "pthread_key_create");

  return key;
}

static void
g_private_impl_free (pthread_key_t *key)
{
  gint status;

  status = pthread_key_delete (*key);
  if G_UNLIKELY (status != 0)
    g_thread_abort (status, "pthread_key_delete");
  free (key);
}

1038
static inline pthread_key_t *
1039
1040
g_private_get_impl (GPrivate *key)
{
1041
  pthread_key_t *impl = g_atomic_pointer_get (&key->p);
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053

  if G_UNLIKELY (impl == NULL)
    {
      impl = g_private_impl_new (key->notify);
      if (!g_atomic_pointer_compare_and_exchange (&key->p, NULL, impl))
        {
          g_private_impl_free (impl);
          impl = key->p;
        }
    }

  return impl;
Allison Karlitskaya's avatar
Allison Karlitskaya committed
1054
1055
}

Matthias Clasen's avatar
Matthias Clasen committed
1056
1057
/**
 * g_private_get:
1058
 * @key: a #GPrivate
Matthias Clasen's avatar
Matthias Clasen committed
1059
 *
1060
 * Returns the current value of the thread local variable @key.
Matthias Clasen's avatar
Matthias Clasen committed
1061
 *
1062
1063
1064
1065
1066
 * If the value has not yet been set in this thread, %NULL is returned.
 * Values are never copied between threads (when a new thread is
 * created, for example).
 *
 * Returns: the thread-local value
Matthias Clasen's avatar
Matthias Clasen committed
1067
 */
Allison Karlitskaya's avatar
Allison Karlitskaya committed
1068
gpointer
Allison Karlitskaya's avatar
Allison Karlitskaya committed
1069
g_private_get (GPrivate *key)
Allison Karlitskaya's avatar
Allison Karlitskaya committed
1070
1071
{
  /* quote POSIX: No errors are returned from pthread_getspecific(). */
1072
  return pthread_getspecific (*g_private_get_impl (key));
Allison Karlitskaya's avatar
Allison Karlitskaya committed
1073
1074
}

Matthias Clasen's avatar
Matthias Clasen committed
1075
1076
/**
 * g_private_set:
1077
1078
 * @key: a #GPrivate
 * @value: the new value
Matthias Clasen's avatar
Matthias Clasen committed
1079
 *
1080
1081
 * Sets the thread local variable @key to have the value @value in the
 * current thread.
Matthias Clasen's avatar
Matthias Clasen committed
1082
 *
1083
1084
 * This function differs from g_private_replace() in the following way:
 * the #GDestroyNotify for @key is not called on the old value.
Matthias Clasen's avatar
Matthias Clasen committed
1085
 */
Allison Karlitskaya's avatar
Allison Karlitskaya committed
1086
void
Allison Karlitskaya's avatar
Allison Karlitskaya committed
1087
1088
g_private_set (GPrivate *key,
               gpointer  value)
Allison Karlitskaya's avatar
Allison Karlitskaya committed
1089
1090
1091
{
  gint status;

1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
  if G_UNLIKELY ((status = pthread_setspecific (*g_private_get_impl (key), value)) != 0)
    g_thread_abort (status, "pthread_setspecific");
}

/**
 * g_private_replace:
 * @key: a #GPrivate
 * @value: the new value
 *
 * Sets the thread local variable @key to have the value @value in the
 * current thread.
 *
 * This function differs from g_private_set() in the following way: if
 * the previous value was non-%NULL then the #GDestroyNotify handler for
 * @key is run on it.
 *
 * Since: 2.32
 **/
void
g_private_replace (GPrivate *key,
                   gpointer  value)
{
  pthread_key_t *impl = g_private_get_impl (key);
  gpointer old;
  gint status;

  old = pthread_getspecific (*impl);
Allison Karlitskaya's avatar
Allison Karlitskaya committed
1119

1120
  if G_UNLIKELY ((status = pthread_setspecific (*impl, value)) != 0)
Allison Karlitskaya's avatar
Allison Karlitskaya committed
1121
    g_thread_abort (status, "pthread_setspecific");
1122
1123
1124

  if (old && key->notify)
    key->notify (old);
Allison Karlitskaya's avatar
Allison Karlitskaya committed
1125
1126
1127
1128
}

/* {{{1 GThread */

1129
1130
1131
#define posix_check_err(err, name) G_STMT_START{			\
  int error = (err); 							\
  if (error)	 		 		 			\
1132
    g_error ("file %s: line %d (%s): error '%s' during '%s'",		\
1133
           __FILE__, __LINE__, G_STRFUNC,				\
1134
1135
1136
           g_strerror (error), name);					\
  }G_STMT_END

1137
#define posix_check_cmd(cmd) posix_check_err (cmd, #cmd)
1138

1139
1140
1141
1142
1143
typedef struct
{
  GRealThread thread;

  pthread_t system_thread;
1144
1145
  gboolean  joined;
  GMutex    lock;
1146
1147
1148
1149
1150

  void *(*proxy) (void *);

  /* Must be statically allocated and valid forever */
  const GThreadSchedulerSettings *scheduler_settings;
1151
1152
} GThreadPosix;

1153
1154
1155
void
g_system_thread_free (GRealThread *thread)
{
1156
1157
  GThreadPosix *pt = (GThreadPosix *) thread;

1158
1159
1160
1161
1162
  if (!pt->joined)
    pthread_detach (pt->system_thread);

  g_mutex_clear (&pt->lock);

1163
  g_slice_free (GThreadPosix, pt);
1164
1165
}

1166
gboolean
1167
1168
1169
1170
g_system_thread_get_scheduler_settings (GThreadSchedulerSettings *scheduler_settings)
{
  /* FIXME: Implement the same for macOS and the BSDs so it doesn't go through
   * the fallback code using an additional thread. */
1171
#if defined(HAVE_SYS_SCHED_GETATTR)
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
  pid_t tid;
  int res;
  /* FIXME: The struct definition does not seem to be possible to pull in
   * via any of the normal system headers and it's only declared in the
   * kernel headers. That's why we hardcode 56 here right now. */
  guint size = 56; /* Size as of Linux 5.3.9 */
  guint flags = 0;

  tid = (pid_t) syscall (SYS_gettid);

  scheduler_settings->attr = g_malloc0 (size);

  do
    {
      int errsv;

      res = syscall (SYS_sched_getattr, tid, scheduler_settings->attr, size, flags);
      errsv = errno;
      if (res == -1)
        {
          if (errsv == EAGAIN)
            {
              continue;
            }
          else if (errsv == E2BIG)
            {
              g_assert (size < G_MAXINT);
              size *= 2;
              scheduler_settings->attr = g_realloc (scheduler_settings->attr, size);
              /* Needs to be zero-initialized */
              memset (scheduler_settings->attr, 0, size);
            }
          else
            {
1206
1207
1208
1209
              g_debug ("Failed to get thread scheduler attributes: %s", g_strerror (errsv));
              g_free (scheduler_settings->attr);

              return FALSE;
1210
1211
1212
1213
            }
        }
    }
  while (res == -1);
1214

1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
  /* Try setting them on the current thread to see if any system policies are
   * in place that would disallow doing so */
  res = syscall (SYS_sched_setattr, tid, scheduler_settings->attr, flags);
  if (res == -1)
    {
      int errsv = errno;

      g_debug ("Failed to set thread scheduler attributes: %s", g_strerror (errsv));
      g_free (scheduler_settings->attr);

      return FALSE;
    }

1228
1229
1230
  return TRUE;
#else
  return FALSE;
1231
1232
1233
#endif
}

1234
#if defined(HAVE_SYS_SCHED_GETATTR)
1235
1236
1237
1238
static void *
linux_pthread_proxy (void *data)
{
  GThreadPosix *thread = data;
1239
  static gboolean printed_scheduler_warning = FALSE;  /* (atomic) */
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251

  /* Set scheduler settings first if requested */
  if (thread->scheduler_settings)
    {
      pid_t tid = 0;
      guint flags = 0;
      int res;
      int errsv;

      tid = (pid_t) syscall (SYS_gettid);
      res = syscall (SYS_sched_setattr, tid, thread->scheduler_settings->attr, flags);
      errsv = errno;
1252
      if (res == -1 && g_atomic_int_compare_and_exchange (&printed_scheduler_warning, FALSE, TRUE))
1253
        g_critical ("Failed to set scheduler settings: %s", g_strerror (errsv));
1254
1255
1256
      else if (res == -1)
        g_debug ("Failed to set scheduler settings: %s", g_strerror (errsv));
      printed_scheduler_warning = TRUE;