Commit 806dd047 by Corentin Gay Committed by Olivier Hainque

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
parent 78e49fb1
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
/* 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);
}
/* 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;
}
/* 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];
......
......@@ -23,72 +23,64 @@ 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. */
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>
#endif
#include <taskLib.h>
#ifndef __RTP__
#include <taskHookLib.h>
#else
# include <vxLib.h>
# include <taskHookLib.h>
#else /* __RTP__ */
# include <errno.h>
#endif
/* Init-once operation.
#endif /* __RTP__ */
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.
/* ----------------------------- Init-once ------------------------------- */
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. */
static void
__release (__gthread_once_t ** __guard)
{
(*__guard)->busy = 0;
}
int
__gthread_once (__gthread_once_t *guard, void (*func)(void))
__gthread_once (__gthread_once_t * __guard, void (*__func) (void))
{
if (guard->done)
if (__guard->done)
return 0;
#ifdef __RTP__
__gthread_lock_library ();
#else
while (!vxTas ((void *)&guard->busy))
/* 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))
{
#ifdef __PPC__
/* This can happen on powerpc, which is using all 32 bits
of the gthread_once_t structure. */
if (guard->done)
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)
if (!__guard->done)
{
func ();
guard->done = 1;
#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;
}
#ifdef __RTP__
__gthread_unlock_library ();
#else
guard->busy = 0;
#endif
__release(&__guard);
return 0;
}
......
# 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
......@@ -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`
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment