mpthreadport.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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 <stdlib.h>
  28. #include <errno.h>
  29. #include "py/runtime.h"
  30. #include "py/mpthread.h"
  31. #include "py/gc.h"
  32. #if MICROPY_PY_THREAD
  33. #include <signal.h>
  34. #include <sched.h>
  35. // this structure forms a linked list, one node per active thread
  36. typedef struct _thread_t {
  37. pthread_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. struct _thread_t *next;
  41. } thread_t;
  42. STATIC pthread_key_t tls_key;
  43. // the mutex controls access to the linked list
  44. STATIC pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER;
  45. STATIC thread_t *thread;
  46. // this is used to synchronise the signal handler of the thread
  47. // it's needed because we can't use any pthread calls in a signal handler
  48. STATIC volatile int thread_signal_done;
  49. // this signal handler is used to scan the regs and stack of a thread
  50. STATIC void mp_thread_gc(int signo, siginfo_t *info, void *context) {
  51. (void)info; // unused
  52. (void)context; // unused
  53. if (signo == SIGUSR1) {
  54. void gc_collect_regs_and_stack(void);
  55. gc_collect_regs_and_stack();
  56. // We have access to the context (regs, stack) of the thread but it seems
  57. // that we don't need the extra information, enough is captured by the
  58. // gc_collect_regs_and_stack function above
  59. //gc_collect_root((void**)context, sizeof(ucontext_t) / sizeof(uintptr_t));
  60. #if MICROPY_ENABLE_PYSTACK
  61. void **ptrs = (void**)(void*)MP_STATE_THREAD(pystack_start);
  62. gc_collect_root(ptrs, (MP_STATE_THREAD(pystack_cur) - MP_STATE_THREAD(pystack_start)) / sizeof(void*));
  63. #endif
  64. thread_signal_done = 1;
  65. }
  66. }
  67. void mp_thread_init(void) {
  68. pthread_key_create(&tls_key, NULL);
  69. pthread_setspecific(tls_key, &mp_state_ctx.thread);
  70. // create first entry in linked list of all threads
  71. thread = malloc(sizeof(thread_t));
  72. thread->id = pthread_self();
  73. thread->ready = 1;
  74. thread->arg = NULL;
  75. thread->next = NULL;
  76. // enable signal handler for garbage collection
  77. struct sigaction sa;
  78. sa.sa_flags = SA_SIGINFO;
  79. sa.sa_sigaction = mp_thread_gc;
  80. sigemptyset(&sa.sa_mask);
  81. sigaction(SIGUSR1, &sa, NULL);
  82. }
  83. // This function scans all pointers that are external to the current thread.
  84. // It does this by signalling all other threads and getting them to scan their
  85. // own registers and stack. Note that there may still be some edge cases left
  86. // with race conditions and root-pointer scanning: a given thread may manipulate
  87. // the global root pointers (in mp_state_ctx) while another thread is doing a
  88. // garbage collection and tracing these pointers.
  89. void mp_thread_gc_others(void) {
  90. pthread_mutex_lock(&thread_mutex);
  91. for (thread_t *th = thread; th != NULL; th = th->next) {
  92. gc_collect_root(&th->arg, 1);
  93. if (th->id == pthread_self()) {
  94. continue;
  95. }
  96. if (!th->ready) {
  97. continue;
  98. }
  99. thread_signal_done = 0;
  100. pthread_kill(th->id, SIGUSR1);
  101. while (thread_signal_done == 0) {
  102. sched_yield();
  103. }
  104. }
  105. pthread_mutex_unlock(&thread_mutex);
  106. }
  107. mp_state_thread_t *mp_thread_get_state(void) {
  108. return (mp_state_thread_t*)pthread_getspecific(tls_key);
  109. }
  110. void mp_thread_set_state(void *state) {
  111. pthread_setspecific(tls_key, state);
  112. }
  113. void mp_thread_start(void) {
  114. pthread_mutex_lock(&thread_mutex);
  115. for (thread_t *th = thread; th != NULL; th = th->next) {
  116. if (th->id == pthread_self()) {
  117. th->ready = 1;
  118. break;
  119. }
  120. }
  121. pthread_mutex_unlock(&thread_mutex);
  122. }
  123. void mp_thread_create(void *(*entry)(void*), void *arg, size_t *stack_size) {
  124. // default stack size is 8k machine-words
  125. if (*stack_size == 0) {
  126. *stack_size = 8192 * BYTES_PER_WORD;
  127. }
  128. // minimum stack size is set by pthreads
  129. if (*stack_size < PTHREAD_STACK_MIN) {
  130. *stack_size = PTHREAD_STACK_MIN;
  131. }
  132. // set thread attributes
  133. pthread_attr_t attr;
  134. int ret = pthread_attr_init(&attr);
  135. if (ret != 0) {
  136. goto er;
  137. }
  138. ret = pthread_attr_setstacksize(&attr, *stack_size);
  139. if (ret != 0) {
  140. goto er;
  141. }
  142. pthread_mutex_lock(&thread_mutex);
  143. // create thread
  144. pthread_t id;
  145. ret = pthread_create(&id, &attr, entry, arg);
  146. if (ret != 0) {
  147. pthread_mutex_unlock(&thread_mutex);
  148. goto er;
  149. }
  150. // adjust stack_size to provide room to recover from hitting the limit
  151. // this value seems to be about right for both 32-bit and 64-bit builds
  152. *stack_size -= 8192;
  153. // add thread to linked list of all threads
  154. thread_t *th = malloc(sizeof(thread_t));
  155. th->id = id;
  156. th->ready = 0;
  157. th->arg = arg;
  158. th->next = thread;
  159. thread = th;
  160. pthread_mutex_unlock(&thread_mutex);
  161. return;
  162. er:
  163. mp_raise_OSError(ret);
  164. }
  165. void mp_thread_finish(void) {
  166. pthread_mutex_lock(&thread_mutex);
  167. // TODO unlink from list
  168. for (thread_t *th = thread; th != NULL; th = th->next) {
  169. if (th->id == pthread_self()) {
  170. th->ready = 0;
  171. break;
  172. }
  173. }
  174. pthread_mutex_unlock(&thread_mutex);
  175. }
  176. void mp_thread_mutex_init(mp_thread_mutex_t *mutex) {
  177. pthread_mutex_init(mutex, NULL);
  178. }
  179. int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait) {
  180. int ret;
  181. if (wait) {
  182. ret = pthread_mutex_lock(mutex);
  183. if (ret == 0) {
  184. return 1;
  185. }
  186. } else {
  187. ret = pthread_mutex_trylock(mutex);
  188. if (ret == 0) {
  189. return 1;
  190. } else if (ret == EBUSY) {
  191. return 0;
  192. }
  193. }
  194. return -ret;
  195. }
  196. void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) {
  197. pthread_mutex_unlock(mutex);
  198. // TODO check return value
  199. }
  200. #endif // MICROPY_PY_THREAD