blob: 8544b03dd18c35c145659ba0f61a169bb0c99e47 [file] [log] [blame]
/* Copyright (C) 2002-2020 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;
}