µGFX  2.9
version 2.9
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.io/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(gMutex *pmutex) {
30  pmutex[0] = 0;
31 }
32 
33 void gfxMutexEnter(gMutex *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(gMutex *pmutex) {
45  pmutex[0] = 0;
46 }
47 
48 void gfxSemInit(gSem *psem, gSemcount val, gSemcount limit) {
49  psem->cnt = val;
50  psem->limit = limit;
51 }
52 
53 gBool gfxSemWait(gSem *psem, gDelay ms) {
54  gTicks starttm, delay;
55 
56  // Convert our delay to ticks
57  starttm = 0;
58  switch (ms) {
59  case gDelayNone:
60  delay = gDelayNone;
61  break;
62  case gDelayForever:
63  delay = gDelayForever;
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 gDelayNone:
77  return gFalse;
78  case gDelayForever:
79  break;
80  default:
81  if (gfxSystemTicks() - starttm >= delay)
82  return gFalse;
83  break;
84  }
85  gfxYield();
86  INTERRUPTS_OFF();
87  }
88  psem->cnt--;
89  INTERRUPTS_ON();
90  return gTrue;
91 }
92 
93 gBool gfxSemWaitI(gSem *psem) {
94  if (psem->cnt <= 0)
95  return gFalse;
96  psem->cnt--;
97  return gTrue;
98 }
99 
100 void gfxSemSignal(gSem *psem) {
101  INTERRUPTS_OFF();
102  gfxSemSignalI(psem);
103  INTERRUPTS_ON();
104 }
105 
106 void gfxSemSignalI(gSem *psem) {
107  if (psem->cnt < psem->limit)
108  psem->cnt++;
109 }
110 
111 /*********************************************************
112  * Sleep functions
113  *********************************************************/
114 
115 void gfxSleepMilliseconds(gDelay ms) {
116  gTicks starttm, delay;
117 
118  // Safety first
119  switch (ms) {
120  case gDelayNone:
121  return;
122  case gDelayForever:
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(gDelay ms) {
138  gTicks starttm, delay;
139 
140  // Safety first
141  switch (ms) {
142  case gDelayNone:
143  return;
144  case gDelayForever:
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  gMemSize size; // Size of the thread stack (including this structure)
179  gThreadreturn (*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 GFXON/GFXOFF - GFXON to auto-detect stack frame structure
246  * STACK_DIR_UP Macro/gBool - GFXON if the stack grows up instead of down
247  * MASK1 Macro/gU32 - The 1st mask of jmp_buf elements that need relocation
248  * MASK2 Macro/gU32 - The 2nd mask of jmp_buf elements that need relocation
249  * STACK_BASE Macro/gMemSize - 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 GFXOFF
256  #define STACK_DIR_UP GFXOFF
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 GFXON
267  #define STACK_DIR_UP stackdirup // GFXON 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 gBool stackdirup;
279  static gU32 jmpmask1;
280  static gU32 jmpmask2;
281  static gMemSize 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  gU32 i;
297  char ** pout;
298  char ** pin;
299  gPtrDiff 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 ((gPtrDiff)(*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  gU32 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, gBool 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, gFalse)
419  #define _gfxStartThread(oldt, newt) _gfxXSwitch(oldt, newt, gTrue)
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 
460 gThread gfxThreadMe(void) {
461  return (gThread)_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(gThreadreturn 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 gThread gfxThreadCreate(void *stackarea, gMemSize stacksz, gThreadpriority prio, GFX_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 gThreadreturn gfxThreadWait(gThread 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 (gThreadreturn)t->param;
569 }
570 
571 #endif
gThreadreturn gfxThreadWait(gThread thread)
Wait for a thread to finish.
void gfxSemSignal(gSem *psem)
Signal a semaphore.
void * gThread
A thread handle.
Definition: gos.h:116
gTicks gfxSystemTicks(void)
Get the current operating system tick time.
void gfxYield(void)
Yield the current thread.
#define GFX_THREAD_FUNCTION(fnName, param)
Declare a thread function.
Definition: gos.h:73
void gfxMutexExit(gMutex *pmutex)
Exit the critical code region protected by the mutex.
gTicks gfxMillisecondsToTicks(gDelay ms)
Convert a given number of millseconds to a number of operating system ticks.
gBool gfxSemWait(gSem *psem, gDelay ms)
Wait on a semaphore.
void gfxExit(void)
Exit the GFX application.
void gfxSystemUnlock(void)
Unlock the operating system previous locked by gfxSystemLock()
void gfxSleepMicroseconds(gDelay us)
Put the current thread to sleep for the specified period in microseconds.
void gfxMutexEnter(gMutex *pmutex)
Enter the critical code region protected by the mutex.
gThread gfxThreadMe(void)
Get the current thread handle.
void * gfxAlloc(gMemSize sz)
Allocate memory.
void gfxSystemLock(void)
Lock the operating system to protect a sequence of code.
void gfxMutexInit(gMutex *pmutex)
Initialise a mutex to protect a region of code from other threads.
gThread gfxThreadCreate(void *stackarea, gMemSize stacksz, gThreadpriority prio, GFX_THREAD_FUNCTION((*fn), p), void *param)
Start a new thread.
gBool gfxSemWaitI(gSem *psem)
Test if a wait on a semaphore can be satisfied immediately.
void gfxSemInit(gSem *psem, gSemcount val, gSemcount limit)
Initialise a Counted Semaphore.
void gfxFree(void *ptr)
Free memory.
void gfxSemSignalI(gSem *psem)
Signal a semaphore.
void gfxSleepMilliseconds(gDelay ms)
Put the current thread to sleep for the specified period in milliseconds.
A mutex.
Definition: gos.h:110
A semaphore.
Definition: gos.h:104