From: Corentin Gay Date: Thu, 14 Nov 2019 15:58:31 +0000 (+0000) Subject: Improve the thread support for VxWorks X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=806dd0472f56fd8fc410f106660b1ad2c7505bd7;p=gcc.git Improve the thread support for VxWorks 2019-11-12 Corentin Gay Jerome Lambourg Olivier Hainque libgcc/ * config/t-gthr-vxworks: New file, add all the gthr-vxworks sources to LIB2ADDEH. * config/t-vxworks: Remove adjustments to LIB2ADDEH. * config/t-vxworks7: Likewise. * config.host: Append a block at the end of the file to add the t-gthr files to the tmake_file list for VxWorks after everything else. * config/vxlib.c: Rename as gthr-vxworks.c. * config/vxlib-tls.c: Rename as gthr-vxworks-tls.c. * config/gthr-vxworks.h: Simplify a few comments. Expose a TAS API and a basic error checking API, both internal. Simplify the __gthread_once_t type definition and initializers. Add sections for condition variables support and for the C++0x thread support, conditioned against Vx653 for the latter. * config/gthr-vxworks.c (__gthread_once): Simplify comments and implementation, leveraging the TAS internal API. * config/gthr-vxworks-tls.c: Introduce an internal TLS data access API, leveraging the general availability of TLS services in VxWorks7 post SR6xxx. (__gthread_setspecific, __gthread_setspecific): Use it. (tls_delete_hook): Likewise, and simplify the enter/leave dtor logic. * config/gthr-vxworks-cond.c: New file. GTHREAD_COND variable support based on VxWorks primitives. * config/gthr-vxworks-thread.c: New file. GTHREAD_CXX0X support based on VxWorks primitives. Co-Authored-By: Jerome Lambourg Co-Authored-By: Olivier Hainque From-SVN: r278249 --- diff --git a/libgcc/ChangeLog b/libgcc/ChangeLog index 845dbd0c11a..21efeb53dbe 100644 --- a/libgcc/ChangeLog +++ b/libgcc/ChangeLog @@ -1,3 +1,37 @@ +2019-11-12 Corentin Gay + Jerome Lambourg + Olivier Hainque + + * config/t-gthr-vxworks: New file, add all the gthr-vxworks + sources to LIB2ADDEH. + * config/t-vxworks: Remove adjustments to LIB2ADDEH. + * config/t-vxworks7: Likewise. + + * config.host: Append a block at the end of the file to add the + t-gthr files to the tmake_file list for VxWorks after everything + else. + + * config/vxlib.c: Rename as gthr-vxworks.c. + * config/vxlib-tls.c: Rename as gthr-vxworks-tls.c. + + * config/gthr-vxworks.h: Simplify a few comments. Expose a TAS + API and a basic error checking API, both internal. Simplify the + __gthread_once_t type definition and initializers. Add sections + for condition variables support and for the C++0x thread support, + conditioned against Vx653 for the latter. + + * config/gthr-vxworks.c (__gthread_once): Simplify comments and + implementation, leveraging the TAS internal API. + * config/gthr-vxworks-tls.c: Introduce an internal TLS data access + API, leveraging the general availability of TLS services in VxWorks7 + post SR6xxx. + (__gthread_setspecific, __gthread_setspecific): Use it. + (tls_delete_hook): Likewise, and simplify the enter/leave dtor logic. + * config/gthr-vxworks-cond.c: New file. GTHREAD_COND variable + support based on VxWorks primitives. + * config/gthr-vxworks-thread.c: New file. GTHREAD_CXX0X support + based on VxWorks primitives. + 2019-11-06 Jerome Lambourg Olivier Hainque diff --git a/libgcc/config.host b/libgcc/config.host index b2004afb02f..8a090bdb54a 100644 --- a/libgcc/config.host +++ b/libgcc/config.host @@ -1513,3 +1513,15 @@ aarch64*-*-*) tm_file="${tm_file} aarch64/value-unwind.h" ;; esac + +# The vxworks threads implementation relies on a few extra sources, +# which we arrange to add after everything else: + +case ${target_thread_file} in +vxworks) + case ${host} in + *-*-vxworks*) + tmake_file="${tmake_file} t-gthr-vxworks" + ;; + esac +esac diff --git a/libgcc/config/gthr-vxworks-cond.c b/libgcc/config/gthr-vxworks-cond.c new file mode 100644 index 00000000000..0747a3daff2 --- /dev/null +++ b/libgcc/config/gthr-vxworks-cond.c @@ -0,0 +1,83 @@ +/* Copyright (C) 2002-2019 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC 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, or (at your option) any later +version. + +GCC 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. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +/* Threads compatibility routines for libgcc2 for VxWorks. + + This file implements the GTHREAD_HAS_COND part of the interface + exposed by gthr-vxworks.h. */ + +#include "gthr.h" +#include + +/* --------------------------- Condition Variables ------------------------ */ + +void +__gthread_cond_init (__gthread_cond_t *cond) +{ + if (!cond) + return; + *cond = semBCreate (SEM_Q_FIFO, SEM_EMPTY); +} + +int +__gthread_cond_destroy (__gthread_cond_t *cond) +{ + if (!cond) + return ERROR; + return __CHECK_RESULT (semDelete (*cond)); +} + +int +__gthread_cond_broadcast (__gthread_cond_t *cond) +{ + if (!cond) + return ERROR; + + return __CHECK_RESULT (semFlush (*cond)); +} + +int +__gthread_cond_wait (__gthread_cond_t *cond, + __gthread_mutex_t *mutex) +{ + if (!cond) + return ERROR; + + if (!mutex) + return ERROR; + + __RETURN_ERRNO_IF_NOT_OK (semGive (*mutex)); + + __RETURN_ERRNO_IF_NOT_OK (semTake (*cond, WAIT_FOREVER)); + + __RETURN_ERRNO_IF_NOT_OK (semTake (*mutex, WAIT_FOREVER)); + + return OK; +} + +int +__gthread_cond_wait_recursive (__gthread_cond_t *cond, + __gthread_recursive_mutex_t *mutex) +{ + return __gthread_cond_wait (cond, mutex); +} diff --git a/libgcc/config/gthr-vxworks-thread.c b/libgcc/config/gthr-vxworks-thread.c new file mode 100644 index 00000000000..3c880ba1f3f --- /dev/null +++ b/libgcc/config/gthr-vxworks-thread.c @@ -0,0 +1,349 @@ +/* Copyright (C) 2002-2019 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC 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, or (at your option) any later +version. + +GCC 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. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +/* Threads compatibility routines for libgcc2 for VxWorks. + + This file implements the GTHREAD_CXX0X part of the interface + exposed by gthr-vxworks.h, using APIs exposed by regular (!AE/653) + VxWorks kernels. */ + +#include "gthr.h" +#include + +#define __TIMESPEC_TO_NSEC(timespec) \ + ((long long)timespec.tv_sec * 1000000000 + (long long)timespec.tv_nsec) + +#define __TIMESPEC_TO_TICKS(timespec) \ + ((long long)(sysClkRateGet() * __TIMESPEC_TO_NSEC(timespec) + 999999999) \ + / 1000000000) + +#ifdef __RTP__ + void tls_delete_hook (); + #define __CALL_DELETE_HOOK(tcb) tls_delete_hook() +#else + /* In kernel mode, we need to pass the TCB to task_delete_hook. The TCB is + the pointer to the WIND_TCB structure and is the ID of the task. */ + void tls_delete_hook (void *TCB); + #define __CALL_DELETE_HOOK(tcb) tls_delete_hook((WIND_TCB *) ((tcb)->task_id)) +#endif + +/* -------------------- Timed Condition Variables --------------------- */ + +int +__gthread_cond_signal (__gthread_cond_t *cond) +{ + if (!cond) + return ERROR; + + return __CHECK_RESULT (semGive (*cond)); +} + +int +__gthread_cond_timedwait (__gthread_cond_t *cond, + __gthread_mutex_t *mutex, + const __gthread_time_t *abs_timeout) +{ + if (!cond) + return ERROR; + + if (!mutex) + return ERROR; + + if (!abs_timeout) + return ERROR; + + struct timespec current; + if (clock_gettime (CLOCK_REALTIME, ¤t) == ERROR) + /* CLOCK_REALTIME is not supported. */ + return ERROR; + + const long long abs_timeout_ticks = __TIMESPEC_TO_TICKS ((*abs_timeout)); + const long long current_ticks = __TIMESPEC_TO_TICKS (current); + + long long waiting_ticks; + + if (current_ticks < abs_timeout_ticks) + waiting_ticks = abs_timeout_ticks - current_ticks; + else + /* The point until we would need to wait is in the past, + no need to wait at all. */ + waiting_ticks = 0; + + /* We check that waiting_ticks can be safely casted as an int. */ + if (waiting_ticks > INT_MAX) + waiting_ticks = INT_MAX; + + __RETURN_ERRNO_IF_NOT_OK (semGive (*mutex)); + + __RETURN_ERRNO_IF_NOT_OK (semTake (*cond, waiting_ticks)); + + __RETURN_ERRNO_IF_NOT_OK (semTake (*mutex, WAIT_FOREVER)); + + return OK; +} + +/* --------------------------- Timed Mutexes ------------------------------ */ + +int +__gthread_mutex_timedlock (__gthread_mutex_t *m, + const __gthread_time_t *abs_time) +{ + if (!m) + return ERROR; + + if (!abs_time) + return ERROR; + + struct timespec current; + if (clock_gettime (CLOCK_REALTIME, ¤t) == ERROR) + /* CLOCK_REALTIME is not supported. */ + return ERROR; + + const long long abs_timeout_ticks = __TIMESPEC_TO_TICKS ((*abs_time)); + const long long current_ticks = __TIMESPEC_TO_TICKS (current); + long long waiting_ticks; + + if (current_ticks < abs_timeout_ticks) + waiting_ticks = abs_timeout_ticks - current_ticks; + else + /* The point until we would need to wait is in the past, + no need to wait at all. */ + waiting_ticks = 0; + + /* Make sure that waiting_ticks can be safely casted as an int. */ + if (waiting_ticks > INT_MAX) + waiting_ticks = INT_MAX; + + return __CHECK_RESULT (semTake (*m, waiting_ticks)); +} + +int +__gthread_recursive_mutex_timedlock (__gthread_recursive_mutex_t *mutex, + const __gthread_time_t *abs_timeout) +{ + return __gthread_mutex_timedlock ((__gthread_mutex_t *)mutex, abs_timeout); +} + +/* ------------------------------ Threads --------------------------------- */ + +/* Task control block initialization and destruction functions. */ + +int +__init_gthread_tcb (__gthread_t __tcb) +{ + if (!__tcb) + return ERROR; + + __gthread_mutex_init (&(__tcb->return_value_available)); + if (__tcb->return_value_available == SEM_ID_NULL) + return ERROR; + + __gthread_mutex_init (&(__tcb->delete_ok)); + if (__tcb->delete_ok == SEM_ID_NULL) + goto return_sem_delete; + + /* We lock the two mutexes used for signaling. */ + if (__gthread_mutex_lock (&(__tcb->delete_ok)) != OK) + goto delete_sem_delete; + + if (__gthread_mutex_lock (&(__tcb->return_value_available)) != OK) + goto delete_sem_delete; + + __tcb->task_id = TASK_ID_NULL; + return OK; + +delete_sem_delete: + semDelete (__tcb->delete_ok); +return_sem_delete: + semDelete (__tcb->return_value_available); + return ERROR; +} + +/* Here, we pass a pointer to a tcb to allow calls from + cleanup attributes. */ +void +__delete_gthread_tcb (__gthread_t* __tcb) +{ + semDelete ((*__tcb)->return_value_available); + semDelete ((*__tcb)->delete_ok); + free (*__tcb); +} + +/* This __gthread_t stores the address of the TCB malloc'ed in + __gthread_create. It is then accessible via __gthread_self(). */ +__thread __gthread_t __local_tcb = NULL; + +__gthread_t +__gthread_self (void) +{ + if (!__local_tcb) + { + /* We are in the initial thread, we need to initialize the TCB. */ + __local_tcb = malloc (sizeof (*__local_tcb)); + if (!__local_tcb) + return NULL; + + if (__init_gthread_tcb (__local_tcb) != OK) + { + __delete_gthread_tcb (&__local_tcb); + return NULL; + } + /* We do not set the mutexes in the structure as a thread is not supposed + to join or detach himself. */ + __local_tcb->task_id = taskIdSelf (); + } + return __local_tcb; +} + +int +__task_wrapper (__gthread_t tcb, FUNCPTR __func, _Vx_usr_arg_t __args) +{ + if (!tcb) + return ERROR; + + __local_tcb = tcb; + + /* We use this variable to avoid memory leaks in the case where + the underlying function throws an exception. */ + __attribute__ ((cleanup (__delete_gthread_tcb))) __gthread_t __tmp = tcb; + + void *return_value = (void *) __func (__args); + tcb->return_value = return_value; + + /* Call the destructors. */ + __CALL_DELETE_HOOK (tcb); + + /* Future calls of join() will be able to retrieve the return value. */ + __gthread_mutex_unlock (&tcb->return_value_available); + + /* We wait for the thread to be joined or detached. */ + __gthread_mutex_lock (&(tcb->delete_ok)); + __gthread_mutex_unlock (&(tcb->delete_ok)); + + /* Memory deallocation is done by the cleanup attribute of the tmp variable. */ + + return OK; +} + +/* Proper gthreads API. */ + +int +__gthread_create (__gthread_t * __threadid, void *(*__func) (void *), + void *__args) +{ + if (!__threadid) + return ERROR; + + int priority; + __RETURN_ERRNO_IF_NOT_OK (taskPriorityGet (taskIdSelf (), &priority)); + + int options; + __RETURN_ERRNO_IF_NOT_OK (taskOptionsGet (taskIdSelf (), &options)); + +#if defined (__SPE__) + options |= VX_SPE_TASK; +#else + options |= VX_FP_TASK; +#endif + options &= VX_USR_TASK_OPTIONS; + + int stacksize = 20 * 1024; + + __gthread_t tcb = malloc (sizeof (*tcb)); + if (!tcb) + return ERROR; + + if (__init_gthread_tcb (tcb) != OK) + { + free (tcb); + return ERROR; + } + + TASK_ID task_id = taskCreate (NULL, + priority, options, stacksize, + (FUNCPTR) & __task_wrapper, + (_Vx_usr_arg_t) tcb, + (_Vx_usr_arg_t) __func, + (_Vx_usr_arg_t) __args, + 0, 0, 0, 0, 0, 0, 0); + + /* If taskCreate succeeds, task_id will be a valid TASK_ID and not zero. */ + __RETURN_ERRNO_IF_NOT_OK (!task_id); + + tcb->task_id = task_id; + *__threadid = tcb; + + return __CHECK_RESULT (taskActivate (task_id)); +} + +int +__gthread_equal (__gthread_t __t1, __gthread_t __t2) +{ + return (__t1 == __t2) ? OK : ERROR; +} + +int +__gthread_yield (void) +{ + return taskDelay (0); +} + +int +__gthread_join (__gthread_t __threadid, void **__value_ptr) +{ + if (!__threadid) + return ERROR; + + /* A thread cannot join itself. */ + if (__threadid->task_id == taskIdSelf ()) + return ERROR; + + /* Waiting for the task to set the return value. */ + __gthread_mutex_lock (&__threadid->return_value_available); + __gthread_mutex_unlock (&__threadid->return_value_available); + + if (__value_ptr) + *__value_ptr = __threadid->return_value; + + /* The task will be safely be deleted. */ + __gthread_mutex_unlock (&(__threadid->delete_ok)); + + __RETURN_ERRNO_IF_NOT_OK (taskWait (__threadid->task_id, WAIT_FOREVER)); + + return OK; +} + +int +__gthread_detach (__gthread_t __threadid) +{ + if (!__threadid) + return ERROR; + + if (taskIdVerify (__threadid->task_id) != OK) + return ERROR; + + /* The task will be safely be deleted. */ + __gthread_mutex_unlock (&(__threadid->delete_ok)); + + return OK; +} diff --git a/libgcc/config/gthr-vxworks-tls.c b/libgcc/config/gthr-vxworks-tls.c new file mode 100644 index 00000000000..cd5f7ac831c --- /dev/null +++ b/libgcc/config/gthr-vxworks-tls.c @@ -0,0 +1,362 @@ +/* Copyright (C) 2002-2018 Free Software Foundation, Inc. + Contributed by Zack Weinberg + +This file is part of GCC. + +GCC 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, or (at your option) any later +version. + +GCC 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. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +/* Threads compatibility routines for libgcc2 for VxWorks. + These are out-of-line routines called from gthr-vxworks.h. + + This file provides the TLS related support routines, calling specific + VxWorks kernel entry points for this purpose. */ + +#include "tconfig.h" +#include "tsystem.h" +#include "gthr.h" + +#if defined(__GTHREADS) + +#include +#ifndef __RTP__ +#include +#endif +#include +#ifndef __RTP__ +#include +#else +#include +#endif + +/* Thread-local storage. + + A gthread TLS key is simply an offset in an array, the address of which + we store in a single pointer field associated with the current task. + + On VxWorks 7, we have direct support for __thread variables and use + such a variable as the pointer "field". On other versions, we resort + to __gthread_get_tls_data and __gthread_set_tls_data functions provided + by the kernel. + + There is also a global array which records which keys are valid and + which have destructors. + + A task delete hook is installed to execute key destructors. The routines + __gthread_enter_tls_dtor_context and __gthread_leave_tls_dtor_context, + which are also provided by the kernel, ensure that it is safe to call + free() on memory allocated by the task being deleted. This is a no-op on + VxWorks 5, but a major undertaking on AE. + + The task delete hook is only installed when at least one thread + has TLS data. This is a necessary precaution, to allow this module + to be unloaded - a module with a hook can not be removed. + + Since this interface is used to allocate only a small number of + keys, the table size is small and static, which simplifies the + code quite a bit. Revisit this if and when it becomes necessary. */ + +#define MAX_KEYS 4 + +/* This is the structure pointed to by the pointer returned + by __gthread_get_tls_data. */ +struct tls_data +{ + int *owner; + void *values[MAX_KEYS]; + unsigned int generation[MAX_KEYS]; +}; + +/* To make sure we only delete TLS data associated with this object, + include a pointer to a local variable in the TLS data object. */ +static int self_owner; + +/* Flag to check whether the delete hook is installed. Once installed + it is only removed when unloading this module. */ +static volatile int delete_hook_installed; + +/* TLS data access internal API. A straight __thread variable on VxWorks 7, + a pointer returned by kernel provided routines otherwise. */ + +#ifdef __VXWORKS7__ + +static __thread struct tls_data *__gthread_tls_data; + +#define VX_GET_TLS_DATA() __gthread_tls_data +#define VX_SET_TLS_DATA(x) __gthread_tls_data = (x) + +#define VX_ENTER_TLS_DTOR() +#define VX_LEAVE_TLS_DTOR() + +#else + +extern void *__gthread_get_tls_data (void); +extern void __gthread_set_tls_data (void *data); + +extern void __gthread_enter_tls_dtor_context (void); +extern void __gthread_leave_tls_dtor_context (void); + +#define VX_GET_TLS_DATA() __gthread_get_tls_data() +#define VX_SET_TLS_DATA(x) __gthread_set_tls_data(x) + +#define VX_ENTER_TLS_DTOR() __gthread_enter_tls_dtor_context () +#define VX_LEAVE_TLS_DTOR() __gthread_leave_tls_dtor_context () + +#endif /* __VXWORKS7__ */ + +/* This is a global structure which records all of the active keys. + + A key is potentially valid (i.e. has been handed out by + __gthread_key_create) iff its generation count in this structure is + even. In that case, the matching entry in the dtors array is a + routine to be called when a thread terminates with a valid, + non-NULL specific value for that key. + + A key is actually valid in a thread T iff the generation count + stored in this structure is equal to the generation count stored in + T's specific-value structure. */ + +typedef void (*tls_dtor) (void *); + +struct tls_keys +{ + tls_dtor dtor[MAX_KEYS]; + unsigned int generation[MAX_KEYS]; +}; + +#define KEY_VALID_P(key) !(tls_keys.generation[key] & 1) + +/* Note: if MAX_KEYS is increased, this initializer must be updated + to match. All the generation counts begin at 1, which means no + key is valid. */ +static struct tls_keys tls_keys = +{ + { NULL, NULL, NULL, NULL }, + { 1, 1, 1, 1 } +}; + +/* This lock protects the tls_keys structure. */ +static __gthread_mutex_t tls_lock; + +static __gthread_once_t tls_init_guard = __GTHREAD_ONCE_INIT; + +/* Internal routines. */ + +/* The task TCB has just been deleted. Call the destructor + function for each TLS key that has both a destructor and + a non-NULL specific value in this thread. + + This routine does not need to take tls_lock; the generation + count protects us from calling a stale destructor. It does + need to read tls_keys.dtor[key] atomically. */ + +void +tls_delete_hook (void *tcb ATTRIBUTE_UNUSED) +{ + struct tls_data *data; + __gthread_key_t key; + + data = VX_GET_TLS_DATA(); + + if (data && data->owner == &self_owner) + { + VX_ENTER_TLS_DTOR(); + for (key = 0; key < MAX_KEYS; key++) + { + if (data->generation[key] == tls_keys.generation[key]) + { + tls_dtor dtor = tls_keys.dtor[key]; + + if (dtor) + dtor (data->values[key]); + } + } + free (data); + + VX_LEAVE_TLS_DTOR(); + VX_SET_TLS_DATA(NULL); + } +} + +/* Initialize global data used by the TLS system. */ +static void +tls_init (void) +{ + __GTHREAD_MUTEX_INIT_FUNCTION (&tls_lock); +} + +static void tls_destructor (void) __attribute__ ((destructor)); +static void +tls_destructor (void) +{ +#ifdef __RTP__ + /* All threads but this one should have exited by now. */ + tls_delete_hook (NULL); +#endif + /* Unregister the hook. */ + if (delete_hook_installed) + taskDeleteHookDelete ((FUNCPTR)tls_delete_hook); + + if (tls_init_guard.done && __gthread_mutex_lock (&tls_lock) != ERROR) + semDelete (tls_lock); +} + +/* External interface */ + +/* Store in KEYP a value which can be passed to __gthread_setspecific/ + __gthread_getspecific to store and retrieve a value which is + specific to each calling thread. If DTOR is not NULL, it will be + called when a thread terminates with a non-NULL specific value for + this key, with the value as its sole argument. */ + +int +__gthread_key_create (__gthread_key_t *keyp, tls_dtor dtor) +{ + __gthread_key_t key; + + __gthread_once (&tls_init_guard, tls_init); + + if (__gthread_mutex_lock (&tls_lock) == ERROR) + return errno; + + for (key = 0; key < MAX_KEYS; key++) + if (!KEY_VALID_P (key)) + goto found_slot; + + /* no room */ + __gthread_mutex_unlock (&tls_lock); + return EAGAIN; + + found_slot: + tls_keys.generation[key]++; /* making it even */ + tls_keys.dtor[key] = dtor; + *keyp = key; + __gthread_mutex_unlock (&tls_lock); + return 0; +} + +/* Invalidate KEY; it can no longer be used as an argument to + setspecific/getspecific. Note that this does NOT call destructor + functions for any live values for this key. */ +int +__gthread_key_delete (__gthread_key_t key) +{ + if (key >= MAX_KEYS) + return EINVAL; + + __gthread_once (&tls_init_guard, tls_init); + + if (__gthread_mutex_lock (&tls_lock) == ERROR) + return errno; + + if (!KEY_VALID_P (key)) + { + __gthread_mutex_unlock (&tls_lock); + return EINVAL; + } + + tls_keys.generation[key]++; /* making it odd */ + tls_keys.dtor[key] = 0; + + __gthread_mutex_unlock (&tls_lock); + return 0; +} + +/* Retrieve the thread-specific value for KEY. If it has never been + set in this thread, or KEY is invalid, returns NULL. + + It does not matter if this function races with key_create or + key_delete; the worst that can happen is you get a value other than + the one that a serialized implementation would have provided. */ + +void * +__gthread_getspecific (__gthread_key_t key) +{ + struct tls_data *data; + + if (key >= MAX_KEYS) + return 0; + + data = GET_VX_TLS_DATA(); + + if (!data) + return 0; + + if (data->generation[key] != tls_keys.generation[key]) + return 0; + + return data->values[key]; +} + +/* Set the thread-specific value for KEY. If KEY is invalid, or + memory allocation fails, returns -1, otherwise 0. + + The generation count protects this function against races with + key_create/key_delete; the worst thing that can happen is that a + value is successfully stored into a dead generation (and then + immediately becomes invalid). However, we do have to make sure + to read tls_keys.generation[key] atomically. */ + +int +__gthread_setspecific (__gthread_key_t key, void *value) +{ + struct tls_data *data; + unsigned int generation; + + if (key >= MAX_KEYS) + return EINVAL; + + data = VX_GET_TLS_DATA(); + + if (!data) + { + if (!delete_hook_installed) + { + /* Install the delete hook. */ + if (__gthread_mutex_lock (&tls_lock) == ERROR) + return ENOMEM; + if (!delete_hook_installed) + { + taskDeleteHookAdd ((FUNCPTR)tls_delete_hook); + delete_hook_installed = 1; + } + __gthread_mutex_unlock (&tls_lock); + } + + data = malloc (sizeof (struct tls_data)); + if (!data) + return ENOMEM; + + memset (data, 0, sizeof (struct tls_data)); + data->owner = &self_owner; + + VX_SET_TLS_DATA(data); + } + + generation = tls_keys.generation[key]; + + if (generation & 1) + return EINVAL; + + data->generation[key] = generation; + data->values[key] = value; + + return 0; +} +#endif /* __GTHREADS */ diff --git a/libgcc/config/gthr-vxworks.c b/libgcc/config/gthr-vxworks.c new file mode 100644 index 00000000000..ddc35933d1a --- /dev/null +++ b/libgcc/config/gthr-vxworks.c @@ -0,0 +1,87 @@ +/* Copyright (C) 2002-2019 Free Software Foundation, Inc. + Contributed by Zack Weinberg + +This file is part of GCC. + +GCC 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, or (at your option) any later +version. + +GCC 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. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +/* Threads compatibility routines for libgcc2 for VxWorks. + + This file implements the init-once service exposed by gthr-vxworks.h. */ + +#include "tconfig.h" +#include "tsystem.h" +#include "gthr.h" + +#if defined(__GTHREADS) + +#include + +#ifndef __RTP__ +# include +# include +#else /* __RTP__ */ +# include +#endif /* __RTP__ */ + +/* ----------------------------- Init-once ------------------------------- */ + +static void +__release (__gthread_once_t ** __guard) +{ + (*__guard)->busy = 0; +} + +int +__gthread_once (__gthread_once_t * __guard, void (*__func) (void)) +{ + if (__guard->done) + return 0; + + /* Busy-wait until we have exclusive access to the state. Check if + another thread managed to perform the init call in the interim. */ + + while (!__TAS(&__guard->busy)) + { + if (__guard->done) + return 0; + taskDelay (1); + } + + if (!__guard->done) + { +#ifndef __USING_SJLJ_EXCEPTIONS__ + /* Setup a cleanup to release the guard when __func() throws an + exception. We cannot use this with SJLJ exceptions as + Unwind_Register calls __gthread_once, leading to an infinite + recursion. */ + __attribute__ ((cleanup (__release))) + __gthread_once_t *__temp = __guard; +#endif + + __func (); + __guard->done = 1; + } + + __release(&__guard); + return 0; +} + +#endif /* __GTHREADS */ diff --git a/libgcc/config/gthr-vxworks.h b/libgcc/config/gthr-vxworks.h index c9214a57a9b..7e3779a010a 100644 --- a/libgcc/config/gthr-vxworks.h +++ b/libgcc/config/gthr-vxworks.h @@ -1,6 +1,6 @@ /* Threads compatibility routines for libgcc2 and libobjc for VxWorks. */ /* Compile this one with gcc. */ -/* Copyright (C) 1997-2019 Free Software Foundation, Inc. +/* Copyright (C) 1997-2018 Free Software Foundation, Inc. Contributed by Mike Stump . This file is part of GCC. @@ -33,139 +33,295 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #include "gthr-posix.h" #else -#ifdef __cplusplus -#define UNUSED(x) -#else -#define UNUSED(x) x __attribute__((__unused__)) + +#include +#include + +/* Conditional compilation directives are easier to read when they fit on a + single line, which is helped by macros with shorter names. */ +#define _VXW_MAJOR _WRS_VXWORKS_MAJOR +#define _VXW_MINOR _WRS_VXWORKS_MINOR +#define _VXW_PRE_69 (_VXW_MAJOR < 6 || (_VXW_MAJOR == 6 && _VXW_MINOR < 9)) + +/* Some VxWorks headers profusely use typedefs of a pointer to a function with + undefined number of arguments. */ +#pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-prototypes" + #include +#pragma GCC diagnostic pop + +#include + + +/* --------------------- Test & Set/Swap internal API --------------------- */ + +/* We use a bare atomic primitive with busy loops to handle mutual exclusion. + Inefficient, but reliable. The actual primitive used depends on the mode + (RTP vs Kernel) and the version of VxWorks. We define a macro and a type + here, for reuse without conditionals cluttering in the code afterwards. */ + +/* RTP, pre 6.9. */ + +#if defined(__RTP__) && _VXW_PRE_69 + +#define __TAS(x) vxCas ((x), 0, 1) +typedef volatile unsigned char __vx_tas_t; + +#endif + +/* RTP, 6.9 and beyond. */ + +#if defined(__RTP__) && !_VXW_PRE_69 + +#define __TAS(x) vxAtomicCas ((x), 0, 1) +typedef atomic_t __vx_tas_t; + +#include + +#endif + +/* Kernel */ + +#if !defined(__RTP__) + +#define __TAS(x) vxTas (x) +typedef volatile unsigned char __vx_tas_t; + #endif #ifdef __cplusplus extern "C" { #endif +/* ------------------------ Base __GTHREADS support ----------------------- */ + #define __GTHREADS 1 #define __gthread_active_p() 1 /* Mutexes are easy, except that they need to be initialized at runtime. */ -#include - -typedef SEM_ID __gthread_mutex_t; /* All VxWorks mutexes are recursive. */ +typedef SEM_ID __gthread_mutex_t; typedef SEM_ID __gthread_recursive_mutex_t; -#define __GTHREAD_MUTEX_INIT_FUNCTION __gthread_mutex_init_function -#define __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION __gthread_recursive_mutex_init_function +#define __GTHREAD_MUTEX_INIT_FUNCTION __gthread_mutex_init +#define __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION __gthread_recursive_mutex_init + +#define __CHECK_RESULT(result) (((result) == OK) ? OK : errnoGet()) + +/* If a call to the VxWorks API fails, we must propagate the errno value. */ +#define __RETURN_ERRNO_IF_NOT_OK(exp) if ((exp) != OK) return errnoGet() + +/* Non re-entrant mutex implementation. Libstdc++ expects the default + gthread mutex to be non reentrant. */ static inline void -__gthread_mutex_init_function (__gthread_mutex_t *mutex) +__gthread_mutex_init (__gthread_mutex_t * __mutex) { - *mutex = semMCreate (SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE); + if (!__mutex) + return; + *__mutex = semBCreate (SEM_Q_PRIORITY, SEM_FULL); } static inline int -__gthread_mutex_destroy (__gthread_mutex_t *mutex) +__gthread_mutex_destroy (__gthread_mutex_t * __mutex) { - semDelete(*mutex); - return 0; + if (!__mutex) + return ERROR; + return __CHECK_RESULT (semDelete (*__mutex)); } static inline int -__gthread_mutex_lock (__gthread_mutex_t *mutex) +__gthread_mutex_lock (__gthread_mutex_t * __mutex) { - return semTake (*mutex, WAIT_FOREVER); + if (!__mutex) + return ERROR; + return __CHECK_RESULT (semTake(*__mutex, WAIT_FOREVER)); } static inline int -__gthread_mutex_trylock (__gthread_mutex_t *mutex) +__gthread_mutex_trylock (__gthread_mutex_t * __mutex) { - return semTake (*mutex, NO_WAIT); + if (!__mutex) + return ERROR; + return __CHECK_RESULT (semTake (*__mutex, NO_WAIT)); } static inline int -__gthread_mutex_unlock (__gthread_mutex_t *mutex) +__gthread_mutex_unlock (__gthread_mutex_t * __mutex) { - return semGive (*mutex); + if (!__mutex) + return ERROR; + return __CHECK_RESULT (semGive (*__mutex)); } +/* Recursive mutex implementation. The only change is that we use semMCreate() + instead of semBCreate(). */ + static inline void -__gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *mutex) +__gthread_recursive_mutex_init (__gthread_recursive_mutex_t * __mutex) { - __gthread_mutex_init_function (mutex); + if (!__mutex) + return; + *__mutex = + semMCreate (SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE); } static inline int -__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *mutex) +__gthread_recursive_mutex_destroy (__gthread_recursive_mutex_t * __mutex) { - return __gthread_mutex_lock (mutex); + return __gthread_mutex_destroy (__mutex); } static inline int -__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *mutex) +__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t * __mutex) { - return __gthread_mutex_trylock (mutex); + return __gthread_mutex_lock (__mutex); } static inline int -__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex) +__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t * __mutex) { - return __gthread_mutex_unlock (mutex); + return __gthread_mutex_trylock (__mutex); } static inline int -__gthread_recursive_mutex_destroy (__gthread_recursive_mutex_t *__mutex) +__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t * __mutex) { - return __gthread_mutex_destroy (__mutex); + return __gthread_mutex_unlock (__mutex); } -/* pthread_once is complicated enough that it's implemented - out-of-line. See config/vxlib.c. */ - typedef struct { -#if !defined(__RTP__) + /* PPC's test-and-set kernel mode implementation requires a pointer aligned + object, of which it only sets the first byte. We use padding in addition + to an alignment request here to maxmise the factors leading to the + desired actual alignment choice by the compiler. */ #if defined(__PPC__) - __attribute ((aligned (__alignof (unsigned)))) -#endif - volatile unsigned char busy; + __attribute ((aligned (__alignof__ (void *)))) #endif + + __vx_tas_t busy; volatile unsigned char done; + #if !defined(__RTP__) && defined(__PPC__) - /* PPC's test-and-set implementation requires a 4 byte aligned - object, of which it only sets the first byte. We use padding - here, in order to maintain some amount of backwards - compatibility. Without this padding, gthread_once objects worked - by accident because they happen to be static objects and the ppc - port automatically increased their alignment to 4 bytes. */ unsigned char pad1; unsigned char pad2; #endif -} -__gthread_once_t; - -#if defined (__RTP__) -# define __GTHREAD_ONCE_INIT { 0 } -#elif defined (__PPC__) -# define __GTHREAD_ONCE_INIT { 0, 0, 0, 0 } -#else -# define __GTHREAD_ONCE_INIT { 0, 0 } +#if !defined(__RTP__) && defined(__PPC64__) + unsigned char pad3; + unsigned char pad4; + unsigned char pad5; + unsigned char pad6; #endif +} __gthread_once_t; + +#define __GTHREAD_ONCE_INIT { 0 } extern int __gthread_once (__gthread_once_t *__once, void (*__func)(void)); -/* Thread-specific data requires a great deal of effort, since VxWorks - is not really set up for it. See config/vxlib.c for the gory - details. All the TSD routines are sufficiently complex that they +/* All the TSD routines are sufficiently complex that they need to be implemented out of line. */ typedef unsigned int __gthread_key_t; -extern int __gthread_key_create (__gthread_key_t *__keyp, void (*__dtor)(void *)); +extern int __gthread_key_create (__gthread_key_t *__keyp, + void (*__dtor)(void *)); extern int __gthread_key_delete (__gthread_key_t __key); extern void *__gthread_getspecific (__gthread_key_t __key); extern int __gthread_setspecific (__gthread_key_t __key, void *__ptr); -#undef UNUSED +/* ------------------ Base condition variables support ------------------- */ + +#define __GTHREAD_HAS_COND 1 + +typedef SEM_ID __gthread_cond_t; + +#define __GTHREAD_COND_INIT_FUNCTION __gthread_cond_init + +/* Condition variable declarations. */ + +extern void __gthread_cond_init (__gthread_cond_t *cond); + +extern int __gthread_cond_destroy (__gthread_cond_t *cond); + +extern int __gthread_cond_broadcast (__gthread_cond_t *cond); + +extern int __gthread_cond_wait (__gthread_cond_t *cond, + __gthread_mutex_t *mutex); + +extern int __gthread_cond_wait_recursive (__gthread_cond_t *cond, + __gthread_recursive_mutex_t *mutex); + +/* ----------------------- C++0x thread support ------------------------- */ + +/* We do not support C++0x threads on that VxWorks 653, which we can + recognize by VTHREADS being defined. */ + +#ifndef VTHREADS + +#define __GTHREADS_CXX0X 1 + +#include +#include +#include +#include +#include + +typedef struct +{ + TASK_ID task_id; + void *return_value; + + /* This mutex is used to block in join() while the return value is + unavailable. */ + __gthread_mutex_t return_value_available; + + /* Before freeing the structure in the task wrapper, we need to wait until + join() or detach() are called on that thread. */ + __gthread_mutex_t delete_ok; +} __gthread_tcb; + +typedef __gthread_tcb *__gthread_t; + +/* Typedefs specific to different vxworks versions. */ +#if _VXW_PRE_69 + typedef int _Vx_usr_arg_t; + #define TASK_ID_NULL ((TASK_ID)NULL) + #define SEM_ID_NULL ((SEM_ID)NULL) +#endif + +typedef struct timespec __gthread_time_t; + +/* Timed mutex lock declarations. */ + +extern int __gthread_mutex_timedlock (__gthread_mutex_t *m, + const __gthread_time_t *abs_time); + +extern int __gthread_recursive_mutex_timedlock + (__gthread_recursive_mutex_t *mutex, + const __gthread_time_t *abs_timeout); + +/* Timed condition variable declarations. */ + +extern int __gthread_cond_signal (__gthread_cond_t *cond); +extern int __gthread_cond_timedwait (__gthread_cond_t *cond, + __gthread_mutex_t *mutex, + const __gthread_time_t *abs_timeout); + +/* gthreads declarations. */ + +extern int __gthread_equal (__gthread_t t1, __gthread_t t2); +extern int __gthread_yield (void); +extern int __gthread_create (__gthread_t *__threadid, + void *(*__func) (void*), + void *__args); +extern int __gthread_join (__gthread_t thread, void **value_ptr); +extern int __gthread_detach (__gthread_t thread); + +extern __gthread_t __gthread_self (void); + +#endif #ifdef __cplusplus } diff --git a/libgcc/config/t-gthr-vxworks b/libgcc/config/t-gthr-vxworks new file mode 100644 index 00000000000..455d0b320d7 --- /dev/null +++ b/libgcc/config/t-gthr-vxworks @@ -0,0 +1,5 @@ +# Extra libgcc2 modules used by gthr-vxworks.h functions +LIB2ADDEH += $(srcdir)/config/gthr-vxworks.c\ + $(srcdir)/config/gthr-vxworks-cond.c\ + $(srcdir)/config/gthr-vxworks-thread.c\ + $(srcdir)/config/gthr-vxworks-tls.c \ No newline at end of file diff --git a/libgcc/config/t-vxworks b/libgcc/config/t-vxworks index 2db8e05966e..757cead6724 100644 --- a/libgcc/config/t-vxworks +++ b/libgcc/config/t-vxworks @@ -6,9 +6,6 @@ LIBGCC2_DEBUG_CFLAGS = LIB2FUNCS_EXCLUDE += _clear_cache LIB2ADD += $(srcdir)/config/vxcache.c -# Extra libgcc2 modules used by gthr-vxworks.h functions -LIB2ADDEH += $(srcdir)/config/vxlib.c $(srcdir)/config/vxlib-tls.c - # This ensures that the correct target headers are used; some VxWorks # system headers have names that collide with GCC's internal (host) # headers, e.g. regs.h. Make sure the local libgcc headers still diff --git a/libgcc/config/t-vxworks7 b/libgcc/config/t-vxworks7 index 054ab7c4091..f2cc904ac08 100644 --- a/libgcc/config/t-vxworks7 +++ b/libgcc/config/t-vxworks7 @@ -6,9 +6,6 @@ LIBGCC2_DEBUG_CFLAGS = LIB2FUNCS_EXCLUDE += _clear_cache LIB2ADD += $(srcdir)/config/vxcache.c -# Extra libgcc2 modules used by gthr-vxworks.h functions -LIB2ADDEH += $(srcdir)/config/vxlib.c $(srcdir)/config/vxlib-tls.c - # This ensures that the correct target headers are used; some VxWorks # system headers have names that collide with GCC's internal (host) # headers, e.g. regs.h. Make sure the local libgcc headers still @@ -21,4 +18,3 @@ LIBGCC2_INCLUDES = -nostdinc -I. \ */mrtp*) echo -I$(VSB_DIR)/usr/h/public -I$(VSB_DIR)/usr/h ;; \ *) echo -I$(VSB_DIR)/krnl/h/system -I$(VSB_DIR)/krnl/h/public ;; \ esac` - diff --git a/libgcc/config/vxlib-tls.c b/libgcc/config/vxlib-tls.c deleted file mode 100644 index b8d6907d81d..00000000000 --- a/libgcc/config/vxlib-tls.c +++ /dev/null @@ -1,370 +0,0 @@ -/* Copyright (C) 2002-2019 Free Software Foundation, Inc. - Contributed by Zack Weinberg - -This file is part of GCC. - -GCC 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, or (at your option) any later -version. - -GCC 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. - -Under Section 7 of GPL version 3, you are granted additional -permissions described in the GCC Runtime Library Exception, version -3.1, as published by the Free Software Foundation. - -You should have received a copy of the GNU General Public License and -a copy of the GCC Runtime Library Exception along with this program; -see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -. */ - -/* Threads compatibility routines for libgcc2 for VxWorks. - These are out-of-line routines called from gthr-vxworks.h. - - This file provides the TLS related support routines, calling specific - VxWorks kernel entry points for this purpose. The base VxWorks 5.x kernels - don't feature these entry points, and we provide gthr_supp_vxw_5x.c as an - option to fill this gap. Asking users to rebuild a kernel is not to be - taken lightly, still, so we have isolated these routines from the rest of - vxlib to ensure that the kernel dependencies are only dragged when really - necessary. */ - -#include "tconfig.h" -#include "tsystem.h" -#include "gthr.h" - -#if defined(__GTHREADS) -#include -#ifndef __RTP__ -#include -#endif -#include -#ifndef __RTP__ -#include -#else -# include -#endif - -/* Thread-local storage. - - We reserve a field in the TCB to point to a dynamically allocated - array which is used to store TLS values. A TLS key is simply an - offset in this array. The exact location of the TCB field is not - known to this code nor to vxlib.c -- all access to it indirects - through the routines __gthread_get_tls_data and - __gthread_set_tls_data, which are provided by the VxWorks kernel. - - There is also a global array which records which keys are valid and - which have destructors. - - A task delete hook is installed to execute key destructors. The - routines __gthread_enter_tls_dtor_context and - __gthread_leave_tls_dtor_context, which are also provided by the - kernel, ensure that it is safe to call free() on memory allocated - by the task being deleted. (This is a no-op on VxWorks 5, but - a major undertaking on AE.) - - The task delete hook is only installed when at least one thread - has TLS data. This is a necessary precaution, to allow this module - to be unloaded - a module with a hook cannot be removed. - - Since this interface is used to allocate only a small number of - keys, the table size is small and static, which simplifies the - code quite a bit. Revisit this if and when it becomes necessary. */ - -#define MAX_KEYS 4 - -/* This is the structure pointed to by the pointer returned - by __gthread_get_tls_data. */ -struct tls_data -{ - int *owner; - void *values[MAX_KEYS]; - unsigned int generation[MAX_KEYS]; -}; - -/* To make sure we only delete TLS data associated with this object, - include a pointer to a local variable in the TLS data object. */ -static int self_owner; - -/* Flag to check whether the delete hook is installed. Once installed - it is only removed when unloading this module. */ -static volatile int delete_hook_installed; - -/* kernel provided routines */ -extern void *__gthread_get_tls_data (void); -extern void __gthread_set_tls_data (void *data); - -extern void __gthread_enter_tls_dtor_context (void); -extern void __gthread_leave_tls_dtor_context (void); - -#ifndef __RTP__ - -extern void *__gthread_get_tsd_data (WIND_TCB *tcb); -extern void __gthread_set_tsd_data (WIND_TCB *tcb, void *data); -extern void __gthread_enter_tsd_dtor_context (WIND_TCB *tcb); -extern void __gthread_leave_tsd_dtor_context (WIND_TCB *tcb); - -#endif /* __RTP__ */ - -/* This is a global structure which records all of the active keys. - - A key is potentially valid (i.e. has been handed out by - __gthread_key_create) iff its generation count in this structure is - even. In that case, the matching entry in the dtors array is a - routine to be called when a thread terminates with a valid, - non-NULL specific value for that key. - - A key is actually valid in a thread T iff the generation count - stored in this structure is equal to the generation count stored in - T's specific-value structure. */ - -typedef void (*tls_dtor) (void *); - -struct tls_keys -{ - tls_dtor dtor[MAX_KEYS]; - unsigned int generation[MAX_KEYS]; -}; - -#define KEY_VALID_P(key) !(tls_keys.generation[key] & 1) - -/* Note: if MAX_KEYS is increased, this initializer must be updated - to match. All the generation counts begin at 1, which means no - key is valid. */ -static struct tls_keys tls_keys = -{ - { 0, 0, 0, 0 }, - { 1, 1, 1, 1 } -}; - -/* This lock protects the tls_keys structure. */ -static __gthread_mutex_t tls_lock; - -static __gthread_once_t tls_init_guard = __GTHREAD_ONCE_INIT; - -/* Internal routines. */ - -/* The task TCB has just been deleted. Call the destructor - function for each TLS key that has both a destructor and - a non-NULL specific value in this thread. - - This routine does not need to take tls_lock; the generation - count protects us from calling a stale destructor. It does - need to read tls_keys.dtor[key] atomically. */ - -static void -tls_delete_hook (void *tcb ATTRIBUTE_UNUSED) -{ - struct tls_data *data; - __gthread_key_t key; - -#ifdef __RTP__ - data = __gthread_get_tls_data (); -#else - /* In kernel mode, we can be called in the context of the thread - doing the killing, so must use the TCB to determine the data of - the thread being killed. */ - data = __gthread_get_tsd_data (tcb); -#endif - - if (data && data->owner == &self_owner) - { -#ifdef __RTP__ - __gthread_enter_tls_dtor_context (); -#else - __gthread_enter_tsd_dtor_context (tcb); -#endif - for (key = 0; key < MAX_KEYS; key++) - { - if (data->generation[key] == tls_keys.generation[key]) - { - tls_dtor dtor = tls_keys.dtor[key]; - - if (dtor) - dtor (data->values[key]); - } - } - free (data); -#ifdef __RTP__ - __gthread_leave_tls_dtor_context (); -#else - __gthread_leave_tsd_dtor_context (tcb); -#endif - -#ifdef __RTP__ - __gthread_set_tls_data (0); -#else - __gthread_set_tsd_data (tcb, 0); -#endif - } -} - -/* Initialize global data used by the TLS system. */ -static void -tls_init (void) -{ - __GTHREAD_MUTEX_INIT_FUNCTION (&tls_lock); -} - -static void tls_destructor (void) __attribute__ ((destructor)); -static void -tls_destructor (void) -{ -#ifdef __RTP__ - /* All threads but this one should have exited by now. */ - tls_delete_hook (NULL); -#endif - /* Unregister the hook. */ - if (delete_hook_installed) - taskDeleteHookDelete ((FUNCPTR)tls_delete_hook); - - if (tls_init_guard.done && __gthread_mutex_lock (&tls_lock) != ERROR) - semDelete (tls_lock); -} - -/* External interface */ - -/* Store in KEYP a value which can be passed to __gthread_setspecific/ - __gthread_getspecific to store and retrieve a value which is - specific to each calling thread. If DTOR is not NULL, it will be - called when a thread terminates with a non-NULL specific value for - this key, with the value as its sole argument. */ - -int -__gthread_key_create (__gthread_key_t *keyp, tls_dtor dtor) -{ - __gthread_key_t key; - - __gthread_once (&tls_init_guard, tls_init); - - if (__gthread_mutex_lock (&tls_lock) == ERROR) - return errno; - - for (key = 0; key < MAX_KEYS; key++) - if (!KEY_VALID_P (key)) - goto found_slot; - - /* no room */ - __gthread_mutex_unlock (&tls_lock); - return EAGAIN; - - found_slot: - tls_keys.generation[key]++; /* making it even */ - tls_keys.dtor[key] = dtor; - *keyp = key; - __gthread_mutex_unlock (&tls_lock); - return 0; -} - -/* Invalidate KEY; it can no longer be used as an argument to - setspecific/getspecific. Note that this does NOT call destructor - functions for any live values for this key. */ -int -__gthread_key_delete (__gthread_key_t key) -{ - if (key >= MAX_KEYS) - return EINVAL; - - __gthread_once (&tls_init_guard, tls_init); - - if (__gthread_mutex_lock (&tls_lock) == ERROR) - return errno; - - if (!KEY_VALID_P (key)) - { - __gthread_mutex_unlock (&tls_lock); - return EINVAL; - } - - tls_keys.generation[key]++; /* making it odd */ - tls_keys.dtor[key] = 0; - - __gthread_mutex_unlock (&tls_lock); - return 0; -} - -/* Retrieve the thread-specific value for KEY. If it has never been - set in this thread, or KEY is invalid, returns NULL. - - It does not matter if this function races with key_create or - key_delete; the worst that can happen is you get a value other than - the one that a serialized implementation would have provided. */ - -void * -__gthread_getspecific (__gthread_key_t key) -{ - struct tls_data *data; - - if (key >= MAX_KEYS) - return 0; - - data = __gthread_get_tls_data (); - - if (!data) - return 0; - - if (data->generation[key] != tls_keys.generation[key]) - return 0; - - return data->values[key]; -} - -/* Set the thread-specific value for KEY. If KEY is invalid, or - memory allocation fails, returns -1, otherwise 0. - - The generation count protects this function against races with - key_create/key_delete; the worst thing that can happen is that a - value is successfully stored into a dead generation (and then - immediately becomes invalid). However, we do have to make sure - to read tls_keys.generation[key] atomically. */ - -int -__gthread_setspecific (__gthread_key_t key, void *value) -{ - struct tls_data *data; - unsigned int generation; - - if (key >= MAX_KEYS) - return EINVAL; - - data = __gthread_get_tls_data (); - if (!data) - { - if (!delete_hook_installed) - { - /* Install the delete hook. */ - if (__gthread_mutex_lock (&tls_lock) == ERROR) - return ENOMEM; - if (!delete_hook_installed) - { - taskDeleteHookAdd ((FUNCPTR)tls_delete_hook); - delete_hook_installed = 1; - } - __gthread_mutex_unlock (&tls_lock); - } - - data = malloc (sizeof (struct tls_data)); - if (!data) - return ENOMEM; - - memset (data, 0, sizeof (struct tls_data)); - data->owner = &self_owner; - __gthread_set_tls_data (data); - } - - generation = tls_keys.generation[key]; - - if (generation & 1) - return EINVAL; - - data->generation[key] = generation; - data->values[key] = value; - - return 0; -} -#endif /* __GTHREADS */ diff --git a/libgcc/config/vxlib.c b/libgcc/config/vxlib.c deleted file mode 100644 index 78b677647a3..00000000000 --- a/libgcc/config/vxlib.c +++ /dev/null @@ -1,95 +0,0 @@ -/* Copyright (C) 2002-2019 Free Software Foundation, Inc. - Contributed by Zack Weinberg - -This file is part of GCC. - -GCC 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, or (at your option) any later -version. - -GCC 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. - -Under Section 7 of GPL version 3, you are granted additional -permissions described in the GCC Runtime Library Exception, version -3.1, as published by the Free Software Foundation. - -You should have received a copy of the GNU General Public License and -a copy of the GCC Runtime Library Exception along with this program; -see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -. */ - -/* Threads compatibility routines for libgcc2 for VxWorks. - These are out-of-line routines called from gthr-vxworks.h. */ - -#include "tconfig.h" -#include "tsystem.h" -#include "gthr.h" - -#if defined(__GTHREADS) -#include -#ifndef __RTP__ -#include -#endif -#include -#ifndef __RTP__ -#include -#else -# include -#endif - -/* Init-once operation. - - This would be a clone of the implementation from gthr-solaris.h, - except that we have a bootstrap problem - the whole point of this - exercise is to prevent double initialization, but if two threads - are racing with each other, once->mutex is liable to be initialized - by both. Then each thread will lock its own mutex, and proceed to - call the initialization routine. - - So instead we use a bare atomic primitive (vxTas()) to handle - mutual exclusion. Threads losing the race then busy-wait, calling - taskDelay() to yield the processor, until the initialization is - completed. Inefficient, but reliable. */ - -int -__gthread_once (__gthread_once_t *guard, void (*func)(void)) -{ - if (guard->done) - return 0; - -#ifdef __RTP__ - __gthread_lock_library (); -#else - while (!vxTas ((void *)&guard->busy)) - { -#ifdef __PPC__ - /* This can happen on powerpc, which is using all 32 bits - of the gthread_once_t structure. */ - if (guard->done) - return 0; -#endif - taskDelay (1); - } -#endif - - /* Only one thread at a time gets here. Check ->done again, then - go ahead and call func() if no one has done it yet. */ - if (!guard->done) - { - func (); - guard->done = 1; - } - -#ifdef __RTP__ - __gthread_unlock_library (); -#else - guard->busy = 0; -#endif - return 0; -} - -#endif /* __GTHREADS */