lib/thread: Add thread_handle
The thread_handle can be used to wait for a thread to exit. I also added a return value to the thread function that will be stored on the handle after it completes. This makes it easy for the callers to check if the thread completed successfully or had an error. The thread_join method uses the handle to block until the thread completes. BUG=b:179699789 TEST=See thread_handle state update and see error code set correctly. Signed-off-by: Raul E Rangel <rrangel@chromium.org> Change-Id: Ie6f64d0c5a5acad4431a605f0b0b5100dc5358ff Reviewed-on: https://review.coreboot.org/c/coreboot/+/56229 Reviewed-by: Karthik Ramasubramanian <kramasub@google.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
parent
3af732ada0
commit
cc01da50b7
|
@ -2,14 +2,30 @@
|
||||||
#ifndef THREAD_H_
|
#ifndef THREAD_H_
|
||||||
#define THREAD_H_
|
#define THREAD_H_
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <bootstate.h>
|
|
||||||
#include <arch/cpu.h>
|
#include <arch/cpu.h>
|
||||||
|
#include <bootstate.h>
|
||||||
|
#include <commonlib/bsd/cb_err.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
struct thread_mutex {
|
struct thread_mutex {
|
||||||
bool locked;
|
bool locked;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum thread_state {
|
||||||
|
THREAD_UNINITIALIZED,
|
||||||
|
THREAD_STARTED,
|
||||||
|
THREAD_DONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct thread_handle {
|
||||||
|
enum thread_state state;
|
||||||
|
/* Only valid when state == THREAD_DONE */
|
||||||
|
enum cb_err error;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Waits until the thread has terminated and returns the error code */
|
||||||
|
enum cb_err thread_join(struct thread_handle *handle);
|
||||||
|
|
||||||
#if ENV_RAMSTAGE && CONFIG(COOP_MULTITASKING)
|
#if ENV_RAMSTAGE && CONFIG(COOP_MULTITASKING)
|
||||||
|
|
||||||
struct thread {
|
struct thread {
|
||||||
|
@ -17,9 +33,10 @@ struct thread {
|
||||||
uintptr_t stack_current;
|
uintptr_t stack_current;
|
||||||
uintptr_t stack_orig;
|
uintptr_t stack_orig;
|
||||||
struct thread *next;
|
struct thread *next;
|
||||||
void (*entry)(void *);
|
enum cb_err (*entry)(void *);
|
||||||
void *entry_arg;
|
void *entry_arg;
|
||||||
int can_yield;
|
int can_yield;
|
||||||
|
struct thread_handle *handle;
|
||||||
};
|
};
|
||||||
|
|
||||||
void threads_initialize(void);
|
void threads_initialize(void);
|
||||||
|
@ -30,12 +47,14 @@ void threads_initialize(void);
|
||||||
void *arch_get_thread_stackbase(void);
|
void *arch_get_thread_stackbase(void);
|
||||||
/* Run func(arrg) on a new thread. Return 0 on successful start of thread, < 0
|
/* Run func(arrg) on a new thread. Return 0 on successful start of thread, < 0
|
||||||
* when thread could not be started. Note that the thread will block the
|
* when thread could not be started. Note that the thread will block the
|
||||||
* current state in the boot state machine until it is complete. */
|
* current state in the boot state machine until it is complete. The thread
|
||||||
int thread_run(void (*func)(void *), void *arg);
|
* handle if populated, will reflect the state and return code of the thread.
|
||||||
|
*/
|
||||||
|
int thread_run(struct thread_handle *handle, enum cb_err (*func)(void *), void *arg);
|
||||||
/* thread_run_until is the same as thread_run() except that it blocks state
|
/* thread_run_until is the same as thread_run() except that it blocks state
|
||||||
* transitions from occurring in the (state, seq) pair of the boot state
|
* transitions from occurring in the (state, seq) pair of the boot state
|
||||||
* machine. */
|
* machine. */
|
||||||
int thread_run_until(void (*func)(void *), void *arg,
|
int thread_run_until(struct thread_handle *handle, enum cb_err (*func)(void *), void *arg,
|
||||||
boot_state_t state, boot_state_sequence_t seq);
|
boot_state_t state, boot_state_sequence_t seq);
|
||||||
|
|
||||||
/* Return 0 on successful yield, < 0 when thread did not yield. */
|
/* Return 0 on successful yield, < 0 when thread did not yield. */
|
||||||
|
@ -74,9 +93,13 @@ void arch_prepare_thread(struct thread *t,
|
||||||
asmlinkage void (*thread_entry)(void *), void *arg);
|
asmlinkage void (*thread_entry)(void *), void *arg);
|
||||||
#else
|
#else
|
||||||
static inline void threads_initialize(void) {}
|
static inline void threads_initialize(void) {}
|
||||||
static inline int thread_run(void (*func)(void *), void *arg) { return -1; }
|
static inline int thread_run(struct thread_handle *handle, enum cb_err (*func)(void *),
|
||||||
static inline int thread_run_until(void (*func)(void *), void *arg, boot_state_t state,
|
void *arg)
|
||||||
boot_state_sequence_t seq)
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
static inline int thread_run_until(struct thread_handle *handle, enum cb_err (*func)(void *),
|
||||||
|
void *arg, boot_state_t state, boot_state_sequence_t seq)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <arch/cpu.h>
|
#include <arch/cpu.h>
|
||||||
#include <bootstate.h>
|
#include <bootstate.h>
|
||||||
|
#include <commonlib/bsd/compiler.h>
|
||||||
#include <console/console.h>
|
#include <console/console.h>
|
||||||
#include <thread.h>
|
#include <thread.h>
|
||||||
#include <timer.h>
|
#include <timer.h>
|
||||||
|
@ -112,7 +113,7 @@ static inline void free_thread(struct thread *t)
|
||||||
/* The idle thread is ran whenever there isn't anything else that is runnable.
|
/* The idle thread is ran whenever there isn't anything else that is runnable.
|
||||||
* It's sole responsibility is to ensure progress is made by running the timer
|
* It's sole responsibility is to ensure progress is made by running the timer
|
||||||
* callbacks. */
|
* callbacks. */
|
||||||
static void idle_thread(void *unused)
|
__noreturn static enum cb_err idle_thread(void *unused)
|
||||||
{
|
{
|
||||||
/* This thread never voluntarily yields. */
|
/* This thread never voluntarily yields. */
|
||||||
thread_coop_disable();
|
thread_coop_disable();
|
||||||
|
@ -133,11 +134,20 @@ static void schedule(struct thread *t)
|
||||||
/* current is still runnable. */
|
/* current is still runnable. */
|
||||||
push_runnable(current);
|
push_runnable(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (t->handle)
|
||||||
|
t->handle->state = THREAD_STARTED;
|
||||||
|
|
||||||
switch_to_thread(t->stack_current, ¤t->stack_current);
|
switch_to_thread(t->stack_current, ¤t->stack_current);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void terminate_thread(struct thread *t)
|
static void terminate_thread(struct thread *t, enum cb_err error)
|
||||||
{
|
{
|
||||||
|
if (t->handle) {
|
||||||
|
t->handle->error = error;
|
||||||
|
t->handle->state = THREAD_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
free_thread(t);
|
free_thread(t);
|
||||||
schedule(NULL);
|
schedule(NULL);
|
||||||
}
|
}
|
||||||
|
@ -145,20 +155,23 @@ static void terminate_thread(struct thread *t)
|
||||||
static void asmlinkage call_wrapper(void *unused)
|
static void asmlinkage call_wrapper(void *unused)
|
||||||
{
|
{
|
||||||
struct thread *current = current_thread();
|
struct thread *current = current_thread();
|
||||||
|
enum cb_err error;
|
||||||
|
|
||||||
current->entry(current->entry_arg);
|
error = current->entry(current->entry_arg);
|
||||||
terminate_thread(current);
|
|
||||||
|
terminate_thread(current, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Block the current state transitions until thread is complete. */
|
/* Block the current state transitions until thread is complete. */
|
||||||
static void asmlinkage call_wrapper_block_current(void *unused)
|
static void asmlinkage call_wrapper_block_current(void *unused)
|
||||||
{
|
{
|
||||||
struct thread *current = current_thread();
|
struct thread *current = current_thread();
|
||||||
|
enum cb_err error;
|
||||||
|
|
||||||
boot_state_current_block();
|
boot_state_current_block();
|
||||||
current->entry(current->entry_arg);
|
error = current->entry(current->entry_arg);
|
||||||
boot_state_current_unblock();
|
boot_state_current_unblock();
|
||||||
terminate_thread(current);
|
terminate_thread(current, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct block_boot_state {
|
struct block_boot_state {
|
||||||
|
@ -171,18 +184,19 @@ static void asmlinkage call_wrapper_block_state(void *arg)
|
||||||
{
|
{
|
||||||
struct block_boot_state *bbs = arg;
|
struct block_boot_state *bbs = arg;
|
||||||
struct thread *current = current_thread();
|
struct thread *current = current_thread();
|
||||||
|
enum cb_err error;
|
||||||
|
|
||||||
boot_state_block(bbs->state, bbs->seq);
|
boot_state_block(bbs->state, bbs->seq);
|
||||||
current->entry(current->entry_arg);
|
error = current->entry(current->entry_arg);
|
||||||
boot_state_unblock(bbs->state, bbs->seq);
|
boot_state_unblock(bbs->state, bbs->seq);
|
||||||
terminate_thread(current);
|
terminate_thread(current, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prepare a thread so that it starts by executing thread_entry(thread_arg).
|
/* Prepare a thread so that it starts by executing thread_entry(thread_arg).
|
||||||
* Within thread_entry() it will call func(arg). */
|
* Within thread_entry() it will call func(arg). */
|
||||||
static void prepare_thread(struct thread *t, void *func, void *arg,
|
static void prepare_thread(struct thread *t, struct thread_handle *handle,
|
||||||
asmlinkage void (*thread_entry)(void *),
|
enum cb_err (*func)(void *), void *arg,
|
||||||
void *thread_arg)
|
asmlinkage void (*thread_entry)(void *), void *thread_arg)
|
||||||
{
|
{
|
||||||
/* Stash the function and argument to run. */
|
/* Stash the function and argument to run. */
|
||||||
t->entry = func;
|
t->entry = func;
|
||||||
|
@ -191,6 +205,9 @@ static void prepare_thread(struct thread *t, void *func, void *arg,
|
||||||
/* All new threads can yield by default. */
|
/* All new threads can yield by default. */
|
||||||
t->can_yield = 1;
|
t->can_yield = 1;
|
||||||
|
|
||||||
|
/* Pointer used to publish the state of thread */
|
||||||
|
t->handle = handle;
|
||||||
|
|
||||||
arch_prepare_thread(t, thread_entry, thread_arg);
|
arch_prepare_thread(t, thread_entry, thread_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +229,7 @@ static void idle_thread_init(void)
|
||||||
die("No threads available for idle thread!\n");
|
die("No threads available for idle thread!\n");
|
||||||
|
|
||||||
/* Queue idle thread to run once all other threads have yielded. */
|
/* Queue idle thread to run once all other threads have yielded. */
|
||||||
prepare_thread(t, idle_thread, NULL, call_wrapper, NULL);
|
prepare_thread(t, NULL, idle_thread, NULL, call_wrapper, NULL);
|
||||||
push_runnable(t);
|
push_runnable(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +292,7 @@ void threads_initialize(void)
|
||||||
initialized = 1;
|
initialized = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int thread_run(void (*func)(void *), void *arg)
|
int thread_run(struct thread_handle *handle, enum cb_err (*func)(void *), void *arg)
|
||||||
{
|
{
|
||||||
struct thread *current;
|
struct thread *current;
|
||||||
struct thread *t;
|
struct thread *t;
|
||||||
|
@ -295,13 +312,13 @@ int thread_run(void (*func)(void *), void *arg)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
prepare_thread(t, func, arg, call_wrapper_block_current, NULL);
|
prepare_thread(t, handle, func, arg, call_wrapper_block_current, NULL);
|
||||||
schedule(t);
|
schedule(t);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int thread_run_until(void (*func)(void *), void *arg,
|
int thread_run_until(struct thread_handle *handle, enum cb_err (*func)(void *), void *arg,
|
||||||
boot_state_t state, boot_state_sequence_t seq)
|
boot_state_t state, boot_state_sequence_t seq)
|
||||||
{
|
{
|
||||||
struct thread *current;
|
struct thread *current;
|
||||||
|
@ -326,7 +343,7 @@ int thread_run_until(void (*func)(void *), void *arg,
|
||||||
bbs = thread_alloc_space(t, sizeof(*bbs));
|
bbs = thread_alloc_space(t, sizeof(*bbs));
|
||||||
bbs->state = state;
|
bbs->state = state;
|
||||||
bbs->seq = seq;
|
bbs->seq = seq;
|
||||||
prepare_thread(t, func, arg, call_wrapper_block_state, bbs);
|
prepare_thread(t, handle, func, arg, call_wrapper_block_state, bbs);
|
||||||
schedule(t);
|
schedule(t);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -379,6 +396,30 @@ void thread_coop_disable(void)
|
||||||
current->can_yield--;
|
current->can_yield--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum cb_err thread_join(struct thread_handle *handle)
|
||||||
|
{
|
||||||
|
struct stopwatch sw;
|
||||||
|
struct thread *current = current_thread();
|
||||||
|
|
||||||
|
assert(handle);
|
||||||
|
assert(current);
|
||||||
|
assert(current->handle != handle);
|
||||||
|
|
||||||
|
if (handle->state == THREAD_UNINITIALIZED)
|
||||||
|
return CB_ERR_ARG;
|
||||||
|
|
||||||
|
stopwatch_init(&sw);
|
||||||
|
|
||||||
|
printk(BIOS_SPEW, "waiting for thread\n");
|
||||||
|
|
||||||
|
while (handle->state != THREAD_DONE)
|
||||||
|
assert(thread_yield() == 0);
|
||||||
|
|
||||||
|
printk(BIOS_SPEW, "took %lu us\n", stopwatch_duration_usecs(&sw));
|
||||||
|
|
||||||
|
return handle->error;
|
||||||
|
}
|
||||||
|
|
||||||
void thread_mutex_lock(struct thread_mutex *mutex)
|
void thread_mutex_lock(struct thread_mutex *mutex)
|
||||||
{
|
{
|
||||||
struct stopwatch sw;
|
struct stopwatch sw;
|
||||||
|
|
Loading…
Reference in New Issue