summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorDamien George <damien.p.george@gmail.com>2017-02-16 18:05:06 +1100
committerDamien George <damien.p.george@gmail.com>2017-03-20 15:20:26 +1100
commit6e74d24f30bca3fb70876b1fb9a6b3a59850b83c (patch)
tree391a713749edac08db0efc45e6ff987b2f437dc0
parentbf29fe2e138a30688cfb94ad3859f903f935ced1 (diff)
downloadmicropython-6e74d24f30bca3fb70876b1fb9a6b3a59850b83c.tar.gz
micropython-6e74d24f30bca3fb70876b1fb9a6b3a59850b83c.zip
py: Add micropython.schedule() function and associated runtime code.
-rw-r--r--lib/utils/interrupt_char.c5
-rw-r--r--py/modmicropython.c14
-rw-r--r--py/mpconfig.h10
-rw-r--r--py/mpstate.h16
-rw-r--r--py/py.mk1
-rw-r--r--py/runtime.c4
-rw-r--r--py/runtime.h10
-rw-r--r--py/scheduler.c117
-rw-r--r--py/vm.c20
9 files changed, 197 insertions, 0 deletions
diff --git a/lib/utils/interrupt_char.c b/lib/utils/interrupt_char.c
index ab4efd9113..344db88c72 100644
--- a/lib/utils/interrupt_char.c
+++ b/lib/utils/interrupt_char.c
@@ -46,4 +46,9 @@ void mp_keyboard_interrupt(void) {
#else
MP_STATE_VM(mp_pending_exception) = MP_STATE_PORT(mp_kbd_exception);
#endif
+ #if MICROPY_ENABLE_SCHEDULER
+ if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) {
+ MP_STATE_VM(sched_state) = MP_SCHED_PENDING;
+ }
+ #endif
}
diff --git a/py/modmicropython.c b/py/modmicropython.c
index 675d169cc4..a74e6aa3cb 100644
--- a/py/modmicropython.c
+++ b/py/modmicropython.c
@@ -29,6 +29,7 @@
#include "py/mpstate.h"
#include "py/builtin.h"
#include "py/stackctrl.h"
+#include "py/runtime.h"
#include "py/gc.h"
// 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);
#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[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_micropython) },
{ 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_unlock), MP_ROM_PTR(&mp_micropython_heap_unlock_obj) },
#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);
diff --git a/py/mpconfig.h b/py/mpconfig.h
index 7e3bd8bdbb..54cf0f3d3d 100644
--- a/py/mpconfig.h
+++ b/py/mpconfig.h
@@ -616,6 +616,16 @@ typedef double mp_float_t;
#define MICROPY_USE_INTERNAL_PRINTF (1)
#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
#ifndef MICROPY_VFS
#define MICROPY_VFS (0)
diff --git a/py/mpstate.h b/py/mpstate.h
index 9e4f54ae5b..29f93870a8 100644
--- a/py/mpstate.h
+++ b/py/mpstate.h
@@ -50,6 +50,16 @@ typedef struct mp_dynamic_compiler_t {
extern mp_dynamic_compiler_t mp_dynamic_compiler;
#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.
typedef struct _mp_state_mem_t {
#if MICROPY_MEM_STATS
@@ -129,6 +139,12 @@ typedef struct _mp_state_vm_t {
// pending exception object (MP_OBJ_NULL if not pending)
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()
#if MICROPY_PY_SYS_EXC_INFO
mp_obj_base_t *cur_exception;
diff --git a/py/py.mk b/py/py.mk
index c4e2a79e66..37bb5d023e 100644
--- a/py/py.mk
+++ b/py/py.mk
@@ -143,6 +143,7 @@ PY_O_BASENAME = \
persistentcode.o \
runtime.o \
runtime_utils.o \
+ scheduler.o \
nativeglue.o \
stackctrl.o \
argcheck.o \
diff --git a/py/runtime.c b/py/runtime.c
index 1f69290ba7..19b55f99ac 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -63,6 +63,10 @@ void mp_init(void) {
// no pending exceptions to start with
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
mp_init_emergency_exception_buf();
diff --git a/py/runtime.h b/py/runtime.h
index 954833b67a..d4b1fa79a6 100644
--- a/py/runtime.h
+++ b/py/runtime.h
@@ -64,6 +64,16 @@ extern const qstr mp_binary_op_method_name[];
void mp_init(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
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);
diff --git a/py/scheduler.c b/py/scheduler.c
new file mode 100644
index 0000000000..30851a4d2b
--- /dev/null
+++ b/py/scheduler.c
@@ -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
diff --git a/py/vm.c b/py/vm.c
index 4ec1ea9066..98eababc04 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -1267,12 +1267,32 @@ yield:
pending_exception_check:
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) {
MARK_EXC_IP_SELECTIVE();
mp_obj_t obj = MP_STATE_VM(mp_pending_exception);
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
RAISE(obj);
}
+ #endif
#if MICROPY_PY_THREAD_GIL
#if MICROPY_PY_THREAD_GIL_VM_DIVISOR