Skip to content

Commit 4ff6e7b

Browse files
committed
pbio/light/animation: Fix lockup on restarting animation.
If it was restarted just as a new frame was being loaded, the animation would get stuck handling timer events forever. Fixes pybricks/support#1295
1 parent a047055 commit 4ff6e7b

File tree

6 files changed

+25
-10
lines changed

6 files changed

+25
-10
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@
2424
loops ([support#1668]).
2525
- Fixed program restarting if the stop button was held to end the program
2626
without an exception ([support#1863]).
27+
- Fixed program lockup when restarting a hub light or light matrix animation
28+
at exact multiples of its animation interval ([support#1295]).
2729

30+
[support#1295]: https://github.com/pybricks/support/issues/1295
2831
[support#1623]: https://github.com/pybricks/support/issues/1623
2932
[support#1661]: https://github.com/pybricks/support/issues/1661
3033
[support#1668]: https://github.com/pybricks/support/issues/1668

lib/pbio/src/light/animation.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ void pbio_light_animation_start(pbio_light_animation_t *animation) {
5050
pbio_light_animation_list_head = animation;
5151

5252
process_start(&pbio_light_animation_process);
53-
// HACK: init timer since we don't call etimer_set()
54-
timer_set(&animation->timer.timer, 0);
55-
// fake a timer event to load the first cell and start the timer
56-
process_post_synch(&pbio_light_animation_process, PROCESS_EVENT_TIMER, &animation->timer);
53+
// Fake a timer event to load the first cell.
54+
PROCESS_CONTEXT_BEGIN(&pbio_light_animation_process);
55+
etimer_set(&animation->timer, 0);
56+
PROCESS_CONTEXT_END(&pbio_light_animation_process);
5757

5858
assert(animation->next_animation != PBIO_LIGHT_ANIMATION_STOPPED);
5959
}
@@ -114,11 +114,11 @@ PROCESS_THREAD(pbio_light_animation_process, ev, data) {
114114
PROCESS_BEGIN();
115115

116116
for (;;) {
117-
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);
117+
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER && etimer_expired(data));
118118
struct etimer *timer = data;
119119
pbio_light_animation_t *animation = PBIO_CONTAINER_OF(timer, pbio_light_animation_t, timer);
120-
clock_time_t interval = animation->next(animation);
121120
if (pbio_light_animation_is_started(animation)) {
121+
clock_time_t interval = animation->next(animation);
122122
etimer_reset_with_new_interval(&animation->timer, interval);
123123
}
124124
}

lib/pbio/test/src/test_animation.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,12 @@ static PT_THREAD(test_light_animation(struct pt *pt)) {
3333
tt_want(!process_is_running(&pbio_light_animation_process));
3434
tt_want(!pbio_light_animation_is_started(&test_animation));
3535

36-
// starting animation should start process and call next() once synchonously
36+
// starting animation should start process and set a timer at 0ms to call
37+
// next() after handling pending events
3738
pbio_light_animation_start(&test_animation);
3839
tt_want(pbio_light_animation_is_started(&test_animation));
3940
tt_want(process_is_running(&pbio_light_animation_process));
41+
pbio_handle_pending_events();
4042
tt_want_uint_op(test_animation_set_hsv_call_count, ==, 1);
4143

4244
// next() should not be called again until after a delay

lib/pbio/test/src/test_color_light.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@ static PT_THREAD(test_color_light(struct pt *pt)) {
6363
// reset call count for next series of tests
6464
test_light_set_hsv_call_count = 0;
6565

66-
// starting animation should call set_hsv() synchonously
66+
// starting animation should call set_hsv() after handling pending events
6767
static const pbio_color_hsv_t hsv = { .h = PBIO_COLOR_HUE_BLUE, .s = 100, .v = 100 };
6868
pbio_color_light_start_blink_animation(&test_light, &hsv, test_blink);
69+
pbio_handle_pending_events();
6970
tt_want_uint_op(test_light_set_hsv_call_count, ==, 1);
7071
// even blink cells turns the light on
7172
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)) {
9293
// reset call count for next series of tests
9394
test_light_set_hsv_call_count = 0;
9495

95-
// starting animation should call set_hsv() synchonously
96+
// starting animation should call set_hsv() after handling pending events
9697
pbio_color_light_start_animation(&test_light, TEST_ANIMATION_TIME, test_animation);
98+
pbio_handle_pending_events();
9799
tt_want_uint_op(test_light_set_hsv_call_count, ==, 1);
98100
tt_want_uint_op(test_light_set_hsv_last_hue, ==, PBIO_COLOR_HUE_CYAN);
99101

lib/pbio/test/src/test_light_matrix.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,11 @@ static PT_THREAD(test_light_matrix(struct pt *pt)) {
8282
IMAGE_DATA(1, 2, 3, 4, 5, 6, 7, 8, 9)), ==, PBIO_SUCCESS);
8383
tt_want_light_matrix_data(1, 2, 3, 4, 5, 6, 7, 8, 9);
8484

85-
// starting animation should call set_pixel() synchonously with the first cell data
85+
// starting animation should schedule timer event at 0 ms to call
86+
// set_pixel() after handling pending events.
8687
test_light_matrix_reset();
8788
pbio_light_matrix_start_animation(&test_light_matrix, test_animation, 2, INTERVAL);
89+
pbio_handle_pending_events();
8890
tt_want_light_matrix_data(1, 2, 3, 4, 5, 6, 7, 8, 9);
8991

9092
// set_pixel() should not be called again until after a delay and it should

lib/pbio/test/test-pbio.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <pbio/button.h>
1010
#include <pbio/int_math.h>
11+
#include <pbio/main.h>
1112

1213
// Use this macro to define tests that _don't_ require a Contiki event loop
1314
#define PBIO_TEST(name) \
@@ -57,6 +58,11 @@ void pbio_test_counter_set_abs_angle(int32_t millidegrees);
5758
PT_YIELD(pt); \
5859
}
5960

61+
static inline void pbio_handle_pending_events(void) {
62+
while (pbio_do_one_event()) {
63+
}
64+
}
65+
6066
#define pbio_test_int_is_close(value, target, tolerance) (pbio_int_math_abs((value) - (target)) <= (tolerance))
6167

6268
#endif // _TEST_PBIO_H_

0 commit comments

Comments
 (0)