From 806dd0472f56fd8fc410f106660b1ad2c7505bd7 Mon Sep 17 00:00:00 2001 From: Corentin Gay Date: Thu, 14 Nov 2019 15:58:31 +0000 Subject: [PATCH] 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 --- libgcc/ChangeLog | 34 ++ libgcc/config.host | 12 + libgcc/config/gthr-vxworks-cond.c | 83 +++++ libgcc/config/gthr-vxworks-thread.c | 349 ++++++++++++++++++ .../{vxlib-tls.c => gthr-vxworks-tls.c} | 110 +++--- libgcc/config/gthr-vxworks.c | 87 +++++ libgcc/config/gthr-vxworks.h | 292 +++++++++++---- libgcc/config/t-gthr-vxworks | 5 + libgcc/config/t-vxworks | 3 - libgcc/config/t-vxworks7 | 4 - libgcc/config/vxlib.c | 95 ----- 11 files changed, 845 insertions(+), 229 deletions(-) create mode 100644 libgcc/config/gthr-vxworks-cond.c create mode 100644 libgcc/config/gthr-vxworks-thread.c rename libgcc/config/{vxlib-tls.c => gthr-vxworks-tls.c} (78%) create mode 100644 libgcc/config/gthr-vxworks.c create mode 100644 libgcc/config/t-gthr-vxworks delete mode 100644 libgcc/config/vxlib.c 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/vxlib-tls.c b/libgcc/config/gthr-vxworks-tls.c similarity index 78% rename from libgcc/config/vxlib-tls.c rename to libgcc/config/gthr-vxworks-tls.c index b8d6907d81d..cd5f7ac831c 100644 --- a/libgcc/config/vxlib-tls.c +++ b/libgcc/config/gthr-vxworks-tls.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002-2019 Free Software Foundation, Inc. +/* Copyright (C) 2002-2018 Free Software Foundation, Inc. Contributed by Zack Weinberg This file is part of GCC. @@ -23,21 +23,17 @@ 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. + 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. */ + VxWorks kernel entry points for this purpose. */ #include "tconfig.h" #include "tsystem.h" #include "gthr.h" #if defined(__GTHREADS) + #include #ifndef __RTP__ #include @@ -46,31 +42,31 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #ifndef __RTP__ #include #else -# include +#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. + 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.) + 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. + 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 @@ -95,21 +91,34 @@ static int self_owner; it is only removed when unloading this module. */ static volatile int delete_hook_installed; -/* kernel provided routines */ +/* 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); -#ifndef __RTP__ +#define VX_GET_TLS_DATA() __gthread_get_tls_data() +#define VX_SET_TLS_DATA(x) __gthread_set_tls_data(x) -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); +#define VX_ENTER_TLS_DTOR() __gthread_enter_tls_dtor_context () +#define VX_LEAVE_TLS_DTOR() __gthread_leave_tls_dtor_context () -#endif /* __RTP__ */ +#endif /* __VXWORKS7__ */ /* This is a global structure which records all of the active keys. @@ -138,7 +147,7 @@ struct tls_keys key is valid. */ static struct tls_keys tls_keys = { - { 0, 0, 0, 0 }, + { NULL, NULL, NULL, NULL }, { 1, 1, 1, 1 } }; @@ -157,28 +166,17 @@ static __gthread_once_t tls_init_guard = __GTHREAD_ONCE_INIT; count protects us from calling a stale destructor. It does need to read tls_keys.dtor[key] atomically. */ -static void +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 - + data = VX_GET_TLS_DATA(); + if (data && data->owner == &self_owner) { -#ifdef __RTP__ - __gthread_enter_tls_dtor_context (); -#else - __gthread_enter_tsd_dtor_context (tcb); -#endif + VX_ENTER_TLS_DTOR(); for (key = 0; key < MAX_KEYS; key++) { if (data->generation[key] == tls_keys.generation[key]) @@ -190,19 +188,11 @@ tls_delete_hook (void *tcb ATTRIBUTE_UNUSED) } } 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 + VX_LEAVE_TLS_DTOR(); + VX_SET_TLS_DATA(NULL); } -} +} /* Initialize global data used by the TLS system. */ static void @@ -303,7 +293,7 @@ __gthread_getspecific (__gthread_key_t key) if (key >= MAX_KEYS) return 0; - data = __gthread_get_tls_data (); + data = GET_VX_TLS_DATA(); if (!data) return 0; @@ -332,7 +322,8 @@ __gthread_setspecific (__gthread_key_t key, void *value) if (key >= MAX_KEYS) return EINVAL; - data = __gthread_get_tls_data (); + data = VX_GET_TLS_DATA(); + if (!data) { if (!delete_hook_installed) @@ -354,7 +345,8 @@ __gthread_setspecific (__gthread_key_t key, void *value) memset (data, 0, sizeof (struct tls_data)); data->owner = &self_owner; - __gthread_set_tls_data (data); + + VX_SET_TLS_DATA(data); } generation = tls_keys.generation[key]; 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) -{ - return __gthread_mutex_lock (mutex); -} - -static inline int -__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *mutex) -{ - return __gthread_mutex_trylock (mutex); -} - -static inline int -__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex) -{ - return __gthread_mutex_unlock (mutex); -} - -static inline int -__gthread_recursive_mutex_destroy (__gthread_recursive_mutex_t *__mutex) +__gthread_recursive_mutex_destroy (__gthread_recursive_mutex_t * __mutex) { return __gthread_mutex_destroy (__mutex); } -/* pthread_once is complicated enough that it's implemented - out-of-line. See config/vxlib.c. */ +static inline int +__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t * __mutex) +{ + return __gthread_mutex_lock (__mutex); +} + +static inline int +__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t * __mutex) +{ + return __gthread_mutex_trylock (__mutex); +} + +static inline int +__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t * __mutex) +{ + return __gthread_mutex_unlock (__mutex); +} 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.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 */