Improve the thread support for VxWorks

2019-11-12  Corentin Gay  <gay@adacore.com>
	    Jerome Lambourg  <lambourg@adacore.com>
	    Olivier Hainque  <hainque@adacore.com>

	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 <lambourg@adacore.com>
Co-Authored-By: Olivier Hainque <hainque@adacore.com>

From-SVN: r278249
This commit is contained in:
Corentin Gay 2019-11-14 15:58:31 +00:00 committed by Olivier Hainque
parent 78e49fb1bc
commit 806dd0472f
11 changed files with 845 additions and 229 deletions

View File

@ -1,3 +1,37 @@
2019-11-12 Corentin Gay <gay@adacore.com>
Jerome Lambourg <lambourg@adacore.com>
Olivier Hainque <hainque@adacore.com>
* 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 <lambourg@adacore.com>
Olivier Hainque <hainque@adacore.com>

View File

@ -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

View File

@ -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
<http://www.gnu.org/licenses/>. */
/* 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 <taskLib.h>
/* --------------------------- 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);
}

View File

@ -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
<http://www.gnu.org/licenses/>. */
/* 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 <taskLib.h>
#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, &current) == 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, &current) == 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;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2002-2019 Free Software Foundation, Inc.
/* Copyright (C) 2002-2018 Free Software Foundation, Inc.
Contributed by Zack Weinberg <zack@codesourcery.com>
This file is part of GCC.
@ -23,21 +23,17 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
/* 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 <vxWorks.h>
#ifndef __RTP__
#include <vxLib.h>
@ -46,31 +42,31 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
#ifndef __RTP__
#include <taskHookLib.h>
#else
# include <errno.h>
#include <errno.h>
#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];

View File

@ -0,0 +1,87 @@
/* Copyright (C) 2002-2019 Free Software Foundation, Inc.
Contributed by Zack Weinberg <zack@codesourcery.com>
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
<http://www.gnu.org/licenses/>. */
/* 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 <vxWorks.h>
#ifndef __RTP__
# include <vxLib.h>
# include <taskHookLib.h>
#else /* __RTP__ */
# include <errno.h>
#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 */

View File

@ -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 <mrs@wrs.com>.
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 <vxWorks.h>
#include <version.h>
/* 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 <semLib.h>
#pragma GCC diagnostic pop
#include <errnoLib.h>
/* --------------------- 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 <vxAtomicLib.h>
#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 <semLib.h>
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 <limits.h>
#include <time.h>
#include <tickLib.h>
#include <sysLib.h>
#include <version.h>
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
}

View File

@ -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

View File

@ -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

View File

@ -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`

View File

@ -1,95 +0,0 @@
/* Copyright (C) 2002-2019 Free Software Foundation, Inc.
Contributed by Zack Weinberg <zack@codesourcery.com>
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
<http://www.gnu.org/licenses/>. */
/* 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 <vxWorks.h>
#ifndef __RTP__
#include <vxLib.h>
#endif
#include <taskLib.h>
#ifndef __RTP__
#include <taskHookLib.h>
#else
# include <errno.h>
#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 */