diff --git a/CHANGELOG.md b/CHANGELOG.md index 2edca6af6..af1603c93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,10 @@ loops ([support#1668]). - Fixed program restarting if the stop button was held to end the program without an exception ([support#1863]). +- Fixed program lockup when restarting a hub light or light matrix animation + at exact multiples of its animation interval ([support#1295]). +[support#1295]: https://github.com/pybricks/support/issues/1295 [support#1623]: https://github.com/pybricks/support/issues/1623 [support#1661]: https://github.com/pybricks/support/issues/1661 [support#1668]: https://github.com/pybricks/support/issues/1668 diff --git a/lib/pbio/src/light/animation.c b/lib/pbio/src/light/animation.c index 651d578cc..9947413dc 100644 --- a/lib/pbio/src/light/animation.c +++ b/lib/pbio/src/light/animation.c @@ -50,10 +50,10 @@ void pbio_light_animation_start(pbio_light_animation_t *animation) { pbio_light_animation_list_head = animation; process_start(&pbio_light_animation_process); - // HACK: init timer since we don't call etimer_set() - timer_set(&animation->timer.timer, 0); - // fake a timer event to load the first cell and start the timer - process_post_synch(&pbio_light_animation_process, PROCESS_EVENT_TIMER, &animation->timer); + // Fake a timer event to load the first cell. + PROCESS_CONTEXT_BEGIN(&pbio_light_animation_process); + etimer_set(&animation->timer, 0); + PROCESS_CONTEXT_END(&pbio_light_animation_process); assert(animation->next_animation != PBIO_LIGHT_ANIMATION_STOPPED); } @@ -114,11 +114,11 @@ PROCESS_THREAD(pbio_light_animation_process, ev, data) { PROCESS_BEGIN(); for (;;) { - PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER); + PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER && etimer_expired(data)); struct etimer *timer = data; pbio_light_animation_t *animation = PBIO_CONTAINER_OF(timer, pbio_light_animation_t, timer); - clock_time_t interval = animation->next(animation); if (pbio_light_animation_is_started(animation)) { + clock_time_t interval = animation->next(animation); etimer_reset_with_new_interval(&animation->timer, interval); } } diff --git a/lib/pbio/test/src/test_animation.c b/lib/pbio/test/src/test_animation.c index 05ff2fc55..36b8cec89 100644 --- a/lib/pbio/test/src/test_animation.c +++ b/lib/pbio/test/src/test_animation.c @@ -33,10 +33,12 @@ static PT_THREAD(test_light_animation(struct pt *pt)) { tt_want(!process_is_running(&pbio_light_animation_process)); tt_want(!pbio_light_animation_is_started(&test_animation)); - // starting animation should start process and call next() once synchonously + // starting animation should start process and set a timer at 0ms to call + // next() after handling pending events pbio_light_animation_start(&test_animation); tt_want(pbio_light_animation_is_started(&test_animation)); tt_want(process_is_running(&pbio_light_animation_process)); + pbio_handle_pending_events(); tt_want_uint_op(test_animation_set_hsv_call_count, ==, 1); // next() should not be called again until after a delay diff --git a/lib/pbio/test/src/test_color_light.c b/lib/pbio/test/src/test_color_light.c index 346599b68..cfc8a054b 100644 --- a/lib/pbio/test/src/test_color_light.c +++ b/lib/pbio/test/src/test_color_light.c @@ -63,9 +63,10 @@ static PT_THREAD(test_color_light(struct pt *pt)) { // reset call count for next series of tests test_light_set_hsv_call_count = 0; - // starting animation should call set_hsv() synchonously + // starting animation should call set_hsv() after handling pending events static const pbio_color_hsv_t hsv = { .h = PBIO_COLOR_HUE_BLUE, .s = 100, .v = 100 }; pbio_color_light_start_blink_animation(&test_light, &hsv, test_blink); + pbio_handle_pending_events(); tt_want_uint_op(test_light_set_hsv_call_count, ==, 1); // even blink cells turns the light on tt_want_uint_op(test_light_set_hsv_last_hue, ==, PBIO_COLOR_HUE_BLUE); @@ -92,8 +93,9 @@ static PT_THREAD(test_color_light(struct pt *pt)) { // reset call count for next series of tests test_light_set_hsv_call_count = 0; - // starting animation should call set_hsv() synchonously + // starting animation should call set_hsv() after handling pending events pbio_color_light_start_animation(&test_light, TEST_ANIMATION_TIME, test_animation); + pbio_handle_pending_events(); tt_want_uint_op(test_light_set_hsv_call_count, ==, 1); tt_want_uint_op(test_light_set_hsv_last_hue, ==, PBIO_COLOR_HUE_CYAN); diff --git a/lib/pbio/test/src/test_light_matrix.c b/lib/pbio/test/src/test_light_matrix.c index 0db48c23d..d2d5abef9 100644 --- a/lib/pbio/test/src/test_light_matrix.c +++ b/lib/pbio/test/src/test_light_matrix.c @@ -82,9 +82,11 @@ static PT_THREAD(test_light_matrix(struct pt *pt)) { IMAGE_DATA(1, 2, 3, 4, 5, 6, 7, 8, 9)), ==, PBIO_SUCCESS); tt_want_light_matrix_data(1, 2, 3, 4, 5, 6, 7, 8, 9); - // starting animation should call set_pixel() synchonously with the first cell data + // starting animation should schedule timer event at 0 ms to call + // set_pixel() after handling pending events. test_light_matrix_reset(); pbio_light_matrix_start_animation(&test_light_matrix, test_animation, 2, INTERVAL); + pbio_handle_pending_events(); tt_want_light_matrix_data(1, 2, 3, 4, 5, 6, 7, 8, 9); // set_pixel() should not be called again until after a delay and it should diff --git a/lib/pbio/test/test-pbio.h b/lib/pbio/test/test-pbio.h index 9a0cc431d..3e7c20fcd 100644 --- a/lib/pbio/test/test-pbio.h +++ b/lib/pbio/test/test-pbio.h @@ -8,6 +8,7 @@ #include #include +#include // Use this macro to define tests that _don't_ require a Contiki event loop #define PBIO_TEST(name) \ @@ -57,6 +58,11 @@ void pbio_test_counter_set_abs_angle(int32_t millidegrees); PT_YIELD(pt); \ } +static inline void pbio_handle_pending_events(void) { + while (pbio_do_one_event()) { + } +} + #define pbio_test_int_is_close(value, target, tolerance) (pbio_int_math_abs((value) - (target)) <= (tolerance)) #endif // _TEST_PBIO_H_