1/* Copyright (C) 2002-2020 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#include <taskLib.h>
32
33#define __TIMESPEC_TO_NSEC(timespec) \
34  ((long long)timespec.tv_sec * 1000000000 + (long long)timespec.tv_nsec)
35
36#define __TIMESPEC_TO_TICKS(timespec) \
37  ((long long)(sysClkRateGet() * __TIMESPEC_TO_NSEC(timespec) + 999999999) \
38    / 1000000000)
39
40#ifdef __RTP__
41  void tls_delete_hook ();
42  #define __CALL_DELETE_HOOK(tcb) tls_delete_hook()
43#else
44  /* In kernel mode, we need to pass the TCB to task_delete_hook. The TCB is
45     the pointer to the WIND_TCB structure and is the ID of the task.  */
46  void tls_delete_hook (void *TCB);
47  #define __CALL_DELETE_HOOK(tcb) tls_delete_hook((WIND_TCB *) ((tcb)->task_id))
48#endif
49
50/* -------------------- Timed Condition Variables --------------------- */
51
52int
53__gthread_cond_signal (__gthread_cond_t *cond)
54{
55  if (!cond)
56    return ERROR;
57
58  return __CHECK_RESULT (semGive (*cond));
59}
60
61int
62__gthread_cond_timedwait (__gthread_cond_t *cond,
63			  __gthread_mutex_t *mutex,
64			  const __gthread_time_t *abs_timeout)
65{
66  if (!cond)
67    return ERROR;
68
69  if (!mutex)
70    return ERROR;
71
72  if (!abs_timeout)
73    return ERROR;
74
75  struct timespec current;
76  if (clock_gettime (CLOCK_REALTIME, &current) == ERROR)
77    /* CLOCK_REALTIME is not supported.  */
78    return ERROR;
79
80  const long long abs_timeout_ticks = __TIMESPEC_TO_TICKS ((*abs_timeout));
81  const long long current_ticks = __TIMESPEC_TO_TICKS (current);
82
83  long long waiting_ticks;
84
85  if (current_ticks < abs_timeout_ticks)
86    waiting_ticks = abs_timeout_ticks - current_ticks;
87  else
88    /* The point until we would need to wait is in the past,
89       no need to wait at all.  */
90    waiting_ticks = 0;
91
92  /* We check that waiting_ticks can be safely casted as an int.  */
93  if (waiting_ticks > INT_MAX)
94    waiting_ticks = INT_MAX;
95
96  __RETURN_ERRNO_IF_NOT_OK (semGive (*mutex));
97
98  __RETURN_ERRNO_IF_NOT_OK (semTake (*cond, waiting_ticks));
99
100  __RETURN_ERRNO_IF_NOT_OK (semTake (*mutex, WAIT_FOREVER));
101
102  return OK;
103}
104
105/* --------------------------- Timed Mutexes ------------------------------ */
106
107int
108__gthread_mutex_timedlock (__gthread_mutex_t *m,
109			   const __gthread_time_t *abs_time)
110{
111  if (!m)
112    return ERROR;
113
114  if (!abs_time)
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_time));
123  const long long current_ticks = __TIMESPEC_TO_TICKS (current);
124  long long waiting_ticks;
125
126  if (current_ticks < abs_timeout_ticks)
127    waiting_ticks = abs_timeout_ticks - current_ticks;
128  else
129    /* The point until we would need to wait is in the past,
130       no need to wait at all.  */
131    waiting_ticks = 0;
132
133  /* Make sure that waiting_ticks can be safely casted as an int.  */
134  if (waiting_ticks > INT_MAX)
135    waiting_ticks = INT_MAX;
136
137  return __CHECK_RESULT (semTake (*m, waiting_ticks));
138}
139
140int
141__gthread_recursive_mutex_timedlock (__gthread_recursive_mutex_t *mutex,
142				     const __gthread_time_t *abs_timeout)
143{
144  return __gthread_mutex_timedlock ((__gthread_mutex_t *)mutex, abs_timeout);
145}
146
147/* ------------------------------ Threads --------------------------------- */
148
149/* Task control block initialization and destruction functions.  */
150
151int
152__init_gthread_tcb (__gthread_t __tcb)
153{
154  if (!__tcb)
155    return ERROR;
156
157  __gthread_mutex_init (&(__tcb->return_value_available));
158  if (__tcb->return_value_available == SEM_ID_NULL)
159    return ERROR;
160
161  __gthread_mutex_init (&(__tcb->delete_ok));
162  if (__tcb->delete_ok == SEM_ID_NULL)
163    goto return_sem_delete;
164
165  /* We lock the two mutexes used for signaling.  */
166  if (__gthread_mutex_lock (&(__tcb->delete_ok)) != OK)
167    goto delete_sem_delete;
168
169  if (__gthread_mutex_lock (&(__tcb->return_value_available)) != OK)
170    goto delete_sem_delete;
171
172  __tcb->task_id = TASK_ID_NULL;
173  return OK;
174
175delete_sem_delete:
176  semDelete (__tcb->delete_ok);
177return_sem_delete:
178  semDelete (__tcb->return_value_available);
179  return ERROR;
180}
181
182/* Here, we pass a pointer to a tcb to allow calls from
183   cleanup attributes.  */
184void
185__delete_gthread_tcb (__gthread_t* __tcb)
186{
187  semDelete ((*__tcb)->return_value_available);
188  semDelete ((*__tcb)->delete_ok);
189  free (*__tcb);
190}
191
192/* This __gthread_t stores the address of the TCB malloc'ed in
193   __gthread_create.  It is then accessible via __gthread_self().  */
194__thread __gthread_t __local_tcb = NULL;
195
196__gthread_t
197__gthread_self (void)
198{
199  if (!__local_tcb)
200    {
201      /* We are in the initial thread, we need to initialize the TCB.  */
202      __local_tcb = malloc (sizeof (*__local_tcb));
203      if (!__local_tcb)
204	return NULL;
205
206      if (__init_gthread_tcb (__local_tcb) != OK)
207	{
208	  __delete_gthread_tcb (&__local_tcb);
209	  return NULL;
210	}
211      /* We do not set the mutexes in the structure as a thread is not supposed
212         to join or detach himself.  */
213      __local_tcb->task_id = taskIdSelf ();
214    }
215  return __local_tcb;
216}
217
218int
219__task_wrapper (__gthread_t tcb, FUNCPTR __func, _Vx_usr_arg_t __args)
220{
221  if (!tcb)
222    return ERROR;
223
224  __local_tcb = tcb;
225
226  /* We use this variable to avoid memory leaks in the case where
227     the underlying function throws an exception.  */
228  __attribute__ ((cleanup (__delete_gthread_tcb))) __gthread_t __tmp = tcb;
229
230  void *return_value = (void *) __func (__args);
231  tcb->return_value = return_value;
232
233  /* Call the destructors.  */
234  __CALL_DELETE_HOOK (tcb);
235
236  /* Future calls of join() will be able to retrieve the return value.  */
237  __gthread_mutex_unlock (&tcb->return_value_available);
238
239  /* We wait for the thread to be joined or detached.  */
240  __gthread_mutex_lock (&(tcb->delete_ok));
241  __gthread_mutex_unlock (&(tcb->delete_ok));
242
243  /* Memory deallocation is done by the cleanup attribute of the tmp variable.  */
244
245  return OK;
246}
247
248/* Proper gthreads API.  */
249
250int
251__gthread_create (__gthread_t * __threadid, void *(*__func) (void *),
252		  void *__args)
253{
254  if (!__threadid)
255    return ERROR;
256
257  int priority;
258  __RETURN_ERRNO_IF_NOT_OK (taskPriorityGet (taskIdSelf (), &priority));
259
260  int options;
261  __RETURN_ERRNO_IF_NOT_OK (taskOptionsGet (taskIdSelf (), &options));
262
263#if defined (__SPE__)
264  options |= VX_SPE_TASK;
265#else
266  options |= VX_FP_TASK;
267#endif
268  options &= VX_USR_TASK_OPTIONS;
269
270  int stacksize = 20 * 1024;
271
272  __gthread_t tcb = malloc (sizeof (*tcb));
273  if (!tcb)
274    return ERROR;
275
276  if (__init_gthread_tcb (tcb) != OK)
277    {
278      free (tcb);
279      return ERROR;
280    }
281
282  TASK_ID task_id = taskCreate (NULL,
283				priority, options, stacksize,
284				(FUNCPTR) & __task_wrapper,
285				(_Vx_usr_arg_t) tcb,
286				(_Vx_usr_arg_t) __func,
287				(_Vx_usr_arg_t) __args,
288				0, 0, 0, 0, 0, 0, 0);
289
290  /* If taskCreate succeeds, task_id will be a valid TASK_ID and not zero.  */
291  __RETURN_ERRNO_IF_NOT_OK (!task_id);
292
293  tcb->task_id = task_id;
294  *__threadid = tcb;
295
296  return __CHECK_RESULT (taskActivate (task_id));
297}
298
299int
300__gthread_equal (__gthread_t __t1, __gthread_t __t2)
301{
302  return (__t1 == __t2) ? OK : ERROR;
303}
304
305int
306__gthread_yield (void)
307{
308  return taskDelay (0);
309}
310
311int
312__gthread_join (__gthread_t __threadid, void **__value_ptr)
313{
314  if (!__threadid)
315    return ERROR;
316
317  /* A thread cannot join itself.  */
318  if (__threadid->task_id == taskIdSelf ())
319    return ERROR;
320
321  /* Waiting for the task to set the return value.  */
322  __gthread_mutex_lock (&__threadid->return_value_available);
323  __gthread_mutex_unlock (&__threadid->return_value_available);
324
325  if (__value_ptr)
326    *__value_ptr = __threadid->return_value;
327
328  /* The task will be safely be deleted.  */
329  __gthread_mutex_unlock (&(__threadid->delete_ok));
330
331  __RETURN_ERRNO_IF_NOT_OK (taskWait (__threadid->task_id, WAIT_FOREVER));
332
333  return OK;
334}
335
336int
337__gthread_detach (__gthread_t __threadid)
338{
339  if (!__threadid)
340    return ERROR;
341
342  if (taskIdVerify (__threadid->task_id) != OK)
343    return ERROR;
344
345  /* The task will be safely be deleted.  */
346  __gthread_mutex_unlock (&(__threadid->delete_ok));
347
348  return OK;
349}
350