modthread.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  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 <string.h>
  28. #include "py/runtime.h"
  29. #include "py/stackctrl.h"
  30. #if MICROPY_PY_THREAD
  31. #include "py/mpthread.h"
  32. #if MICROPY_DEBUG_VERBOSE // print debugging info
  33. #define DEBUG_PRINT (1)
  34. #define DEBUG_printf DEBUG_printf
  35. #else // don't print debugging info
  36. #define DEBUG_PRINT (0)
  37. #define DEBUG_printf(...) (void)0
  38. #endif
  39. /****************************************************************/
  40. // Lock object
  41. STATIC const mp_obj_type_t mp_type_thread_lock;
  42. typedef struct _mp_obj_thread_lock_t {
  43. mp_obj_base_t base;
  44. mp_thread_mutex_t mutex;
  45. volatile bool locked;
  46. } mp_obj_thread_lock_t;
  47. STATIC mp_obj_thread_lock_t *mp_obj_new_thread_lock(void) {
  48. mp_obj_thread_lock_t *self = mp_obj_malloc(mp_obj_thread_lock_t, &mp_type_thread_lock);
  49. mp_thread_mutex_init(&self->mutex);
  50. self->locked = false;
  51. return self;
  52. }
  53. STATIC mp_obj_t thread_lock_acquire(size_t n_args, const mp_obj_t *args) {
  54. mp_obj_thread_lock_t *self = MP_OBJ_TO_PTR(args[0]);
  55. bool wait = true;
  56. if (n_args > 1) {
  57. wait = mp_obj_get_int(args[1]);
  58. // TODO support timeout arg
  59. }
  60. MP_THREAD_GIL_EXIT();
  61. int ret = mp_thread_mutex_lock(&self->mutex, wait);
  62. MP_THREAD_GIL_ENTER();
  63. if (ret == 0) {
  64. return mp_const_false;
  65. } else if (ret == 1) {
  66. self->locked = true;
  67. return mp_const_true;
  68. } else {
  69. mp_raise_OSError(-ret);
  70. }
  71. }
  72. STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(thread_lock_acquire_obj, 1, 3, thread_lock_acquire);
  73. STATIC mp_obj_t thread_lock_release(mp_obj_t self_in) {
  74. mp_obj_thread_lock_t *self = MP_OBJ_TO_PTR(self_in);
  75. if (!self->locked) {
  76. mp_raise_msg(&mp_type_RuntimeError, NULL);
  77. }
  78. self->locked = false;
  79. MP_THREAD_GIL_EXIT();
  80. mp_thread_mutex_unlock(&self->mutex);
  81. MP_THREAD_GIL_ENTER();
  82. return mp_const_none;
  83. }
  84. STATIC MP_DEFINE_CONST_FUN_OBJ_1(thread_lock_release_obj, thread_lock_release);
  85. STATIC mp_obj_t thread_lock_locked(mp_obj_t self_in) {
  86. mp_obj_thread_lock_t *self = MP_OBJ_TO_PTR(self_in);
  87. return mp_obj_new_bool(self->locked);
  88. }
  89. STATIC MP_DEFINE_CONST_FUN_OBJ_1(thread_lock_locked_obj, thread_lock_locked);
  90. STATIC mp_obj_t thread_lock___exit__(size_t n_args, const mp_obj_t *args) {
  91. (void)n_args; // unused
  92. return thread_lock_release(args[0]);
  93. }
  94. STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(thread_lock___exit___obj, 4, 4, thread_lock___exit__);
  95. STATIC const mp_rom_map_elem_t thread_lock_locals_dict_table[] = {
  96. { MP_ROM_QSTR(MP_QSTR_acquire), MP_ROM_PTR(&thread_lock_acquire_obj) },
  97. { MP_ROM_QSTR(MP_QSTR_release), MP_ROM_PTR(&thread_lock_release_obj) },
  98. { MP_ROM_QSTR(MP_QSTR_locked), MP_ROM_PTR(&thread_lock_locked_obj) },
  99. { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&thread_lock_acquire_obj) },
  100. { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&thread_lock___exit___obj) },
  101. };
  102. STATIC MP_DEFINE_CONST_DICT(thread_lock_locals_dict, thread_lock_locals_dict_table);
  103. STATIC MP_DEFINE_CONST_OBJ_TYPE(
  104. mp_type_thread_lock,
  105. MP_QSTR_lock,
  106. MP_TYPE_FLAG_NONE,
  107. locals_dict, &thread_lock_locals_dict
  108. );
  109. /****************************************************************/
  110. // _thread module
  111. STATIC size_t thread_stack_size = 0;
  112. STATIC mp_obj_t mod_thread_get_ident(void) {
  113. return mp_obj_new_int_from_uint(mp_thread_get_id());
  114. }
  115. STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_thread_get_ident_obj, mod_thread_get_ident);
  116. STATIC mp_obj_t mod_thread_stack_size(size_t n_args, const mp_obj_t *args) {
  117. mp_obj_t ret = mp_obj_new_int_from_uint(thread_stack_size);
  118. if (n_args == 0) {
  119. thread_stack_size = 0;
  120. } else {
  121. thread_stack_size = mp_obj_get_int(args[0]);
  122. }
  123. return ret;
  124. }
  125. STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_thread_stack_size_obj, 0, 1, mod_thread_stack_size);
  126. typedef struct _thread_entry_args_t {
  127. mp_obj_dict_t *dict_locals;
  128. mp_obj_dict_t *dict_globals;
  129. size_t stack_size;
  130. mp_obj_t fun;
  131. size_t n_args;
  132. size_t n_kw;
  133. mp_obj_t args[];
  134. } thread_entry_args_t;
  135. STATIC void *thread_entry(void *args_in) {
  136. // Execution begins here for a new thread. We do not have the GIL.
  137. thread_entry_args_t *args = (thread_entry_args_t *)args_in;
  138. mp_state_thread_t ts;
  139. mp_thread_set_state(&ts);
  140. mp_stack_set_top(&ts + 1); // need to include ts in root-pointer scan
  141. mp_stack_set_limit(args->stack_size);
  142. #if MICROPY_ENABLE_PYSTACK
  143. // TODO threading and pystack is not fully supported, for now just make a small stack
  144. mp_obj_t mini_pystack[128];
  145. mp_pystack_init(mini_pystack, &mini_pystack[128]);
  146. #endif
  147. // The GC starts off unlocked on this thread.
  148. ts.gc_lock_depth = 0;
  149. ts.nlr_jump_callback_top = NULL;
  150. ts.mp_pending_exception = MP_OBJ_NULL;
  151. // set locals and globals from the calling context
  152. mp_locals_set(args->dict_locals);
  153. mp_globals_set(args->dict_globals);
  154. MP_THREAD_GIL_ENTER();
  155. // signal that we are set up and running
  156. mp_thread_start();
  157. // TODO set more thread-specific state here:
  158. // cur_exception (root pointer)
  159. DEBUG_printf("[thread] start ts=%p args=%p stack=%p\n", &ts, &args, MP_STATE_THREAD(stack_top));
  160. nlr_buf_t nlr;
  161. if (nlr_push(&nlr) == 0) {
  162. mp_call_function_n_kw(args->fun, args->n_args, args->n_kw, args->args);
  163. nlr_pop();
  164. } else {
  165. // uncaught exception
  166. // check for SystemExit
  167. mp_obj_base_t *exc = (mp_obj_base_t *)nlr.ret_val;
  168. if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exc->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) {
  169. // swallow exception silently
  170. } else {
  171. // print exception out
  172. mp_printf(MICROPY_ERROR_PRINTER, "Unhandled exception in thread started by ");
  173. mp_obj_print_helper(MICROPY_ERROR_PRINTER, args->fun, PRINT_REPR);
  174. mp_printf(MICROPY_ERROR_PRINTER, "\n");
  175. mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(exc));
  176. }
  177. }
  178. DEBUG_printf("[thread] finish ts=%p\n", &ts);
  179. // signal that we are finished
  180. mp_thread_finish();
  181. MP_THREAD_GIL_EXIT();
  182. return NULL;
  183. }
  184. STATIC mp_obj_t mod_thread_start_new_thread(size_t n_args, const mp_obj_t *args) {
  185. // This structure holds the Python function and arguments for thread entry.
  186. // We copy all arguments into this structure to keep ownership of them.
  187. // We must be very careful about root pointers because this pointer may
  188. // disappear from our address space before the thread is created.
  189. thread_entry_args_t *th_args;
  190. // get positional arguments
  191. size_t pos_args_len;
  192. mp_obj_t *pos_args_items;
  193. mp_obj_get_array(args[1], &pos_args_len, &pos_args_items);
  194. // check for keyword arguments
  195. if (n_args == 2) {
  196. // just position arguments
  197. th_args = m_new_obj_var(thread_entry_args_t, args, mp_obj_t, pos_args_len);
  198. th_args->n_kw = 0;
  199. } else {
  200. // positional and keyword arguments
  201. if (mp_obj_get_type(args[2]) != &mp_type_dict) {
  202. mp_raise_TypeError(MP_ERROR_TEXT("expecting a dict for keyword args"));
  203. }
  204. mp_map_t *map = &((mp_obj_dict_t *)MP_OBJ_TO_PTR(args[2]))->map;
  205. th_args = m_new_obj_var(thread_entry_args_t, args, mp_obj_t, pos_args_len + 2 * map->used);
  206. th_args->n_kw = map->used;
  207. // copy across the keyword arguments
  208. for (size_t i = 0, n = pos_args_len; i < map->alloc; ++i) {
  209. if (mp_map_slot_is_filled(map, i)) {
  210. th_args->args[n++] = map->table[i].key;
  211. th_args->args[n++] = map->table[i].value;
  212. }
  213. }
  214. }
  215. // copy across the positional arguments
  216. th_args->n_args = pos_args_len;
  217. memcpy(th_args->args, pos_args_items, pos_args_len * sizeof(mp_obj_t));
  218. // pass our locals and globals into the new thread
  219. th_args->dict_locals = mp_locals_get();
  220. th_args->dict_globals = mp_globals_get();
  221. // set the stack size to use
  222. th_args->stack_size = thread_stack_size;
  223. // set the function for thread entry
  224. th_args->fun = args[0];
  225. // spawn the thread!
  226. return mp_obj_new_int_from_uint(mp_thread_create(thread_entry, th_args, &th_args->stack_size));
  227. }
  228. STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_thread_start_new_thread_obj, 2, 3, mod_thread_start_new_thread);
  229. STATIC mp_obj_t mod_thread_exit(void) {
  230. mp_raise_type(&mp_type_SystemExit);
  231. }
  232. STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_thread_exit_obj, mod_thread_exit);
  233. STATIC mp_obj_t mod_thread_allocate_lock(void) {
  234. return MP_OBJ_FROM_PTR(mp_obj_new_thread_lock());
  235. }
  236. STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_thread_allocate_lock_obj, mod_thread_allocate_lock);
  237. STATIC const mp_rom_map_elem_t mp_module_thread_globals_table[] = {
  238. { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__thread) },
  239. { MP_ROM_QSTR(MP_QSTR_LockType), MP_ROM_PTR(&mp_type_thread_lock) },
  240. { MP_ROM_QSTR(MP_QSTR_get_ident), MP_ROM_PTR(&mod_thread_get_ident_obj) },
  241. { MP_ROM_QSTR(MP_QSTR_stack_size), MP_ROM_PTR(&mod_thread_stack_size_obj) },
  242. { MP_ROM_QSTR(MP_QSTR_start_new_thread), MP_ROM_PTR(&mod_thread_start_new_thread_obj) },
  243. { MP_ROM_QSTR(MP_QSTR_exit), MP_ROM_PTR(&mod_thread_exit_obj) },
  244. { MP_ROM_QSTR(MP_QSTR_allocate_lock), MP_ROM_PTR(&mod_thread_allocate_lock_obj) },
  245. };
  246. STATIC MP_DEFINE_CONST_DICT(mp_module_thread_globals, mp_module_thread_globals_table);
  247. const mp_obj_module_t mp_module_thread = {
  248. .base = { &mp_type_module },
  249. .globals = (mp_obj_dict_t *)&mp_module_thread_globals,
  250. };
  251. MP_REGISTER_MODULE(MP_QSTR__thread, mp_module_thread);
  252. #endif // MICROPY_PY_THREAD