1 /******************************************************************************
2 @file threads.c
4 @brief TIMAC 2.0 API CC1310, CC1350
6 Group: WCS LPC
7 $Target Devices: Linux: AM335x, Embedded Devices: CC1310, CC1350$
9 ******************************************************************************
10 $License: BSD3 2016 $
12 Copyright (c) 2015, Texas Instruments Incorporated
13 All rights reserved.
15 Redistribution and use in source and binary forms, with or without
16 modification, are permitted provided that the following conditions
17 are met:
19 * Redistributions of source code must retain the above copyright
20 notice, this list of conditions and the following disclaimer.
22 * Redistributions in binary form must reproduce the above copyright
23 notice, this list of conditions and the following disclaimer in the
24 documentation and/or other materials provided with the distribution.
26 * Neither the name of Texas Instruments Incorporated nor the names of
27 its contributors may be used to endorse or promote products derived
28 from this software without specific prior written permission.
30 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
31 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
32 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
35 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
36 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
37 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
38 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
39 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
40 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 ******************************************************************************
42 $Release Name: TI-15.4Stack Linux x64 SDK ENG$
43 $Release Date: Mar 08, 2017 (2.01.00.10)$
44 *****************************************************************************/
46 #include "compiler.h"
47 #include "threads.h"
49 #include "log.h"
50 #include "fatal.h"
52 #include <malloc.h>
53 #include <string.h>
55 #include "hlos_specific.h"
57 /*!
58 * @struct thread - [private] details about a thread
59 */
61 struct thread {
62 /*! pointer to verify this is a thread or not */
63 int *checkvalue;
64 /*! Name of this thread for debug purposes */
65 const char *dbg_name;
67 /*! Has been requested to die */
68 bool is_dead;
70 /*! The os identfier for this thread */
71 intptr_t os_id;
73 /*! The thread function */
74 threadfunc_t *func;
76 /*! The parameter for this thread */
77 intptr_t cookie;
79 /*! The startup flags for this thread */
80 int startflags;
82 /*! Exit value of the thread if it has exited) */
83 intptr_t exit_value;
85 /*! Flag indicating if the thread is alive or not (exited) */
86 bool is_alive;
88 /*! Or did we kill this thread? */
89 bool killed;
91 /* Next thread in list of known threads */
92 struct thread *pNext;
93 };
95 /*!
96 * @var all_threads
97 * @brief [private] list head for all threads
98 */
99 static struct thread *all_threads;
100 /*!
101 * !var check value to determine if thread structure is a thread
102 */
103 static int thread_checkvalue;
105 /*!
106 * @var thread_mutex
107 * @brief [private] Thread list mutex
108 */
110 /*!
111 * @brief [private] get debug name for this thread
112 * @param internal pointer to the thread details
113 * @returns printable string representing name of the thread
114 */
115 static const char *_thread_name(struct thread *pT)
116 {
117 const char *cp;
118 /*
119 * HERE THERE BE DRAGONS....
120 * see thread destroy
121 *
122 * There is a race condition...
123 *
124 * Time(A)
125 *
126 * Some thread(FOO) - requests the name of *THIS* thread(bar)
127 * Perhaps for logging purposes.
128 *
129 * Time(B)
130 * However, thread(bar) destroys it self, or dies
131 * and the thread gets destroyed ..
132 *
133 * Time(C)
134 * As a result of destruction(bar), the dbg name is destroyed
135 * actually it is free() ...
136 *
137 * Time(D)
138 * The actual log message occurs and references the name
139 * that was fetched in condition (A)
140 *
141 * What this will do is cause a reference to memory that
142 * has been already freed().
143 *
144 * BECAUSE: The debug names are *ONLY* used if and only if
145 * an error has occured, or debug logging is turned on.
146 *
147 * It is not a runtime critical thing..
148 *
149 * Thus this is an extremely rare condition that we are not
150 * going to solve.
151 */
153 if(pT == NULL)
154 {
155 return ("not-a-thread");
156 }
158 cp = pT->dbg_name;
159 if(cp == NULL)
160 {
161 cp = "thread-no-name";
162 }
163 return (cp);
164 }
166 /*!
167 * @brief [private] Convert a thread handle into an internal representation
168 * @param h - the thread handle
169 * @returns - if valid, a pointer to the thread details otherwise null
170 */
171 static struct thread *h2p(intptr_t h)
172 {
173 struct thread *pT;
175 /* cast it */
176 pT = (struct thread *)(h);
178 /* And if it seems ok... keep checking */
179 if(pT)
180 {
182 if(pT->checkvalue == &thread_checkvalue)
183 {
184 return (pT);
185 }
186 }
187 /* Hmm... print an error message */
188 LOG_printf(LOG_ERROR, "%p: is not a thread\n", (void *)pT);
189 return (NULL);
190 }
192 /*!
193 * @brief [private] Lock our list of threads
194 * @return void
195 */
196 static void thread_list_lock(void)
197 {
198 _ATOMIC_global_lock();
199 }
201 /*!
202 * @brief [private] Unlock our list of threads
203 * @return void
204 */
205 static void thread_list_unlock(void)
206 {
207 _ATOMIC_global_unlock();
208 }
210 /*!
211 * @brief Wrapper to start a thread and handle return values
212 */
213 static intptr_t thread_wrapper(intptr_t param)
214 {
215 struct thread *pT;
216 intptr_t r;
218 /* recover our data structure from the pthread parameter */
219 pT = (struct thread *)(param);
221 /* Future we might do stuff here. */
222 pT->is_alive = true;
223 /* life begins.... */
224 r = (*(pT->func))(pT->cookie);
225 /* time to exit */
226 pT->exit_value = r;
227 pT->is_alive = false;
228 return (r);
229 }
231 /*!
232 * Create a thread
233 *
234 * Public function defined in threads.h
235 */
236 intptr_t THREAD_create(const char *name,
237 threadfunc_t *func,
238 intptr_t cookie,
239 int startflags)
240 {
241 struct thread *pT;
243 /* dup the name */
244 if(name == NULL)
245 {
246 name = "noname";
247 }
248 name = strdup(name);
250 /* get space for details */
251 pT = calloc(1,sizeof(*pT));
252 if(pT == NULL)
253 {
254 free_const((const void *)(name));
255 LOG_printf(LOG_ERROR,"no memory for thread?\n");
256 return (0);
257 }
259 /* fill in details struct */
260 pT->dbg_name = name;
261 pT->checkvalue = &thread_checkvalue;
262 pT->startflags = startflags;
263 pT->cookie = cookie;
264 pT->func = func;
265 pT->is_alive = false;
266 pT->is_dead = false;
267 pT->exit_value = 0;
269 /* we only support default now */
270 if(pT->startflags != THREAD_FLAGS_DEFAULT)
271 {
272 BUG_HERE("THREAD_create() flag not supported\n");
273 }
274 LOG_printf(LOG_DBG_THREAD, "%s: THREAD_create()\n", _thread_name(pT));
276 /* add to the list */
277 thread_list_lock();
278 pT->pNext = all_threads;
279 all_threads = pT;
280 thread_list_unlock();
282 /* create the thread */
283 pT->os_id = _THREAD_create(pT->dbg_name, thread_wrapper, (intptr_t)(pT));
284 if(pT->os_id == 0)
285 {
286 THREAD_destroy((intptr_t)(pT));
287 pT = NULL;
288 }
290 return ((intptr_t)(pT));
291 }
293 /*
294 * Destroy a thread
295 *
296 * Public function defined in treads.h
297 */
299 void THREAD_destroy(intptr_t h)
300 {
301 struct thread *pT;
302 struct thread **ppT;
304 pT = h2p(h);
305 if(pT == NULL)
306 {
307 return;
308 }
310 if(pT->is_alive)
311 {
312 pT->killed = true;
313 _THREAD_destroy(pT->os_id);
314 pT->os_id = 0;
315 /* nothing we can do.. */
316 }
317 /* remove from thread linked list */
319 thread_list_lock();
320 ppT = &(all_threads);
321 while(*ppT)
322 {
323 if((*ppT) != pT)
324 {
325 ppT = &((*ppT)->pNext);
326 continue;
327 }
328 else
329 {
330 break;
331 }
332 }
334 if(*ppT)
335 {
336 /* Found, remove from list */
337 (*ppT) = (*ppT)->pNext;
338 }
339 else
340 {
341 BUG_HERE("Thread (%s) @ %p not found in list of known threads\n",
342 pT->dbg_name, pT );
343 }
345 thread_list_unlock();
347 if(pT->dbg_name)
348 {
349 /*
350 * HERE THERE BE DRAGONS ...
351 * See the race condition described in the
352 * function: _thread_name()
353 *
354 * If this truely is a problem during debug
355 * the solution (workaround) is this:
356 *
357 * If you are debugging - do not free() the
358 * thread name
359 */
360 free_const((const void *)(pT->dbg_name));
361 }
363 memset((void *)(pT), 0, sizeof(*pT));
364 free((void *)(pT));
365 }
367 /*!
368 * @brief [private] Get our own thread details pointer
369 */
370 static struct thread *_thread_self(void)
371 {
372 intptr_t whoami;
373 struct thread *pT;
375 whoami = _THREAD_self();
376 /* find our self */
377 thread_list_lock();
378 pT = all_threads;
379 while(pT)
380 {
381 if(pT->os_id == whoami)
382 {
383 break;
384 }
385 pT = pT->pNext;
386 }
387 thread_list_unlock();
388 return (pT);
389 }
391 /*
392 * Return the debug name of the current thread
393 *
394 * Public function defined in threads.h
395 */
396 const char *THREAD_selfName(void)
397 {
398 struct thread *pT;
400 pT = _thread_self();
402 return (_thread_name(pT));
403 }
405 /*
406 * Return the debug name of a specific thread
407 *
408 * Public function defined in threads.h
409 */
410 const char *THREAD_name(intptr_t h)
411 {
412 struct thread *pT;
414 pT = h2p(h);
416 return (_thread_name(pT));
417 }
419 /*
420 * Return the id of the current thread
421 *
422 * Public function defined in threads.h
423 */
424 intptr_t THREAD_self(void)
425 {
426 struct thread *pT;
428 pT = _thread_self();
430 return ((intptr_t)(pT));
431 }
433 /*
434 * Return the exit value of a thread
435 *
436 * Public function defined in threads.h
437 */
438 intptr_t THREAD_getExitValue(intptr_t h)
439 {
440 struct thread *pT;
442 pT = h2p(h);
443 if(pT == NULL)
444 {
445 return (-1);
446 }
447 return (pT->exit_value);
448 }
450 /*
451 * Called by threads to exit their execution.
452 *
453 * Public function defined in threads.h
454 */
455 void THREAD_exit(intptr_t value)
456 {
457 struct thread *pT;
459 /* find our self */
460 pT = _thread_self();
461 LOG_printf(LOG_DBG_THREAD,
462 "%s: THREAD_Exit(%d)\n",
463 _thread_name(pT),
464 (int)value);
466 /* record our expiry value */
467 pT->exit_value = value;
468 /* we are dead jim.. */
469 pT->is_alive = false;
470 /* and die */
471 _THREAD_exit();
472 }
474 /*
475 * Return true if this thread is still alive (not exited)
476 *
477 * Public function defined in threads.h
478 */
479 bool THREAD_isAlive(intptr_t h)
480 {
481 struct thread *pT;
483 pT = h2p(h);
484 if(pT == NULL)
485 {
486 return (false);
487 }
488 return (pT->is_alive);
489 }
491 /*
492 * ========================================
493 * Texas Instruments Micro Controller Style
494 * ========================================
495 * Local Variables:
496 * mode: c
497 * c-file-style: "bsd"
498 * tab-width: 4
499 * c-basic-offset: 4
500 * indent-tabs-mode: nil
501 * End:
502 * vim:set filetype=c tabstop=4 shiftwidth=4 expandtab=true
503 */