version 2.8
gos_x_threads.c
1 /*
2  * This file is subject to the terms of the GFX License. If a copy of
3  * the license was not distributed with this file, you can obtain one at:
4  *
5  * http://ugfx.org/license.html
6  */
7 
8 #include "../../gfx.h"
9 
10 #if GOS_NEED_X_THREADS
11 
12 /*********************************************************
13  * Semaphores and critical region functions
14  *********************************************************/
15 
16 #if !defined(INTERRUPTS_OFF) || !defined(INTERRUPTS_ON)
17  #define INTERRUPTS_OFF()
18  #define INTERRUPTS_ON()
19 #endif
20 
21 void gfxSystemLock(void) {
22  INTERRUPTS_OFF();
23 }
24 
25 void gfxSystemUnlock(void) {
26  INTERRUPTS_ON();
27 }
28 
29 void gfxMutexInit(gfxMutex *pmutex) {
30  pmutex[0] = 0;
31 }
32 
33 void gfxMutexEnter(gfxMutex *pmutex) {
34  INTERRUPTS_OFF();
35  while (pmutex[0]) {
36  INTERRUPTS_ON();
37  gfxYield();
38  INTERRUPTS_OFF();
39  }
40  pmutex[0] = 1;
41  INTERRUPTS_ON();
42 }
43 
44 void gfxMutexExit(gfxMutex *pmutex) {
45  pmutex[0] = 0;
46 }
47 
48 void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit) {
49  psem->cnt = val;
50  psem->limit = limit;
51 }
52 
53 bool_t gfxSemWait(gfxSem *psem, delaytime_t ms) {
54  systemticks_t starttm, delay;
55 
56  // Convert our delay to ticks
57  starttm = 0;
58  switch (ms) {
59  case TIME_IMMEDIATE:
60  delay = TIME_IMMEDIATE;
61  break;
62  case TIME_INFINITE:
63  delay = TIME_INFINITE;
64  break;
65  default:
66  delay = gfxMillisecondsToTicks(ms);
67  if (!delay) delay = 1;
68  starttm = gfxSystemTicks();
69  }
70 
71  INTERRUPTS_OFF();
72  while (psem->cnt <= 0) {
73  INTERRUPTS_ON();
74  // Check if we have exceeded the defined delay
75  switch (delay) {
76  case TIME_IMMEDIATE:
77  return FALSE;
78  case TIME_INFINITE:
79  break;
80  default:
81  if (gfxSystemTicks() - starttm >= delay)
82  return FALSE;
83  break;
84  }
85  gfxYield();
86  INTERRUPTS_OFF();
87  }
88  psem->cnt--;
89  INTERRUPTS_ON();
90  return TRUE;
91 }
92 
93 bool_t gfxSemWaitI(gfxSem *psem) {
94  if (psem->cnt <= 0)
95  return FALSE;
96  psem->cnt--;
97  return TRUE;
98 }
99 
100 void gfxSemSignal(gfxSem *psem) {
101  INTERRUPTS_OFF();
102  gfxSemSignalI(psem);
103  INTERRUPTS_ON();
104 }
105 
106 void gfxSemSignalI(gfxSem *psem) {
107  if (psem->cnt < psem->limit)
108  psem->cnt++;
109 }
110 
111 /*********************************************************
112  * Sleep functions
113  *********************************************************/
114 
115 void gfxSleepMilliseconds(delaytime_t ms) {
116  systemticks_t starttm, delay;
117 
118  // Safety first
119  switch (ms) {
120  case TIME_IMMEDIATE:
121  return;
122  case TIME_INFINITE:
123  while(1)
124  gfxYield();
125  return;
126  }
127 
128  // Convert our delay to ticks
129  delay = gfxMillisecondsToTicks(ms);
130  starttm = gfxSystemTicks();
131 
132  do {
133  gfxYield();
134  } while (gfxSystemTicks() - starttm < delay);
135 }
136 
137 void gfxSleepMicroseconds(delaytime_t ms) {
138  systemticks_t starttm, delay;
139 
140  // Safety first
141  switch (ms) {
142  case TIME_IMMEDIATE:
143  return;
144  case TIME_INFINITE:
145  while(1)
146  gfxYield();
147  return;
148  }
149 
150  // Convert our delay to ticks
151  delay = gfxMillisecondsToTicks(ms/1000);
152  starttm = gfxSystemTicks();
153 
154  do {
155  gfxYield();
156  } while (gfxSystemTicks() - starttm < delay);
157 }
158 
159 /*********************************************************
160  * Threading functions
161  *********************************************************/
162 
163 /** For each scheduler the following need to be defined...
164  *
165  * void _gfxThreadsInit(void); - Initialise the scheduler
166  * void _gfxStartThread(thread *oldt, thread *newt); - Start a new thread
167  * void _gfxTaskSwitch(thread *oldt, thread *newt); - Switch to a different thread
168  *
169  */
170 
171 typedef struct thread {
172  struct thread * next; // Next thread
173  int flags; // Flags
174  #define FLG_THD_ALLOC 0x0001
175  #define FLG_THD_MAIN 0x0002
176  #define FLG_THD_DEAD 0x0004
177  #define FLG_THD_WAIT 0x0008
178  size_t size; // Size of the thread stack (including this structure)
179  threadreturn_t (*fn)(void *param); // Thread function
180  void * param; // Parameter for the thread function
181  void * cxt; // The current thread context.
182  } thread;
183 
184 typedef struct threadQ {
185  thread *head;
186  thread *tail;
187 } threadQ;
188 
189 static threadQ readyQ; // The list of ready threads
190 static threadQ deadQ; // Where we put threads waiting to be deallocated
191 thread * _gfxCurrentThread; // The current running thread - unfortunately this has to be non-static for the keil compiler
192 static thread mainthread; // The main thread context
193 
194 #undef GFX_THREADS_DONE
195 
196 #if GFX_CPU == GFX_CPU_CORTEX_M0 || GFX_CPU == GFX_CPU_CORTEX_M1
197  #include "gos_x_threads_cortexm01.h"
198 #elif GFX_CPU == GFX_CPU_CORTEX_M3 || GFX_CPU == GFX_CPU_CORTEX_M4 || GFX_CPU == GFX_CPU_CORTEX_M7
199  #include "gos_x_threads_cortexm347.h"
200 #elif GFX_CPU == GFX_CPU_CORTEX_M4_FP || GFX_CPU == GFX_CPU_CORTEX_M7_FP
201  #include "gos_x_threads_cortexm47fp.h"
202 #endif
203 
204 #ifndef GFX_THREADS_DONE
205  #define GFX_THREADS_DONE
206 
207  #include <string.h> // Prototype for memcpy()
208  #include <setjmp.h>
209 
210  /**
211  * Some compilers define a _setjmp() and a setjmp().
212  * The difference between them is that setjmp() saves the signal masks.
213  * That is of no use to us so we prefer to use the _setjmp() methods.
214  * If they don't exist compile them to be the standard setjmp() function.
215  * Similarly for longjmp().
216  */
217  #if (!defined(setjmp) && !defined(_setjmp)) || GFX_COMPILER == GFX_COMPILER_KEIL || GFX_COMPILER == GFX_COMPILER_MINGW32 || GFX_COMPILER == GFX_COMPILER_MINGW64
218  #define CXT_SAVE setjmp
219  #else
220  #define CXT_SAVE _setjmp
221  #endif
222  #if (!defined(longjmp) && !defined(_longjmp)) || GFX_COMPILER == GFX_COMPILER_KEIL || GFX_COMPILER == GFX_COMPILER_MINGW32 || GFX_COMPILER == GFX_COMPILER_MINGW64
223  #define CXT_RESTORE longjmp
224  #else
225  #define CXT_RESTORE _longjmp
226  #endif
227 
228  // A place to store the main thread context.
229  // All other threads will store the context directly after the thread structure (as part of the stack space).
230  static jmp_buf maincxt;
231 
232  /**
233  * There are some compilers we know how they store the jmpbuf. For those
234  * we can use the constant macro definitions. For others we have to "auto-detect".
235  * Auto-detection is hairy and there is no guarantee it will work on all architectures.
236  * For those it doesn't - read the compiler manuals and the library source code to
237  * work out the correct macro values.
238  * You can use the debugger to work out the values for your compiler and put them here.
239  * Defining these macros as constant values makes the system behaviour guaranteed but also
240  * makes your code compiler and cpu architecture dependent. It also saves a heap of code
241  * and a few bytes of RAM.
242  *
243  * MACROS:
244  *
245  * AUTO_DETECT_STACKFRAME TRUE/FALSE - TRUE to auto-detect stack frame structure
246  * STACK_DIR_UP Macro/bool_t - TRUE if the stack grows up instead of down
247  * MASK1 Macro/uint32_t - The 1st mask of jmp_buf elements that need relocation
248  * MASK2 Macro/uint32_t - The 2nd mask of jmp_buf elements that need relocation
249  * STACK_BASE Macro/size_t - The base of the stack frame relative to the local variables
250  * _gfxThreadsInit() Macro/Function - Initialise the scheduler
251  *
252  */
253  #if GFX_COMPILER == GFX_COMPILER_MINGW32
254 
255  #define AUTO_DETECT_STACKFRAME FALSE
256  #define STACK_DIR_UP FALSE
257  #define MASK1 0x00000011
258  #define MASK2 0x00000000
259  #define STACK_BASE 12
260  #define _gfxThreadsInit() mainthread.cxt = maincxt
261 
262  #else
263 
264  // Use auto-detection of the stack frame format
265  // Assumes all the relevant stuff to be relocated is in the first 256 bytes of the jmpbuf.
266  #define AUTO_DETECT_STACKFRAME TRUE
267  #define STACK_DIR_UP stackdirup // TRUE if the stack grow up instead of down
268  #define MASK1 jmpmask1 // The 1st mask of jmp_buf elements that need relocation
269  #define MASK2 jmpmask2 // The 2nd mask of jmp_buf elements that need relocation
270  #define STACK_BASE stackbase // The base of the stack frame relative to the local variables
271 
272  // The structure for the saved stack frame information
273  typedef struct saveloc {
274  char * localptr;
275  jmp_buf cxt;
276  } saveloc;
277 
278  static bool_t stackdirup;
279  static uint32_t jmpmask1;
280  static uint32_t jmpmask2;
281  static size_t stackbase;
282  static saveloc *pframeinfo;
283 
284  // These two functions are not static to prevent the compiler removing them as functions
285  void _gfxGetStackState(void) {
286  char *c;
287  pframeinfo->localptr = (char *)&c;
288  CXT_SAVE(pframeinfo->cxt);
289  }
290  void _gfxGetStackStateInFn(void) {
291  pframeinfo++;
292  _gfxGetStackState();
293  pframeinfo--;
294  }
295  static void _gfxThreadsInit(void) {
296  uint32_t i;
297  char ** pout;
298  char ** pin;
299  size_t diff;
300  char * framebase;
301  saveloc tmpsaveloc[2];
302 
303  // Create the main thread context
304  mainthread.cxt = maincxt;
305 
306  // Allocate a buffer to store our test data
307  pframeinfo = tmpsaveloc;
308 
309  // Get details of the stack frame from within a function
310  _gfxGetStackStateInFn();
311 
312  // Get details of the stack frame outside the function
313  _gfxGetStackState();
314 
315  /* Work out the frame entries to relocate by treating the jump buffer as an array of pointers */
316  stackdirup = pframeinfo[1].localptr > pframeinfo[0].localptr;
317  pout = (char **)pframeinfo[0].cxt;
318  pin = (char **)pframeinfo[1].cxt;
319  diff = pframeinfo[0].localptr - pframeinfo[1].localptr;
320  framebase = pframeinfo[0].localptr;
321  jmpmask1 = jmpmask2 = 0;
322  for (i = 0; i < sizeof(jmp_buf)/sizeof(char *); i++, pout++, pin++) {
323  if ((size_t)(*pout - *pin) == diff) {
324  if (i < 32)
325  jmpmask1 |= 1 << i;
326  else
327  jmpmask2 |= 1 << (i-32);
328 
329  if (stackdirup) {
330  if (framebase > *pout)
331  framebase = *pout;
332  } else {
333  if (framebase < *pout)
334  framebase = *pout;
335  }
336  }
337  }
338  stackbase = stackdirup ? (pframeinfo[0].localptr - framebase) : (framebase - pframeinfo[0].localptr);
339  }
340 
341  #endif
342 
343  // Move the stack frame and relocate the context data
344  static void _gfxAdjustCxt(thread *t) {
345  char ** s;
346  char * nf;
347  int diff;
348  uint32_t i;
349 
350  // Copy the stack frame
351  s = 0;
352  #if AUTO_DETECT_STACKFRAME
353  if (STACK_DIR_UP) { // Stack grows up
354  nf = (char *)(t) + sizeof(thread) + sizeof(jmp_buf) + STACK_BASE;
355  memcpy(t+1, (char *)&s - STACK_BASE, STACK_BASE+sizeof(char *));
356  } else { // Stack grows down
357  nf = (char *)(t) + t->size - (STACK_BASE + sizeof(char *));
358  memcpy(nf, &s, STACK_BASE+sizeof(char *));
359  }
360  #elif STACK_DIR_UP
361  // Stack grows up
362  nf = (char *)(t) + sizeof(thread) + sizeof(jmp_buf) + STACK_BASE;
363  memcpy(t+1, (char *)&s - STACK_BASE, STACK_BASE+sizeof(char *));
364  #else
365  // Stack grows down
366  nf = (char *)(t) + t->size - (STACK_BASE + sizeof(char *));
367  memcpy(nf, &s, STACK_BASE+sizeof(char *));
368  #endif
369 
370  // Relocate the context data
371  s = (char **)(t->cxt);
372  diff = nf - (char *)&s;
373 
374  // Relocate the elements we know need to be relocated
375  for (i = MASK1; i ; i >>= 1, s++) {
376  if ((i & 1))
377  *s += diff;
378  }
379  #ifdef MASK2
380  s = (char **)(t->cxt)+32;
381  for (i = MASK2; i ; i >>= 1, s++) {
382  if ((i & 1))
383  *s += diff;
384  }
385  #endif
386  }
387  static void _gfxXSwitch(thread *oldt, thread *newt, bool_t doBuildFrame) {
388 
389  // Save the old context
390  if (CXT_SAVE(oldt->cxt)) return;
391 
392  // Do we need to build a new context?
393  if (doBuildFrame) {
394 
395  // Save our existing context as a starting point for the new context
396  newt->cxt = newt+1;
397  if (CXT_SAVE(newt->cxt)) {
398 
399  // We are now running the new thread
400 
401  // We can't use any of the above function parameters here
402  // as we are on a different stack.
403 
404  // Run the users function.
405  gfxThreadExit(_gfxCurrentThread->fn(_gfxCurrentThread->param));
406 
407  // We never get here as gfxThreadExit() never returns
408  }
409 
410  // Adjust the new context so the stack references are correct
411  _gfxAdjustCxt(newt);
412  }
413 
414  // Start the new context
415  CXT_RESTORE(newt->cxt, 1);
416  }
417 
418  #define _gfxTaskSwitch(oldt, newt) _gfxXSwitch(oldt, newt, FALSE)
419  #define _gfxStartThread(oldt, newt) _gfxXSwitch(oldt, newt, TRUE)
420 #endif
421 #undef GFX_THREADS_DONE
422 
423 static void Qinit(threadQ * q) {
424  q->head = q->tail = 0;
425 }
426 
427 static void Qadd(threadQ * q, thread *t) {
428  t->next = 0;
429  if (q->head) {
430  q->tail->next = t;
431  q->tail = t;
432  } else
433  q->head = q->tail = t;
434 }
435 
436 static thread *Qpop(threadQ * q) {
437  struct thread * t;
438 
439  if (!q->head)
440  return 0;
441  t = q->head;
442  q->head = t->next;
443  return t;
444 }
445 
446 void _gosThreadsInit(void) {
447  Qinit(&readyQ);
448 
449  mainthread.next = 0;
450  mainthread.size = sizeof(thread);
451  mainthread.flags = FLG_THD_MAIN;
452  mainthread.fn = 0;
453  mainthread.param = 0;
454 
455  _gfxThreadsInit();
456 
457  _gfxCurrentThread = &mainthread;
458 }
459 
461  return (gfxThreadHandle)_gfxCurrentThread;
462 }
463 
464 // Check if there are dead processes to deallocate
465 static void cleanUpDeadThreads(void) {
466  thread *p;
467 
468  while ((p = Qpop(&deadQ)))
469  gfxFree(p);
470 }
471 
472 void gfxYield(void) {
473  thread *me;
474 
475  // Clean up zombies
476  cleanUpDeadThreads();
477 
478  // Is there another thread to run?
479  if (!readyQ.head)
480  return;
481 
482  Qadd(&readyQ, me = _gfxCurrentThread);
483  _gfxCurrentThread = Qpop(&readyQ);
484  _gfxTaskSwitch(me, _gfxCurrentThread);
485 }
486 
487 // This routine is not currently public - but it could be.
488 void gfxThreadExit(threadreturn_t ret) {
489  thread *me;
490 
491  // Save the results in case someone is waiting
492  me = _gfxCurrentThread;
493  me->param = (void *)ret;
494  me->flags |= FLG_THD_DEAD;
495 
496  // Add us to the dead list if we need deallocation as we can't free ourselves.
497  // If someone is waiting on the thread they will do the cleanup.
498  if ((me->flags & (FLG_THD_ALLOC|FLG_THD_WAIT)) == FLG_THD_ALLOC)
499  Qadd(&deadQ, me);
500 
501  // Set the next thread. Exit if it was the last thread
502  if (!(_gfxCurrentThread = Qpop(&readyQ)))
503  gfxExit();
504 
505  // Switch to the new thread
506  _gfxTaskSwitch(me, _gfxCurrentThread);
507 
508  // We never get back here as we didn't re-queue ourselves
509 }
510 
511 gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param) {
512  thread * t;
513  thread * me;
514  (void) prio;
515 
516  // Ensure we have a minimum stack size
517  if (stacksz < sizeof(thread)+64) {
518  stacksz = sizeof(thread)+64;
519  stackarea = 0;
520  }
521 
522  if (stackarea) {
523  t = (thread *)stackarea;
524  t->flags = 0;
525  } else {
526  t = (thread *)gfxAlloc(stacksz);
527  if (!t)
528  return 0;
529  t->flags = FLG_THD_ALLOC;
530  }
531  t->size = stacksz;
532  t->fn = fn;
533  t->param = param;
534 
535  // Add the current thread to the queue because we are starting a new thread.
536  me = _gfxCurrentThread;
537  Qadd(&readyQ, me);
538  _gfxCurrentThread = t;
539 
540  _gfxStartThread(me, t);
541 
542  // Return the new thread handle
543  return t;
544 }
545 
546 threadreturn_t gfxThreadWait(gfxThreadHandle th) {
547  thread * t;
548 
549  t = th;
550  if (t == _gfxCurrentThread)
551  return -1;
552 
553  // Mark that we are waiting
554  t->flags |= FLG_THD_WAIT;
555 
556  // Wait for the thread to die
557  while(!(t->flags & FLG_THD_DEAD))
558  gfxYield();
559 
560  // Unmark
561  t->flags &= ~FLG_THD_WAIT;
562 
563  // Clean up resources if needed
564  if (t->flags & FLG_THD_ALLOC)
565  gfxFree(t);
566 
567  // Return the status left by the dead process
568  return (threadreturn_t)t->param;
569 }
570 
571 #endif /* GFX_USE_OS_RAW32 */
void * gfxThreadHandle
A thread handle.
Definition: gos.h:117
void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit)
Initialise a Counted Semaphore.
systemticks_t gfxSystemTicks(void)
Get the current operating system tick time.
#define FALSE
Generic &#39;false&#39; boolean constant.
Definition: gfx.h:31
systemticks_t gfxMillisecondsToTicks(delaytime_t ms)
Convert a given number of millseconds to a number of operating system ticks.
void * gfxAlloc(size_t sz)
Allocate memory.
bool_t gfxSemWait(gfxSem *psem, delaytime_t ms)
Wait on a semaphore.
void gfxSemSignalI(gfxSem *psem)
Signal a semaphore.
A semaphore.
Definition: gos.h:105
void gfxMutexExit(gfxMutex *pmutex)
Exit the critical code region protected by the mutex.
void gfxSystemLock(void)
Lock the operating system to protect a sequence of code.
#define DECLARE_THREAD_FUNCTION(fnName, param)
Declare a thread function.
Definition: gos.h:62
void gfxExit(void)
Exit the GFX application.
void gfxFree(void *ptr)
Free memory.
void gfxMutexEnter(gfxMutex *pmutex)
Enter the critical code region protected by the mutex.
void gfxSemSignal(gfxSem *psem)
Signal a semaphore.
void gfxSleepMicroseconds(delaytime_t us)
Put the current thread to sleep for the specified period in microseconds.
gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn), p), void *param)
Start a new thread.
A mutex.
Definition: gos.h:111
threadreturn_t gfxThreadWait(gfxThreadHandle thread)
Wait for a thread to finish.
void gfxMutexInit(gfxMutex *pmutex)
Initialise a mutex to protect a region of code from other threads.
void gfxYield(void)
Yield the current thread.
gfxThreadHandle gfxThreadMe(void)
Get the current thread handle.
#define TRUE
Generic &#39;true&#39; boolean constant.
Definition: gfx.h:38
void gfxSleepMilliseconds(delaytime_t ms)
Put the current thread to sleep for the specified period in milliseconds.
bool_t gfxSemWaitI(gfxSem *psem)
Test if a wait on a semaphore can be satisfied immediately.
void gfxSystemUnlock(void)
Unlock the operating system previous locked by gfxSystemLock()