diff options
author | Ahmad Fatoum <a.fatoum@pengutronix.de> | 2021-06-28 09:07:31 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2021-06-28 14:44:31 +0200 |
commit | 997cca0f15dcc8b8c96f542ac66c2eaad353f88e (patch) | |
tree | 941d04b2f898ecf622624feccb52d466413598fc /common/bthread.c | |
parent | c32343519de1774c09995118d0bae56999902a37 (diff) | |
download | barebox-997cca0f15dcc8b8c96f542ac66c2eaad353f88e.tar.gz barebox-997cca0f15dcc8b8c96f542ac66c2eaad353f88e.tar.xz |
bthread: replace blocking bthread_stop with nonblocking bthread_cancel
When bthread were first merged, they could be scheduled in any context
and bthread_stop could just keep rescheduling until the bthread in
question exits after which it would return the exit code.
Now that bthreads are only scheduled in command context, bthread_stop
also can only be scheduled in command context, making it much less
useful and easier to shoot yourself in the foot with.
Avoid this by introducing a bthread_cancel function instead that will
asynchronously terminate the thread. For most purposes that should be
fine, because bthread_stop is used to synchronize cleanup and we can
move the cleanup into the thread instead.
The only exception is the bthread command, which relies on being able to
wait on bthreads to complete. For these __bthread_stop remains available,
but should not be used in new code.
This fixes a hang that is encountered when the usb mass storage gadget
unbind is called from a poller leading barebox to wait indefinitely.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Link: https://lore.barebox.org/20210628070732.16812-2-a.fatoum@pengutronix.de
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'common/bthread.c')
-rw-r--r-- | common/bthread.c | 44 |
1 files changed, 33 insertions, 11 deletions
diff --git a/common/bthread.c b/common/bthread.c index 48248dfad4..46e6987149 100644 --- a/common/bthread.c +++ b/common/bthread.c @@ -32,10 +32,12 @@ static struct bthread { #endif u8 awake :1; u8 should_stop :1; + u8 should_clean :1; u8 has_stopped :1; } main_thread = { .list = LIST_HEAD_INIT(main_thread.list), .name = "main", + .awake = true, }; struct bthread *current = &main_thread; @@ -66,7 +68,7 @@ bool bthread_is_main(struct bthread *bthread) return bthread == &main_thread; } -void bthread_free(struct bthread *bthread) +static void bthread_free(struct bthread *bthread) { free(bthread->stack); free(bthread->name); @@ -104,6 +106,8 @@ struct bthread *bthread_create(void (*threadfn)(void *), void *data, if (len < 0) goto err; + list_add_tail(&bthread->list, ¤t->list); + /* set up bthread context with the new stack */ initjmp(bthread->jmp_buf, bthread_trampoline, bthread->stack + CONFIG_STACK_SIZE); @@ -121,26 +125,31 @@ void *bthread_data(struct bthread *bthread) void bthread_wake(struct bthread *bthread) { - if (bthread->awake) - return; - list_add_tail(&bthread->list, ¤t->list); bthread->awake = true; } void bthread_suspend(struct bthread *bthread) { - if (!bthread->awake) - return; bthread->awake = false; - list_del(&bthread->list); +} + +void bthread_cancel(struct bthread *bthread) +{ + bthread->should_stop = true; + bthread->should_clean = true; } void __bthread_stop(struct bthread *bthread) { bthread->should_stop = true; + pr_debug("waiting for %s to stop\n", bthread->name); + while (!bthread->has_stopped) bthread_reschedule(); + + list_del(&bthread->list); + bthread_free(bthread); } int bthread_should_stop(void) @@ -163,10 +172,23 @@ void bthread_info(void) void bthread_reschedule(void) { - struct bthread *next = list_next_entry(current, list); - if (current != next) - pr_debug("switch %s -> %s\n", current->name, next->name); - bthread_schedule(next); + struct bthread *next, *tmp; + + if (current == list_next_entry(current, list)) + return; + + list_for_each_entry_safe(next, tmp, ¤t->list, list) { + if (next->awake) { + pr_debug("switch %s -> %s\n", current->name, next->name); + bthread_schedule(next); + return; + } + if (next->has_stopped && next->should_clean) { + pr_debug("deleting %s\n", next->name); + list_del(&next->list); + bthread_free(next); + } + } } void bthread_schedule(struct bthread *to) |