Skip to content
This repository was archived by the owner on Aug 2, 2020. It is now read-only.

Commit 6e74d24

Browse files
committed
py: Add micropython.schedule() function and associated runtime code.
1 parent bf29fe2 commit 6e74d24

File tree

9 files changed

+197
-0
lines changed

9 files changed

+197
-0
lines changed

lib/utils/interrupt_char.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,9 @@ void mp_keyboard_interrupt(void) {
4646
#else
4747
MP_STATE_VM(mp_pending_exception) = MP_STATE_PORT(mp_kbd_exception);
4848
#endif
49+
#if MICROPY_ENABLE_SCHEDULER
50+
if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) {
51+
MP_STATE_VM(sched_state) = MP_SCHED_PENDING;
52+
}
53+
#endif
4954
}

py/modmicropython.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "py/mpstate.h"
3030
#include "py/builtin.h"
3131
#include "py/stackctrl.h"
32+
#include "py/runtime.h"
3233
#include "py/gc.h"
3334

3435
// Various builtins specific to MicroPython runtime,
@@ -128,6 +129,16 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_heap_unlock_obj, mp_micropython_
128129
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_alloc_emergency_exception_buf_obj, mp_alloc_emergency_exception_buf);
129130
#endif
130131

132+
#if MICROPY_ENABLE_SCHEDULER
133+
STATIC mp_obj_t mp_micropython_schedule(mp_obj_t function, mp_obj_t arg) {
134+
if (!mp_sched_schedule(function, arg)) {
135+
mp_raise_msg(&mp_type_RuntimeError, "schedule stack full");
136+
}
137+
return mp_const_none;
138+
}
139+
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_micropython_schedule_obj, mp_micropython_schedule);
140+
#endif
141+
131142
STATIC const mp_rom_map_elem_t mp_module_micropython_globals_table[] = {
132143
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_micropython) },
133144
{ 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[] = {
151162
{ MP_ROM_QSTR(MP_QSTR_heap_lock), MP_ROM_PTR(&mp_micropython_heap_lock_obj) },
152163
{ MP_ROM_QSTR(MP_QSTR_heap_unlock), MP_ROM_PTR(&mp_micropython_heap_unlock_obj) },
153164
#endif
165+
#if MICROPY_ENABLE_SCHEDULER
166+
{ MP_ROM_QSTR(MP_QSTR_schedule), MP_ROM_PTR(&mp_micropython_schedule_obj) },
167+
#endif
154168
};
155169

156170
STATIC MP_DEFINE_CONST_DICT(mp_module_micropython_globals, mp_module_micropython_globals_table);

py/mpconfig.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,16 @@ typedef double mp_float_t;
616616
#define MICROPY_USE_INTERNAL_PRINTF (1)
617617
#endif
618618

619+
// Support for internal scheduler
620+
#ifndef MICROPY_ENABLE_SCHEDULER
621+
#define MICROPY_ENABLE_SCHEDULER (0)
622+
#endif
623+
624+
// Maximum number of entries in the scheduler
625+
#ifndef MICROPY_SCHEDULER_DEPTH
626+
#define MICROPY_SCHEDULER_DEPTH (4)
627+
#endif
628+
619629
// Support for generic VFS sub-system
620630
#ifndef MICROPY_VFS
621631
#define MICROPY_VFS (0)

py/mpstate.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ typedef struct mp_dynamic_compiler_t {
5050
extern mp_dynamic_compiler_t mp_dynamic_compiler;
5151
#endif
5252

53+
// These are the values for sched_state
54+
#define MP_SCHED_IDLE (1)
55+
#define MP_SCHED_LOCKED (-1)
56+
#define MP_SCHED_PENDING (0) // 0 so it's a quick check in the VM
57+
58+
typedef struct _mp_sched_item_t {
59+
mp_obj_t func;
60+
mp_obj_t arg;
61+
} mp_sched_item_t;
62+
5363
// This structure hold information about the memory allocation system.
5464
typedef struct _mp_state_mem_t {
5565
#if MICROPY_MEM_STATS
@@ -129,6 +139,12 @@ typedef struct _mp_state_vm_t {
129139
// pending exception object (MP_OBJ_NULL if not pending)
130140
volatile mp_obj_t mp_pending_exception;
131141

142+
#if MICROPY_ENABLE_SCHEDULER
143+
volatile int16_t sched_state;
144+
uint16_t sched_sp;
145+
mp_sched_item_t sched_stack[MICROPY_SCHEDULER_DEPTH];
146+
#endif
147+
132148
// current exception being handled, for sys.exc_info()
133149
#if MICROPY_PY_SYS_EXC_INFO
134150
mp_obj_base_t *cur_exception;

py/py.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ PY_O_BASENAME = \
143143
persistentcode.o \
144144
runtime.o \
145145
runtime_utils.o \
146+
scheduler.o \
146147
nativeglue.o \
147148
stackctrl.o \
148149
argcheck.o \

py/runtime.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ void mp_init(void) {
6363

6464
// no pending exceptions to start with
6565
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
66+
#if MICROPY_ENABLE_SCHEDULER
67+
MP_STATE_VM(sched_state) = MP_SCHED_IDLE;
68+
MP_STATE_VM(sched_sp) = 0;
69+
#endif
6670

6771
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
6872
mp_init_emergency_exception_buf();

py/runtime.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,16 @@ extern const qstr mp_binary_op_method_name[];
6464
void mp_init(void);
6565
void mp_deinit(void);
6666

67+
void mp_handle_pending(void);
68+
void mp_handle_pending_tail(mp_uint_t atomic_state);
69+
70+
#if MICROPY_ENABLE_SCHEDULER
71+
void mp_sched_lock(void);
72+
void mp_sched_unlock(void);
73+
static inline unsigned int mp_sched_num_pending(void) { return MP_STATE_VM(sched_sp); }
74+
bool mp_sched_schedule(mp_obj_t function, mp_obj_t arg);
75+
#endif
76+
6777
// extra printing method specifically for mp_obj_t's which are integral type
6878
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);
6979

py/scheduler.c

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2017 Damien P. George
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+
27+
#include <stdio.h>
28+
29+
#include "py/runtime.h"
30+
31+
#if MICROPY_ENABLE_SCHEDULER
32+
33+
// A variant of this is inlined in the VM at the pending exception check
34+
void mp_handle_pending(void) {
35+
if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) {
36+
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
37+
mp_obj_t obj = MP_STATE_VM(mp_pending_exception);
38+
if (obj != MP_OBJ_NULL) {
39+
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
40+
if (!mp_sched_num_pending()) {
41+
MP_STATE_VM(sched_state) = MP_SCHED_IDLE;
42+
}
43+
MICROPY_END_ATOMIC_SECTION(atomic_state);
44+
nlr_raise(obj);
45+
}
46+
mp_handle_pending_tail(atomic_state);
47+
}
48+
}
49+
50+
// This function should only be called be mp_sched_handle_pending,
51+
// or by the VM's inlined version of that function.
52+
void mp_handle_pending_tail(mp_uint_t atomic_state) {
53+
MP_STATE_VM(sched_state) = MP_SCHED_LOCKED;
54+
if (MP_STATE_VM(sched_sp) > 0) {
55+
mp_sched_item_t item = MP_STATE_VM(sched_stack)[--MP_STATE_VM(sched_sp)];
56+
MICROPY_END_ATOMIC_SECTION(atomic_state);
57+
mp_call_function_1_protected(item.func, item.arg);
58+
} else {
59+
MICROPY_END_ATOMIC_SECTION(atomic_state);
60+
}
61+
mp_sched_unlock();
62+
}
63+
64+
void mp_sched_lock(void) {
65+
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
66+
if (MP_STATE_VM(sched_state) < 0) {
67+
--MP_STATE_VM(sched_state);
68+
} else {
69+
MP_STATE_VM(sched_state) = MP_SCHED_LOCKED;
70+
}
71+
MICROPY_END_ATOMIC_SECTION(atomic_state);
72+
}
73+
74+
void mp_sched_unlock(void) {
75+
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
76+
if (++MP_STATE_VM(sched_state) == 0) {
77+
// vm became unlocked
78+
if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL || mp_sched_num_pending()) {
79+
MP_STATE_VM(sched_state) = MP_SCHED_PENDING;
80+
} else {
81+
MP_STATE_VM(sched_state) = MP_SCHED_IDLE;
82+
}
83+
}
84+
MICROPY_END_ATOMIC_SECTION(atomic_state);
85+
}
86+
87+
bool mp_sched_schedule(mp_obj_t function, mp_obj_t arg) {
88+
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
89+
bool ret;
90+
if (MP_STATE_VM(sched_sp) < MICROPY_SCHEDULER_DEPTH) {
91+
if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) {
92+
MP_STATE_VM(sched_state) = MP_SCHED_PENDING;
93+
}
94+
MP_STATE_VM(sched_stack)[MP_STATE_VM(sched_sp)].func = function;
95+
MP_STATE_VM(sched_stack)[MP_STATE_VM(sched_sp)].arg = arg;
96+
++MP_STATE_VM(sched_sp);
97+
ret = true;
98+
} else {
99+
// schedule stack is full
100+
ret = false;
101+
}
102+
MICROPY_END_ATOMIC_SECTION(atomic_state);
103+
return ret;
104+
}
105+
106+
#else // MICROPY_ENABLE_SCHEDULER
107+
108+
// A variant of this is inlined in the VM at the pending exception check
109+
void mp_handle_pending(void) {
110+
if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) {
111+
mp_obj_t obj = MP_STATE_VM(mp_pending_exception);
112+
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
113+
nlr_raise(obj);
114+
}
115+
}
116+
117+
#endif // MICROPY_ENABLE_SCHEDULER

py/vm.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,12 +1267,32 @@ unwind_jump:;
12671267

12681268
pending_exception_check:
12691269
MICROPY_VM_HOOK_LOOP
1270+
1271+
#if MICROPY_ENABLE_SCHEDULER
1272+
// This is an inlined variant of mp_handle_pending
1273+
if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) {
1274+
MARK_EXC_IP_SELECTIVE();
1275+
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
1276+
mp_obj_t obj = MP_STATE_VM(mp_pending_exception);
1277+
if (obj != MP_OBJ_NULL) {
1278+
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
1279+
if (!mp_sched_num_pending()) {
1280+
MP_STATE_VM(sched_state) = MP_SCHED_IDLE;
1281+
}
1282+
MICROPY_END_ATOMIC_SECTION(atomic_state);
1283+
RAISE(obj);
1284+
}
1285+
mp_handle_pending_tail(atomic_state);
1286+
}
1287+
#else
1288+
// This is an inlined variant of mp_handle_pending
12701289
if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) {
12711290
MARK_EXC_IP_SELECTIVE();
12721291
mp_obj_t obj = MP_STATE_VM(mp_pending_exception);
12731292
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
12741293
RAISE(obj);
12751294
}
1295+
#endif
12761296

12771297
#if MICROPY_PY_THREAD_GIL
12781298
#if MICROPY_PY_THREAD_GIL_VM_DIVISOR

0 commit comments

Comments
 (0)