Coverage Report

Created: 2026-10-17 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Python/thread_pthread.h
Line
Count
Source
1
#include "pycore_interp.h"        // _PyInterpreterState.threads.stacksize
2
#include "pycore_pythread.h"      // _POSIX_SEMAPHORES
3
#include "pycore_time.h"          // _PyTime_FromMicrosecondsClamup()
4
5
/* Posix threads interface */
6
7
#include <stdlib.h>
8
#include <string.h>
9
#if defined(__APPLE__) || defined(HAVE_PTHREAD_DESTRUCTOR)
10
#define destructor xxdestructor
11
#endif
12
#ifndef HAVE_PTHREAD_STUBS
13
#  include <pthread.h>
14
#endif
15
#if defined(__APPLE__) || defined(HAVE_PTHREAD_DESTRUCTOR)
16
#undef destructor
17
#endif
18
#include <signal.h>
19
#include <unistd.h>             /* pause(), also getthrid() on OpenBSD */
20
21
#ifdef HAVE_GETTID
22
#   include <unistd.h>          // gettid()
23
#elif defined(__linux__)
24
#   include <sys/syscall.h>     /* syscall(SYS_gettid) */
25
#elif defined(__FreeBSD__)
26
#   include <pthread_np.h>      /* pthread_getthreadid_np() */
27
#elif defined(__FreeBSD_kernel__)
28
#   include <sys/syscall.h>     /* syscall(SYS_thr_self) */
29
#elif defined(_AIX)
30
#   include <sys/thread.h>      /* thread_self() */
31
#elif defined(__NetBSD__)
32
#   include <lwp.h>             /* _lwp_self() */
33
#elif defined(__DragonFly__)
34
#   include <sys/lwp.h>         /* lwp_gettid() */
35
#elif defined(__sun__) && SIZEOF_LONG >= 8
36
#   include <thread.h>
37
#endif
38
39
/* The POSIX spec requires that use of pthread_attr_setstacksize
40
   be conditional on _POSIX_THREAD_ATTR_STACKSIZE being defined. */
41
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
42
#ifndef THREAD_STACK_SIZE
43
0
#define THREAD_STACK_SIZE       0       /* use default stack size */
44
#endif
45
46
/* The default stack size for new threads on BSD is small enough that
47
 * we'll get hard crashes instead of 'maximum recursion depth exceeded'
48
 * exceptions.
49
 *
50
 * The default stack size below is the empirically determined minimal stack
51
 * sizes where a simple recursive function doesn't cause a hard crash.
52
 *
53
 * For macOS the value of THREAD_STACK_SIZE is determined in configure.ac
54
 * as it also depends on the other configure options like chosen sanitizer
55
 * runtimes.
56
 */
57
#if defined(__FreeBSD__) && defined(THREAD_STACK_SIZE) && THREAD_STACK_SIZE == 0
58
#undef  THREAD_STACK_SIZE
59
#define THREAD_STACK_SIZE       0x400000
60
#endif
61
#if defined(_AIX) && defined(THREAD_STACK_SIZE) && THREAD_STACK_SIZE == 0
62
#undef  THREAD_STACK_SIZE
63
#define THREAD_STACK_SIZE       0x200000
64
#endif
65
/* bpo-38852: test_threading.test_recursion_limit() checks that 1000 recursive
66
   Python calls (default recursion limit) doesn't crash, but raise a regular
67
   RecursionError exception. In debug mode, Python function calls allocates
68
   more memory on the stack, so use a stack of 8 MiB. */
69
#if defined(__ANDROID__) && defined(THREAD_STACK_SIZE) && THREAD_STACK_SIZE == 0
70
#   ifdef Py_DEBUG
71
#   undef  THREAD_STACK_SIZE
72
#   define THREAD_STACK_SIZE    0x800000
73
#   endif
74
#endif
75
#if defined(__VXWORKS__) && defined(THREAD_STACK_SIZE) && THREAD_STACK_SIZE == 0
76
#undef  THREAD_STACK_SIZE
77
#define THREAD_STACK_SIZE       0x100000
78
#endif
79
/* for safety, ensure a viable minimum stacksize */
80
0
#define THREAD_STACK_MIN        0x8000  /* 32 KiB */
81
#else  /* !_POSIX_THREAD_ATTR_STACKSIZE */
82
#ifdef THREAD_STACK_SIZE
83
#error "THREAD_STACK_SIZE defined but _POSIX_THREAD_ATTR_STACKSIZE undefined"
84
#endif
85
#endif
86
87
/* The POSIX spec says that implementations supporting the sem_*
88
   family of functions must indicate this by defining
89
   _POSIX_SEMAPHORES. */
90
#ifdef _POSIX_SEMAPHORES
91
/* On FreeBSD 4.x, _POSIX_SEMAPHORES is defined empty, so
92
   we need to add 0 to make it work there as well. */
93
#if (_POSIX_SEMAPHORES+0) == -1
94
#  define HAVE_BROKEN_POSIX_SEMAPHORES
95
#else
96
#  include <semaphore.h>
97
#  include <errno.h>
98
#endif
99
#endif
100
101
/* Thread sanitizer doesn't currently support sem_clockwait */
102
#ifdef _Py_THREAD_SANITIZER
103
#undef HAVE_SEM_CLOCKWAIT
104
#endif
105
106
107
/* On platforms that don't use standard POSIX threads pthread_sigmask()
108
 * isn't present.  DEC threads uses sigprocmask() instead as do most
109
 * other UNIX International compliant systems that don't have the full
110
 * pthread implementation.
111
 */
112
#if defined(HAVE_PTHREAD_SIGMASK) && !defined(HAVE_BROKEN_PTHREAD_SIGMASK)
113
#  define SET_THREAD_SIGMASK pthread_sigmask
114
#else
115
#  define SET_THREAD_SIGMASK sigprocmask
116
#endif
117
118
119
/*
120
 * pthread_cond support
121
 */
122
123
111
#define condattr_monotonic _PyRuntime.threads._condattr_monotonic.ptr
124
125
static void
126
init_condattr(void)
127
37
{
128
37
#ifdef CONDATTR_MONOTONIC
129
111
# define ca _PyRuntime.threads._condattr_monotonic.val
130
    // XXX We need to check the return code?
131
37
    pthread_condattr_init(&ca);
132
    // XXX We need to run pthread_condattr_destroy() during runtime fini.
133
37
    if (pthread_condattr_setclock(&ca, CLOCK_MONOTONIC) == 0) {
134
37
        condattr_monotonic = &ca;  // Use monotonic clock
135
37
    }
136
37
# undef ca
137
37
#endif  // CONDATTR_MONOTONIC
138
37
}
139
140
int
141
_PyThread_cond_init(PyCOND_T *cond)
142
74
{
143
74
    return pthread_cond_init(cond, condattr_monotonic);
144
74
}
145
146
147
void
148
_PyThread_cond_after(long long us, struct timespec *abs)
149
0
{
150
0
    PyTime_t timeout = _PyTime_FromMicrosecondsClamp(us);
151
0
    PyTime_t t;
152
0
#ifdef CONDATTR_MONOTONIC
153
0
    if (condattr_monotonic) {
154
        // silently ignore error: cannot report error to the caller
155
0
        (void)PyTime_MonotonicRaw(&t);
156
0
    }
157
0
    else
158
0
#endif
159
0
    {
160
        // silently ignore error: cannot report error to the caller
161
0
        (void)PyTime_TimeRaw(&t);
162
0
    }
163
0
    t = _PyTime_Add(t, timeout);
164
0
    _PyTime_AsTimespec_clamp(t, abs);
165
0
}
166
167
168
/* A pthread mutex isn't sufficient to model the Python lock type
169
 * because, according to Draft 5 of the docs (P1003.4a/D5), both of the
170
 * following are undefined:
171
 *  -> a thread tries to lock a mutex it already has locked
172
 *  -> a thread tries to unlock a mutex locked by a different thread
173
 * pthread mutexes are designed for serializing threads over short pieces
174
 * of code anyway, so wouldn't be an appropriate implementation of
175
 * Python's locks regardless.
176
 *
177
 * The pthread_lock struct implements a Python lock as a "locked?" bit
178
 * and a <condition, mutex> pair.  In general, if the bit can be acquired
179
 * instantly, it is, else the pair is used to block the thread until the
180
 * bit is cleared.     9 May 1994 tim@ksr.com
181
 */
182
183
typedef struct {
184
    char             locked; /* 0=unlocked, 1=locked */
185
    /* a <cond, mutex> pair to handle an acquire of a locked lock */
186
    pthread_cond_t   lock_released;
187
    pthread_mutex_t  mut;
188
} pthread_lock;
189
190
#define CHECK_STATUS(name)  if (status != 0) { perror(name); error = 1; }
191
#define CHECK_STATUS_PTHREAD(name)  if (status != 0) { fprintf(stderr, \
192
    "%s: %s\n", name, strerror(status)); error = 1; }
193
194
/*
195
 * Initialization for the current runtime.
196
 */
197
static void
198
PyThread__init_thread(void)
199
37
{
200
    // The library is only initialized once in the process,
201
    // regardless of how many times the Python runtime is initialized.
202
37
    static int lib_initialized = 0;
203
37
    if (!lib_initialized) {
204
37
        lib_initialized = 1;
205
#if defined(_AIX) && defined(__GNUC__)
206
        extern void pthread_init(void);
207
        pthread_init();
208
#endif
209
37
    }
210
37
    init_condattr();
211
37
}
212
213
/*
214
 * Thread support.
215
 */
216
217
/* bpo-33015: pythread_callback struct and pythread_wrapper() cast
218
   "void func(void *)" to "void* func(void *)": always return NULL.
219
220
   PyThread_start_new_thread() uses "void func(void *)" type, whereas
221
   pthread_create() requires a void* return value. */
222
typedef struct {
223
    void (*func) (void *);
224
    void *arg;
225
} pythread_callback;
226
227
static void *
228
pythread_wrapper(void *arg)
229
0
{
230
    /* copy func and func_arg and free the temporary structure */
231
0
    pythread_callback *callback = arg;
232
0
    void (*func)(void *) = callback->func;
233
0
    void *func_arg = callback->arg;
234
0
    free(callback);
235
236
0
    func(func_arg);
237
0
    return NULL;
238
0
}
239
240
static int
241
do_start_joinable_thread(void (*func)(void *), void *arg, pthread_t* out_id)
242
0
{
243
0
    pthread_t th;
244
0
    int status;
245
0
#if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
246
0
    pthread_attr_t attrs;
247
0
#endif
248
0
#if defined(THREAD_STACK_SIZE)
249
0
    size_t      tss;
250
0
#endif
251
252
0
    if (!initialized)
253
0
        PyThread_init_thread();
254
255
0
#if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
256
0
    if (pthread_attr_init(&attrs) != 0)
257
0
        return -1;
258
0
#endif
259
0
#if defined(THREAD_STACK_SIZE)
260
0
    PyThreadState *tstate = _PyThreadState_GET();
261
0
    size_t stacksize = tstate ? tstate->interp->threads.stacksize : 0;
262
0
    tss = (stacksize != 0) ? stacksize : THREAD_STACK_SIZE;
263
0
    if (tss != 0) {
264
0
        if (pthread_attr_setstacksize(&attrs, tss) != 0) {
265
0
            pthread_attr_destroy(&attrs);
266
0
            return -1;
267
0
        }
268
0
    }
269
0
#endif
270
0
#if defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
271
0
    pthread_attr_setscope(&attrs, PTHREAD_SCOPE_SYSTEM);
272
0
#endif
273
274
    // Use free() instead of PyMem_RawFree() in pythread_wrapper() to avoid a
275
    // data race if another thread calls PyMem_SetAllocator() in parallel.
276
0
    pythread_callback *callback = malloc(sizeof(pythread_callback));
277
278
0
    if (callback == NULL) {
279
0
      return -1;
280
0
    }
281
282
0
    callback->func = func;
283
0
    callback->arg = arg;
284
285
0
    status = pthread_create(&th,
286
0
#if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
287
0
                             &attrs,
288
#else
289
                             (pthread_attr_t*)NULL,
290
#endif
291
0
                             pythread_wrapper, callback);
292
293
0
#if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
294
0
    pthread_attr_destroy(&attrs);
295
0
#endif
296
297
0
    if (status != 0) {
298
0
        free(callback);
299
0
        return -1;
300
0
    }
301
0
    *out_id = th;
302
0
    return 0;
303
0
}
304
305
/* Helper to convert pthread_t to PyThread_ident_t. POSIX allows pthread_t to be
306
   non-arithmetic, e.g., musl typedefs it as a pointer. */
307
static PyThread_ident_t
308
81.4M
_pthread_t_to_ident(pthread_t value) {
309
// Cast through an integer type of the same size to avoid sign-extension.
310
81.4M
#if SIZEOF_PTHREAD_T == SIZEOF_VOID_P
311
81.4M
    return (uintptr_t) value;
312
#elif SIZEOF_PTHREAD_T == SIZEOF_LONG
313
    return (unsigned long) value;
314
#elif SIZEOF_PTHREAD_T == SIZEOF_INT
315
    return (unsigned int) value;
316
#elif SIZEOF_PTHREAD_T == SIZEOF_LONG_LONG
317
    return (unsigned long long) value;
318
#else
319
#error "Unsupported SIZEOF_PTHREAD_T value"
320
#endif
321
81.4M
}
322
323
int
324
PyThread_start_joinable_thread(void (*func)(void *), void *arg,
325
0
                               PyThread_ident_t* ident, PyThread_handle_t* handle) {
326
0
    pthread_t th = (pthread_t) 0;
327
0
    if (do_start_joinable_thread(func, arg, &th)) {
328
0
        return -1;
329
0
    }
330
0
    *ident = _pthread_t_to_ident(th);
331
0
    *handle = (PyThread_handle_t) th;
332
0
    assert(th == (pthread_t) *handle);
333
0
    return 0;
334
0
}
335
336
unsigned long
337
PyThread_start_new_thread(void (*func)(void *), void *arg)
338
0
{
339
0
    pthread_t th = (pthread_t) 0;
340
0
    if (do_start_joinable_thread(func, arg, &th)) {
341
0
        return PYTHREAD_INVALID_THREAD_ID;
342
0
    }
343
0
    pthread_detach(th);
344
0
    return (unsigned long) _pthread_t_to_ident(th);;
345
0
}
346
347
int
348
0
PyThread_join_thread(PyThread_handle_t th) {
349
0
    return pthread_join((pthread_t) th, NULL);
350
0
}
351
352
int
353
0
PyThread_detach_thread(PyThread_handle_t th) {
354
0
    return pthread_detach((pthread_t) th);
355
0
}
356
357
/* XXX This implementation is considered (to quote Tim Peters) "inherently
358
   hosed" because:
359
     - It does not guarantee the promise that a non-zero integer is returned.
360
     - The cast to unsigned long is inherently unsafe.
361
     - It is not clear that the 'volatile' (for AIX?) are any longer necessary.
362
*/
363
PyThread_ident_t
364
81.4M
PyThread_get_thread_ident_ex(void) {
365
81.4M
    volatile pthread_t threadid;
366
81.4M
    if (!initialized)
367
37
        PyThread_init_thread();
368
81.4M
    threadid = pthread_self();
369
81.4M
    return _pthread_t_to_ident(threadid);
370
81.4M
}
371
372
unsigned long
373
PyThread_get_thread_ident(void)
374
78.5M
{
375
78.5M
    return (unsigned long) PyThread_get_thread_ident_ex();
376
78.5M
}
377
378
#ifdef PY_HAVE_THREAD_NATIVE_ID
379
unsigned long
380
PyThread_get_thread_native_id(void)
381
45
{
382
45
    if (!initialized)
383
0
        PyThread_init_thread();
384
#ifdef __APPLE__
385
    uint64_t native_id;
386
    (void) pthread_threadid_np(NULL, &native_id);
387
#elif defined(HAVE_GETTID)
388
    pid_t native_id;
389
45
    native_id = gettid();
390
#elif defined(__linux__)
391
    pid_t native_id;
392
    native_id = syscall(SYS_gettid);
393
#elif defined(__FreeBSD__)
394
    int native_id;
395
    native_id = pthread_getthreadid_np();
396
#elif defined(__FreeBSD_kernel__)
397
    long native_id;
398
    syscall(SYS_thr_self, &native_id);
399
#elif defined(__OpenBSD__)
400
    pid_t native_id;
401
    native_id = getthrid();
402
#elif defined(_AIX)
403
    tid_t native_id;
404
    native_id = thread_self();
405
#elif defined(__NetBSD__)
406
    lwpid_t native_id;
407
    native_id = _lwp_self();
408
#elif defined(__DragonFly__)
409
    // lwp_gettid() is only unique within a process, so combine it with the pid.
410
    unsigned long native_id = (unsigned long)getpid() << 32 | lwp_gettid();
411
#elif defined(__sun__) && SIZEOF_LONG >= 8
412
    unsigned long native_id = (unsigned long)getpid() << 32 | thr_self();
413
#endif
414
45
    return (unsigned long) native_id;
415
45
}
416
#endif
417
418
void _Py_NO_RETURN
419
PyThread_exit_thread(void)
420
0
{
421
0
    if (!initialized)
422
0
        exit(0);
423
#if defined(__wasi__)
424
    /*
425
     * wasi-threads doesn't have pthread_exit right now
426
     * cf. https://github.com/WebAssembly/wasi-threads/issues/7
427
     */
428
    abort();
429
#else
430
0
    pthread_exit(0);
431
0
#endif
432
0
}
433
434
void _Py_NO_RETURN
435
PyThread_hang_thread(void)
436
0
{
437
0
    while (1) {
438
#if defined(__wasi__)
439
        sleep(9999999);  // WASI doesn't have pause() ?!
440
#else
441
0
        pause();
442
0
#endif
443
0
    }
444
0
}
445
446
447
/* set the thread stack size.
448
 * Return 0 if size is valid, -1 if size is invalid,
449
 * -2 if setting stack size is not supported.
450
 */
451
static int
452
_pythread_pthread_set_stacksize(size_t size)
453
0
{
454
0
#if defined(THREAD_STACK_SIZE)
455
0
    pthread_attr_t attrs;
456
0
    size_t tss_min;
457
0
    int rc = 0;
458
0
#endif
459
460
    /* set to default */
461
0
    if (size == 0) {
462
0
        _PyInterpreterState_GET()->threads.stacksize = 0;
463
0
        return 0;
464
0
    }
465
466
0
#if defined(THREAD_STACK_SIZE)
467
0
#if defined(PTHREAD_STACK_MIN)
468
0
    tss_min = PTHREAD_STACK_MIN > THREAD_STACK_MIN ? PTHREAD_STACK_MIN
469
0
                                                   : THREAD_STACK_MIN;
470
#else
471
    tss_min = THREAD_STACK_MIN;
472
#endif
473
0
    if (size >= tss_min) {
474
        /* validate stack size by setting thread attribute */
475
0
        if (pthread_attr_init(&attrs) == 0) {
476
0
            rc = pthread_attr_setstacksize(&attrs, size);
477
0
            pthread_attr_destroy(&attrs);
478
0
            if (rc == 0) {
479
0
                _PyInterpreterState_GET()->threads.stacksize = size;
480
0
                return 0;
481
0
            }
482
0
        }
483
0
    }
484
0
    return -1;
485
#else
486
    return -2;
487
#endif
488
0
}
489
490
0
#define THREAD_SET_STACKSIZE(x) _pythread_pthread_set_stacksize(x)
491
492
493
/* Thread Local Storage (TLS) API
494
495
   This API is DEPRECATED since Python 3.7.  See PEP 539 for details.
496
*/
497
498
/* Issue #25658: On platforms where native TLS key is defined in a way that
499
   cannot be safely cast to int, PyThread_create_key returns immediately a
500
   failure status and other TLS functions all are no-ops.  This indicates
501
   clearly that the old API is not supported on platforms where it cannot be
502
   used reliably, and that no effort will be made to add such support.
503
504
   Note: PTHREAD_KEY_T_IS_COMPATIBLE_WITH_INT will be unnecessary after
505
   removing this API.
506
*/
507
508
int
509
PyThread_create_key(void)
510
0
{
511
0
#ifdef PTHREAD_KEY_T_IS_COMPATIBLE_WITH_INT
512
0
    pthread_key_t key;
513
0
    int fail = pthread_key_create(&key, NULL);
514
0
    if (fail)
515
0
        return -1;
516
0
    if (key > INT_MAX) {
517
        /* Issue #22206: handle integer overflow */
518
0
        pthread_key_delete(key);
519
0
        errno = ENOMEM;
520
0
        return -1;
521
0
    }
522
0
    return (int)key;
523
#else
524
    return -1;  /* never return valid key value. */
525
#endif
526
0
}
527
528
void
529
PyThread_delete_key(int key)
530
0
{
531
0
#ifdef PTHREAD_KEY_T_IS_COMPATIBLE_WITH_INT
532
0
    pthread_key_delete(key);
533
0
#endif
534
0
}
535
536
void
537
PyThread_delete_key_value(int key)
538
0
{
539
0
#ifdef PTHREAD_KEY_T_IS_COMPATIBLE_WITH_INT
540
0
    pthread_setspecific(key, NULL);
541
0
#endif
542
0
}
543
544
int
545
PyThread_set_key_value(int key, void *value)
546
0
{
547
0
#ifdef PTHREAD_KEY_T_IS_COMPATIBLE_WITH_INT
548
0
    int fail = pthread_setspecific(key, value);
549
0
    return fail ? -1 : 0;
550
#else
551
    return -1;
552
#endif
553
0
}
554
555
void *
556
PyThread_get_key_value(int key)
557
0
{
558
0
#ifdef PTHREAD_KEY_T_IS_COMPATIBLE_WITH_INT
559
0
    return pthread_getspecific(key);
560
#else
561
    return NULL;
562
#endif
563
0
}
564
565
566
void
567
PyThread_ReInitTLS(void)
568
0
{
569
0
}
570
571
572
/* Thread Specific Storage (TSS) API
573
574
   Platform-specific components of TSS API implementation.
575
*/
576
577
int
578
PyThread_tss_create(Py_tss_t *key)
579
37
{
580
37
    assert(key != NULL);
581
    /* If the key has been created, function is silently skipped. */
582
37
    if (key->_is_initialized) {
583
0
        return 0;
584
0
    }
585
586
37
    int fail = pthread_key_create(&(key->_key), NULL);
587
37
    if (fail) {
588
0
        return -1;
589
0
    }
590
37
    key->_is_initialized = 1;
591
37
    return 0;
592
37
}
593
594
void
595
PyThread_tss_delete(Py_tss_t *key)
596
0
{
597
0
    assert(key != NULL);
598
    /* If the key has not been created, function is silently skipped. */
599
0
    if (!key->_is_initialized) {
600
0
        return;
601
0
    }
602
603
0
    pthread_key_delete(key->_key);
604
    /* pthread has not provided the defined invalid value for the key. */
605
0
    key->_is_initialized = 0;
606
0
}
607
608
int
609
PyThread_tss_set(Py_tss_t *key, void *value)
610
0
{
611
0
    assert(key != NULL);
612
0
    int fail = pthread_setspecific(key->_key, value);
613
0
    return fail ? -1 : 0;
614
0
}
615
616
void *
617
PyThread_tss_get(Py_tss_t *key)
618
0
{
619
    assert(key != NULL);
620
0
    return pthread_getspecific(key->_key);
621
0
}