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