1 /* Copyright (C) 2002-2020 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 #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 
52 int
__gthread_cond_signal(__gthread_cond_t * cond)53 __gthread_cond_signal (__gthread_cond_t *cond)
54 {
55   if (!cond)
56     return ERROR;
57 
58   return __CHECK_RESULT (semGive (*cond));
59 }
60 
61 int
__gthread_cond_timedwait(__gthread_cond_t * cond,__gthread_mutex_t * mutex,const __gthread_time_t * abs_timeout)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 
107 int
__gthread_mutex_timedlock(__gthread_mutex_t * m,const __gthread_time_t * abs_time)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 
140 int
__gthread_recursive_mutex_timedlock(__gthread_recursive_mutex_t * mutex,const __gthread_time_t * abs_timeout)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 
151 int
__init_gthread_tcb(__gthread_t __tcb)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 
175 delete_sem_delete:
176   semDelete (__tcb->delete_ok);
177 return_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.  */
184 void
__delete_gthread_tcb(__gthread_t * __tcb)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
__gthread_self(void)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 
218 int
__task_wrapper(__gthread_t tcb,FUNCPTR __func,_Vx_usr_arg_t __args)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 
250 int
__gthread_create(__gthread_t * __threadid,void * (* __func)(void *),void * __args)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 
299 int
__gthread_equal(__gthread_t __t1,__gthread_t __t2)300 __gthread_equal (__gthread_t __t1, __gthread_t __t2)
301 {
302   return (__t1 == __t2) ? OK : ERROR;
303 }
304 
305 int
__gthread_yield(void)306 __gthread_yield (void)
307 {
308   return taskDelay (0);
309 }
310 
311 int
__gthread_join(__gthread_t __threadid,void ** __value_ptr)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 
336 int
__gthread_detach(__gthread_t __threadid)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