1 /*
2 * ThreadX C/C++ Library Support
3 *
4 * Copyright 1983-2019 Green Hills Software LLC.
5 *
6 * This program is the property of Green Hills Software LLC.,
7 * its contents are proprietary information and no part of it
8 * is to be disclosed to anyone except employees of Green Hills
9 * Software LLC., or as agreed in writing signed by the President
10 * of Green Hills Software LLC.
11 */
12
13 #include "tx_ghs.h"
14 #ifndef TX_DISABLE_ERROR_CHECKING
15 #define TX_DISABLE_ERROR_CHECKING
16 #endif
17 #include "tx_api.h"
18 #include <setjmp.h>
19 #include <string.h>
20
21 /* Allow these routines to access the following ThreadX global variables. */
22 extern ULONG _tx_thread_created_count;
23 extern TX_THREAD *_tx_thread_created_ptr;
24 extern TX_THREAD *_tx_thread_current_ptr;
25
26 #if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500)
27 /* Thread-local storage routines for Green Hills releases 5.x and above. */
28 /*
29 Thread-Local (Per-Thread) Library Data Retrieval
30 ================================================
31
32 __ghs_ThreadLocalStorage_specifier defines all library data items
33 that the Green Hills libraries allow to be allocated per-thread.
34
35 An implementation can choose which of these data items to allocate
36 for each thread. For example, an implementation may choose to
37 allocate an errno value for each thread, but not the strtok_saved_pos
38 pointer. The application could then use strtok_r instead of strtok for
39 correct operation.
40
41 To add per-thread library data, define one of the
42 TX_THREAD_EXTENSION_* macros in tx_port.h to include the data item
43 or items in each thread control block TX_THREAD.
44
45 If C++ with exceptions is being used, the __eh_globals entry must be
46 allocated for each thread. This is typically done by default using
47 TX_THREAD_EXTENSION_1 in tx_port.h.
48
49 If __ghs_GetThreadLocalStorageItem is customized to return a
50 per-thread errno value, you should also:
51
52 * Customize the System Library for your project
53 * Define the preprocessor symbol USE_THREAD_LOCAL_ERRNO in
54 src/libsys/ind_errn.c
55
56 If you customize the System Library, you should remove ind_thrd.c
57 from the libsys.gpj subproject.
58
59 */
60
61 /* Provide global __eh_globals value to support C++ exception handling
62 outside a thread context. This name also forces this module to be
63 included in the linked program instead of the ind_thrd.o module from
64 the System Library libsys.a.
65 */
66 static void *__eh_globals;
67
68 #pragma ghs startnomisra
__ghs_GetThreadLocalStorageItem(int specifier)69 void *__ghs_GetThreadLocalStorageItem(int specifier)
70 {
71 void *ptlsitem = (void *)0;
72 switch (specifier) {
73 case (int)__ghs_TLS_Errno:
74 /* Set ptslsitem to the address of the per-thread errno value.
75 The per-thread errno value should have the type int.
76
77 If returning a per-thread errno value, follow the steps
78 above.
79
80 This item is used by numerous library functions.
81 */
82 break;
83 case (int)__ghs_TLS_SignalHandlers:
84 /* Set ptslsitem to the address of the per-thread SignalHandlers
85 array. The per-thread SignalHandlers array should have the
86 array type as in the following declaration:
87 SignalHandler SignalHandlers[_SIGMAX];
88 The SignalHandler type and _SIGMAX constant are defined in
89 ind_thrd.h.
90
91 This item is used by the library functions signal() and
92 raise().
93 */
94 break;
95 case (int)__ghs_TLS_asctime_buff:
96 /* Set ptslsitem to the address of the per-thread asctime_buff
97 array. The per-thread asctime_buff array should have the
98 array type as in the following declaration:
99 char asctime_buff[30];
100
101 This item is used by the library functions asctime() and
102 ctime(). The library provides asctime_r() and ctime_r(),
103 inherently thread-safe versions of these functions.
104 */
105 break;
106 case (int)__ghs_TLS_tmpnam_space:
107 /* Set ptslsitem to the address of the per-thread tmpnam_space
108 array. The per-thread tmpnam_space array should have the
109 array type as in the following declaration:
110 char tmpnam_space[L_tmpnam];
111 The constant is defined in <stdio.h>
112
113 This item is used by the library function tmpnam() when
114 passed NULL. The library provides tmpnam_r(), an
115 inherently thread-safe version of tmpnam().
116 */
117 break;
118 case (int)__ghs_TLS_strtok_saved_pos:
119 /* Set ptslsitem to the address of the per-thread
120 strtok_saved_pos pointer. The per-thread strtok_saved_pos
121 pointer should have the type "char *".
122
123 This item is used by the library function strtok().
124 The library provides strtok_r(), an inherently thread-safe
125 version of strtok().
126 */
127 break;
128 case (int)__ghs_TLS_gmtime_temp:
129 /* Set ptslsitem to the address of the per-thread gmtime_temp
130 value. The per-thread gmtime_temp value should have the
131 type "struct tm" defined in time.h, included by indos.h.
132
133 This item is used by the library functions gmtime() and
134 localtime(). The library provides gmtime_r() and
135 localtime_r(), inherently thread-safe versions of these
136 functions.
137 */
138 break;
139 case (int)__ghs_TLS___eh_globals:
140 /* Set ptslsitem to the address of the per-thread __eh_globals
141 value. The per-thread __eh_globals value should have the
142 type "void *".
143
144 This item is used by C++ exception handling.
145 */
146 if (_tx_thread_current_ptr)
147 ptlsitem = (void *)&(_tx_thread_current_ptr->tx_thread_eh_globals);
148 else
149 /* Use the global __eh_globals pointer. */
150 ptlsitem = (void *)&__eh_globals;
151 break;
152 }
153 return ptlsitem;
154 }
155 #pragma ghs endnomisra
156 #else
157 /* Thread-local storage routines for Green Hills releases 4.x and 3.x . */
158
159 /*
160 * ThreadX C and C++ thread-safe library support routines.
161 *
162 * This implementation merely tries to guarantee thread safety within
163 * individual C library calls such as malloc() and free(), but it does
164 * not attempt to solve the problems associated with the following
165 * multithreaded issues:
166 *
167 * 1. Use of errno. This can be made thread-safe by adding errno
168 * to TX_THREAD_PORT_EXTENSION and using that within a modified
169 * version of libsys/ind_errno.c.
170 *
171 * 2. Thread safety ACROSS library calls. Certain C library calls either
172 * return pointers to statically-allocated data structures or maintain
173 * state across calls. These include strtok(), asctime(), gmtime(),
174 * tmpnam(NULL), signal(). To make such C library routines thread-safe
175 * would require adding a ThreadLocalStorage struct to the thread control
176 * block TX_THREAD. Since relatively few applications make use of these
177 * library routines, the implementation provided here uses a single, global
178 * ThreadLocalStorage data structure rather than greatly increasing the size
179 * of the thread control block TX_THREAD.
180 *
181 * The ThreadX global variable _tx_thread_current_ptr points to the
182 * current thread's control block TX_THREAD. If a ThreadLocalStorage struct
183 * called tx_tls is placed in TX_THREAD, the function GetThreadLocalStorage
184 * should be modified to return &(_tx_thread_current_ptr->tx_tls).
185 */
186
187 static ThreadLocalStorage GlobalTLS;
188
GetThreadLocalStorage()189 ThreadLocalStorage *GetThreadLocalStorage()
190 {
191 return &GlobalTLS;
192 }
193 #endif
194
195 /*
196 * Use a global ThreadX mutex to implement thread safety within C and C++
197 * library routines.
198 *
199 */
200 TX_MUTEX __ghLockMutex;
201
202 /*
203 * Acquire general lock. Blocks until the lock becomes available.
204 * Use tx_mutex_get to implement __ghsLock
205 */
__ghsLock(void)206 void __ghsLock(void)
207 {
208 tx_mutex_get(&__ghLockMutex, TX_WAIT_FOREVER);
209 }
210
211 /*
212 * Release general lock
213 * Use tx_mutex_put to implement __ghsUnlock
214 */
__ghsUnlock(void)215 void __ghsUnlock(void)
216 {
217 tx_mutex_put(&__ghLockMutex);
218 }
219
220 /* ThreadX Initialization function prototype. */
221 void _tx_initialize_kernel_setup(void);
222
__gh_lock_init(void)223 void __gh_lock_init(void)
224 {
225 /* Initialize the low-level portions of ThreadX. */
226 _tx_initialize_kernel_setup();
227
228 /* Create the global thread lock mutex. */
229 tx_mutex_create(&__ghLockMutex, "__ghLockMutex", TX_NO_INHERIT);
230 }
231
232 /*
233 Saving State Across setjmp() Calls
234 ==================================
235
236 These routines can be used to save and restore arbitrary state
237 across calls to setjmp() and longjmp().
238 */
__ghs_SaveSignalContext(jmp_buf jmpbuf)239 int __ghs_SaveSignalContext(jmp_buf jmpbuf)
240 {
241 return 0;
242 }
243
244 /* Restore arbitrary state across a longjmp() */
__ghs_RestoreSignalContext(jmp_buf jmpbuf)245 void __ghs_RestoreSignalContext(jmp_buf jmpbuf)
246 {
247 }
248
249 #if defined(__GHS_VERSION_NUMBER) && (__GHS_VERSION_NUMBER < 560)
250 /*
251 C++ Exception Handling
252 ======================
253
254 These routines allow C++ exceptions to be used in multiple threads.
255 The default implementation uses __ghs_GetThreadLocalStorageItem
256 to return a thread-specific __eh_globals pointer.
257
258 */
259
260 /* Must be called after __cpp_exception_init() is called to allocate
261 * and initialize the per-thread exception handling structure */
__get_eh_globals(void)262 void *__get_eh_globals(void)
263 {
264 #if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500)
265 return *(void **)__ghs_GetThreadLocalStorageItem(__ghs_TLS___eh_globals);
266 #else
267 if (_tx_thread_current_ptr)
268
269 /* Return thread-specific __eh_globals pointer. */
270 return _tx_thread_current_ptr->tx_thread_eh_globals;
271 else
272 /* Return the global __eh_globals pointer. */
273 return GlobalTLS.__eh_globals;
274 #endif
275 }
276 #endif
277
278 #if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500)
279 #pragma weak __cpp_exception_init
280 extern void __cpp_exception_init(void **);
281 #pragma weak __cpp_exception_cleanup
282 extern void __cpp_exception_cleanup(void **);
283
284 /* __tx_cpp_exception_init retrieves the eh_globals field from
285 thread-local storage and calls __cpp_exception_init.
286 */
__tx_cpp_exception_init(TX_THREAD * thread_ptr)287 void __tx_cpp_exception_init(TX_THREAD *thread_ptr) {
288 void **peh_globals;
289 if(__cpp_exception_init) {
290 if (thread_ptr)
291 peh_globals = &(thread_ptr->tx_thread_eh_globals);
292 else
293 /* Use the global __eh_globals pointer. */
294 peh_globals = &__eh_globals;
295 __cpp_exception_init(peh_globals);
296 }
297 }
298
299 /* __tx_cpp_exception_cleanup retrieves the eh_globals field from
300 thread-local storage and calls __cpp_exception_cleanup.
301 */
__tx_cpp_exception_cleanup(TX_THREAD * thread_ptr)302 void __tx_cpp_exception_cleanup(TX_THREAD *thread_ptr) {
303 void **peh_globals;
304 if(__cpp_exception_cleanup) {
305 if (thread_ptr)
306 peh_globals = &(thread_ptr->tx_thread_eh_globals);
307 else
308 /* Use the global __eh_globals pointer. */
309 peh_globals = &__eh_globals;
310 __cpp_exception_cleanup(peh_globals);
311 }
312 }
313
314 /* __ghs_cpp_exception_init is called from ind_crt1.o to initialize
315 exceptions for the global context.
316 */
__ghs_cpp_exception_init()317 void __ghs_cpp_exception_init() {
318 __tx_cpp_exception_init((void *)0);
319 }
320
321 /* __ghs_cpp_exception_cleanup is called from ind_exit.o to clean up
322 exceptions for the global context.
323 */
__ghs_cpp_exception_cleanup(TX_THREAD * thread_ptr)324 void __ghs_cpp_exception_cleanup(TX_THREAD *thread_ptr) {
325 __tx_cpp_exception_cleanup((void *)0);
326 }
327 #endif
328
329
330 /*
331 File Locks
332 ======================
333
334 These routines can be customized to implement per-file locks to allow
335 thread-safe I/O.
336
337 */
338
339 /* Acquire lock for FILE *addr */
__ghs_flock_file(void * addr)340 void __ghs_flock_file(void *addr)
341 {
342 tx_mutex_get((TX_MUTEX *)addr, TX_WAIT_FOREVER);
343 }
344
345 /* Release lock for FILE *addr */
__ghs_funlock_file(void * addr)346 void __ghs_funlock_file(void *addr)
347 {
348 tx_mutex_put((TX_MUTEX *)addr);
349 }
350
351 /* Non blocking acquire lock for FILE *addr. May return -1 if */
352 /* not implemented. Returns 0 on success and nonzero otherwise. */
__ghs_ftrylock_file(void * addr)353 int __ghs_ftrylock_file(void *addr)
354 {
355 return -1;
356 }
357
358 /* Calls to initialize local lock data structures before they */
359 /* are used. */
__ghs_flock_create(void ** addr)360 void __ghs_flock_create(void **addr)
361 {
362 *addr = (void *)(&__ghLockMutex);
363 }
__ghs_flock_destroy(void * addr)364 void __ghs_flock_destroy(void *addr) {}
365
366
367 /*
368 * ThreadX Peak Stack Checking support routines.
369 *
370 * All of these routines are called by MULTI's ThreadX-aware debugging
371 * package to determine the peak stack use for one thread or for all threads.
372 *
373 * These routines are included in this file in order to guarantee that they will
374 * be available while debugging with MULTI. These routines are not referenced by
375 * any other part of the ThreadX system.
376 *
377 * _txs_thread_stack_check: return the peak stack usage for a thread.
378 *
379 * _txs_thread_stack_check_2: store the peak stack usage for all threads
380 * in the tx_thread_stack_size field of each thread
381 * control block, TX_THREAD. This routine takes
382 * advantage of the redundancy within the TX_THREAD
383 * structure since tx_thread_stack_size can be computed
384 * from the tx_thread_stack_start and tx_thread_stack_end
385 * fields of TX_THREAD.
386 *
387 * _txs_thread_stack_check_2_fixup: clean up from the _txs_thread_stack_check_2
388 * call by computing the stack size for each
389 * thread and storing the result in the
390 * tx_thread_stack_size field of each thread control
391 * block TX_THREAD.
392 *
393 * These three routines do not support architectures such as i960 or StarCore
394 * where the stack grows up instead of down.
395 *
396 */
397 #ifndef TX_DISABLE_STACK_CHECKING
398
_txs_thread_stack_check(TX_THREAD * thread_ptr)399 ULONG _txs_thread_stack_check(TX_THREAD *thread_ptr)
400 {
401 CHAR *cp; /* Pointer inside thread's stack. */
402
403 /* Search through the thread's stack to find the highest address modified. */
404 for ( cp = (CHAR *)thread_ptr->tx_thread_stack_start;
405 cp <= (CHAR *)thread_ptr->tx_thread_stack_end; ++cp ) {
406
407 /* Check if this byte in the stack contains something other than TX_STACK_FILL. */
408 if (*cp != (char)TX_STACK_FILL) {
409
410 /* Assume cp points to the locating marking the peak stack use.
411 Return the number of bytes from cp up to and including the
412 end of the stack. */
413 return (((ULONG)thread_ptr->tx_thread_stack_end) - (ULONG)cp + 1);
414 }
415 }
416 return thread_ptr->tx_thread_stack_size;
417 }
418
419
_txs_thread_stack_check_2(void)420 int _txs_thread_stack_check_2(void) {
421 CHAR * cp; /* Pointer inside thread's stack. */
422 TX_THREAD * tp; /* Pointer to each thread. */
423
424 /* If no threads are created, return immediately. */
425 if (!_tx_thread_created_count)
426 return 0;
427
428 /* Start iterating through the threads in the system. Assume that we always
429 have at least one thread (the system timer thread) in the system. */
430 tp = _tx_thread_created_ptr;
431
432 do {
433
434 /* Search through the thread's stack to find the highest address modified. */
435 for ( cp = (CHAR *)tp->tx_thread_stack_start; cp <= (CHAR *)tp->tx_thread_stack_end;
436 ++cp ) {
437
438 /* Check if this byte in the stack contains something other than TX_STACK_FILL. */
439 if (*cp != (char)TX_STACK_FILL) {
440
441 /* Assume cp points to the locating marking the peak stack use.
442 Store the number of bytes from cp up to and including the
443 end of the stack in the tx_thread_stack_size field. */
444 tp->tx_thread_stack_size = ((ULONG)tp->tx_thread_stack_end) - (ULONG)cp + 1;
445 break;
446 }
447
448 }
449
450 /* Continue with the next thread. */
451 tp = tp->tx_thread_created_next;
452
453 /* Loop until we point to the first thread again. */
454 } while ( tp != _tx_thread_created_ptr );
455
456 return 0;
457 }
458
_txs_thread_stack_check_2_fixup(void)459 int _txs_thread_stack_check_2_fixup(void) {
460 TX_THREAD * tp; /* Pointer to each thread. */
461
462 /* If no threads are created, return immediately. */
463 if (!_tx_thread_created_count)
464 return 0;
465
466 /* Start iterating through the threads in the system. Assume that we always
467 have at least one thread (the system timer thread) in the system. */
468 tp = _tx_thread_created_ptr;
469
470 do {
471
472 /* Compute the tx_thread_stack_size field by using the tx_thread_stack_end and
473 tx_thread_stack_start fields. */
474 tp->tx_thread_stack_size = (ULONG)tp->tx_thread_stack_end-(ULONG)tp->tx_thread_stack_start+1;
475
476 /* Continue with the next thread. */
477 tp = tp->tx_thread_created_next;
478
479 /* Loop until we point to the first thread again. */
480 } while ( tp != _tx_thread_created_ptr );
481
482 return 0;
483 }
484
485 #endif /* TX_DISABLE_STACK_CHECKING */
486