123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- /* Copyright (C) 2002-2022 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"
- #if __GTHREADS_CXX0X
- #include <taskLib.h>
- #include <stdlib.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 (void);
- #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
- int
- __gthread_cond_signal (__gthread_cond_t *cond)
- {
- if (!cond)
- return ERROR;
- /* If nobody is waiting, skip the semGive altogether: no one can get
- in line while we hold the mutex associated with *COND. We could
- skip this test altogether, but it's presumed cheaper than going
- through the give and take below, and that a signal without a
- waiter occurs often enough for the test to be worth it. */
- SEM_INFO info;
- memset (&info, 0, sizeof (info));
- __RETURN_ERRNO_IF_NOT_OK (semInfoGet (*cond, &info));
- if (info.numTasks == 0)
- return OK;
- int ret = __CHECK_RESULT (semGive (*cond));
- /* It might be the case, however, that when we called semInfo, there
- was a waiter just about to timeout, and by the time we called
- semGive, it had already timed out, so our semGive would leave the
- *cond semaphore full, so the next caller of wait would pass
- through. We don't want that. So, make sure we leave the
- semaphore empty. Despite the window in which the semaphore will
- be full, this works because:
- - we're holding the mutex, so nobody else can semGive, and any
- pending semTakes are actually within semExchange. there might
- be others blocked to acquire the mutex, but those are not
- relevant for the analysis.
- - if there was another non-timed out waiter, semGive will wake it
- up immediately instead of leaving the semaphore full, so the
- semTake below will time out, and the semantics are as expected
- - otherwise, if all waiters timed out before the semGive (or if
- there weren't any to begin with), our semGive completed leaving
- the semaphore full, and our semTake below will consume it
- before any other waiter has a change to reach the semExchange,
- because we're holding the mutex. */
- if (ret == OK)
- semTake (*cond, NO_WAIT);
- return ret;
- }
- /* -------------------- Timed Condition Variables --------------------- */
- 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;
- int ret = __CHECK_RESULT (semExchange (*mutex, *cond, waiting_ticks));
- __RETURN_ERRNO_IF_NOT_OK (semTake (*mutex, WAIT_FOREVER));
- return ret;
- }
- /* --------------------------- 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;
- }
- #endif
|