/* 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 . */ /* Threads compatibility routines for libgcc2 for VxWorks. This file implements the GTHREAD_CXX0X part of the interface exposed by gthr-vxworks.h, using APIs exposed by regular (!AE/653) VxWorks kernels. */ #include "gthr.h" #include #define __TIMESPEC_TO_NSEC(timespec) \ ((long long)timespec.tv_sec * 1000000000 + (long long)timespec.tv_nsec) #define __TIMESPEC_TO_TICKS(timespec) \ ((long long)(sysClkRateGet() * __TIMESPEC_TO_NSEC(timespec) + 999999999) \ / 1000000000) #ifdef __RTP__ void tls_delete_hook (); #define __CALL_DELETE_HOOK(tcb) tls_delete_hook() #else /* In kernel mode, we need to pass the TCB to task_delete_hook. The TCB is the pointer to the WIND_TCB structure and is the ID of the task. */ void tls_delete_hook (void *TCB); #define __CALL_DELETE_HOOK(tcb) tls_delete_hook((WIND_TCB *) ((tcb)->task_id)) #endif /* -------------------- Timed Condition Variables --------------------- */ int __gthread_cond_signal (__gthread_cond_t *cond) { if (!cond) return ERROR; return __CHECK_RESULT (semGive (*cond)); } int __gthread_cond_timedwait (__gthread_cond_t *cond, __gthread_mutex_t *mutex, const __gthread_time_t *abs_timeout) { if (!cond) return ERROR; if (!mutex) return ERROR; if (!abs_timeout) return ERROR; struct timespec current; if (clock_gettime (CLOCK_REALTIME, ¤t) == ERROR) /* CLOCK_REALTIME is not supported. */ return ERROR; const long long abs_timeout_ticks = __TIMESPEC_TO_TICKS ((*abs_timeout)); const long long current_ticks = __TIMESPEC_TO_TICKS (current); long long waiting_ticks; if (current_ticks < abs_timeout_ticks) waiting_ticks = abs_timeout_ticks - current_ticks; else /* The point until we would need to wait is in the past, no need to wait at all. */ waiting_ticks = 0; /* We check that waiting_ticks can be safely casted as an int. */ if (waiting_ticks > INT_MAX) waiting_ticks = INT_MAX; __RETURN_ERRNO_IF_NOT_OK (semGive (*mutex)); __RETURN_ERRNO_IF_NOT_OK (semTake (*cond, waiting_ticks)); __RETURN_ERRNO_IF_NOT_OK (semTake (*mutex, WAIT_FOREVER)); return OK; } /* --------------------------- Timed Mutexes ------------------------------ */ int __gthread_mutex_timedlock (__gthread_mutex_t *m, const __gthread_time_t *abs_time) { if (!m) return ERROR; if (!abs_time) return ERROR; struct timespec current; if (clock_gettime (CLOCK_REALTIME, ¤t) == ERROR) /* CLOCK_REALTIME is not supported. */ return ERROR; const long long abs_timeout_ticks = __TIMESPEC_TO_TICKS ((*abs_time)); const long long current_ticks = __TIMESPEC_TO_TICKS (current); long long waiting_ticks; if (current_ticks < abs_timeout_ticks) waiting_ticks = abs_timeout_ticks - current_ticks; else /* The point until we would need to wait is in the past, no need to wait at all. */ waiting_ticks = 0; /* Make sure that waiting_ticks can be safely casted as an int. */ if (waiting_ticks > INT_MAX) waiting_ticks = INT_MAX; return __CHECK_RESULT (semTake (*m, waiting_ticks)); } int __gthread_recursive_mutex_timedlock (__gthread_recursive_mutex_t *mutex, const __gthread_time_t *abs_timeout) { return __gthread_mutex_timedlock ((__gthread_mutex_t *)mutex, abs_timeout); } /* ------------------------------ Threads --------------------------------- */ /* Task control block initialization and destruction functions. */ int __init_gthread_tcb (__gthread_t __tcb) { if (!__tcb) return ERROR; __gthread_mutex_init (&(__tcb->return_value_available)); if (__tcb->return_value_available == SEM_ID_NULL) return ERROR; __gthread_mutex_init (&(__tcb->delete_ok)); if (__tcb->delete_ok == SEM_ID_NULL) goto return_sem_delete; /* We lock the two mutexes used for signaling. */ if (__gthread_mutex_lock (&(__tcb->delete_ok)) != OK) goto delete_sem_delete; if (__gthread_mutex_lock (&(__tcb->return_value_available)) != OK) goto delete_sem_delete; __tcb->task_id = TASK_ID_NULL; return OK; delete_sem_delete: semDelete (__tcb->delete_ok); return_sem_delete: semDelete (__tcb->return_value_available); return ERROR; } /* Here, we pass a pointer to a tcb to allow calls from cleanup attributes. */ void __delete_gthread_tcb (__gthread_t* __tcb) { semDelete ((*__tcb)->return_value_available); semDelete ((*__tcb)->delete_ok); free (*__tcb); } /* This __gthread_t stores the address of the TCB malloc'ed in __gthread_create. It is then accessible via __gthread_self(). */ __thread __gthread_t __local_tcb = NULL; __gthread_t __gthread_self (void) { if (!__local_tcb) { /* We are in the initial thread, we need to initialize the TCB. */ __local_tcb = malloc (sizeof (*__local_tcb)); if (!__local_tcb) return NULL; if (__init_gthread_tcb (__local_tcb) != OK) { __delete_gthread_tcb (&__local_tcb); return NULL; } /* We do not set the mutexes in the structure as a thread is not supposed to join or detach himself. */ __local_tcb->task_id = taskIdSelf (); } return __local_tcb; } int __task_wrapper (__gthread_t tcb, FUNCPTR __func, _Vx_usr_arg_t __args) { if (!tcb) return ERROR; __local_tcb = tcb; /* We use this variable to avoid memory leaks in the case where the underlying function throws an exception. */ __attribute__ ((cleanup (__delete_gthread_tcb))) __gthread_t __tmp = tcb; void *return_value = (void *) __func (__args); tcb->return_value = return_value; /* Call the destructors. */ __CALL_DELETE_HOOK (tcb); /* Future calls of join() will be able to retrieve the return value. */ __gthread_mutex_unlock (&tcb->return_value_available); /* We wait for the thread to be joined or detached. */ __gthread_mutex_lock (&(tcb->delete_ok)); __gthread_mutex_unlock (&(tcb->delete_ok)); /* Memory deallocation is done by the cleanup attribute of the tmp variable. */ return OK; } /* Proper gthreads API. */ int __gthread_create (__gthread_t * __threadid, void *(*__func) (void *), void *__args) { if (!__threadid) return ERROR; int priority; __RETURN_ERRNO_IF_NOT_OK (taskPriorityGet (taskIdSelf (), &priority)); int options; __RETURN_ERRNO_IF_NOT_OK (taskOptionsGet (taskIdSelf (), &options)); #if defined (__SPE__) options |= VX_SPE_TASK; #else options |= VX_FP_TASK; #endif options &= VX_USR_TASK_OPTIONS; int stacksize = 20 * 1024; __gthread_t tcb = malloc (sizeof (*tcb)); if (!tcb) return ERROR; if (__init_gthread_tcb (tcb) != OK) { free (tcb); return ERROR; } TASK_ID task_id = taskCreate (NULL, priority, options, stacksize, (FUNCPTR) & __task_wrapper, (_Vx_usr_arg_t) tcb, (_Vx_usr_arg_t) __func, (_Vx_usr_arg_t) __args, 0, 0, 0, 0, 0, 0, 0); /* If taskCreate succeeds, task_id will be a valid TASK_ID and not zero. */ __RETURN_ERRNO_IF_NOT_OK (!task_id); tcb->task_id = task_id; *__threadid = tcb; return __CHECK_RESULT (taskActivate (task_id)); } int __gthread_equal (__gthread_t __t1, __gthread_t __t2) { return (__t1 == __t2) ? OK : ERROR; } int __gthread_yield (void) { return taskDelay (0); } int __gthread_join (__gthread_t __threadid, void **__value_ptr) { if (!__threadid) return ERROR; /* A thread cannot join itself. */ if (__threadid->task_id == taskIdSelf ()) return ERROR; /* Waiting for the task to set the return value. */ __gthread_mutex_lock (&__threadid->return_value_available); __gthread_mutex_unlock (&__threadid->return_value_available); if (__value_ptr) *__value_ptr = __threadid->return_value; /* The task will be safely be deleted. */ __gthread_mutex_unlock (&(__threadid->delete_ok)); __RETURN_ERRNO_IF_NOT_OK (taskWait (__threadid->task_id, WAIT_FOREVER)); return OK; } int __gthread_detach (__gthread_t __threadid) { if (!__threadid) return ERROR; if (taskIdVerify (__threadid->task_id) != OK) return ERROR; /* The task will be safely be deleted. */ __gthread_mutex_unlock (&(__threadid->delete_ok)); return OK; }