• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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