Coverage Report

Created: 2025-07-04 06:49

/src/cpython/Python/thread.c
Line
Count
Source (jump to first uncovered line)
1
2
/* Thread package.
3
   This is intended to be usable independently from Python.
4
   The implementation for system foobar is in a file thread_foobar.h
5
   which is included by this file dependent on config settings.
6
   Stuff shared by all thread_*.h files is collected here. */
7
8
#include "Python.h"
9
#include "pycore_ceval.h"         // _PyEval_MakePendingCalls()
10
#include "pycore_pystate.h"       // _PyInterpreterState_GET()
11
#include "pycore_pythread.h"      // _POSIX_THREADS
12
#include "pycore_runtime.h"       // _PyRuntime
13
#include "pycore_structseq.h"     // _PyStructSequence_FiniBuiltin()
14
15
#ifndef DONT_HAVE_STDIO_H
16
#  include <stdio.h>
17
#endif
18
19
#include <stdlib.h>
20
21
22
// Define PY_TIMEOUT_MAX constant.
23
#ifdef _POSIX_THREADS
24
   // PyThread_acquire_lock_timed() uses (us * 1000) to convert microseconds
25
   // to nanoseconds.
26
#  define PY_TIMEOUT_MAX_VALUE (LLONG_MAX / 1000)
27
#elif defined (NT_THREADS)
28
   // WaitForSingleObject() accepts timeout in milliseconds in the range
29
   // [0; 0xFFFFFFFE] (DWORD type). INFINITE value (0xFFFFFFFF) means no
30
   // timeout. 0xFFFFFFFE milliseconds is around 49.7 days.
31
#  if 0xFFFFFFFELL < LLONG_MAX / 1000
32
#    define PY_TIMEOUT_MAX_VALUE (0xFFFFFFFELL * 1000)
33
#  else
34
#    define PY_TIMEOUT_MAX_VALUE LLONG_MAX
35
#  endif
36
#else
37
#  define PY_TIMEOUT_MAX_VALUE LLONG_MAX
38
#endif
39
const long long PY_TIMEOUT_MAX = PY_TIMEOUT_MAX_VALUE;
40
41
42
/* Forward declaration */
43
static void PyThread__init_thread(void);
44
45
114M
#define initialized _PyRuntime.threads.initialized
46
47
void
48
PyThread_init_thread(void)
49
48
{
50
48
    if (initialized) {
51
32
        return;
52
32
    }
53
16
    initialized = 1;
54
16
    PyThread__init_thread();
55
16
}
56
57
#if defined(HAVE_PTHREAD_STUBS)
58
#   define PYTHREAD_NAME "pthread-stubs"
59
#   include "thread_pthread_stubs.h"
60
#elif defined(_USE_PTHREADS)  /* AKA _PTHREADS */
61
#   if defined(__EMSCRIPTEN__) && !defined(__EMSCRIPTEN_PTHREADS__)
62
#     define PYTHREAD_NAME "pthread-stubs"
63
#   else
64
16
#     define PYTHREAD_NAME "pthread"
65
#   endif
66
#   include "thread_pthread.h"
67
#elif defined(NT_THREADS)
68
#   define PYTHREAD_NAME "nt"
69
#   include "thread_nt.h"
70
#else
71
#   error "Require native threads. See https://bugs.python.org/issue31370"
72
#endif
73
74
75
/*
76
 * Lock support.
77
 */
78
79
PyThread_type_lock
80
PyThread_allocate_lock(void)
81
996
{
82
996
    if (!initialized) {
83
0
        PyThread_init_thread();
84
0
    }
85
86
996
    PyMutex *lock = (PyMutex *)PyMem_RawMalloc(sizeof(PyMutex));
87
996
    if (lock) {
88
996
        *lock = (PyMutex){0};
89
996
    }
90
91
996
    return (PyThread_type_lock)lock;
92
996
}
93
94
void
95
PyThread_free_lock(PyThread_type_lock lock)
96
944
{
97
944
    PyMem_RawFree(lock);
98
944
}
99
100
PyLockStatus
101
PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
102
                            int intr_flag)
103
2.83k
{
104
2.83k
    PyTime_t timeout;  // relative timeout
105
2.83k
    if (microseconds >= 0) {
106
        // bpo-41710: PyThread_acquire_lock_timed() cannot report timeout
107
        // overflow to the caller, so clamp the timeout to
108
        // [PyTime_MIN, PyTime_MAX].
109
        //
110
        // PyTime_MAX nanoseconds is around 292.3 years.
111
        //
112
        // _thread.Lock.acquire() and _thread.RLock.acquire() raise an
113
        // OverflowError if microseconds is greater than PY_TIMEOUT_MAX.
114
2.83k
        timeout = _PyTime_FromMicrosecondsClamp(microseconds);
115
2.83k
    }
116
0
    else {
117
0
        timeout = -1;
118
0
    }
119
120
2.83k
    _PyLockFlags flags = _Py_LOCK_DONT_DETACH;
121
2.83k
    if (intr_flag) {
122
0
        flags |= _PY_FAIL_IF_INTERRUPTED;
123
0
    }
124
125
2.83k
    return _PyMutex_LockTimed((PyMutex *)lock, timeout, flags);
126
2.83k
}
127
128
void
129
PyThread_release_lock(PyThread_type_lock lock)
130
2.83k
{
131
2.83k
    PyMutex_Unlock((PyMutex *)lock);
132
2.83k
}
133
134
int
135
_PyThread_at_fork_reinit(PyThread_type_lock *lock)
136
0
{
137
0
    _PyMutex_at_fork_reinit((PyMutex *)lock);
138
0
    return 0;
139
0
}
140
141
int
142
PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
143
2.83k
{
144
2.83k
    return PyThread_acquire_lock_timed(lock, waitflag ? -1 : 0, /*intr_flag=*/0);
145
2.83k
}
146
147
148
/* return the current thread stack size */
149
size_t
150
PyThread_get_stacksize(void)
151
0
{
152
0
    return _PyInterpreterState_GET()->threads.stacksize;
153
0
}
154
155
/* Only platforms defining a THREAD_SET_STACKSIZE() macro
156
   in thread_<platform>.h support changing the stack size.
157
   Return 0 if stack size is valid,
158
      -1 if stack size value is invalid,
159
      -2 if setting stack size is not supported. */
160
int
161
PyThread_set_stacksize(size_t size)
162
0
{
163
0
#if defined(THREAD_SET_STACKSIZE)
164
0
    return THREAD_SET_STACKSIZE(size);
165
#else
166
    return -2;
167
#endif
168
0
}
169
170
171
int
172
PyThread_ParseTimeoutArg(PyObject *arg, int blocking, PY_TIMEOUT_T *timeout_p)
173
0
{
174
0
    assert(_PyTime_FromSeconds(-1) == PyThread_UNSET_TIMEOUT);
175
0
    if (arg == NULL || arg == Py_None) {
176
0
        *timeout_p = blocking ? PyThread_UNSET_TIMEOUT : 0;
177
0
        return 0;
178
0
    }
179
0
    if (!blocking) {
180
0
        PyErr_SetString(PyExc_ValueError,
181
0
                        "can't specify a timeout for a non-blocking call");
182
0
        return -1;
183
0
    }
184
185
0
    PyTime_t timeout;
186
0
    if (_PyTime_FromSecondsObject(&timeout, arg, _PyTime_ROUND_TIMEOUT) < 0) {
187
0
        return -1;
188
0
    }
189
0
    if (timeout < 0) {
190
0
        PyErr_SetString(PyExc_ValueError,
191
0
                        "timeout value must be a non-negative number");
192
0
        return -1;
193
0
    }
194
195
0
    if (_PyTime_AsMicroseconds(timeout,
196
0
                               _PyTime_ROUND_TIMEOUT) > PY_TIMEOUT_MAX) {
197
0
        PyErr_SetString(PyExc_OverflowError,
198
0
                        "timeout value is too large");
199
0
        return -1;
200
0
    }
201
0
    *timeout_p = timeout;
202
0
    return 0;
203
0
}
204
205
PyLockStatus
206
PyThread_acquire_lock_timed_with_retries(PyThread_type_lock lock,
207
                                         PY_TIMEOUT_T timeout)
208
0
{
209
0
    PyThreadState *tstate = _PyThreadState_GET();
210
0
    PyTime_t endtime = 0;
211
0
    if (timeout > 0) {
212
0
        endtime = _PyDeadline_Init(timeout);
213
0
    }
214
215
0
    PyLockStatus r;
216
0
    do {
217
0
        PyTime_t microseconds;
218
0
        microseconds = _PyTime_AsMicroseconds(timeout, _PyTime_ROUND_CEILING);
219
220
        /* first a simple non-blocking try without releasing the GIL */
221
0
        r = PyThread_acquire_lock_timed(lock, 0, 0);
222
0
        if (r == PY_LOCK_FAILURE && microseconds != 0) {
223
0
            Py_BEGIN_ALLOW_THREADS
224
0
            r = PyThread_acquire_lock_timed(lock, microseconds, 1);
225
0
            Py_END_ALLOW_THREADS
226
0
        }
227
228
0
        if (r == PY_LOCK_INTR) {
229
            /* Run signal handlers if we were interrupted.  Propagate
230
             * exceptions from signal handlers, such as KeyboardInterrupt, by
231
             * passing up PY_LOCK_INTR.  */
232
0
            if (_PyEval_MakePendingCalls(tstate) < 0) {
233
0
                return PY_LOCK_INTR;
234
0
            }
235
236
            /* If we're using a timeout, recompute the timeout after processing
237
             * signals, since those can take time.  */
238
0
            if (timeout > 0) {
239
0
                timeout = _PyDeadline_Get(endtime);
240
241
                /* Check for negative values, since those mean block forever.
242
                 */
243
0
                if (timeout < 0) {
244
0
                    r = PY_LOCK_FAILURE;
245
0
                }
246
0
            }
247
0
        }
248
0
    } while (r == PY_LOCK_INTR);  /* Retry if we were interrupted. */
249
250
0
    return r;
251
0
}
252
253
254
/* Thread Specific Storage (TSS) API
255
256
   Cross-platform components of TSS API implementation.
257
*/
258
259
Py_tss_t *
260
PyThread_tss_alloc(void)
261
0
{
262
0
    Py_tss_t *new_key = (Py_tss_t *)PyMem_RawMalloc(sizeof(Py_tss_t));
263
0
    if (new_key == NULL) {
264
0
        return NULL;
265
0
    }
266
0
    new_key->_is_initialized = 0;
267
0
    return new_key;
268
0
}
269
270
void
271
PyThread_tss_free(Py_tss_t *key)
272
0
{
273
0
    if (key != NULL) {
274
0
        PyThread_tss_delete(key);
275
0
        PyMem_RawFree((void *)key);
276
0
    }
277
0
}
278
279
int
280
PyThread_tss_is_created(Py_tss_t *key)
281
0
{
282
0
    assert(key != NULL);
283
0
    return key->_is_initialized;
284
0
}
285
286
287
PyDoc_STRVAR(threadinfo__doc__,
288
"sys.thread_info\n\
289
\n\
290
A named tuple holding information about the thread implementation.");
291
292
static PyStructSequence_Field threadinfo_fields[] = {
293
    {"name",    "name of the thread implementation"},
294
    {"lock",    "name of the lock implementation"},
295
    {"version", "name and version of the thread library"},
296
    {0}
297
};
298
299
static PyStructSequence_Desc threadinfo_desc = {
300
    "sys.thread_info",           /* name */
301
    threadinfo__doc__,           /* doc */
302
    threadinfo_fields,           /* fields */
303
    3
304
};
305
306
static PyTypeObject ThreadInfoType;
307
308
PyObject*
309
PyThread_GetInfo(void)
310
16
{
311
16
    PyObject *threadinfo, *value;
312
16
    int pos = 0;
313
16
#if (defined(_POSIX_THREADS) && defined(HAVE_CONFSTR) \
314
16
     && defined(_CS_GNU_LIBPTHREAD_VERSION))
315
16
    char buffer[255];
316
16
    int len;
317
16
#endif
318
319
16
    PyInterpreterState *interp = _PyInterpreterState_GET();
320
16
    if (_PyStructSequence_InitBuiltin(interp, &ThreadInfoType, &threadinfo_desc) < 0) {
321
0
        return NULL;
322
0
    }
323
324
16
    threadinfo = PyStructSequence_New(&ThreadInfoType);
325
16
    if (threadinfo == NULL)
326
0
        return NULL;
327
328
16
    value = PyUnicode_FromString(PYTHREAD_NAME);
329
16
    if (value == NULL) {
330
0
        Py_DECREF(threadinfo);
331
0
        return NULL;
332
0
    }
333
16
    PyStructSequence_SET_ITEM(threadinfo, pos++, value);
334
335
#ifdef HAVE_PTHREAD_STUBS
336
    value = Py_NewRef(Py_None);
337
#elif defined(_POSIX_THREADS)
338
    value = PyUnicode_FromString("pymutex");
339
16
    if (value == NULL) {
340
0
        Py_DECREF(threadinfo);
341
0
        return NULL;
342
0
    }
343
#else
344
    value = Py_NewRef(Py_None);
345
#endif
346
16
    PyStructSequence_SET_ITEM(threadinfo, pos++, value);
347
348
16
#if (defined(_POSIX_THREADS) && defined(HAVE_CONFSTR) \
349
16
     && defined(_CS_GNU_LIBPTHREAD_VERSION))
350
16
    value = NULL;
351
16
    len = confstr(_CS_GNU_LIBPTHREAD_VERSION, buffer, sizeof(buffer));
352
16
    if (1 < len && (size_t)len < sizeof(buffer)) {
353
16
        value = PyUnicode_DecodeFSDefaultAndSize(buffer, len-1);
354
16
        if (value == NULL)
355
0
            PyErr_Clear();
356
16
    }
357
16
    if (value == NULL)
358
0
#endif
359
0
    {
360
0
        value = Py_NewRef(Py_None);
361
0
    }
362
16
    PyStructSequence_SET_ITEM(threadinfo, pos++, value);
363
16
    return threadinfo;
364
16
}
365
366
367
void
368
_PyThread_FiniType(PyInterpreterState *interp)
369
0
{
370
0
    _PyStructSequence_FiniBuiltin(interp, &ThreadInfoType);
371
0
}