]> git.ipfire.org Git - thirdparty/gcc.git/blob - libgcc/config/gthr-vxworks-thread.c
Update copyright years.
[thirdparty/gcc.git] / libgcc / config / gthr-vxworks-thread.c
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
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
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
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
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
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
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
218 int
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
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
300 __gthread_equal (__gthread_t __t1, __gthread_t __t2)
301 {
302 return (__t1 == __t2) ? OK : ERROR;
303 }
304
305 int
306 __gthread_yield (void)
307 {
308 return taskDelay (0);
309 }
310
311 int
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
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 }