The official µGFX library repository.

gos_x_threads.c 14KB

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