1 /* Copyright (C) 2002-2021 Free Software Foundation, Inc.
3 This file is part of GCC.
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
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
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.
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/>. */
24 /* Threads compatibility routines for libgcc2 for VxWorks.
26 This file implements the GTHREAD_CXX0X part of the interface
27 exposed by gthr-vxworks.h, using APIs exposed by regular (!AE/653)
37 #define __TIMESPEC_TO_NSEC(timespec) \
38 ((long long)timespec.tv_sec * 1000000000 + (long long)timespec.tv_nsec)
40 #define __TIMESPEC_TO_TICKS(timespec) \
41 ((long long)(sysClkRateGet() * __TIMESPEC_TO_NSEC(timespec) + 999999999) \
45 void tls_delete_hook (void);
46 #define __CALL_DELETE_HOOK(tcb) tls_delete_hook()
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))
55 __gthread_cond_signal (__gthread_cond_t
*cond
)
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. */
66 memset (&info
, 0, sizeof (info
));
67 __RETURN_ERRNO_IF_NOT_OK (semInfoGet (*cond
, &info
));
68 if (info
.numTasks
== 0)
71 int ret
= __CHECK_RESULT (semGive (*cond
));
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:
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.
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
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. */
96 semTake (*cond
, NO_WAIT
);
101 /* -------------------- Timed Condition Variables --------------------- */
104 __gthread_cond_timedwait (__gthread_cond_t
*cond
,
105 __gthread_mutex_t
*mutex
,
106 const __gthread_time_t
*abs_timeout
)
117 struct timespec current
;
118 if (clock_gettime (CLOCK_REALTIME
, ¤t
) == ERROR
)
119 /* CLOCK_REALTIME is not supported. */
122 const long long abs_timeout_ticks
= __TIMESPEC_TO_TICKS ((*abs_timeout
));
123 const long long current_ticks
= __TIMESPEC_TO_TICKS (current
);
125 long long waiting_ticks
;
127 if (current_ticks
< abs_timeout_ticks
)
128 waiting_ticks
= abs_timeout_ticks
- current_ticks
;
130 /* The point until we would need to wait is in the past,
131 no need to wait at all. */
134 /* We check that waiting_ticks can be safely casted as an int. */
135 if (waiting_ticks
> INT_MAX
)
136 waiting_ticks
= INT_MAX
;
138 int ret
= __CHECK_RESULT (semExchange (*mutex
, *cond
, waiting_ticks
));
140 __RETURN_ERRNO_IF_NOT_OK (semTake (*mutex
, WAIT_FOREVER
));
145 /* --------------------------- Timed Mutexes ------------------------------ */
148 __gthread_mutex_timedlock (__gthread_mutex_t
*m
,
149 const __gthread_time_t
*abs_time
)
157 struct timespec current
;
158 if (clock_gettime (CLOCK_REALTIME
, ¤t
) == ERROR
)
159 /* CLOCK_REALTIME is not supported. */
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
;
166 if (current_ticks
< abs_timeout_ticks
)
167 waiting_ticks
= abs_timeout_ticks
- current_ticks
;
169 /* The point until we would need to wait is in the past,
170 no need to wait at all. */
173 /* Make sure that waiting_ticks can be safely casted as an int. */
174 if (waiting_ticks
> INT_MAX
)
175 waiting_ticks
= INT_MAX
;
177 return __CHECK_RESULT (semTake (*m
, waiting_ticks
));
181 __gthread_recursive_mutex_timedlock (__gthread_recursive_mutex_t
*mutex
,
182 const __gthread_time_t
*abs_timeout
)
184 return __gthread_mutex_timedlock ((__gthread_mutex_t
*)mutex
, abs_timeout
);
187 /* ------------------------------ Threads --------------------------------- */
189 /* Task control block initialization and destruction functions. */
192 __init_gthread_tcb (__gthread_t __tcb
)
197 __gthread_mutex_init (&(__tcb
->return_value_available
));
198 if (__tcb
->return_value_available
== SEM_ID_NULL
)
201 __gthread_mutex_init (&(__tcb
->delete_ok
));
202 if (__tcb
->delete_ok
== SEM_ID_NULL
)
203 goto return_sem_delete
;
205 /* We lock the two mutexes used for signaling. */
206 if (__gthread_mutex_lock (&(__tcb
->delete_ok
)) != OK
)
207 goto delete_sem_delete
;
209 if (__gthread_mutex_lock (&(__tcb
->return_value_available
)) != OK
)
210 goto delete_sem_delete
;
212 __tcb
->task_id
= TASK_ID_NULL
;
216 semDelete (__tcb
->delete_ok
);
218 semDelete (__tcb
->return_value_available
);
222 /* Here, we pass a pointer to a tcb to allow calls from
223 cleanup attributes. */
225 __delete_gthread_tcb (__gthread_t
* __tcb
)
227 semDelete ((*__tcb
)->return_value_available
);
228 semDelete ((*__tcb
)->delete_ok
);
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
;
237 __gthread_self (void)
241 /* We are in the initial thread, we need to initialize the TCB. */
242 __local_tcb
= malloc (sizeof (*__local_tcb
));
246 if (__init_gthread_tcb (__local_tcb
) != OK
)
248 __delete_gthread_tcb (&__local_tcb
);
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 ();
259 __task_wrapper (__gthread_t tcb
, FUNCPTR __func
, _Vx_usr_arg_t __args
)
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
;
270 void *return_value
= (void *) __func (__args
);
271 tcb
->return_value
= return_value
;
273 /* Call the destructors. */
274 __CALL_DELETE_HOOK (tcb
);
276 /* Future calls of join() will be able to retrieve the return value. */
277 __gthread_mutex_unlock (&tcb
->return_value_available
);
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
));
283 /* Memory deallocation is done by the cleanup attribute of the tmp variable. */
288 /* Proper gthreads API. */
291 __gthread_create (__gthread_t
* __threadid
, void *(*__func
) (void *),
298 __RETURN_ERRNO_IF_NOT_OK (taskPriorityGet (taskIdSelf (), &priority
));
301 __RETURN_ERRNO_IF_NOT_OK (taskOptionsGet (taskIdSelf (), &options
));
303 #if defined (__SPE__)
304 options
|= VX_SPE_TASK
;
306 options
|= VX_FP_TASK
;
308 options
&= VX_USR_TASK_OPTIONS
;
310 int stacksize
= 20 * 1024;
312 __gthread_t tcb
= malloc (sizeof (*tcb
));
316 if (__init_gthread_tcb (tcb
) != OK
)
322 TASK_ID task_id
= taskCreate (NULL
,
323 priority
, options
, stacksize
,
324 (FUNCPTR
) & __task_wrapper
,
326 (_Vx_usr_arg_t
) __func
,
327 (_Vx_usr_arg_t
) __args
,
328 0, 0, 0, 0, 0, 0, 0);
330 /* If taskCreate succeeds, task_id will be a valid TASK_ID and not zero. */
331 __RETURN_ERRNO_IF_NOT_OK (!task_id
);
333 tcb
->task_id
= task_id
;
336 return __CHECK_RESULT (taskActivate (task_id
));
340 __gthread_equal (__gthread_t __t1
, __gthread_t __t2
)
342 return (__t1
== __t2
) ? OK
: ERROR
;
346 __gthread_yield (void)
348 return taskDelay (0);
352 __gthread_join (__gthread_t __threadid
, void **__value_ptr
)
357 /* A thread cannot join itself. */
358 if (__threadid
->task_id
== taskIdSelf ())
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
);
366 *__value_ptr
= __threadid
->return_value
;
368 /* The task will be safely be deleted. */
369 __gthread_mutex_unlock (&(__threadid
->delete_ok
));
371 __RETURN_ERRNO_IF_NOT_OK (taskWait (__threadid
->task_id
, WAIT_FOREVER
));
377 __gthread_detach (__gthread_t __threadid
)
382 if (taskIdVerify (__threadid
->task_id
) != OK
)
385 /* The task will be safely be deleted. */
386 __gthread_mutex_unlock (&(__threadid
->delete_ok
));