1 /* Threads compatibility routines for libgcc2 and libobjc for VxWorks. */
2 /* Compile this one with gcc. */
3 /* Copyright (C) 1997-2023 Free Software Foundation, Inc.
4 Contributed by Mike Stump <mrs@wrs.com>.
5
6 This file is part of GCC.
7
8 GCC is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 3, or (at your option) any later
11 version.
12
13 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
17
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
21
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25 <http://www.gnu.org/licenses/>. */
26
27 #ifndef GCC_GTHR_VXWORKS_H
28 #define GCC_GTHR_VXWORKS_H
29
30 #ifdef _LIBOBJC
31
32 /* libobjc requires the optional pthreads component. */
33 #include "gthr-posix.h"
34
35 #else
36
37 #include <vxWorks.h>
38 #include <_vxworks-versions.h>
39
40 /* Some VxWorks headers profusely use typedefs of a pointer to a function with
41 undefined number of arguments. Arrange to ignore declaration errors in C++,
42 which is achievable by ignoring Wstrict-prototypes diagnostics even when the
43 option is registered as only valid for c/objc. */
44 #pragma GCC diagnostic push
45 #pragma GCC diagnostic ignored "-Wpragmas"
46 #pragma GCC diagnostic ignored "-Wstrict-prototypes"
47 #include <semLib.h>
48 #pragma GCC diagnostic pop
49
50 #include <errnoLib.h>
51
52
53 /* --------------------- Test & Set/Swap internal API --------------------- */
54
55 /* We use a bare atomic primitive with busy loops to handle mutual exclusion.
56 Inefficient, but reliable. The actual primitive used depends on the mode
57 (RTP vs Kernel) and the version of VxWorks. We define a macro and a type
58 here, for reuse without conditionals cluttering in the code afterwards. */
59
60 /* RTP, pre 6.9. */
61
62 #if defined(__RTP__) && _VXWORKS_PRE(6,9)
63
64 #define __TAS(x) vxCas ((x), 0, 1)
65 typedef volatile unsigned char __vx_tas_t;
66
67 #endif
68
69 /* RTP, 6.9 and beyond. */
70
71 #if defined(__RTP__) && !_VXWORKS_PRE(6,9)
72
73 #define __TAS(x) vxAtomicCas ((x), 0, 1)
74 typedef atomic_t __vx_tas_t;
75
76 /* Our implementation will need the system headers to use the vxAtomic
77 primitives. Other includers won't and could actually be incompatible
78 with this inclusion, for instance libstdc++ sources compiled in C++
79 98 mode while AtomicLib for C++ requires C++ 11 at least. */
80
81 #if defined(IN_LIBGCC2)
82 #include <vxAtomicLib.h>
83 #endif
84
85 #endif
86
87 /* Kernel */
88
89 #if !defined(__RTP__)
90
91 #define __TAS(x) vxTas (x)
92 typedef volatile unsigned char __vx_tas_t;
93
94 #endif
95
96 #ifdef __cplusplus
97 extern "C" {
98 #endif
99
100 /* ------------------------ Base __GTHREADS support ----------------------- */
101
102 #define __GTHREADS 1
103 #define __gthread_active_p() 1
104
105 /* Mutexes are easy, except that they need to be initialized at runtime. */
106
107 /* All VxWorks mutexes are recursive. */
108 typedef SEM_ID __gthread_mutex_t;
109 typedef SEM_ID __gthread_recursive_mutex_t;
110 #define __GTHREAD_MUTEX_INIT_FUNCTION __gthread_mutex_init
111 #define __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION __gthread_recursive_mutex_init
112
113 #define __CHECK_RESULT(result) (((result) == OK) ? OK : errnoGet())
114
115 /* If a call to the VxWorks API fails, we must propagate the errno value. */
116 #define __RETURN_ERRNO_IF_NOT_OK(exp) if ((exp) != OK) return errnoGet()
117
118 /* Non re-entrant mutex implementation. Libstdc++ expects the default
119 gthread mutex to be non reentrant. */
120
121 static inline void
122 __gthread_mutex_init (__gthread_mutex_t * __mutex)
123 {
124 if (!__mutex)
125 return;
126 *__mutex = semBCreate (SEM_Q_PRIORITY, SEM_FULL);
127 }
128
129 static inline int
130 __gthread_mutex_destroy (__gthread_mutex_t * __mutex)
131 {
132 if (!__mutex)
133 return ERROR;
134 return __CHECK_RESULT (semDelete (*__mutex));
135 }
136
137 static inline int
138 __gthread_mutex_lock (__gthread_mutex_t * __mutex)
139 {
140 if (!__mutex)
141 return ERROR;
142 return __CHECK_RESULT (semTake(*__mutex, WAIT_FOREVER));
143 }
144
145 static inline int
146 __gthread_mutex_trylock (__gthread_mutex_t * __mutex)
147 {
148 if (!__mutex)
149 return ERROR;
150 return __CHECK_RESULT (semTake (*__mutex, NO_WAIT));
151 }
152
153 static inline int
154 __gthread_mutex_unlock (__gthread_mutex_t * __mutex)
155 {
156 if (!__mutex)
157 return ERROR;
158 return __CHECK_RESULT (semGive (*__mutex));
159 }
160
161 /* Recursive mutex implementation. The only change is that we use semMCreate()
162 instead of semBCreate(). */
163
164 static inline void
165 __gthread_recursive_mutex_init (__gthread_recursive_mutex_t * __mutex)
166 {
167 if (!__mutex)
168 return;
169 *__mutex =
170 semMCreate (SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE);
171 }
172
173 static inline int
174 __gthread_recursive_mutex_destroy (__gthread_recursive_mutex_t * __mutex)
175 {
176 return __gthread_mutex_destroy (__mutex);
177 }
178
179 static inline int
180 __gthread_recursive_mutex_lock (__gthread_recursive_mutex_t * __mutex)
181 {
182 return __gthread_mutex_lock (__mutex);
183 }
184
185 static inline int
186 __gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t * __mutex)
187 {
188 return __gthread_mutex_trylock (__mutex);
189 }
190
191 static inline int
192 __gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t * __mutex)
193 {
194 return __gthread_mutex_unlock (__mutex);
195 }
196
197 typedef struct
198 {
199 /* PPC's test-and-set kernel mode implementation requires a pointer aligned
200 object, of which it only sets the first byte. We use padding in addition
201 to an alignment request here to maxmise the factors leading to the
202 desired actual alignment choice by the compiler. */
203 #if defined(__PPC__)
204 __attribute ((aligned (__alignof__ (void *))))
205 #endif
206
207 __vx_tas_t busy;
208 volatile unsigned char done;
209
210 #if !defined(__RTP__) && defined(__PPC__)
211 unsigned char pad1;
212 unsigned char pad2;
213 #endif
214 #if !defined(__RTP__) && defined(__PPC64__)
215 unsigned char pad3;
216 unsigned char pad4;
217 unsigned char pad5;
218 unsigned char pad6;
219 #endif
220 } __gthread_once_t;
221
222 #define __GTHREAD_ONCE_INIT {}
223
224 extern int __gthread_once (__gthread_once_t *__once, void (*__func)(void));
225
226 /* All the TSD routines are sufficiently complex that they
227 need to be implemented out of line. */
228
229 typedef unsigned int __gthread_key_t;
230
231 extern int __gthread_key_create (__gthread_key_t *__keyp,
232 void (*__dtor)(void *));
233 extern int __gthread_key_delete (__gthread_key_t __key);
234
235 extern void *__gthread_getspecific (__gthread_key_t __key);
236 extern int __gthread_setspecific (__gthread_key_t __key, void *__ptr);
237
238 /* ------------------ Base condition variables support ------------------- */
239
240 /* VxWorks prio to 6 misses a few services key to a correct
241 implementation of condition variables with reasonable complexity.
242 semExchange in particular. */
243
244 #if _VXWORKS_MAJOR_GE(6)
245
246 #define __GTHREAD_HAS_COND 1
247
248 typedef SEM_ID __gthread_cond_t;
249
250 #define __GTHREAD_COND_INIT_FUNCTION __gthread_cond_init
251
252 /* Condition variable declarations. */
253
254 extern void __gthread_cond_init (__gthread_cond_t *cond);
255
256 extern int __gthread_cond_destroy (__gthread_cond_t *cond);
257
258 extern int __gthread_cond_broadcast (__gthread_cond_t *cond);
259
260 extern int __gthread_cond_wait (__gthread_cond_t *cond,
261 __gthread_mutex_t *mutex);
262
263 extern int __gthread_cond_wait_recursive (__gthread_cond_t *cond,
264 __gthread_recursive_mutex_t *mutex);
265
266 #endif
267
268 /* ----------------------- C++0x thread support ------------------------- */
269
270 /* We do not support C++0x threads on that VxWorks 653, which we can
271 recognize by VTHREADS being defined. */
272
273 #if _VXWORKS_MAJOR_GE(6) && !defined(VTHREADS)
274
275 #define __GTHREADS_CXX0X 1
276
277 #include <limits.h>
278 #include <time.h>
279 #include <tickLib.h>
280 #include <sysLib.h>
281 #include <version.h>
282
283 typedef struct
284 {
285 TASK_ID task_id;
286 void *return_value;
287
288 /* This mutex is used to block in join() while the return value is
289 unavailable. */
290 __gthread_mutex_t return_value_available;
291
292 /* Before freeing the structure in the task wrapper, we need to wait until
293 join() or detach() are called on that thread. */
294 __gthread_mutex_t delete_ok;
295 } __gthread_tcb;
296
297 typedef __gthread_tcb *__gthread_t;
298
299 /* Typedefs specific to different vxworks versions. */
300 #if _VXWORKS_PRE(6,9)
301 typedef int _Vx_usr_arg_t;
302 #define TASK_ID_NULL ((TASK_ID)NULL)
303 #define SEM_ID_NULL ((SEM_ID)NULL)
304 #endif
305
306 typedef struct timespec __gthread_time_t;
307
308 /* Timed mutex lock declarations. */
309
310 extern int __gthread_mutex_timedlock (__gthread_mutex_t *m,
311 const __gthread_time_t *abs_time);
312
313 extern int __gthread_recursive_mutex_timedlock
314 (__gthread_recursive_mutex_t *mutex,
315 const __gthread_time_t *abs_timeout);
316
317 /* Timed condition variable declarations. */
318
319 extern int __gthread_cond_signal (__gthread_cond_t *cond);
320 extern int __gthread_cond_timedwait (__gthread_cond_t *cond,
321 __gthread_mutex_t *mutex,
322 const __gthread_time_t *abs_timeout);
323
324 /* gthreads declarations. */
325
326 extern int __gthread_equal (__gthread_t t1, __gthread_t t2);
327 extern int __gthread_yield (void);
328 extern int __gthread_create (__gthread_t *__threadid,
329 void *(*__func) (void*),
330 void *__args);
331 extern int __gthread_join (__gthread_t thread, void **value_ptr);
332 extern int __gthread_detach (__gthread_t thread);
333
334 extern __gthread_t __gthread_self (void);
335
336 #endif /* _VXWORKS_MAJOR_GE(6) && !defined(VTHREADS) */
337
338 #ifdef __cplusplus
339 }
340 #endif
341
342 #endif /* not _LIBOBJC */
343
344 #endif /* gthr-vxworks.h */