1/* Copyright (C) 2002-2020 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#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 52int 53__gthread_cond_signal (__gthread_cond_t *cond) 54{ 55 if (!cond) 56 return ERROR; 57 58 return __CHECK_RESULT (semGive (*cond)); 59} 60 61int 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, ¤t) == 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 107int 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, ¤t) == 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 140int 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 151int 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 175delete_sem_delete: 176 semDelete (__tcb->delete_ok); 177return_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. */ 184void 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 218int 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 250int 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 299int 300__gthread_equal (__gthread_t __t1, __gthread_t __t2) 301{ 302 return (__t1 == __t2) ? OK : ERROR; 303} 304 305int 306__gthread_yield (void) 307{ 308 return taskDelay (0); 309} 310 311int 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 336int 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