1/* Copyright (C) 2002-2022 Free Software Foundation, Inc.
2
3This file is part of GCC.
4
5GCC is free software; you can redistribute it and/or modify it under
6the terms of the GNU General Public License as published by the Free
7Software Foundation; either version 3, or (at your option) any later
8version.
9
10GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11WARRANTY; without even the implied warranty of MERCHANTABILITY or
12FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13for more details.
14
15Under Section 7 of GPL version 3, you are granted additional
16permissions described in the GCC Runtime Library Exception, version
173.1, as published by the Free Software Foundation.
18
19You should have received a copy of the GNU General Public License and
20a copy of the GCC Runtime Library Exception along with this program;
21see 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
54int
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
103int
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
147int
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
180int
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
191int
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
215delete_sem_delete:
216  semDelete (__tcb->delete_ok);
217return_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.  */
224void
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
258int
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
290int
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
339int
340__gthread_equal (__gthread_t __t1, __gthread_t __t2)
341{
342  return (__t1 == __t2) ? OK : ERROR;
343}
344
345int
346__gthread_yield (void)
347{
348  return taskDelay (0);
349}
350
351int
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
376int
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
392