| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- /*
- * This file is part of the MicroPython project, http://micropython.org/
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
- #include <stdio.h>
- #include <string.h>
- #include "py/runtime.h"
- #include "py/stackctrl.h"
- #if MICROPY_PY_THREAD
- #include "py/mpthread.h"
- #if MICROPY_DEBUG_VERBOSE // print debugging info
- #define DEBUG_PRINT (1)
- #define DEBUG_printf DEBUG_printf
- #else // don't print debugging info
- #define DEBUG_PRINT (0)
- #define DEBUG_printf(...) (void)0
- #endif
- /****************************************************************/
- // Lock object
- static const mp_obj_type_t mp_type_thread_lock;
- typedef struct _mp_obj_thread_lock_t {
- mp_obj_base_t base;
- mp_thread_mutex_t mutex;
- volatile bool locked;
- } mp_obj_thread_lock_t;
- static mp_obj_thread_lock_t *mp_obj_new_thread_lock(void) {
- mp_obj_thread_lock_t *self = mp_obj_malloc(mp_obj_thread_lock_t, &mp_type_thread_lock);
- mp_thread_mutex_init(&self->mutex);
- self->locked = false;
- return self;
- }
- static mp_obj_t thread_lock_acquire(size_t n_args, const mp_obj_t *args) {
- mp_obj_thread_lock_t *self = MP_OBJ_TO_PTR(args[0]);
- bool wait = true;
- if (n_args > 1) {
- wait = mp_obj_get_int(args[1]);
- // TODO support timeout arg
- }
- MP_THREAD_GIL_EXIT();
- int ret = mp_thread_mutex_lock(&self->mutex, wait);
- MP_THREAD_GIL_ENTER();
- if (ret == 0) {
- return mp_const_false;
- } else if (ret == 1) {
- self->locked = true;
- return mp_const_true;
- } else {
- mp_raise_OSError(-ret);
- }
- }
- static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(thread_lock_acquire_obj, 1, 3, thread_lock_acquire);
- static mp_obj_t thread_lock_release(mp_obj_t self_in) {
- mp_obj_thread_lock_t *self = MP_OBJ_TO_PTR(self_in);
- if (!self->locked) {
- mp_raise_msg(&mp_type_RuntimeError, NULL);
- }
- self->locked = false;
- MP_THREAD_GIL_EXIT();
- mp_thread_mutex_unlock(&self->mutex);
- MP_THREAD_GIL_ENTER();
- return mp_const_none;
- }
- static MP_DEFINE_CONST_FUN_OBJ_1(thread_lock_release_obj, thread_lock_release);
- static mp_obj_t thread_lock_locked(mp_obj_t self_in) {
- mp_obj_thread_lock_t *self = MP_OBJ_TO_PTR(self_in);
- return mp_obj_new_bool(self->locked);
- }
- static MP_DEFINE_CONST_FUN_OBJ_1(thread_lock_locked_obj, thread_lock_locked);
- static mp_obj_t thread_lock___exit__(size_t n_args, const mp_obj_t *args) {
- (void)n_args; // unused
- return thread_lock_release(args[0]);
- }
- static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(thread_lock___exit___obj, 4, 4, thread_lock___exit__);
- static const mp_rom_map_elem_t thread_lock_locals_dict_table[] = {
- { MP_ROM_QSTR(MP_QSTR_acquire), MP_ROM_PTR(&thread_lock_acquire_obj) },
- { MP_ROM_QSTR(MP_QSTR_release), MP_ROM_PTR(&thread_lock_release_obj) },
- { MP_ROM_QSTR(MP_QSTR_locked), MP_ROM_PTR(&thread_lock_locked_obj) },
- { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&thread_lock_acquire_obj) },
- { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&thread_lock___exit___obj) },
- };
- static MP_DEFINE_CONST_DICT(thread_lock_locals_dict, thread_lock_locals_dict_table);
- static MP_DEFINE_CONST_OBJ_TYPE(
- mp_type_thread_lock,
- MP_QSTR_lock,
- MP_TYPE_FLAG_NONE,
- locals_dict, &thread_lock_locals_dict
- );
- /****************************************************************/
- // _thread module
- static size_t thread_stack_size = 0;
- static mp_obj_t mod_thread_get_ident(void) {
- return mp_obj_new_int_from_uint(mp_thread_get_id());
- }
- static MP_DEFINE_CONST_FUN_OBJ_0(mod_thread_get_ident_obj, mod_thread_get_ident);
- static mp_obj_t mod_thread_stack_size(size_t n_args, const mp_obj_t *args) {
- mp_obj_t ret = mp_obj_new_int_from_uint(thread_stack_size);
- if (n_args == 0) {
- thread_stack_size = 0;
- } else {
- thread_stack_size = mp_obj_get_int(args[0]);
- }
- return ret;
- }
- static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_thread_stack_size_obj, 0, 1, mod_thread_stack_size);
- typedef struct _thread_entry_args_t {
- mp_obj_dict_t *dict_locals;
- mp_obj_dict_t *dict_globals;
- size_t stack_size;
- mp_obj_t fun;
- size_t n_args;
- size_t n_kw;
- mp_obj_t args[];
- } thread_entry_args_t;
- static void *thread_entry(void *args_in) {
- // Execution begins here for a new thread. We do not have the GIL.
- thread_entry_args_t *args = (thread_entry_args_t *)args_in;
- mp_state_thread_t ts;
- mp_thread_init_state(&ts, args->stack_size, args->dict_locals, args->dict_globals);
- #if MICROPY_ENABLE_PYSTACK
- // TODO threading and pystack is not fully supported, for now just make a small stack
- mp_obj_t mini_pystack[128];
- mp_pystack_init(mini_pystack, &mini_pystack[128]);
- #endif
- MP_THREAD_GIL_ENTER();
- // signal that we are set up and running
- mp_thread_start();
- // TODO set more thread-specific state here:
- // cur_exception (root pointer)
- DEBUG_printf("[thread] start ts=%p args=%p stack=%p\n", &ts, &args, MP_STATE_THREAD(stack_top));
- nlr_buf_t nlr;
- if (nlr_push(&nlr) == 0) {
- mp_call_function_n_kw(args->fun, args->n_args, args->n_kw, args->args);
- nlr_pop();
- } else {
- // uncaught exception
- // check for SystemExit
- mp_obj_base_t *exc = (mp_obj_base_t *)nlr.ret_val;
- if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exc->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) {
- // swallow exception silently
- } else {
- // print exception out
- mp_printf(MICROPY_ERROR_PRINTER, "Unhandled exception in thread started by ");
- mp_obj_print_helper(MICROPY_ERROR_PRINTER, args->fun, PRINT_REPR);
- mp_printf(MICROPY_ERROR_PRINTER, "\n");
- mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(exc));
- }
- }
- DEBUG_printf("[thread] finish ts=%p\n", &ts);
- // signal that we are finished
- mp_thread_finish();
- MP_THREAD_GIL_EXIT();
- return NULL;
- }
- static mp_obj_t mod_thread_start_new_thread(size_t n_args, const mp_obj_t *args) {
- // This structure holds the Python function and arguments for thread entry.
- // We copy all arguments into this structure to keep ownership of them.
- // We must be very careful about root pointers because this pointer may
- // disappear from our address space before the thread is created.
- thread_entry_args_t *th_args;
- // get positional arguments
- size_t pos_args_len;
- mp_obj_t *pos_args_items;
- mp_obj_get_array(args[1], &pos_args_len, &pos_args_items);
- // check for keyword arguments
- if (n_args == 2) {
- // just position arguments
- th_args = m_new_obj_var(thread_entry_args_t, args, mp_obj_t, pos_args_len);
- th_args->n_kw = 0;
- } else {
- // positional and keyword arguments
- if (mp_obj_get_type(args[2]) != &mp_type_dict) {
- mp_raise_TypeError(MP_ERROR_TEXT("expecting a dict for keyword args"));
- }
- mp_map_t *map = &((mp_obj_dict_t *)MP_OBJ_TO_PTR(args[2]))->map;
- th_args = m_new_obj_var(thread_entry_args_t, args, mp_obj_t, pos_args_len + 2 * map->used);
- th_args->n_kw = map->used;
- // copy across the keyword arguments
- for (size_t i = 0, n = pos_args_len; i < map->alloc; ++i) {
- if (mp_map_slot_is_filled(map, i)) {
- th_args->args[n++] = map->table[i].key;
- th_args->args[n++] = map->table[i].value;
- }
- }
- }
- // copy across the positional arguments
- th_args->n_args = pos_args_len;
- memcpy(th_args->args, pos_args_items, pos_args_len * sizeof(mp_obj_t));
- // pass our locals and globals into the new thread
- th_args->dict_locals = mp_locals_get();
- th_args->dict_globals = mp_globals_get();
- // set the stack size to use
- th_args->stack_size = thread_stack_size;
- // set the function for thread entry
- th_args->fun = args[0];
- // spawn the thread!
- return mp_obj_new_int_from_uint(mp_thread_create(thread_entry, th_args, &th_args->stack_size));
- }
- static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_thread_start_new_thread_obj, 2, 3, mod_thread_start_new_thread);
- static mp_obj_t mod_thread_exit(void) {
- mp_raise_type(&mp_type_SystemExit);
- }
- static MP_DEFINE_CONST_FUN_OBJ_0(mod_thread_exit_obj, mod_thread_exit);
- static mp_obj_t mod_thread_allocate_lock(void) {
- return MP_OBJ_FROM_PTR(mp_obj_new_thread_lock());
- }
- static MP_DEFINE_CONST_FUN_OBJ_0(mod_thread_allocate_lock_obj, mod_thread_allocate_lock);
- static const mp_rom_map_elem_t mp_module_thread_globals_table[] = {
- { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__thread) },
- { MP_ROM_QSTR(MP_QSTR_LockType), MP_ROM_PTR(&mp_type_thread_lock) },
- { MP_ROM_QSTR(MP_QSTR_get_ident), MP_ROM_PTR(&mod_thread_get_ident_obj) },
- { MP_ROM_QSTR(MP_QSTR_stack_size), MP_ROM_PTR(&mod_thread_stack_size_obj) },
- { MP_ROM_QSTR(MP_QSTR_start_new_thread), MP_ROM_PTR(&mod_thread_start_new_thread_obj) },
- { MP_ROM_QSTR(MP_QSTR_exit), MP_ROM_PTR(&mod_thread_exit_obj) },
- { MP_ROM_QSTR(MP_QSTR_allocate_lock), MP_ROM_PTR(&mod_thread_allocate_lock_obj) },
- };
- static MP_DEFINE_CONST_DICT(mp_module_thread_globals, mp_module_thread_globals_table);
- const mp_obj_module_t mp_module_thread = {
- .base = { &mp_type_module },
- .globals = (mp_obj_dict_t *)&mp_module_thread_globals,
- };
- MP_REGISTER_MODULE(MP_QSTR__thread, mp_module_thread);
- #endif // MICROPY_PY_THREAD
|