/* * Copyright © 2024 Nick Bowler * * Helpers for hosts using POSIX threading (pthreads) API. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The pthread_in_use detection is based on Gnulib code written by Bruno * Haible, distributed with the following copyright and permission notice: * * Copyright (C) 2005-2022 Free Software Foundation, Inc. * * This file is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include extern int glthread_in_use(); #if !defined c11_threads_in_use # if HAVE_THREADS_H && USE_POSIX_THREADS_FROM_LIBC # define c11_threads_in_use 1 # elif HAVE_THREADS_H && USE_POSIX_THREADS_WEAK # include # pragma weak thrd_exit # define c11_threads_in_use (thrd_exit != NULL) # else # define c11_threads_in_use 0 # endif #endif #if PTHREAD_IN_USE_DETECTION_HARD # define pthread_in_use glthread_in_use() #elif USE_POSIX_THREADS_WEAK # pragma weak pthread_mutexattr_gettype # define pthread_in_use (pthread_mutexattr_gettype || c11_threads_in_use) #else # define pthread_in_use 1 #endif #if USE_POSIX_THREADS_WEAK # pragma weak pthread_key_create # pragma weak pthread_getspecific # pragma weak pthread_setspecific # pragma weak pthread_once #endif static struct { union { pthread_key_t mt; #if USE_POSIX_THREADS_WEAK void *st; #endif } u; #if USE_POSIX_THREADS_WEAK signed char valid; #else bool valid; #endif } tls_key; #define tls_key_valid (tls_key.valid != 0) static void init_once_cb(void); static void *tls_get(void) { #if USE_POSIX_THREADS_WEAK if (tls_key.valid < 0) return tls_key.u.st; #endif return pthread_getspecific(tls_key.u.mt); } static int tls_set(void *p) { #if USE_POSIX_THREADS_WEAK if (tls_key.valid < 0) return (tls_key.u.st = p, 1); #endif return !pthread_setspecific(tls_key.u.mt, p); } static void init_once_with_tls(void) { tls_key.valid = !pthread_key_create(&tls_key.u.mt, free); init_once_cb(); } static int init_once(void) { if (pthread_in_use) { static pthread_once_t ctrl = PTHREAD_ONCE_INIT; return !pthread_once(&ctrl, init_once_with_tls); } else if (!tls_key_valid) { tls_key.valid = -1; init_once_cb(); } return 1; }