modthread.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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_init_state(&ts, args->stack_size, args->dict_locals, args->dict_globals);
  140. #if MICROPY_ENABLE_PYSTACK
  141. // TODO threading and pystack is not fully supported, for now just make a small stack
  142. mp_obj_t mini_pystack[128];
  143. mp_pystack_init(mini_pystack, &mini_pystack[128]);
  144. #endif
  145. MP_THREAD_GIL_ENTER();
  146. // signal that we are set up and running
  147. mp_thread_start();
  148. // TODO set more thread-specific state here:
  149. // cur_exception (root pointer)
  150. DEBUG_printf("[thread] start ts=%p args=%p stack=%p\n", &ts, &args, MP_STATE_THREAD(stack_top));
  151. nlr_buf_t nlr;
  152. if (nlr_push(&nlr) == 0) {
  153. mp_call_function_n_kw(args->fun, args->n_args, args->n_kw, args->args);
  154. nlr_pop();
  155. } else {
  156. // uncaught exception
  157. // check for SystemExit
  158. mp_obj_base_t *exc = (mp_obj_base_t *)nlr.ret_val;
  159. if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exc->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) {
  160. // swallow exception silently
  161. } else {
  162. // print exception out
  163. mp_printf(MICROPY_ERROR_PRINTER, "Unhandled exception in thread started by ");
  164. mp_obj_print_helper(MICROPY_ERROR_PRINTER, args->fun, PRINT_REPR);
  165. mp_printf(MICROPY_ERROR_PRINTER, "\n");
  166. mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(exc));
  167. }
  168. }
  169. DEBUG_printf("[thread] finish ts=%p\n", &ts);
  170. // signal that we are finished
  171. mp_thread_finish();
  172. MP_THREAD_GIL_EXIT();
  173. return NULL;
  174. }
  175. static mp_obj_t mod_thread_start_new_thread(size_t n_args, const mp_obj_t *args) {
  176. // This structure holds the Python function and arguments for thread entry.
  177. // We copy all arguments into this structure to keep ownership of them.
  178. // We must be very careful about root pointers because this pointer may
  179. // disappear from our address space before the thread is created.
  180. thread_entry_args_t *th_args;
  181. // get positional arguments
  182. size_t pos_args_len;
  183. mp_obj_t *pos_args_items;
  184. mp_obj_get_array(args[1], &pos_args_len, &pos_args_items);
  185. // check for keyword arguments
  186. if (n_args == 2) {
  187. // just position arguments
  188. th_args = m_new_obj_var(thread_entry_args_t, args, mp_obj_t, pos_args_len);
  189. th_args->n_kw = 0;
  190. } else {
  191. // positional and keyword arguments
  192. if (mp_obj_get_type(args[2]) != &mp_type_dict) {
  193. mp_raise_TypeError(MP_ERROR_TEXT("expecting a dict for keyword args"));
  194. }
  195. mp_map_t *map = &((mp_obj_dict_t *)MP_OBJ_TO_PTR(args[2]))->map;
  196. th_args = m_new_obj_var(thread_entry_args_t, args, mp_obj_t, pos_args_len + 2 * map->used);
  197. th_args->n_kw = map->used;
  198. // copy across the keyword arguments
  199. for (size_t i = 0, n = pos_args_len; i < map->alloc; ++i) {
  200. if (mp_map_slot_is_filled(map, i)) {
  201. th_args->args[n++] = map->table[i].key;
  202. th_args->args[n++] = map->table[i].value;
  203. }
  204. }
  205. }
  206. // copy across the positional arguments
  207. th_args->n_args = pos_args_len;
  208. memcpy(th_args->args, pos_args_items, pos_args_len * sizeof(mp_obj_t));
  209. // pass our locals and globals into the new thread
  210. th_args->dict_locals = mp_locals_get();
  211. th_args->dict_globals = mp_globals_get();
  212. // set the stack size to use
  213. th_args->stack_size = thread_stack_size;
  214. // set the function for thread entry
  215. th_args->fun = args[0];
  216. // spawn the thread!
  217. return mp_obj_new_int_from_uint(mp_thread_create(thread_entry, th_args, &th_args->stack_size));
  218. }
  219. static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_thread_start_new_thread_obj, 2, 3, mod_thread_start_new_thread);
  220. static mp_obj_t mod_thread_exit(void) {
  221. mp_raise_type(&mp_type_SystemExit);
  222. }
  223. static MP_DEFINE_CONST_FUN_OBJ_0(mod_thread_exit_obj, mod_thread_exit);
  224. static mp_obj_t mod_thread_allocate_lock(void) {
  225. return MP_OBJ_FROM_PTR(mp_obj_new_thread_lock());
  226. }
  227. static MP_DEFINE_CONST_FUN_OBJ_0(mod_thread_allocate_lock_obj, mod_thread_allocate_lock);
  228. static const mp_rom_map_elem_t mp_module_thread_globals_table[] = {
  229. { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__thread) },
  230. { MP_ROM_QSTR(MP_QSTR_LockType), MP_ROM_PTR(&mp_type_thread_lock) },
  231. { MP_ROM_QSTR(MP_QSTR_get_ident), MP_ROM_PTR(&mod_thread_get_ident_obj) },
  232. { MP_ROM_QSTR(MP_QSTR_stack_size), MP_ROM_PTR(&mod_thread_stack_size_obj) },
  233. { MP_ROM_QSTR(MP_QSTR_start_new_thread), MP_ROM_PTR(&mod_thread_start_new_thread_obj) },
  234. { MP_ROM_QSTR(MP_QSTR_exit), MP_ROM_PTR(&mod_thread_exit_obj) },
  235. { MP_ROM_QSTR(MP_QSTR_allocate_lock), MP_ROM_PTR(&mod_thread_allocate_lock_obj) },
  236. };
  237. static MP_DEFINE_CONST_DICT(mp_module_thread_globals, mp_module_thread_globals_table);
  238. const mp_obj_module_t mp_module_thread = {
  239. .base = { &mp_type_module },
  240. .globals = (mp_obj_dict_t *)&mp_module_thread_globals,
  241. };
  242. MP_REGISTER_MODULE(MP_QSTR__thread, mp_module_thread);
  243. #endif // MICROPY_PY_THREAD