1/* Copyright (C) 2002-2022 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 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 54int 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 103int 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, ¤t) == 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 147int 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, ¤t) == 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 180int 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 191int 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 215delete_sem_delete: 216 semDelete (__tcb->delete_ok); 217return_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. */ 224void 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 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 258int 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 290int 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 339int 340__gthread_equal (__gthread_t __t1, __gthread_t __t2) 341{ 342 return (__t1 == __t2) ? OK : ERROR; 343} 344 345int 346__gthread_yield (void) 347{ 348 return taskDelay (0); 349} 350 351int 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 376int 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