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