mpthreadport.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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. th->id = m_new_obj(struct rt_thread);
  113. if (th->id == NULL) {
  114. nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread id"));
  115. }
  116. th->stack = m_new(uint8_t, *stack_size);
  117. if (th->stack == NULL) {
  118. nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread stack"));
  119. }
  120. mp_thread_mutex_lock(&thread_mutex, 1);
  121. // adjust the stack_size to provide room to recover from hitting the limit
  122. *stack_size -= 1024;
  123. // add thread to linked list of all threads
  124. th->status = MP_THREAD_STATUS_READY;
  125. th->arg = arg;
  126. th->stack_len = *stack_size / 4;
  127. th->next = thread_root;
  128. thread_root = th;
  129. rt_thread_init(th->id, name, (void (*)(void *))entry, arg, th->stack, *stack_size, priority, count++);
  130. rt_thread_startup(th->id);
  131. mp_thread_mutex_unlock(&thread_mutex);
  132. }
  133. void mp_thread_create(void *(*entry)(void*), void *arg, size_t *stack_size) {
  134. static uint8_t count = 0;
  135. int priority = rt_thread_self()->current_priority;
  136. char name[RT_NAME_MAX];
  137. if (priority > 0) {
  138. priority --;
  139. }
  140. /* build name */
  141. rt_snprintf(name, sizeof(name), "mp%02d", count++);
  142. mp_thread_create_ex(entry, arg, stack_size, priority, name);
  143. }
  144. void mp_thread_finish(void) {
  145. thread_t *prev = NULL;
  146. mp_thread_mutex_lock(&thread_mutex, 1);
  147. for (thread_t *th = thread_root; th != NULL;prev = th, th = th->next) {
  148. // unlink the node from the list
  149. if (th->id == rt_thread_self()) {
  150. if (prev != NULL) {
  151. prev->next = th->next;
  152. } else {
  153. // move the start pointer
  154. thread_root = th->next;
  155. }
  156. th->status = MP_THREAD_STATUS_FINISH;
  157. // explicitly release all its memory
  158. m_del_obj(struct rt_thread, th->id);
  159. m_del(uint8_t, th->stack, th->stack_len);
  160. m_del_obj(thread_t, th);
  161. break;
  162. }
  163. }
  164. mp_thread_mutex_unlock(&thread_mutex);
  165. }
  166. void mp_thread_mutex_init(mp_thread_mutex_t *mutex) {
  167. static uint8_t count = 0;
  168. char name[RT_NAME_MAX];
  169. if (!mutex->is_init) {
  170. /* build name */
  171. rt_snprintf(name, sizeof(name), "mp%02d", count++);
  172. rt_mutex_init(&(mutex->mutex), name, RT_IPC_FLAG_FIFO);
  173. mutex->is_init = 1;
  174. }
  175. }
  176. int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait) {
  177. return (RT_EOK == rt_mutex_take(&(mutex->mutex), wait ? RT_WAITING_FOREVER : 0));
  178. }
  179. void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) {
  180. rt_mutex_release(&(mutex->mutex));
  181. }
  182. void mp_thread_deinit(void) {
  183. // detach all ready and running mpy thread
  184. for (thread_t *th = thread_root; th != NULL; th = th->next) {
  185. if (th != &thread_root_node && th->status != MP_THREAD_STATUS_FINISH) {
  186. rt_thread_detach(th->id);
  187. }
  188. }
  189. // allow RT-Thread to clean-up the threads
  190. rt_thread_delay(200);
  191. }
  192. #endif /* MICROPY_PY_THREAD */