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