modthread.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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 = m_new_obj(mp_obj_thread_lock_t);
  49. self->base.type = &mp_type_thread_lock;
  50. mp_thread_mutex_init(&self->mutex);
  51. self->locked = false;
  52. return self;
  53. }
  54. STATIC mp_obj_t thread_lock_acquire(size_t n_args, const mp_obj_t *args) {
  55. mp_obj_thread_lock_t *self = MP_OBJ_TO_PTR(args[0]);
  56. bool wait = true;
  57. if (n_args > 1) {
  58. wait = mp_obj_get_int(args[1]);
  59. // TODO support timeout arg
  60. }
  61. MP_THREAD_GIL_EXIT();
  62. int ret = mp_thread_mutex_lock(&self->mutex, wait);
  63. MP_THREAD_GIL_ENTER();
  64. if (ret == 0) {
  65. return mp_const_false;
  66. } else if (ret == 1) {
  67. self->locked = true;
  68. return mp_const_true;
  69. } else {
  70. mp_raise_OSError(-ret);
  71. }
  72. }
  73. STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(thread_lock_acquire_obj, 1, 3, thread_lock_acquire);
  74. STATIC mp_obj_t thread_lock_release(mp_obj_t self_in) {
  75. mp_obj_thread_lock_t *self = MP_OBJ_TO_PTR(self_in);
  76. if (!self->locked) {
  77. mp_raise_msg(&mp_type_RuntimeError, NULL);
  78. }
  79. self->locked = false;
  80. MP_THREAD_GIL_EXIT();
  81. mp_thread_mutex_unlock(&self->mutex);
  82. MP_THREAD_GIL_ENTER();
  83. return mp_const_none;
  84. }
  85. STATIC MP_DEFINE_CONST_FUN_OBJ_1(thread_lock_release_obj, thread_lock_release);
  86. STATIC mp_obj_t thread_lock_locked(mp_obj_t self_in) {
  87. mp_obj_thread_lock_t *self = MP_OBJ_TO_PTR(self_in);
  88. return mp_obj_new_bool(self->locked);
  89. }
  90. STATIC MP_DEFINE_CONST_FUN_OBJ_1(thread_lock_locked_obj, thread_lock_locked);
  91. STATIC mp_obj_t thread_lock___exit__(size_t n_args, const mp_obj_t *args) {
  92. (void)n_args; // unused
  93. return thread_lock_release(args[0]);
  94. }
  95. STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(thread_lock___exit___obj, 4, 4, thread_lock___exit__);
  96. STATIC const mp_rom_map_elem_t thread_lock_locals_dict_table[] = {
  97. { MP_ROM_QSTR(MP_QSTR_acquire), MP_ROM_PTR(&thread_lock_acquire_obj) },
  98. { MP_ROM_QSTR(MP_QSTR_release), MP_ROM_PTR(&thread_lock_release_obj) },
  99. { MP_ROM_QSTR(MP_QSTR_locked), MP_ROM_PTR(&thread_lock_locked_obj) },
  100. { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&thread_lock_acquire_obj) },
  101. { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&thread_lock___exit___obj) },
  102. };
  103. STATIC MP_DEFINE_CONST_DICT(thread_lock_locals_dict, thread_lock_locals_dict_table);
  104. STATIC const mp_obj_type_t mp_type_thread_lock = {
  105. { &mp_type_type },
  106. .name = MP_QSTR_lock,
  107. .locals_dict = (mp_obj_dict_t*)&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((uintptr_t)mp_thread_get_state());
  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. // set locals and globals from the calling context
  148. mp_locals_set(args->dict_locals);
  149. mp_globals_set(args->dict_globals);
  150. MP_THREAD_GIL_ENTER();
  151. // signal that we are set up and running
  152. mp_thread_start();
  153. // TODO set more thread-specific state here:
  154. // mp_pending_exception? (root pointer)
  155. // cur_exception (root pointer)
  156. DEBUG_printf("[thread] start ts=%p args=%p stack=%p\n", &ts, &args, MP_STATE_THREAD(stack_top));
  157. nlr_buf_t nlr;
  158. if (nlr_push(&nlr) == 0) {
  159. mp_call_function_n_kw(args->fun, args->n_args, args->n_kw, args->args);
  160. nlr_pop();
  161. } else {
  162. // uncaught exception
  163. // check for SystemExit
  164. mp_obj_base_t *exc = (mp_obj_base_t*)nlr.ret_val;
  165. if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exc->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) {
  166. // swallow exception silently
  167. } else {
  168. // print exception out
  169. mp_printf(MICROPY_ERROR_PRINTER, "Unhandled exception in thread started by ");
  170. mp_obj_print_helper(MICROPY_ERROR_PRINTER, args->fun, PRINT_REPR);
  171. mp_printf(MICROPY_ERROR_PRINTER, "\n");
  172. mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(exc));
  173. }
  174. }
  175. DEBUG_printf("[thread] finish ts=%p\n", &ts);
  176. // signal that we are finished
  177. mp_thread_finish();
  178. MP_THREAD_GIL_EXIT();
  179. return NULL;
  180. }
  181. STATIC mp_obj_t mod_thread_start_new_thread(size_t n_args, const mp_obj_t *args) {
  182. // This structure holds the Python function and arguments for thread entry.
  183. // We copy all arguments into this structure to keep ownership of them.
  184. // We must be very careful about root pointers because this pointer may
  185. // disappear from our address space before the thread is created.
  186. thread_entry_args_t *th_args;
  187. // get positional arguments
  188. size_t pos_args_len;
  189. mp_obj_t *pos_args_items;
  190. mp_obj_get_array(args[1], &pos_args_len, &pos_args_items);
  191. // check for keyword arguments
  192. if (n_args == 2) {
  193. // just position arguments
  194. th_args = m_new_obj_var(thread_entry_args_t, mp_obj_t, pos_args_len);
  195. th_args->n_kw = 0;
  196. } else {
  197. // positional and keyword arguments
  198. if (mp_obj_get_type(args[2]) != &mp_type_dict) {
  199. mp_raise_TypeError("expecting a dict for keyword args");
  200. }
  201. mp_map_t *map = &((mp_obj_dict_t*)MP_OBJ_TO_PTR(args[2]))->map;
  202. th_args = m_new_obj_var(thread_entry_args_t, mp_obj_t, pos_args_len + 2 * map->used);
  203. th_args->n_kw = map->used;
  204. // copy across the keyword arguments
  205. for (size_t i = 0, n = pos_args_len; i < map->alloc; ++i) {
  206. if (MP_MAP_SLOT_IS_FILLED(map, i)) {
  207. th_args->args[n++] = map->table[i].key;
  208. th_args->args[n++] = map->table[i].value;
  209. }
  210. }
  211. }
  212. // copy agross the positional arguments
  213. th_args->n_args = pos_args_len;
  214. memcpy(th_args->args, pos_args_items, pos_args_len * sizeof(mp_obj_t));
  215. // pass our locals and globals into the new thread
  216. th_args->dict_locals = mp_locals_get();
  217. th_args->dict_globals = mp_globals_get();
  218. // set the stack size to use
  219. th_args->stack_size = thread_stack_size;
  220. // set the function for thread entry
  221. th_args->fun = args[0];
  222. // spawn the thread!
  223. mp_thread_create(thread_entry, th_args, &th_args->stack_size);
  224. return mp_const_none;
  225. }
  226. STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_thread_start_new_thread_obj, 2, 3, mod_thread_start_new_thread);
  227. STATIC mp_obj_t mod_thread_exit(void) {
  228. nlr_raise(mp_obj_new_exception(&mp_type_SystemExit));
  229. }
  230. STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_thread_exit_obj, mod_thread_exit);
  231. STATIC mp_obj_t mod_thread_allocate_lock(void) {
  232. return MP_OBJ_FROM_PTR(mp_obj_new_thread_lock());
  233. }
  234. STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_thread_allocate_lock_obj, mod_thread_allocate_lock);
  235. STATIC const mp_rom_map_elem_t mp_module_thread_globals_table[] = {
  236. { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__thread) },
  237. { MP_ROM_QSTR(MP_QSTR_LockType), MP_ROM_PTR(&mp_type_thread_lock) },
  238. { MP_ROM_QSTR(MP_QSTR_get_ident), MP_ROM_PTR(&mod_thread_get_ident_obj) },
  239. { MP_ROM_QSTR(MP_QSTR_stack_size), MP_ROM_PTR(&mod_thread_stack_size_obj) },
  240. { MP_ROM_QSTR(MP_QSTR_start_new_thread), MP_ROM_PTR(&mod_thread_start_new_thread_obj) },
  241. { MP_ROM_QSTR(MP_QSTR_exit), MP_ROM_PTR(&mod_thread_exit_obj) },
  242. { MP_ROM_QSTR(MP_QSTR_allocate_lock), MP_ROM_PTR(&mod_thread_allocate_lock_obj) },
  243. };
  244. STATIC MP_DEFINE_CONST_DICT(mp_module_thread_globals, mp_module_thread_globals_table);
  245. const mp_obj_module_t mp_module_thread = {
  246. .base = { &mp_type_module },
  247. .globals = (mp_obj_dict_t*)&mp_module_thread_globals,
  248. };
  249. #endif // MICROPY_PY_THREAD