Skip to content

Commit a463521

Browse files
committed
Display: Prohibit allocation in iterrupts. This prevents non-deterministic and impossible to debug errors caused by aysynchronous GC.
1 parent d9e5380 commit a463521

File tree

5 files changed

+87
-52
lines changed

5 files changed

+87
-52
lines changed

docs/display.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ Functions
4747
Note that the ``wait``, ``loop`` and ``clear`` arguments must be specified
4848
using their keyword.
4949

50+
.. note::
51+
52+
If using a generator as the ``iterable``, then take care not to allocate any memory
53+
in the generator as allocating memory in an interrupt is prohibited and will raise a
54+
``MemoryError``.
55+
5056
.. py:function:: scroll(string, delay=150, \*, wait=True, loop=False, monospace=False)
5157
5258
Similar to ``show``, but scrolls the ``string`` horizontally instead. The

examples/analog_watch.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ def ticks():
3030
on = not on
3131

3232
#Run a clock speeded up 60 times, so we can watch the animation.
33-
display.show(ticks(), 1000)
34-
35-
36-
33+
for tick in ticks():
34+
display.show(tick)
35+
sleep(1000)

inc/microbit/microbitimage.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ typedef struct _greyscale_t {
3737
uint8_t width;
3838
uint8_t byte_data[]; /* Static initializer for this will have to be C, not C++ */
3939
void clear();
40+
void shiftLeftInplace(mp_int_t n);
41+
void shiftRightInplace(mp_int_t n);
4042

4143
/* Thiese are internal methods and it is up to the caller to validate the inputs */
4244
uint8_t getPixelValue(mp_int_t x, mp_int_t y);

source/microbit/microbitdisplay.cpp

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
extern "C" {
3232
#include "py/runtime.h"
33+
#include "py/gc.h"
3334
#include "modmicrobit.h"
3435
#include "microbitimage.h"
3536
#include "microbitdisplay.h"
@@ -130,7 +131,6 @@ static volatile bool wakeup_event = false;
130131
static mp_uint_t async_delay = 1000;
131132
static mp_uint_t async_tick = 0;
132133
static bool async_clear = false;
133-
static bool async_error = true;
134134

135135
STATIC void async_stop(void) {
136136
async_iterator = NULL;
@@ -281,6 +281,30 @@ static int32_t callback(void) {
281281
return render_timings[brightness];
282282
}
283283

284+
static void draw_object(mp_obj_t obj) {
285+
microbit_display_obj_t *display = (microbit_display_obj_t*)MP_STATE_PORT(async_data)[0];
286+
if (obj == MP_OBJ_STOP_ITERATION) {
287+
if (async_clear) {
288+
microbit_display_show(&microbit_display_obj, BLANK_IMAGE);
289+
async_clear = false;
290+
} else {
291+
async_stop();
292+
}
293+
} else if (mp_obj_get_type(obj) == &microbit_image_type) {
294+
microbit_display_show(display, (microbit_image_obj_t *)obj);
295+
} else if (MP_OBJ_IS_STR(obj)) {
296+
mp_uint_t len;
297+
const char *str = mp_obj_str_get_data(obj, &len);
298+
if (len == 1) {
299+
microbit_display_show(display, microbit_image_for_char(str[0]));
300+
} else {
301+
async_stop();
302+
}
303+
} else {
304+
MP_STATE_VM(mp_pending_exception) = mp_obj_new_exception_msg(&mp_type_TypeError, "not an image.");
305+
async_stop();
306+
}
307+
}
284308

285309
static void microbit_display_update(void) {
286310
async_tick += MILLISECONDS_PER_MACRO_TICK;
@@ -295,44 +319,29 @@ static void microbit_display_update(void) {
295319
async_stop();
296320
break;
297321
}
298-
microbit_display_obj_t *display = (microbit_display_obj_t*)MP_STATE_PORT(async_data)[0];
299322
/* WARNING: We are executing in an interrupt handler.
300323
* If an exception is raised here then we must hand it to the VM. */
301324
mp_obj_t obj;
302325
nlr_buf_t nlr;
326+
gc_lock();
303327
if (nlr_push(&nlr) == 0) {
304328
obj = mp_iternext_allow_raise(async_iterator);
305329
nlr_pop();
330+
gc_unlock();
306331
} else {
332+
gc_unlock();
307333
if (!mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t*)nlr.ret_val)->type),
308334
MP_OBJ_FROM_PTR(&mp_type_StopIteration))) {
309-
// an exception other than StopIteration, so set it for the VM to raise later
335+
// An exception other than StopIteration, so set it for the VM to raise later
336+
// If memory error, write an appropriate message.
337+
if (mp_obj_get_type(nlr.ret_val) == &mp_type_MemoryError) {
338+
mp_printf(&mp_plat_print, "Allocation in interrupt handler");
339+
}
310340
MP_STATE_VM(mp_pending_exception) = MP_OBJ_FROM_PTR(nlr.ret_val);
311341
}
312342
obj = MP_OBJ_STOP_ITERATION;
313343
}
314-
if (obj == MP_OBJ_STOP_ITERATION) {
315-
if (async_clear) {
316-
microbit_display_show(&microbit_display_obj, BLANK_IMAGE);
317-
async_clear = false;
318-
} else {
319-
async_stop();
320-
}
321-
} else if (mp_obj_get_type(obj) == &microbit_image_type) {
322-
microbit_display_show(display, (microbit_image_obj_t *)obj);
323-
} else if (MP_OBJ_IS_STR(obj)) {
324-
mp_uint_t len;
325-
const char *str = mp_obj_str_get_data(obj, &len);
326-
if (len == 1) {
327-
microbit_display_show(display, microbit_image_for_char(str[0]));
328-
} else {
329-
async_error = true;
330-
async_stop();
331-
}
332-
} else {
333-
async_error = true;
334-
async_stop();
335-
}
344+
draw_object(obj);
336345
break;
337346
}
338347
case ASYNC_MODE_CLEAR:
@@ -367,13 +376,14 @@ void microbit_display_animate(microbit_display_obj_t *self, mp_obj_t iterable, m
367376
MP_STATE_PORT(async_data)[0] = NULL;
368377
MP_STATE_PORT(async_data)[1] = NULL;
369378
async_iterator = mp_getiter(iterable);
370-
async_error = false;
371379
async_delay = delay;
372380
async_clear = clear;
373381
MP_STATE_PORT(async_data)[0] = self; // so it doesn't get GC'd
374382
MP_STATE_PORT(async_data)[1] = async_iterator;
375383
wakeup_event = false;
376384
async_mode = ASYNC_MODE_ANIMATION;
385+
mp_obj_t obj = mp_iternext_allow_raise(async_iterator);
386+
draw_object(obj);
377387
if (wait) {
378388
wait_for_event();
379389
}

source/microbit/microbitimage.cpp

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -142,31 +142,50 @@ greyscale_t *microbit_image_obj_t::invert() {
142142
return result;
143143
}
144144

145-
greyscale_t *microbit_image_obj_t::shiftLeft(mp_int_t n) {
146-
mp_int_t w = this->width();
147-
mp_int_t h = this->height();
148-
n = max(n, -w);
149-
n = min(n, w);
150-
mp_int_t src_start = max(n, 0);
151-
mp_int_t src_end = min(w+n,w);
152-
mp_int_t dest = max(0,-n);
153-
greyscale_t *result = greyscale_new(w, h);
154-
for (mp_int_t x = 0; x < dest; ++x) {
145+
/** Internal method, does no error checking. Result is undefined if n < 0 or n > width */
146+
void greyscale_t::shiftLeftInplace(mp_int_t n) {
147+
mp_int_t w = this->width;
148+
mp_int_t h = this->height;
149+
for (mp_int_t x = n; x < w; ++x) {
155150
for (mp_int_t y = 0; y < h; y++) {
156-
result->setPixelValue(x, y, 0);
151+
this->setPixelValue(x-n, y, this->getPixelValue(x, y));
157152
}
158153
}
159-
for (mp_int_t x = src_start; x < src_end; ++x) {
154+
for (mp_int_t x = w-n; x < w; ++x) {
160155
for (mp_int_t y = 0; y < h; y++) {
161-
result->setPixelValue(dest, y, this->getPixelValue(x, y));
156+
this->setPixelValue(x, y, 0);
162157
}
163-
++dest;
164158
}
165-
for (mp_int_t x = dest; x < w; ++x) {
159+
}
160+
161+
/** Internal method, does no error checking. Result is undefined if n < 0 or n > width */
162+
void greyscale_t::shiftRightInplace(mp_int_t n) {
163+
if (n < 0)
164+
return;
165+
mp_int_t w = this->width;
166+
mp_int_t h = this->height;
167+
for (mp_int_t x = w-1; x >= n; --x) {
166168
for (mp_int_t y = 0; y < h; y++) {
167-
result->setPixelValue(x, y, 0);
169+
this->setPixelValue(x, y, this->getPixelValue(x-n, y));
168170
}
169171
}
172+
for (mp_int_t x = 0; x < n; ++x) {
173+
for (mp_int_t y = 0; y < h; y++) {
174+
this->setPixelValue(x, y, 0);
175+
}
176+
}
177+
}
178+
179+
greyscale_t *microbit_image_obj_t::shiftLeft(mp_int_t n) {
180+
mp_int_t w = this->width();
181+
if (n <= -w || n >= w)
182+
return greyscale_new(w, this->height());
183+
greyscale_t *result = this->copy();
184+
if (n >= 0) {
185+
result->shiftLeftInplace(n);
186+
} else {
187+
result->shiftRightInplace(-n);
188+
}
170189
return result;
171190
}
172191

@@ -643,7 +662,7 @@ typedef struct _scrolling_string_t {
643662
typedef struct _scrolling_string_iterator_t {
644663
mp_obj_base_t base;
645664
mp_obj_t ref;
646-
microbit_image_obj_t *img;
665+
greyscale_t *img;
647666
char const *next_char;
648667
char const *end;
649668
uint8_t offset;
@@ -689,7 +708,7 @@ STATIC mp_obj_t get_microbit_scrolling_string_iter(mp_obj_t o_in) {
689708
scrolling_string_t *str = (scrolling_string_t *)o_in;
690709
scrolling_string_iterator_t *result = m_new_obj(scrolling_string_iterator_t);
691710
result->base.type = &microbit_scrolling_string_iterator_type;
692-
result->img = BLANK_IMAGE;
711+
result->img = greyscale_new(5,5);
693712
result->offset = 0;
694713
result->next_char = str->str;
695714
result->ref = str->ref;
@@ -714,14 +733,13 @@ STATIC mp_obj_t microbit_scrolling_string_iter_next(mp_obj_t o_in) {
714733
if (iter->next_char == iter->end && iter->offset == 5) {
715734
return MP_OBJ_STOP_ITERATION;
716735
}
717-
greyscale_t *result = iter->img->shiftLeft(1);
718-
iter->img = (microbit_image_obj_t*)result;
736+
iter->img->shiftLeftInplace(1);
719737
const unsigned char *font_data;
720738
if (iter->offset < iter->offset_limit) {
721739
font_data = get_font_data_from_char(iter->right);
722740
for (int y = 0; y < 5; ++y) {
723741
int pix = get_pixel_from_font_data(font_data, iter->offset, y)*MAX_BRIGHTNESS;
724-
result->setPixelValue(4, y, pix);
742+
iter->img->setPixelValue(4, y, pix);
725743
}
726744
} else if (iter->offset == iter->offset_limit) {
727745
++iter->next_char;
@@ -742,7 +760,7 @@ STATIC mp_obj_t microbit_scrolling_string_iter_next(mp_obj_t o_in) {
742760
}
743761
}
744762
++iter->offset;
745-
return result;
763+
return iter->img;
746764
}
747765

748766
const mp_obj_type_t microbit_scrolling_string_type = {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy