mpthreadport.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /*
  2. * This file is part of the MicroPython project, http://micropython.org/
  3. *
  4. * The MIT License (MIT)
  5. *
  6. * Copyright (c) 2018 Armink (armink.ztl@gmail.com)
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. * THE SOFTWARE.
  25. */
  26. #include "stdio.h"
  27. #include "py/mpconfig.h"
  28. #include "py/mpstate.h"
  29. #include "py/gc.h"
  30. #include "py/mpthread.h"
  31. #include "mpthreadport.h"
  32. #include <rthw.h>
  33. #if MICROPY_PY_THREAD
  34. #define MP_THREAD_MIN_STACK_SIZE (4 * 1024)
  35. #define MP_THREAD_DEFAULT_STACK_SIZE (MP_THREAD_MIN_STACK_SIZE + 1024)
  36. #define MP_THREAD_STATUS_READY 0
  37. #define MP_THREAD_STATUS_RUNNING 1
  38. #define MP_THREAD_STATUS_FINISH 2
  39. typedef struct _thread_t {
  40. rt_thread_t id; // system id of thread
  41. int status; // whether the thread is ready, running and finish
  42. void *arg; // thread Python args, a GC root pointer
  43. void *stack; // pointer to the stack
  44. size_t stack_len; // number of words in the stack
  45. struct _thread_t *next;
  46. } thread_t;
  47. // the mutex controls access to the linked list
  48. STATIC mp_thread_mutex_t thread_mutex;
  49. STATIC thread_t thread_root_node;
  50. STATIC thread_t *thread_root; // root pointer, handled by mp_thread_gc_others
  51. /**
  52. * thread port initialization
  53. *
  54. * @param stack MicroPython main thread stack start address
  55. * @param stack_len number of words in the stack
  56. */
  57. void mp_thread_init(void *stack, uint32_t stack_len) {
  58. mp_thread_set_state(&mp_state_ctx.thread);
  59. thread_root = &thread_root_node;
  60. thread_root->id = rt_thread_self();
  61. thread_root->status = MP_THREAD_STATUS_RUNNING;
  62. thread_root->arg = NULL;
  63. thread_root->stack = stack;
  64. thread_root->stack_len = stack_len;
  65. thread_root->next = NULL;
  66. mp_thread_mutex_init(&thread_mutex);
  67. }
  68. void mp_thread_gc_others(void) {
  69. mp_thread_mutex_lock(&thread_mutex, 1);
  70. for (thread_t *th = thread_root; th != NULL; th = th->next) {
  71. // the root node not using the mpy heap
  72. if (th != &thread_root_node) {
  73. gc_collect_root((void**)&th, 1);
  74. gc_collect_root(&th->arg, 1); // probably not needed
  75. }
  76. if (th->status == MP_THREAD_STATUS_READY) {
  77. continue;
  78. }
  79. gc_collect_root((void**) &th->id, 1); // probably not needed
  80. gc_collect_root(th->stack, th->stack_len); // probably not needed
  81. }
  82. mp_thread_mutex_unlock(&thread_mutex);
  83. }
  84. mp_state_thread_t *mp_thread_get_state(void) {
  85. return (mp_state_thread_t *)(rt_thread_self()->user_data);
  86. }
  87. void mp_thread_set_state(mp_state_thread_t *state) {
  88. rt_thread_self()->user_data = (rt_uint32_t)state;
  89. }
  90. void mp_thread_start(void) {
  91. mp_thread_mutex_lock(&thread_mutex, 1);
  92. for (thread_t *th = thread_root; th != NULL; th = th->next) {
  93. if (th->id == rt_thread_self()) {
  94. th->status = MP_THREAD_STATUS_RUNNING;
  95. break;
  96. }
  97. }
  98. mp_thread_mutex_unlock(&thread_mutex);
  99. }
  100. void mp_thread_create_ex(void *(*entry)(void*), void *arg, size_t *stack_size, int priority, char *name) {
  101. static uint8_t count = 0;
  102. if (*stack_size == 0) {
  103. *stack_size = MP_THREAD_DEFAULT_STACK_SIZE; // default stack size
  104. } else if (*stack_size < MP_THREAD_MIN_STACK_SIZE) {
  105. *stack_size = MP_THREAD_MIN_STACK_SIZE; // minimum stack size
  106. }
  107. // allocate the linked-list node, TCB and stack (must be outside thread_mutex lock)
  108. thread_t *th = m_new_obj(thread_t);
  109. if (th == NULL) {
  110. nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread obj"));
  111. }
  112. else {
  113. th->id = m_new_obj(struct rt_thread);
  114. if (th->id == NULL) {
  115. nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread id"));
  116. }
  117. th->stack = m_new(uint8_t, *stack_size);
  118. if (th->stack == NULL) {
  119. nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread stack"));
  120. }
  121. }
  122. mp_thread_mutex_lock(&thread_mutex, 1);
  123. // adjust the stack_size to provide room to recover from hitting the limit
  124. *stack_size -= 1024;
  125. // add thread to linked list of all threads
  126. th->status = MP_THREAD_STATUS_READY;
  127. th->arg = arg;
  128. th->stack_len = *stack_size / 4;
  129. th->next = thread_root;
  130. thread_root = th;
  131. rt_thread_init(th->id, name, (void (*)(void *))entry, arg, th->stack, *stack_size, priority, count++);
  132. rt_thread_startup(th->id);
  133. mp_thread_mutex_unlock(&thread_mutex);
  134. }
  135. void mp_thread_create(void *(*entry)(void*), void *arg, size_t *stack_size) {
  136. static uint8_t count = 0;
  137. int priority = rt_thread_self()->current_priority;
  138. char name[RT_NAME_MAX];
  139. if (priority > 0) {
  140. priority --;
  141. }
  142. /* build name */
  143. rt_snprintf(name, sizeof(name), "mp%02d", count++);
  144. mp_thread_create_ex(entry, arg, stack_size, priority, name);
  145. }
  146. void mp_thread_finish(void) {
  147. thread_t *prev = NULL;
  148. mp_thread_mutex_lock(&thread_mutex, 1);
  149. for (thread_t *th = thread_root; th != NULL;prev = th, th = th->next) {
  150. // unlink the node from the list
  151. if (th->id == rt_thread_self()) {
  152. if (prev != NULL) {
  153. prev->next = th->next;
  154. } else {
  155. // move the start pointer
  156. thread_root = th->next;
  157. }
  158. th->status = MP_THREAD_STATUS_FINISH;
  159. // explicitly release all its memory
  160. m_del_obj(struct rt_thread, th->id);
  161. m_del(uint8_t, th->stack, th->stack_len);
  162. m_del_obj(thread_t, th);
  163. break;
  164. }
  165. }
  166. mp_thread_mutex_unlock(&thread_mutex);
  167. }
  168. void mp_thread_mutex_init(mp_thread_mutex_t *mutex) {
  169. static uint8_t count = 0;
  170. char name[RT_NAME_MAX];
  171. if (!mutex->is_init) {
  172. /* build name */
  173. rt_snprintf(name, sizeof(name), "mp%02d", count++);
  174. rt_mutex_init(&(mutex->mutex), name, RT_IPC_FLAG_FIFO);
  175. mutex->is_init = 1;
  176. }
  177. }
  178. int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait) {
  179. return (RT_EOK == rt_mutex_take(&(mutex->mutex), wait ? RT_WAITING_FOREVER : 0));
  180. }
  181. void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) {
  182. rt_mutex_release(&(mutex->mutex));
  183. }
  184. void mp_thread_deinit(void) {
  185. // detach all ready and running mpy thread
  186. for (thread_t *th = thread_root; th != NULL; th = th->next) {
  187. if (th != &thread_root_node && th->status != MP_THREAD_STATUS_FINISH) {
  188. rt_thread_detach(th->id);
  189. }
  190. }
  191. // allow RT-Thread to clean-up the threads
  192. rt_thread_delay(200);
  193. }
  194. #endif /* MICROPY_PY_THREAD */