mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-11-23 10:54:07 +08:00
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:
parent
78e49fb1bc
commit
806dd0472f
@ -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>
|
||||
|
||||
|
@ -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
|
||||
|
83
libgcc/config/gthr-vxworks-cond.c
Normal file
83
libgcc/config/gthr-vxworks-cond.c
Normal 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);
|
||||
}
|
349
libgcc/config/gthr-vxworks-thread.c
Normal file
349
libgcc/config/gthr-vxworks-thread.c
Normal 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, ¤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;
|
||||
}
|
@ -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];
|
87
libgcc/config/gthr-vxworks.c
Normal file
87
libgcc/config/gthr-vxworks.c
Normal 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 */
|
@ -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
|
||||
}
|
||||
|
5
libgcc/config/t-gthr-vxworks
Normal file
5
libgcc/config/t-gthr-vxworks
Normal 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
|
@ -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
|
||||
|
@ -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`
|
||||
|
||||
|
@ -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 */
|
Loading…
Reference in New Issue
Block a user