1 /* Copyright (C) 2002-2021 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
__gthread_cond_signal(__gthread_cond_t * cond)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
__gthread_cond_timedwait(__gthread_cond_t * cond,__gthread_mutex_t * mutex,const __gthread_time_t * abs_timeout)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
__gthread_mutex_timedlock(__gthread_mutex_t * m,const __gthread_time_t * abs_time)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
__gthread_recursive_mutex_timedlock(__gthread_recursive_mutex_t * mutex,const __gthread_time_t * abs_timeout)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
__init_gthread_tcb(__gthread_t __tcb)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
__delete_gthread_tcb(__gthread_t * __tcb)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
__gthread_self(void)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
__task_wrapper(__gthread_t tcb,FUNCPTR __func,_Vx_usr_arg_t __args)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
__gthread_create(__gthread_t * __threadid,void * (* __func)(void *),void * __args)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
__gthread_equal(__gthread_t __t1,__gthread_t __t2)340 __gthread_equal (__gthread_t __t1, __gthread_t __t2)
341 {
342   return (__t1 == __t2) ? OK : ERROR;
343 }
344 
345 int
__gthread_yield(void)346 __gthread_yield (void)
347 {
348   return taskDelay (0);
349 }
350 
351 int
__gthread_join(__gthread_t __threadid,void ** __value_ptr)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
__gthread_detach(__gthread_t __threadid)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