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_structseq.h" // _PyStructSequence_FiniBuiltin()
12 #include "pycore_pythread.h" // _POSIX_THREADS
13
14 #ifndef DONT_HAVE_STDIO_H
15 # include <stdio.h>
16 #endif
17
18 #include <stdlib.h>
19
20
21 // Define PY_TIMEOUT_MAX constant.
22 #ifdef _POSIX_THREADS
23 // PyThread_acquire_lock_timed() uses (us * 1000) to convert microseconds
24 // to nanoseconds.
25 # define PY_TIMEOUT_MAX_VALUE (LLONG_MAX / 1000)
26 #elif defined (NT_THREADS)
27 // WaitForSingleObject() accepts timeout in milliseconds in the range
28 // [0; 0xFFFFFFFE] (DWORD type). INFINITE value (0xFFFFFFFF) means no
29 // timeout. 0xFFFFFFFE milliseconds is around 49.7 days.
30 # if 0xFFFFFFFELL < LLONG_MAX / 1000
31 # define PY_TIMEOUT_MAX_VALUE (0xFFFFFFFELL * 1000)
32 # else
33 # define PY_TIMEOUT_MAX_VALUE LLONG_MAX
34 # endif
35 #else
36 # define PY_TIMEOUT_MAX_VALUE LLONG_MAX
37 #endif
38 const long long PY_TIMEOUT_MAX = PY_TIMEOUT_MAX_VALUE;
39
40
41 static void PyThread__init_thread(void); /* Forward */
42
43 #define initialized _PyRuntime.threads.initialized
44
45 void
PyThread_init_thread(void)46 PyThread_init_thread(void)
47 {
48 if (initialized) {
49 return;
50 }
51 initialized = 1;
52 PyThread__init_thread();
53 }
54
55 #if defined(HAVE_PTHREAD_STUBS)
56 # define PYTHREAD_NAME "pthread-stubs"
57 # include "thread_pthread_stubs.h"
58 #elif defined(_USE_PTHREADS) /* AKA _PTHREADS */
59 # if defined(__EMSCRIPTEN__) && !defined(__EMSCRIPTEN_PTHREADS__)
60 # define PYTHREAD_NAME "pthread-stubs"
61 # else
62 # define PYTHREAD_NAME "pthread"
63 # endif
64 # include "thread_pthread.h"
65 #elif defined(NT_THREADS)
66 # define PYTHREAD_NAME "nt"
67 # include "thread_nt.h"
68 #else
69 # error "Require native threads. See https://bugs.python.org/issue31370"
70 #endif
71
72
73 /* return the current thread stack size */
74 size_t
PyThread_get_stacksize(void)75 PyThread_get_stacksize(void)
76 {
77 return _PyInterpreterState_GET()->threads.stacksize;
78 }
79
80 /* Only platforms defining a THREAD_SET_STACKSIZE() macro
81 in thread_<platform>.h support changing the stack size.
82 Return 0 if stack size is valid,
83 -1 if stack size value is invalid,
84 -2 if setting stack size is not supported. */
85 int
PyThread_set_stacksize(size_t size)86 PyThread_set_stacksize(size_t size)
87 {
88 #if defined(THREAD_SET_STACKSIZE)
89 return THREAD_SET_STACKSIZE(size);
90 #else
91 return -2;
92 #endif
93 }
94
95
96 int
PyThread_ParseTimeoutArg(PyObject * arg,int blocking,PY_TIMEOUT_T * timeout_p)97 PyThread_ParseTimeoutArg(PyObject *arg, int blocking, PY_TIMEOUT_T *timeout_p)
98 {
99 assert(_PyTime_FromSeconds(-1) == PyThread_UNSET_TIMEOUT);
100 if (arg == NULL || arg == Py_None) {
101 *timeout_p = blocking ? PyThread_UNSET_TIMEOUT : 0;
102 return 0;
103 }
104 if (!blocking) {
105 PyErr_SetString(PyExc_ValueError,
106 "can't specify a timeout for a non-blocking call");
107 return -1;
108 }
109
110 PyTime_t timeout;
111 if (_PyTime_FromSecondsObject(&timeout, arg, _PyTime_ROUND_TIMEOUT) < 0) {
112 return -1;
113 }
114 if (timeout < 0) {
115 PyErr_SetString(PyExc_ValueError,
116 "timeout value must be a non-negative number");
117 return -1;
118 }
119
120 if (_PyTime_AsMicroseconds(timeout,
121 _PyTime_ROUND_TIMEOUT) > PY_TIMEOUT_MAX) {
122 PyErr_SetString(PyExc_OverflowError,
123 "timeout value is too large");
124 return -1;
125 }
126 *timeout_p = timeout;
127 return 0;
128 }
129
130 PyLockStatus
PyThread_acquire_lock_timed_with_retries(PyThread_type_lock lock,PY_TIMEOUT_T timeout)131 PyThread_acquire_lock_timed_with_retries(PyThread_type_lock lock,
132 PY_TIMEOUT_T timeout)
133 {
134 PyThreadState *tstate = _PyThreadState_GET();
135 PyTime_t endtime = 0;
136 if (timeout > 0) {
137 endtime = _PyDeadline_Init(timeout);
138 }
139
140 PyLockStatus r;
141 do {
142 PyTime_t microseconds;
143 microseconds = _PyTime_AsMicroseconds(timeout, _PyTime_ROUND_CEILING);
144
145 /* first a simple non-blocking try without releasing the GIL */
146 r = PyThread_acquire_lock_timed(lock, 0, 0);
147 if (r == PY_LOCK_FAILURE && microseconds != 0) {
148 Py_BEGIN_ALLOW_THREADS
149 r = PyThread_acquire_lock_timed(lock, microseconds, 1);
150 Py_END_ALLOW_THREADS
151 }
152
153 if (r == PY_LOCK_INTR) {
154 /* Run signal handlers if we were interrupted. Propagate
155 * exceptions from signal handlers, such as KeyboardInterrupt, by
156 * passing up PY_LOCK_INTR. */
157 if (_PyEval_MakePendingCalls(tstate) < 0) {
158 return PY_LOCK_INTR;
159 }
160
161 /* If we're using a timeout, recompute the timeout after processing
162 * signals, since those can take time. */
163 if (timeout > 0) {
164 timeout = _PyDeadline_Get(endtime);
165
166 /* Check for negative values, since those mean block forever.
167 */
168 if (timeout < 0) {
169 r = PY_LOCK_FAILURE;
170 }
171 }
172 }
173 } while (r == PY_LOCK_INTR); /* Retry if we were interrupted. */
174
175 return r;
176 }
177
178
179 /* Thread Specific Storage (TSS) API
180
181 Cross-platform components of TSS API implementation.
182 */
183
184 Py_tss_t *
PyThread_tss_alloc(void)185 PyThread_tss_alloc(void)
186 {
187 Py_tss_t *new_key = (Py_tss_t *)PyMem_RawMalloc(sizeof(Py_tss_t));
188 if (new_key == NULL) {
189 return NULL;
190 }
191 new_key->_is_initialized = 0;
192 return new_key;
193 }
194
195 void
PyThread_tss_free(Py_tss_t * key)196 PyThread_tss_free(Py_tss_t *key)
197 {
198 if (key != NULL) {
199 PyThread_tss_delete(key);
200 PyMem_RawFree((void *)key);
201 }
202 }
203
204 int
PyThread_tss_is_created(Py_tss_t * key)205 PyThread_tss_is_created(Py_tss_t *key)
206 {
207 assert(key != NULL);
208 return key->_is_initialized;
209 }
210
211
212 PyDoc_STRVAR(threadinfo__doc__,
213 "sys.thread_info\n\
214 \n\
215 A named tuple holding information about the thread implementation.");
216
217 static PyStructSequence_Field threadinfo_fields[] = {
218 {"name", "name of the thread implementation"},
219 {"lock", "name of the lock implementation"},
220 {"version", "name and version of the thread library"},
221 {0}
222 };
223
224 static PyStructSequence_Desc threadinfo_desc = {
225 "sys.thread_info", /* name */
226 threadinfo__doc__, /* doc */
227 threadinfo_fields, /* fields */
228 3
229 };
230
231 static PyTypeObject ThreadInfoType;
232
233 PyObject*
PyThread_GetInfo(void)234 PyThread_GetInfo(void)
235 {
236 PyObject *threadinfo, *value;
237 int pos = 0;
238 #if (defined(_POSIX_THREADS) && defined(HAVE_CONFSTR) \
239 && defined(_CS_GNU_LIBPTHREAD_VERSION))
240 char buffer[255];
241 int len;
242 #endif
243
244 PyInterpreterState *interp = _PyInterpreterState_GET();
245 if (_PyStructSequence_InitBuiltin(interp, &ThreadInfoType, &threadinfo_desc) < 0) {
246 return NULL;
247 }
248
249 threadinfo = PyStructSequence_New(&ThreadInfoType);
250 if (threadinfo == NULL)
251 return NULL;
252
253 value = PyUnicode_FromString(PYTHREAD_NAME);
254 if (value == NULL) {
255 Py_DECREF(threadinfo);
256 return NULL;
257 }
258 PyStructSequence_SET_ITEM(threadinfo, pos++, value);
259
260 #ifdef HAVE_PTHREAD_STUBS
261 value = Py_NewRef(Py_None);
262 #elif defined(_POSIX_THREADS)
263 #ifdef USE_SEMAPHORES
264 value = PyUnicode_FromString("semaphore");
265 #else
266 value = PyUnicode_FromString("mutex+cond");
267 #endif
268 if (value == NULL) {
269 Py_DECREF(threadinfo);
270 return NULL;
271 }
272 #else
273 value = Py_NewRef(Py_None);
274 #endif
275 PyStructSequence_SET_ITEM(threadinfo, pos++, value);
276
277 #if (defined(_POSIX_THREADS) && defined(HAVE_CONFSTR) \
278 && defined(_CS_GNU_LIBPTHREAD_VERSION))
279 value = NULL;
280 len = confstr(_CS_GNU_LIBPTHREAD_VERSION, buffer, sizeof(buffer));
281 if (1 < len && (size_t)len < sizeof(buffer)) {
282 value = PyUnicode_DecodeFSDefaultAndSize(buffer, len-1);
283 if (value == NULL)
284 PyErr_Clear();
285 }
286 if (value == NULL)
287 #endif
288 {
289 value = Py_NewRef(Py_None);
290 }
291 PyStructSequence_SET_ITEM(threadinfo, pos++, value);
292 return threadinfo;
293 }
294
295
296 void
_PyThread_FiniType(PyInterpreterState * interp)297 _PyThread_FiniType(PyInterpreterState *interp)
298 {
299 _PyStructSequence_FiniBuiltin(interp, &ThreadInfoType);
300 }
301