STATIC mp_obj_t mod_time_sleep(mp_obj_t arg) { #if MICROPY_PY_BUILTINS_FLOAT struct timeval tv; mp_float_t val = mp_obj_get_float(arg); double ipart; tv.tv_usec = round(modf(val, &ipart) * 1000000); tv.tv_sec = ipart; int res; while (1) { MP_THREAD_GIL_EXIT(); res = sleep_select(0, NULL, NULL, NULL, &tv); MP_THREAD_GIL_ENTER(); #if MICROPY_SELECT_REMAINING_TIME // TODO: This assumes Linux behavior of modifying tv to the remaining // time. if (res != -1 || errno != EINTR) { break; } if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { return mp_const_none; } //printf("select: EINTR: %ld:%ld\n", tv.tv_sec, tv.tv_usec); #else break; #endif } RAISE_ERRNO(res, errno); #else // TODO: Handle EINTR MP_THREAD_GIL_EXIT(); sleep(mp_obj_get_int(arg)); MP_THREAD_GIL_ENTER(); #endif return mp_const_none; }
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_set_state(&ts); mp_stack_set_top(&ts + 1); // need to include ts in root-pointer scan mp_stack_set_limit(args->stack_size); #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 // set locals and globals from the calling context mp_locals_set(args->dict_locals); mp_globals_set(args->dict_globals); MP_THREAD_GIL_ENTER(); // signal that we are set up and running mp_thread_start(); // TODO set more thread-specific state here: // mp_pending_exception? (root pointer) // 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 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 } #if MICROPY_PY_THREAD_GIL if (self->locked) { if (!wait) { return mp_const_false; } do { MP_THREAD_GIL_EXIT(); MP_THREAD_GIL_ENTER(); } while (self->locked); } self->locked = true; return mp_const_true; #else int ret = mp_thread_mutex_lock(&self->mutex, wait); if (ret == 0) { return mp_const_false; } else if (ret == 1) { self->locked = true; return mp_const_true; } else { mp_raise_OSError(-ret); } #endif }
static int _socket_getaddrinfo2(const mp_obj_t host, const mp_obj_t portx, struct addrinfo **resp) { const struct addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_STREAM, }; mp_obj_t port = portx; if (MP_OBJ_IS_SMALL_INT(port)) { // This is perverse, because lwip_getaddrinfo promptly converts it back to an int, but // that's the API we have to work with ... port = mp_obj_str_binary_op(MP_BINARY_OP_MODULO, mp_obj_new_str_via_qstr("%s", 2), port); } const char *host_str = mp_obj_str_get_str(host); const char *port_str = mp_obj_str_get_str(port); if (host_str[0] == '\0') { // a host of "" is equivalent to the default/all-local IP address host_str = "0.0.0.0"; } MP_THREAD_GIL_EXIT(); int res = lwip_getaddrinfo(host_str, port_str, &hints, resp); MP_THREAD_GIL_ENTER(); return res; }
STATIC mp_obj_t socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_in) { socket_obj_t *self = MP_OBJ_TO_PTR(self_in); // get the buffer to send mp_buffer_info_t bufinfo; mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ); // create the destination address struct sockaddr_in to; to.sin_len = sizeof(to); to.sin_family = AF_INET; to.sin_port = lwip_htons(netutils_parse_inet_addr(addr_in, (uint8_t*)&to.sin_addr, NETUTILS_BIG)); // send the data for (int i=0; i<=self->retries; i++) { MP_THREAD_GIL_EXIT(); int ret = lwip_sendto_r(self->fd, bufinfo.buf, bufinfo.len, 0, (struct sockaddr*)&to, sizeof(to)); MP_THREAD_GIL_ENTER(); if (ret > 0) return mp_obj_new_int_from_uint(ret); if (ret == -1 && errno != EWOULDBLOCK) { exception_from_errno(errno); } check_for_exceptions(); } mp_raise_OSError(MP_ETIMEDOUT); }
STATIC mp_obj_t socket_accept(const mp_obj_t arg0) { socket_obj_t *self = MP_OBJ_TO_PTR(arg0); struct sockaddr addr; socklen_t addr_len = sizeof(addr); int new_fd = -1; for (int i=0; i<=self->retries; i++) { MP_THREAD_GIL_EXIT(); new_fd = lwip_accept_r(self->fd, &addr, &addr_len); MP_THREAD_GIL_ENTER(); if (new_fd >= 0) break; if (errno != EAGAIN) exception_from_errno(errno); check_for_exceptions(); } if (new_fd < 0) mp_raise_OSError(MP_ETIMEDOUT); // create new socket object socket_obj_t *sock = m_new_obj_with_finaliser(socket_obj_t); sock->base.type = self->base.type; sock->fd = new_fd; sock->domain = self->domain; sock->type = self->type; sock->proto = self->proto; _socket_settimeout(sock, UINT64_MAX); // make the return value uint8_t *ip = (uint8_t*)&((struct sockaddr_in*)&addr)->sin_addr; mp_uint_t port = lwip_ntohs(((struct sockaddr_in*)&addr)->sin_port); mp_obj_tuple_t *client = mp_obj_new_tuple(2, NULL); client->items[0] = sock; client->items[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG); return client; }
STATIC mp_obj_t thread_lock_release(mp_obj_t self_in) { mp_obj_thread_lock_t *self = MP_OBJ_TO_PTR(self_in); // TODO check if already unlocked self->locked = false; MP_THREAD_GIL_EXIT(); mp_thread_mutex_unlock(&self->mutex); MP_THREAD_GIL_ENTER(); return mp_const_none; }
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_uint_t socket_stream_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { socket_obj_t *sock = self_in; for (int i=0; i<=sock->retries; i++) { MP_THREAD_GIL_EXIT(); int r = lwip_write_r(sock->fd, buf, size); MP_THREAD_GIL_ENTER(); if (r > 0) return r; if (r < 0 && errno != EWOULDBLOCK) { *errcode = errno; return MP_STREAM_ERROR; } check_for_exceptions(); } *errcode = sock->retries == 0 ? MP_EWOULDBLOCK : MP_ETIMEDOUT; return MP_STREAM_ERROR; }
int _socket_send(socket_obj_t *sock, const char *data, size_t datalen) { int sentlen = 0; for (int i=0; i<=sock->retries && sentlen < datalen; i++) { MP_THREAD_GIL_EXIT(); int r = lwip_write_r(sock->fd, data+sentlen, datalen-sentlen); MP_THREAD_GIL_ENTER(); if (r < 0 && errno != EWOULDBLOCK) exception_from_errno(errno); if (r > 0) sentlen += r; check_for_exceptions(); } if (sentlen == 0) mp_raise_OSError(MP_ETIMEDOUT); return sentlen; }
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_set_state(&ts); mp_stack_set_top(&ts + 1); // need to include ts in root-pointer scan mp_stack_set_limit(args->stack_size); MP_THREAD_GIL_ENTER(); // signal that we are set up and running mp_thread_start(); // TODO set more thread-specific state here: // mp_pending_exception? (root pointer) // cur_exception (root pointer) // dict_locals? (root pointer) uPy doesn't make a new locals dict for functions, just for classes, so it's different to CPy 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(&mp_plat_print, "Unhandled exception in thread started by "); mp_obj_print_helper(&mp_plat_print, args->fun, PRINT_REPR); mp_printf(&mp_plat_print, "\n"); mp_obj_print_exception(&mp_plat_print, 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 socket_connect(const mp_obj_t arg0, const mp_obj_t arg1) { socket_obj_t *self = MP_OBJ_TO_PTR(arg0); struct addrinfo *res; _socket_getaddrinfo(arg1, &res); MP_THREAD_GIL_EXIT(); int r = lwip_connect_r(self->fd, res->ai_addr, res->ai_addrlen); MP_THREAD_GIL_ENTER(); lwip_freeaddrinfo(res); if (r != 0) { exception_from_errno(errno); } return mp_const_none; }
STATIC mp_uint_t socket_stream_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { socket_obj_t *sock = self_in; // XXX Would be nicer to use RTC to handle timeouts for (int i=0; i<=sock->retries; i++) { MP_THREAD_GIL_EXIT(); int r = lwip_recvfrom_r(sock->fd, buf, size, 0, NULL, NULL); MP_THREAD_GIL_ENTER(); if (r >= 0) return r; if (r < 0 && errno != EWOULDBLOCK) { *errcode = errno; return MP_STREAM_ERROR; } check_for_exceptions(); } *errcode = sock->retries == 0 ? MP_EWOULDBLOCK : MP_ETIMEDOUT; return MP_STREAM_ERROR; }
mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in, struct sockaddr *from, socklen_t *from_len) { socket_obj_t *sock = MP_OBJ_TO_PTR(self_in); size_t len = mp_obj_get_int(len_in); vstr_t vstr; vstr_init_len(&vstr, len); // XXX Would be nicer to use RTC to handle timeouts for (int i=0; i<=sock->retries; i++) { MP_THREAD_GIL_EXIT(); int r = lwip_recvfrom_r(sock->fd, vstr.buf, len, 0, from, from_len); MP_THREAD_GIL_ENTER(); if (r >= 0) { vstr.len = r; return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } if (errno != EWOULDBLOCK) exception_from_errno(errno); check_for_exceptions(); } mp_raise_OSError(MP_ETIMEDOUT); }