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_KBD_EXCEPTION |
32 | // This function may be called asynchronously at any time so only do the bare minimum. |
33 | void MICROPY_WRAP_MP_KEYBOARD_INTERRUPT(mp_keyboard_interrupt)(void) { |
34 | MP_STATE_VM(mp_kbd_exception).traceback_data = NULL; |
35 | MP_STATE_VM(mp_pending_exception) = MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)); |
36 | #if MICROPY_ENABLE_SCHEDULER |
37 | if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) { |
38 | MP_STATE_VM(sched_state) = MP_SCHED_PENDING; |
39 | } |
40 | #endif |
41 | } |
42 | #endif |
43 | |
44 | #if MICROPY_ENABLE_SCHEDULER |
45 | |
46 | #define IDX_MASK(i) ((i) & (MICROPY_SCHEDULER_DEPTH - 1)) |
47 | |
48 | // This is a macro so it is guaranteed to be inlined in functions like |
49 | // mp_sched_schedule that may be located in a special memory region. |
50 | #define mp_sched_full() (mp_sched_num_pending() == MICROPY_SCHEDULER_DEPTH) |
51 | |
52 | static inline bool mp_sched_empty(void) { |
53 | MP_STATIC_ASSERT(MICROPY_SCHEDULER_DEPTH <= 255); // MICROPY_SCHEDULER_DEPTH must fit in 8 bits |
54 | MP_STATIC_ASSERT((IDX_MASK(MICROPY_SCHEDULER_DEPTH) == 0)); // MICROPY_SCHEDULER_DEPTH must be a power of 2 |
55 | |
56 | return mp_sched_num_pending() == 0; |
57 | } |
58 | |
59 | // A variant of this is inlined in the VM at the pending exception check |
60 | void mp_handle_pending(bool raise_exc) { |
61 | if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { |
62 | mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); |
63 | // Re-check state is still pending now that we're in the atomic section. |
64 | if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { |
65 | mp_obj_t obj = MP_STATE_VM(mp_pending_exception); |
66 | if (obj != MP_OBJ_NULL) { |
67 | MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; |
68 | if (!mp_sched_num_pending()) { |
69 | MP_STATE_VM(sched_state) = MP_SCHED_IDLE; |
70 | } |
71 | if (raise_exc) { |
72 | MICROPY_END_ATOMIC_SECTION(atomic_state); |
73 | nlr_raise(obj); |
74 | } |
75 | } |
76 | mp_handle_pending_tail(atomic_state); |
77 | } else { |
78 | MICROPY_END_ATOMIC_SECTION(atomic_state); |
79 | } |
80 | } |
81 | } |
82 | |
83 | // This function should only be called by mp_handle_pending, |
84 | // or by the VM's inlined version of that function. |
85 | void mp_handle_pending_tail(mp_uint_t atomic_state) { |
86 | MP_STATE_VM(sched_state) = MP_SCHED_LOCKED; |
87 | if (!mp_sched_empty()) { |
88 | mp_sched_item_t item = MP_STATE_VM(sched_queue)[MP_STATE_VM(sched_idx)]; |
89 | MP_STATE_VM(sched_idx) = IDX_MASK(MP_STATE_VM(sched_idx) + 1); |
90 | --MP_STATE_VM(sched_len); |
91 | MICROPY_END_ATOMIC_SECTION(atomic_state); |
92 | mp_call_function_1_protected(item.func, item.arg); |
93 | } else { |
94 | MICROPY_END_ATOMIC_SECTION(atomic_state); |
95 | } |
96 | mp_sched_unlock(); |
97 | } |
98 | |
99 | void mp_sched_lock(void) { |
100 | mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); |
101 | if (MP_STATE_VM(sched_state) < 0) { |
102 | --MP_STATE_VM(sched_state); |
103 | } else { |
104 | MP_STATE_VM(sched_state) = MP_SCHED_LOCKED; |
105 | } |
106 | MICROPY_END_ATOMIC_SECTION(atomic_state); |
107 | } |
108 | |
109 | void mp_sched_unlock(void) { |
110 | mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); |
111 | assert(MP_STATE_VM(sched_state) < 0); |
112 | if (++MP_STATE_VM(sched_state) == 0) { |
113 | // vm became unlocked |
114 | if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL || mp_sched_num_pending()) { |
115 | MP_STATE_VM(sched_state) = MP_SCHED_PENDING; |
116 | } else { |
117 | MP_STATE_VM(sched_state) = MP_SCHED_IDLE; |
118 | } |
119 | } |
120 | MICROPY_END_ATOMIC_SECTION(atomic_state); |
121 | } |
122 | |
123 | bool MICROPY_WRAP_MP_SCHED_SCHEDULE(mp_sched_schedule)(mp_obj_t function, mp_obj_t arg) { |
124 | mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); |
125 | bool ret; |
126 | if (!mp_sched_full()) { |
127 | if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) { |
128 | MP_STATE_VM(sched_state) = MP_SCHED_PENDING; |
129 | } |
130 | uint8_t iput = IDX_MASK(MP_STATE_VM(sched_idx) + MP_STATE_VM(sched_len)++); |
131 | MP_STATE_VM(sched_queue)[iput].func = function; |
132 | MP_STATE_VM(sched_queue)[iput].arg = arg; |
133 | ret = true; |
134 | } else { |
135 | // schedule queue is full |
136 | ret = false; |
137 | } |
138 | MICROPY_END_ATOMIC_SECTION(atomic_state); |
139 | return ret; |
140 | } |
141 | |
142 | #else // MICROPY_ENABLE_SCHEDULER |
143 | |
144 | // A variant of this is inlined in the VM at the pending exception check |
145 | void mp_handle_pending(bool raise_exc) { |
146 | if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { |
147 | mp_obj_t obj = MP_STATE_VM(mp_pending_exception); |
148 | MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; |
149 | if (raise_exc) { |
150 | nlr_raise(obj); |
151 | } |
152 | } |
153 | } |
154 | |
155 | #endif // MICROPY_ENABLE_SCHEDULER |
156 | |