mpthreadport.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /*
  2. * This file is part of the MicroPython project, http://micropython.org/
  3. *
  4. * The MIT License (MIT)
  5. *
  6. * Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd
  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/runtime.h"
  28. #include "py/gc.h"
  29. #include "py/mpthread.h"
  30. #include "py/mphal.h"
  31. #include "mptask.h"
  32. #include "task.h"
  33. #include "irq.h"
  34. #if MICROPY_PY_THREAD
  35. // this structure forms a linked list, one node per active thread
  36. typedef struct _thread_t {
  37. TaskHandle_t id; // system id of thread
  38. int ready; // whether the thread is ready and running
  39. void *arg; // thread Python args, a GC root pointer
  40. void *stack; // pointer to the stack
  41. size_t stack_len; // number of words in the stack
  42. struct _thread_t *next;
  43. } thread_t;
  44. // the mutex controls access to the linked list
  45. STATIC mp_thread_mutex_t thread_mutex;
  46. STATIC thread_t thread_entry0;
  47. STATIC thread_t *thread; // root pointer, handled bp mp_thread_gc_others
  48. void mp_thread_init(void) {
  49. mp_thread_mutex_init(&thread_mutex);
  50. mp_thread_set_state(&mp_state_ctx.thread);
  51. // create first entry in linked list of all threads
  52. thread = &thread_entry0;
  53. thread->id = xTaskGetCurrentTaskHandle();
  54. thread->ready = 1;
  55. thread->arg = NULL;
  56. thread->stack = mpTaskStack;
  57. thread->stack_len = MICROPY_TASK_STACK_LEN;
  58. thread->next = NULL;
  59. }
  60. void mp_thread_gc_others(void) {
  61. mp_thread_mutex_lock(&thread_mutex, 1);
  62. for (thread_t *th = thread; th != NULL; th = th->next) {
  63. gc_collect_root((void**)&th, 1);
  64. gc_collect_root(&th->arg, 1); // probably not needed
  65. if (th->id == xTaskGetCurrentTaskHandle()) {
  66. continue;
  67. }
  68. if (!th->ready) {
  69. continue;
  70. }
  71. gc_collect_root(th->stack, th->stack_len); // probably not needed
  72. }
  73. mp_thread_mutex_unlock(&thread_mutex);
  74. }
  75. mp_state_thread_t *mp_thread_get_state(void) {
  76. return pvTaskGetThreadLocalStoragePointer(NULL, 0);
  77. }
  78. void mp_thread_set_state(void *state) {
  79. vTaskSetThreadLocalStoragePointer(NULL, 0, state);
  80. }
  81. void mp_thread_start(void) {
  82. mp_thread_mutex_lock(&thread_mutex, 1);
  83. for (thread_t *th = thread; th != NULL; th = th->next) {
  84. if (th->id == xTaskGetCurrentTaskHandle()) {
  85. th->ready = 1;
  86. break;
  87. }
  88. }
  89. mp_thread_mutex_unlock(&thread_mutex);
  90. }
  91. STATIC void *(*ext_thread_entry)(void*) = NULL;
  92. STATIC void freertos_entry(void *arg) {
  93. if (ext_thread_entry) {
  94. ext_thread_entry(arg);
  95. }
  96. vTaskDelete(NULL);
  97. for (;;) {
  98. }
  99. }
  100. void mp_thread_create(void *(*entry)(void*), void *arg, size_t *stack_size) {
  101. // store thread entry function into a global variable so we can access it
  102. ext_thread_entry = entry;
  103. if (*stack_size == 0) {
  104. *stack_size = 4096; // default stack size
  105. } else if (*stack_size < 2048) {
  106. *stack_size = 2048; // minimum stack size
  107. }
  108. // allocate TCB, stack and linked-list node (must be outside thread_mutex lock)
  109. StaticTask_t *tcb = m_new(StaticTask_t, 1);
  110. StackType_t *stack = m_new(StackType_t, *stack_size / sizeof(StackType_t));
  111. thread_t *th = m_new_obj(thread_t);
  112. mp_thread_mutex_lock(&thread_mutex, 1);
  113. // create thread
  114. TaskHandle_t id = xTaskCreateStatic(freertos_entry, "Thread", *stack_size / sizeof(void*), arg, 2, stack, tcb);
  115. if (id == NULL) {
  116. mp_thread_mutex_unlock(&thread_mutex);
  117. mp_raise_msg(&mp_type_OSError, "can't create thread");
  118. }
  119. // add thread to linked list of all threads
  120. th->id = id;
  121. th->ready = 0;
  122. th->arg = arg;
  123. th->stack = stack;
  124. th->stack_len = *stack_size / sizeof(StackType_t);
  125. th->next = thread;
  126. thread = th;
  127. mp_thread_mutex_unlock(&thread_mutex);
  128. // adjust stack_size to provide room to recover from hitting the limit
  129. *stack_size -= 512;
  130. }
  131. void mp_thread_finish(void) {
  132. mp_thread_mutex_lock(&thread_mutex, 1);
  133. // TODO unlink from list
  134. for (thread_t *th = thread; th != NULL; th = th->next) {
  135. if (th->id == xTaskGetCurrentTaskHandle()) {
  136. th->ready = 0;
  137. break;
  138. }
  139. }
  140. mp_thread_mutex_unlock(&thread_mutex);
  141. }
  142. void mp_thread_mutex_init(mp_thread_mutex_t *mutex) {
  143. mutex->handle = xSemaphoreCreateMutexStatic(&mutex->buffer);
  144. }
  145. // To allow hard interrupts to work with threading we only take/give the semaphore
  146. // if we are not within an interrupt context and interrupts are enabled.
  147. int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait) {
  148. if ((HAL_NVIC_INT_CTRL_REG & HAL_VECTACTIVE_MASK) == 0 && query_irq() == IRQ_STATE_ENABLED) {
  149. int ret = xSemaphoreTake(mutex->handle, wait ? portMAX_DELAY : 0);
  150. return ret == pdTRUE;
  151. } else {
  152. return 1;
  153. }
  154. }
  155. void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) {
  156. if ((HAL_NVIC_INT_CTRL_REG & HAL_VECTACTIVE_MASK) == 0 && query_irq() == IRQ_STATE_ENABLED) {
  157. xSemaphoreGive(mutex->handle);
  158. // TODO check return value
  159. }
  160. }
  161. #endif // MICROPY_PY_THREAD