(root)/
gcc-13.2.0/
libgcc/
config/
gthr-vxworks-thread.c
       1  /* Copyright (C) 2002-2023 Free Software Foundation, Inc.
       2  
       3  This file is part of GCC.
       4  
       5  GCC is free software; you can redistribute it and/or modify it under
       6  the terms of the GNU General Public License as published by the Free
       7  Software Foundation; either version 3, or (at your option) any later
       8  version.
       9  
      10  GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      11  WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      13  for more details.
      14  
      15  Under Section 7 of GPL version 3, you are granted additional
      16  permissions described in the GCC Runtime Library Exception, version
      17  3.1, as published by the Free Software Foundation.
      18  
      19  You should have received a copy of the GNU General Public License and
      20  a copy of the GCC Runtime Library Exception along with this program;
      21  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
      22  <http://www.gnu.org/licenses/>.  */
      23  
      24  /* Threads compatibility routines for libgcc2 for VxWorks.
      25  
      26     This file implements the GTHREAD_CXX0X part of the interface
      27     exposed by gthr-vxworks.h, using APIs exposed by regular (!AE/653)
      28     VxWorks kernels.  */
      29  
      30  #include "gthr.h"
      31  
      32  #if __GTHREADS_CXX0X
      33  
      34  #include <taskLib.h>
      35  #include <stdlib.h>
      36  
      37  #define __TIMESPEC_TO_NSEC(timespec) \
      38    ((long long)timespec.tv_sec * 1000000000 + (long long)timespec.tv_nsec)
      39  
      40  #define __TIMESPEC_TO_TICKS(timespec) \
      41    ((long long)(sysClkRateGet() * __TIMESPEC_TO_NSEC(timespec) + 999999999) \
      42      / 1000000000)
      43  
      44  #ifdef __RTP__
      45    void tls_delete_hook (void);
      46    #define __CALL_DELETE_HOOK(tcb) tls_delete_hook()
      47  #else
      48    /* In kernel mode, we need to pass the TCB to task_delete_hook. The TCB is
      49       the pointer to the WIND_TCB structure and is the ID of the task.  */
      50    void tls_delete_hook (void *TCB);
      51    #define __CALL_DELETE_HOOK(tcb) tls_delete_hook((WIND_TCB *) ((tcb)->task_id))
      52  #endif
      53  
      54  int
      55  __gthread_cond_signal (__gthread_cond_t *cond)
      56  {
      57    if (!cond)
      58      return ERROR;
      59  
      60    /* If nobody is waiting, skip the semGive altogether: no one can get
      61       in line while we hold the mutex associated with *COND.  We could
      62       skip this test altogether, but it's presumed cheaper than going
      63       through the give and take below, and that a signal without a
      64       waiter occurs often enough for the test to be worth it.  */
      65    SEM_INFO info;
      66    memset (&info, 0, sizeof (info));
      67    __RETURN_ERRNO_IF_NOT_OK (semInfoGet (*cond, &info));
      68    if (info.numTasks == 0)
      69      return OK;
      70  
      71    int ret = __CHECK_RESULT (semGive (*cond));
      72  
      73    /* It might be the case, however, that when we called semInfo, there
      74       was a waiter just about to timeout, and by the time we called
      75       semGive, it had already timed out, so our semGive would leave the
      76       *cond semaphore full, so the next caller of wait would pass
      77       through.  We don't want that.  So, make sure we leave the
      78       semaphore empty.  Despite the window in which the semaphore will
      79       be full, this works because:
      80  
      81       - we're holding the mutex, so nobody else can semGive, and any
      82         pending semTakes are actually within semExchange.  there might
      83         be others blocked to acquire the mutex, but those are not
      84         relevant for the analysis.
      85  
      86       - if there was another non-timed out waiter, semGive will wake it
      87         up immediately instead of leaving the semaphore full, so the
      88         semTake below will time out, and the semantics are as expected
      89  
      90       - otherwise, if all waiters timed out before the semGive (or if
      91         there weren't any to begin with), our semGive completed leaving
      92         the semaphore full, and our semTake below will consume it
      93         before any other waiter has a change to reach the semExchange,
      94         because we're holding the mutex.  */
      95    if (ret == OK)
      96      semTake (*cond, NO_WAIT);
      97  
      98    return ret;
      99  }
     100  
     101  /* -------------------- Timed Condition Variables --------------------- */
     102  
     103  int
     104  __gthread_cond_timedwait (__gthread_cond_t *cond,
     105  			  __gthread_mutex_t *mutex,
     106  			  const __gthread_time_t *abs_timeout)
     107  {
     108    if (!cond)
     109      return ERROR;
     110  
     111    if (!mutex)
     112      return ERROR;
     113  
     114    if (!abs_timeout)
     115      return ERROR;
     116  
     117    struct timespec current;
     118    if (clock_gettime (CLOCK_REALTIME, &current) == ERROR)
     119      /* CLOCK_REALTIME is not supported.  */
     120      return ERROR;
     121  
     122    const long long abs_timeout_ticks = __TIMESPEC_TO_TICKS ((*abs_timeout));
     123    const long long current_ticks = __TIMESPEC_TO_TICKS (current);
     124  
     125    long long waiting_ticks;
     126  
     127    if (current_ticks < abs_timeout_ticks)
     128      waiting_ticks = abs_timeout_ticks - current_ticks;
     129    else
     130      /* The point until we would need to wait is in the past,
     131         no need to wait at all.  */
     132      waiting_ticks = 0;
     133  
     134    /* We check that waiting_ticks can be safely casted as an int.  */
     135    if (waiting_ticks > INT_MAX)
     136      waiting_ticks = INT_MAX;
     137  
     138    int ret = __CHECK_RESULT (semExchange (*mutex, *cond, waiting_ticks));
     139  
     140    __RETURN_ERRNO_IF_NOT_OK (semTake (*mutex, WAIT_FOREVER));
     141  
     142    return ret;
     143  }
     144  
     145  /* --------------------------- Timed Mutexes ------------------------------ */
     146  
     147  int
     148  __gthread_mutex_timedlock (__gthread_mutex_t *m,
     149  			   const __gthread_time_t *abs_time)
     150  {
     151    if (!m)
     152      return ERROR;
     153  
     154    if (!abs_time)
     155      return ERROR;
     156  
     157    struct timespec current;
     158    if (clock_gettime (CLOCK_REALTIME, &current) == ERROR)
     159      /* CLOCK_REALTIME is not supported.  */
     160      return ERROR;
     161  
     162    const long long abs_timeout_ticks = __TIMESPEC_TO_TICKS ((*abs_time));
     163    const long long current_ticks = __TIMESPEC_TO_TICKS (current);
     164    long long waiting_ticks;
     165  
     166    if (current_ticks < abs_timeout_ticks)
     167      waiting_ticks = abs_timeout_ticks - current_ticks;
     168    else
     169      /* The point until we would need to wait is in the past,
     170         no need to wait at all.  */
     171      waiting_ticks = 0;
     172  
     173    /* Make sure that waiting_ticks can be safely casted as an int.  */
     174    if (waiting_ticks > INT_MAX)
     175      waiting_ticks = INT_MAX;
     176  
     177    return __CHECK_RESULT (semTake (*m, waiting_ticks));
     178  }
     179  
     180  int
     181  __gthread_recursive_mutex_timedlock (__gthread_recursive_mutex_t *mutex,
     182  				     const __gthread_time_t *abs_timeout)
     183  {
     184    return __gthread_mutex_timedlock ((__gthread_mutex_t *)mutex, abs_timeout);
     185  }
     186  
     187  /* ------------------------------ Threads --------------------------------- */
     188  
     189  /* Task control block initialization and destruction functions.  */
     190  
     191  int
     192  __init_gthread_tcb (__gthread_t __tcb)
     193  {
     194    if (!__tcb)
     195      return ERROR;
     196  
     197    __gthread_mutex_init (&(__tcb->return_value_available));
     198    if (__tcb->return_value_available == SEM_ID_NULL)
     199      return ERROR;
     200  
     201    __gthread_mutex_init (&(__tcb->delete_ok));
     202    if (__tcb->delete_ok == SEM_ID_NULL)
     203      goto return_sem_delete;
     204  
     205    /* We lock the two mutexes used for signaling.  */
     206    if (__gthread_mutex_lock (&(__tcb->delete_ok)) != OK)
     207      goto delete_sem_delete;
     208  
     209    if (__gthread_mutex_lock (&(__tcb->return_value_available)) != OK)
     210      goto delete_sem_delete;
     211  
     212    __tcb->task_id = TASK_ID_NULL;
     213    return OK;
     214  
     215  delete_sem_delete:
     216    semDelete (__tcb->delete_ok);
     217  return_sem_delete:
     218    semDelete (__tcb->return_value_available);
     219    return ERROR;
     220  }
     221  
     222  /* Here, we pass a pointer to a tcb to allow calls from
     223     cleanup attributes.  */
     224  void
     225  __delete_gthread_tcb (__gthread_t* __tcb)
     226  {
     227    semDelete ((*__tcb)->return_value_available);
     228    semDelete ((*__tcb)->delete_ok);
     229    free (*__tcb);
     230  }
     231  
     232  /* This __gthread_t stores the address of the TCB malloc'ed in
     233     __gthread_create.  It is then accessible via __gthread_self().  */
     234  __thread __gthread_t __local_tcb = NULL;
     235  
     236  __gthread_t
     237  __gthread_self (void)
     238  {
     239    if (!__local_tcb)
     240      {
     241        /* We are in the initial thread, we need to initialize the TCB.  */
     242        __local_tcb = malloc (sizeof (*__local_tcb));
     243        if (!__local_tcb)
     244  	return NULL;
     245  
     246        if (__init_gthread_tcb (__local_tcb) != OK)
     247  	{
     248  	  __delete_gthread_tcb (&__local_tcb);
     249  	  return NULL;
     250  	}
     251        /* We do not set the mutexes in the structure as a thread is not supposed
     252           to join or detach himself.  */
     253        __local_tcb->task_id = taskIdSelf ();
     254      }
     255    return __local_tcb;
     256  }
     257  
     258  int
     259  __task_wrapper (__gthread_t tcb, FUNCPTR __func, _Vx_usr_arg_t __args)
     260  {
     261    if (!tcb)
     262      return ERROR;
     263  
     264    __local_tcb = tcb;
     265  
     266    /* We use this variable to avoid memory leaks in the case where
     267       the underlying function throws an exception.  */
     268    __attribute__ ((cleanup (__delete_gthread_tcb))) __gthread_t __tmp = tcb;
     269  
     270    void *return_value = (void *) __func (__args);
     271    tcb->return_value = return_value;
     272  
     273    /* Call the destructors.  */
     274    __CALL_DELETE_HOOK (tcb);
     275  
     276    /* Future calls of join() will be able to retrieve the return value.  */
     277    __gthread_mutex_unlock (&tcb->return_value_available);
     278  
     279    /* We wait for the thread to be joined or detached.  */
     280    __gthread_mutex_lock (&(tcb->delete_ok));
     281    __gthread_mutex_unlock (&(tcb->delete_ok));
     282  
     283    /* Memory deallocation is done by the cleanup attribute of the tmp variable.  */
     284  
     285    return OK;
     286  }
     287  
     288  /* Proper gthreads API.  */
     289  
     290  int
     291  __gthread_create (__gthread_t * __threadid, void *(*__func) (void *),
     292  		  void *__args)
     293  {
     294    if (!__threadid)
     295      return ERROR;
     296  
     297    int priority;
     298    __RETURN_ERRNO_IF_NOT_OK (taskPriorityGet (taskIdSelf (), &priority));
     299  
     300    int options;
     301    __RETURN_ERRNO_IF_NOT_OK (taskOptionsGet (taskIdSelf (), &options));
     302  
     303  #if defined (__SPE__)
     304    options |= VX_SPE_TASK;
     305  #else
     306    options |= VX_FP_TASK;
     307  #endif
     308    options &= VX_USR_TASK_OPTIONS;
     309  
     310    int stacksize = 20 * 1024;
     311  
     312    __gthread_t tcb = malloc (sizeof (*tcb));
     313    if (!tcb)
     314      return ERROR;
     315  
     316    if (__init_gthread_tcb (tcb) != OK)
     317      {
     318        free (tcb);
     319        return ERROR;
     320      }
     321  
     322    TASK_ID task_id = taskCreate (NULL,
     323  				priority, options, stacksize,
     324  				(FUNCPTR) & __task_wrapper,
     325  				(_Vx_usr_arg_t) tcb,
     326  				(_Vx_usr_arg_t) __func,
     327  				(_Vx_usr_arg_t) __args,
     328  				0, 0, 0, 0, 0, 0, 0);
     329  
     330    /* If taskCreate succeeds, task_id will be a valid TASK_ID and not zero.  */
     331    __RETURN_ERRNO_IF_NOT_OK (!task_id);
     332  
     333    tcb->task_id = task_id;
     334    *__threadid = tcb;
     335  
     336    return __CHECK_RESULT (taskActivate (task_id));
     337  }
     338  
     339  int
     340  __gthread_equal (__gthread_t __t1, __gthread_t __t2)
     341  {
     342    return (__t1 == __t2) ? OK : ERROR;
     343  }
     344  
     345  int
     346  __gthread_yield (void)
     347  {
     348    return taskDelay (0);
     349  }
     350  
     351  int
     352  __gthread_join (__gthread_t __threadid, void **__value_ptr)
     353  {
     354    if (!__threadid)
     355      return ERROR;
     356  
     357    /* A thread cannot join itself.  */
     358    if (__threadid->task_id == taskIdSelf ())
     359      return ERROR;
     360  
     361    /* Waiting for the task to set the return value.  */
     362    __gthread_mutex_lock (&__threadid->return_value_available);
     363    __gthread_mutex_unlock (&__threadid->return_value_available);
     364  
     365    if (__value_ptr)
     366      *__value_ptr = __threadid->return_value;
     367  
     368    /* The task will be safely be deleted.  */
     369    __gthread_mutex_unlock (&(__threadid->delete_ok));
     370  
     371    __RETURN_ERRNO_IF_NOT_OK (taskWait (__threadid->task_id, WAIT_FOREVER));
     372  
     373    return OK;
     374  }
     375  
     376  int
     377  __gthread_detach (__gthread_t __threadid)
     378  {
     379    if (!__threadid)
     380      return ERROR;
     381  
     382    if (taskIdVerify (__threadid->task_id) != OK)
     383      return ERROR;
     384  
     385    /* The task will be safely be deleted.  */
     386    __gthread_mutex_unlock (&(__threadid->delete_ok));
     387  
     388    return OK;
     389  }
     390  
     391  #endif