py: Add micropython.schedule() function and associated runtime code.
This commit is contained in:
parent
bf29fe2e13
commit
6e74d24f30
@ -46,4 +46,9 @@ void mp_keyboard_interrupt(void) {
|
|||||||
#else
|
#else
|
||||||
MP_STATE_VM(mp_pending_exception) = MP_STATE_PORT(mp_kbd_exception);
|
MP_STATE_VM(mp_pending_exception) = MP_STATE_PORT(mp_kbd_exception);
|
||||||
#endif
|
#endif
|
||||||
|
#if MICROPY_ENABLE_SCHEDULER
|
||||||
|
if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) {
|
||||||
|
MP_STATE_VM(sched_state) = MP_SCHED_PENDING;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "py/mpstate.h"
|
#include "py/mpstate.h"
|
||||||
#include "py/builtin.h"
|
#include "py/builtin.h"
|
||||||
#include "py/stackctrl.h"
|
#include "py/stackctrl.h"
|
||||||
|
#include "py/runtime.h"
|
||||||
#include "py/gc.h"
|
#include "py/gc.h"
|
||||||
|
|
||||||
// Various builtins specific to MicroPython runtime,
|
// Various builtins specific to MicroPython runtime,
|
||||||
@ -128,6 +129,16 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_heap_unlock_obj, mp_micropython_
|
|||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_alloc_emergency_exception_buf_obj, mp_alloc_emergency_exception_buf);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_alloc_emergency_exception_buf_obj, mp_alloc_emergency_exception_buf);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if MICROPY_ENABLE_SCHEDULER
|
||||||
|
STATIC mp_obj_t mp_micropython_schedule(mp_obj_t function, mp_obj_t arg) {
|
||||||
|
if (!mp_sched_schedule(function, arg)) {
|
||||||
|
mp_raise_msg(&mp_type_RuntimeError, "schedule stack full");
|
||||||
|
}
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_micropython_schedule_obj, mp_micropython_schedule);
|
||||||
|
#endif
|
||||||
|
|
||||||
STATIC const mp_rom_map_elem_t mp_module_micropython_globals_table[] = {
|
STATIC const mp_rom_map_elem_t mp_module_micropython_globals_table[] = {
|
||||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_micropython) },
|
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_micropython) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_const), MP_ROM_PTR(&mp_identity_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_const), MP_ROM_PTR(&mp_identity_obj) },
|
||||||
@ -151,6 +162,9 @@ STATIC const mp_rom_map_elem_t mp_module_micropython_globals_table[] = {
|
|||||||
{ MP_ROM_QSTR(MP_QSTR_heap_lock), MP_ROM_PTR(&mp_micropython_heap_lock_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_heap_lock), MP_ROM_PTR(&mp_micropython_heap_lock_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_heap_unlock), MP_ROM_PTR(&mp_micropython_heap_unlock_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_heap_unlock), MP_ROM_PTR(&mp_micropython_heap_unlock_obj) },
|
||||||
#endif
|
#endif
|
||||||
|
#if MICROPY_ENABLE_SCHEDULER
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_schedule), MP_ROM_PTR(&mp_micropython_schedule_obj) },
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
STATIC MP_DEFINE_CONST_DICT(mp_module_micropython_globals, mp_module_micropython_globals_table);
|
STATIC MP_DEFINE_CONST_DICT(mp_module_micropython_globals, mp_module_micropython_globals_table);
|
||||||
|
@ -616,6 +616,16 @@ typedef double mp_float_t;
|
|||||||
#define MICROPY_USE_INTERNAL_PRINTF (1)
|
#define MICROPY_USE_INTERNAL_PRINTF (1)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Support for internal scheduler
|
||||||
|
#ifndef MICROPY_ENABLE_SCHEDULER
|
||||||
|
#define MICROPY_ENABLE_SCHEDULER (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Maximum number of entries in the scheduler
|
||||||
|
#ifndef MICROPY_SCHEDULER_DEPTH
|
||||||
|
#define MICROPY_SCHEDULER_DEPTH (4)
|
||||||
|
#endif
|
||||||
|
|
||||||
// Support for generic VFS sub-system
|
// Support for generic VFS sub-system
|
||||||
#ifndef MICROPY_VFS
|
#ifndef MICROPY_VFS
|
||||||
#define MICROPY_VFS (0)
|
#define MICROPY_VFS (0)
|
||||||
|
16
py/mpstate.h
16
py/mpstate.h
@ -50,6 +50,16 @@ typedef struct mp_dynamic_compiler_t {
|
|||||||
extern mp_dynamic_compiler_t mp_dynamic_compiler;
|
extern mp_dynamic_compiler_t mp_dynamic_compiler;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// These are the values for sched_state
|
||||||
|
#define MP_SCHED_IDLE (1)
|
||||||
|
#define MP_SCHED_LOCKED (-1)
|
||||||
|
#define MP_SCHED_PENDING (0) // 0 so it's a quick check in the VM
|
||||||
|
|
||||||
|
typedef struct _mp_sched_item_t {
|
||||||
|
mp_obj_t func;
|
||||||
|
mp_obj_t arg;
|
||||||
|
} mp_sched_item_t;
|
||||||
|
|
||||||
// This structure hold information about the memory allocation system.
|
// This structure hold information about the memory allocation system.
|
||||||
typedef struct _mp_state_mem_t {
|
typedef struct _mp_state_mem_t {
|
||||||
#if MICROPY_MEM_STATS
|
#if MICROPY_MEM_STATS
|
||||||
@ -129,6 +139,12 @@ typedef struct _mp_state_vm_t {
|
|||||||
// pending exception object (MP_OBJ_NULL if not pending)
|
// pending exception object (MP_OBJ_NULL if not pending)
|
||||||
volatile mp_obj_t mp_pending_exception;
|
volatile mp_obj_t mp_pending_exception;
|
||||||
|
|
||||||
|
#if MICROPY_ENABLE_SCHEDULER
|
||||||
|
volatile int16_t sched_state;
|
||||||
|
uint16_t sched_sp;
|
||||||
|
mp_sched_item_t sched_stack[MICROPY_SCHEDULER_DEPTH];
|
||||||
|
#endif
|
||||||
|
|
||||||
// current exception being handled, for sys.exc_info()
|
// current exception being handled, for sys.exc_info()
|
||||||
#if MICROPY_PY_SYS_EXC_INFO
|
#if MICROPY_PY_SYS_EXC_INFO
|
||||||
mp_obj_base_t *cur_exception;
|
mp_obj_base_t *cur_exception;
|
||||||
|
1
py/py.mk
1
py/py.mk
@ -143,6 +143,7 @@ PY_O_BASENAME = \
|
|||||||
persistentcode.o \
|
persistentcode.o \
|
||||||
runtime.o \
|
runtime.o \
|
||||||
runtime_utils.o \
|
runtime_utils.o \
|
||||||
|
scheduler.o \
|
||||||
nativeglue.o \
|
nativeglue.o \
|
||||||
stackctrl.o \
|
stackctrl.o \
|
||||||
argcheck.o \
|
argcheck.o \
|
||||||
|
@ -63,6 +63,10 @@ void mp_init(void) {
|
|||||||
|
|
||||||
// no pending exceptions to start with
|
// no pending exceptions to start with
|
||||||
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
|
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
|
||||||
|
#if MICROPY_ENABLE_SCHEDULER
|
||||||
|
MP_STATE_VM(sched_state) = MP_SCHED_IDLE;
|
||||||
|
MP_STATE_VM(sched_sp) = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
|
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
|
||||||
mp_init_emergency_exception_buf();
|
mp_init_emergency_exception_buf();
|
||||||
|
10
py/runtime.h
10
py/runtime.h
@ -64,6 +64,16 @@ extern const qstr mp_binary_op_method_name[];
|
|||||||
void mp_init(void);
|
void mp_init(void);
|
||||||
void mp_deinit(void);
|
void mp_deinit(void);
|
||||||
|
|
||||||
|
void mp_handle_pending(void);
|
||||||
|
void mp_handle_pending_tail(mp_uint_t atomic_state);
|
||||||
|
|
||||||
|
#if MICROPY_ENABLE_SCHEDULER
|
||||||
|
void mp_sched_lock(void);
|
||||||
|
void mp_sched_unlock(void);
|
||||||
|
static inline unsigned int mp_sched_num_pending(void) { return MP_STATE_VM(sched_sp); }
|
||||||
|
bool mp_sched_schedule(mp_obj_t function, mp_obj_t arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
// extra printing method specifically for mp_obj_t's which are integral type
|
// extra printing method specifically for mp_obj_t's which are integral type
|
||||||
int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec);
|
int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec);
|
||||||
|
|
||||||
|
117
py/scheduler.c
Normal file
117
py/scheduler.c
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017 Damien P. George
|
||||||
|
*
|
||||||
|
* 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 "py/runtime.h"
|
||||||
|
|
||||||
|
#if MICROPY_ENABLE_SCHEDULER
|
||||||
|
|
||||||
|
// A variant of this is inlined in the VM at the pending exception check
|
||||||
|
void mp_handle_pending(void) {
|
||||||
|
if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) {
|
||||||
|
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
|
||||||
|
mp_obj_t obj = MP_STATE_VM(mp_pending_exception);
|
||||||
|
if (obj != MP_OBJ_NULL) {
|
||||||
|
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
|
||||||
|
if (!mp_sched_num_pending()) {
|
||||||
|
MP_STATE_VM(sched_state) = MP_SCHED_IDLE;
|
||||||
|
}
|
||||||
|
MICROPY_END_ATOMIC_SECTION(atomic_state);
|
||||||
|
nlr_raise(obj);
|
||||||
|
}
|
||||||
|
mp_handle_pending_tail(atomic_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function should only be called be mp_sched_handle_pending,
|
||||||
|
// or by the VM's inlined version of that function.
|
||||||
|
void mp_handle_pending_tail(mp_uint_t atomic_state) {
|
||||||
|
MP_STATE_VM(sched_state) = MP_SCHED_LOCKED;
|
||||||
|
if (MP_STATE_VM(sched_sp) > 0) {
|
||||||
|
mp_sched_item_t item = MP_STATE_VM(sched_stack)[--MP_STATE_VM(sched_sp)];
|
||||||
|
MICROPY_END_ATOMIC_SECTION(atomic_state);
|
||||||
|
mp_call_function_1_protected(item.func, item.arg);
|
||||||
|
} else {
|
||||||
|
MICROPY_END_ATOMIC_SECTION(atomic_state);
|
||||||
|
}
|
||||||
|
mp_sched_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void mp_sched_lock(void) {
|
||||||
|
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
|
||||||
|
if (MP_STATE_VM(sched_state) < 0) {
|
||||||
|
--MP_STATE_VM(sched_state);
|
||||||
|
} else {
|
||||||
|
MP_STATE_VM(sched_state) = MP_SCHED_LOCKED;
|
||||||
|
}
|
||||||
|
MICROPY_END_ATOMIC_SECTION(atomic_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mp_sched_unlock(void) {
|
||||||
|
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
|
||||||
|
if (++MP_STATE_VM(sched_state) == 0) {
|
||||||
|
// vm became unlocked
|
||||||
|
if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL || mp_sched_num_pending()) {
|
||||||
|
MP_STATE_VM(sched_state) = MP_SCHED_PENDING;
|
||||||
|
} else {
|
||||||
|
MP_STATE_VM(sched_state) = MP_SCHED_IDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MICROPY_END_ATOMIC_SECTION(atomic_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mp_sched_schedule(mp_obj_t function, mp_obj_t arg) {
|
||||||
|
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
|
||||||
|
bool ret;
|
||||||
|
if (MP_STATE_VM(sched_sp) < MICROPY_SCHEDULER_DEPTH) {
|
||||||
|
if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) {
|
||||||
|
MP_STATE_VM(sched_state) = MP_SCHED_PENDING;
|
||||||
|
}
|
||||||
|
MP_STATE_VM(sched_stack)[MP_STATE_VM(sched_sp)].func = function;
|
||||||
|
MP_STATE_VM(sched_stack)[MP_STATE_VM(sched_sp)].arg = arg;
|
||||||
|
++MP_STATE_VM(sched_sp);
|
||||||
|
ret = true;
|
||||||
|
} else {
|
||||||
|
// schedule stack is full
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
MICROPY_END_ATOMIC_SECTION(atomic_state);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // MICROPY_ENABLE_SCHEDULER
|
||||||
|
|
||||||
|
// A variant of this is inlined in the VM at the pending exception check
|
||||||
|
void mp_handle_pending(void) {
|
||||||
|
if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) {
|
||||||
|
mp_obj_t obj = MP_STATE_VM(mp_pending_exception);
|
||||||
|
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
|
||||||
|
nlr_raise(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MICROPY_ENABLE_SCHEDULER
|
20
py/vm.c
20
py/vm.c
@ -1267,12 +1267,32 @@ yield:
|
|||||||
|
|
||||||
pending_exception_check:
|
pending_exception_check:
|
||||||
MICROPY_VM_HOOK_LOOP
|
MICROPY_VM_HOOK_LOOP
|
||||||
|
|
||||||
|
#if MICROPY_ENABLE_SCHEDULER
|
||||||
|
// This is an inlined variant of mp_handle_pending
|
||||||
|
if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) {
|
||||||
|
MARK_EXC_IP_SELECTIVE();
|
||||||
|
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
|
||||||
|
mp_obj_t obj = MP_STATE_VM(mp_pending_exception);
|
||||||
|
if (obj != MP_OBJ_NULL) {
|
||||||
|
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
|
||||||
|
if (!mp_sched_num_pending()) {
|
||||||
|
MP_STATE_VM(sched_state) = MP_SCHED_IDLE;
|
||||||
|
}
|
||||||
|
MICROPY_END_ATOMIC_SECTION(atomic_state);
|
||||||
|
RAISE(obj);
|
||||||
|
}
|
||||||
|
mp_handle_pending_tail(atomic_state);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// This is an inlined variant of mp_handle_pending
|
||||||
if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) {
|
if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) {
|
||||||
MARK_EXC_IP_SELECTIVE();
|
MARK_EXC_IP_SELECTIVE();
|
||||||
mp_obj_t obj = MP_STATE_VM(mp_pending_exception);
|
mp_obj_t obj = MP_STATE_VM(mp_pending_exception);
|
||||||
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
|
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
|
||||||
RAISE(obj);
|
RAISE(obj);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if MICROPY_PY_THREAD_GIL
|
#if MICROPY_PY_THREAD_GIL
|
||||||
#if MICROPY_PY_THREAD_GIL_VM_DIVISOR
|
#if MICROPY_PY_THREAD_GIL_VM_DIVISOR
|
||||||
|
Loading…
x
Reference in New Issue
Block a user