You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
473 lines
16 KiB
473 lines
16 KiB
/* |
|
* Copyright (C) 2014 Freie Universität Berlin |
|
* |
|
* This file is subject to the terms and conditions of the GNU Lesser |
|
* General Public License v2.1. See the file LICENSE in the top level |
|
* directory for more details. |
|
*/ |
|
|
|
/** |
|
* @defgroup core_thread Threading |
|
* @ingroup core |
|
* @brief Support for multi-threading |
|
* |
|
* Priorities |
|
* ========== |
|
* |
|
* As RIOT is using a fixed priority @ref core_sched "scheduling algorithm", |
|
* threads are scheduled based on their priority. The priority is fixed for |
|
* every thread and specified during the thread's creation by the `priority` |
|
* parameter. |
|
* |
|
* The lower the priority value, the higher the priority of the thread, |
|
* with 0 being the highest possible priority. |
|
* |
|
* The lowest possible priority is @ref THREAD_PRIORITY_IDLE - 1. |
|
* |
|
* @note Assigning the same priority to two or more threads is usually not a |
|
* good idea. A thread in RIOT may run until it yields (@ref |
|
* thread_yield) or another thread with higher priority is runnable (@ref |
|
* STATUS_ON_RUNQUEUE) again. Multiple threads with the same priority |
|
* will therefore be scheduled cooperatively: when one of them is running, |
|
* all others with the same priority depend on it to yield (or be interrupted |
|
* by a thread with higher priority). |
|
* This may make it difficult to determine when which of them gets |
|
* scheduled and how much CPU time they will get. In most applications, |
|
* the number of threads in application is significantly smaller than the |
|
* number of available priorities, so assigning distinct priorities per |
|
* thread should not be a problem. Only assign the same priority to |
|
* multiple threads if you know what you are doing! |
|
* |
|
* Thread Behavior |
|
* =============== |
|
* In addition to the priority, flags can be used when creating a thread to |
|
* alter the thread's behavior after creation. The following flags are available: |
|
* |
|
* Flags | Description |
|
* ----------------------------- | -------------------------------------------------- |
|
* @ref THREAD_CREATE_SLEEPING | the thread will sleep until woken up manually |
|
* @ref THREAD_CREATE_WOUT_YIELD | the thread might not run immediately after creation |
|
* @ref THREAD_CREATE_STACKTEST | measures the stack's memory usage |
|
* |
|
* Thread creation |
|
* =============== |
|
* Creating a new thread is internally done in two steps: |
|
* 1. the new thread's stack is initialized depending on the platform |
|
* 2. the new thread is added to the scheduler and the scheduler is run (if not |
|
* indicated otherwise) |
|
* |
|
* @note Creating threads from within an ISR is currently supported, however it |
|
* is considered to be a bad programming practice and we strongly |
|
* discourage you from doing so. |
|
* |
|
* Usage |
|
* ----- |
|
* ~~~~~~~~~~~~~~~~~~~~~~~~ {.c} |
|
* #include "thread.h" |
|
* |
|
* char rcv_thread_stack[THREAD_STACKSIZE_MAIN]; |
|
* |
|
* void *rcv_thread(void *arg) |
|
* { |
|
* (void) arg; |
|
* msg_t m; |
|
* |
|
* while (1) { |
|
* msg_receive(&m); |
|
* printf("Got msg from %" PRIkernel_pid "\n", m.sender_pid); |
|
* } |
|
* |
|
* return NULL; |
|
* } |
|
* |
|
* int main(void) |
|
* { |
|
* thread_create(rcv_thread_stack, sizeof(rcv_thread_stack), |
|
* THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_STACKTEST, |
|
* rcv_thread, NULL, "rcv_thread"); |
|
* } |
|
* ~~~~~~~~~~~~~~~~~~~~~~~~ |
|
* |
|
* Reading from the top down, you can see that first, stack memory for our thread |
|
* `rcv_thread` is preallocated, followed by an implementation of the thread's |
|
* function. Communication between threads is done using @ref core_msg. In this |
|
* case, `rcv_thread` will print the process id of each thread that sent a |
|
* message to `rcv_thread`. |
|
* |
|
* After it has been properly defined, `rcv_thread` is created with a call to |
|
* @ref thread_create() in `main()`. It is assigned a priority of |
|
* `THREAD_PRIORITY_MAIN - 1`, i.e. a slightly *higher* priority than the main |
|
* thread. Since neither the `THREAD_CREATE_SLEEPING` nor the |
|
* `THREAD_CREATE_WOUT_YIELD` flag is set, `rcv_thread` will be executed |
|
* immediately. |
|
* |
|
* @note If the messages to the thread are sent using @ref msg_try_send() or |
|
* from an ISR, activate your thread's message queue by calling |
|
* @ref msg_init_queue() to prevent messages from being dropped when |
|
* they can't be handled right away. The same applies if you'd like |
|
* msg_send() to your thread to be non-blocking. For more details, see |
|
* @ref core_msg "the Messaging documentation". |
|
* |
|
* @{ |
|
* |
|
* @file |
|
* @brief Threading API |
|
* |
|
* @author Kaspar Schleiser <kaspar@schleiser.de> |
|
*/ |
|
|
|
#ifndef THREAD_H |
|
#define THREAD_H |
|
|
|
#include "clist.h" |
|
#include "cib.h" |
|
#include "msg.h" |
|
#include "arch/thread_arch.h" |
|
#include "cpu_conf.h" |
|
#include "sched.h" |
|
|
|
#ifdef MODULE_CORE_THREAD_FLAGS |
|
#include "thread_flags.h" |
|
#endif |
|
|
|
#ifdef __cplusplus |
|
extern "C" { |
|
#endif |
|
|
|
/** |
|
* @name List of thread states |
|
* @{ |
|
*/ |
|
#define STATUS_NOT_FOUND (-1) /**< Describes an illegal thread status */ |
|
|
|
/** |
|
* @name Blocked states |
|
* @{ |
|
*/ |
|
#define STATUS_STOPPED 0 /**< has terminated */ |
|
#define STATUS_SLEEPING 1 /**< sleeping */ |
|
#define STATUS_MUTEX_BLOCKED 2 /**< waiting for a locked mutex */ |
|
#define STATUS_RECEIVE_BLOCKED 3 /**< waiting for a message */ |
|
#define STATUS_SEND_BLOCKED 4 /**< waiting for message to be delivered*/ |
|
#define STATUS_REPLY_BLOCKED 5 /**< waiting for a message response */ |
|
#define STATUS_FLAG_BLOCKED_ANY 6 /**< waiting for any flag from flag_mask*/ |
|
#define STATUS_FLAG_BLOCKED_ALL 7 /**< waiting for all flags in flag_mask */ |
|
#define STATUS_MBOX_BLOCKED 8 /**< waiting for get/put on mbox */ |
|
/** @} */ |
|
|
|
/** |
|
* @name Queued states |
|
* @{*/ |
|
#define STATUS_ON_RUNQUEUE STATUS_RUNNING /**< to check if on run queue: |
|
`st >= STATUS_ON_RUNQUEUE` */ |
|
#define STATUS_RUNNING 9 /**< currently running */ |
|
#define STATUS_PENDING 10 /**< waiting to be scheduled to run */ |
|
/** @} */ |
|
/** @} */ |
|
|
|
/** |
|
* @brief @c thread_t holds thread's context data. |
|
*/ |
|
struct _thread { |
|
char *sp; /**< thread's stack pointer */ |
|
uint8_t status; /**< thread's status */ |
|
uint8_t priority; /**< thread's priority */ |
|
|
|
kernel_pid_t pid; /**< thread's process id */ |
|
|
|
#ifdef MODULE_CORE_THREAD_FLAGS |
|
thread_flags_t flags; /**< currently set flags */ |
|
#endif |
|
|
|
clist_node_t rq_entry; /**< run queue entry */ |
|
|
|
#if defined(MODULE_CORE_MSG) || defined(MODULE_CORE_THREAD_FLAGS) \ |
|
|| defined(MODULE_CORE_MBOX) |
|
void *wait_data; /**< used by msg, mbox and thread |
|
flags */ |
|
#endif |
|
#if defined(MODULE_CORE_MSG) |
|
list_node_t msg_waiters; /**< threads waiting on message */ |
|
cib_t msg_queue; /**< message queue */ |
|
msg_t *msg_array; /**< memory holding messages */ |
|
#endif |
|
|
|
#if defined(DEVELHELP) || defined(SCHED_TEST_STACK) || defined(MODULE_MPU_STACK_GUARD) |
|
char *stack_start; /**< thread's stack start address */ |
|
#endif |
|
#ifdef DEVELHELP |
|
const char *name; /**< thread's name */ |
|
int stack_size; /**< thread's stack size */ |
|
#endif |
|
}; |
|
|
|
/** |
|
* @def THREAD_STACKSIZE_DEFAULT |
|
* @brief A reasonable default stack size that will suffice most smaller tasks |
|
* |
|
* @note This value must be defined by the CPU specific implementation, please |
|
* take a look at @c cpu/$CPU/include/cpu_conf.h |
|
*/ |
|
#ifndef THREAD_STACKSIZE_DEFAULT |
|
#error THREAD_STACKSIZE_DEFAULT must be defined per CPU |
|
#endif |
|
#ifdef DOXYGEN |
|
#define THREAD_STACKSIZE_DEFAULT |
|
#endif |
|
|
|
/** |
|
* @def THREAD_STACKSIZE_IDLE |
|
* @brief Size of the idle task's stack in bytes |
|
* |
|
* @note This value must be defined by the CPU specific implementation, please |
|
* take a look at @c cpu/$CPU/include/cpu_conf.h |
|
*/ |
|
#ifndef THREAD_STACKSIZE_IDLE |
|
#error THREAD_STACKSIZE_IDLE must be defined per CPU |
|
#endif |
|
#ifdef DOXYGEN |
|
#define THREAD_STACKSIZE_IDLE |
|
#endif |
|
|
|
/** |
|
* @def THREAD_EXTRA_STACKSIZE_PRINTF |
|
* @ingroup conf |
|
* @brief Size of the task's printf stack in bytes |
|
* |
|
* @note This value must be defined by the CPU specific implementation, please |
|
* take a look at @c cpu/$CPU/include/cpu_conf.h |
|
*/ |
|
#ifndef THREAD_EXTRA_STACKSIZE_PRINTF |
|
#error THREAD_EXTRA_STACKSIZE_PRINTF must be defined per CPU |
|
#endif |
|
#ifdef DOXYGEN |
|
#define THREAD_EXTRA_STACKSIZE_PRINTF |
|
#endif |
|
|
|
/** |
|
* @def THREAD_STACKSIZE_MAIN |
|
* @brief Size of the main task's stack in bytes |
|
*/ |
|
#ifndef THREAD_STACKSIZE_MAIN |
|
#define THREAD_STACKSIZE_MAIN (THREAD_STACKSIZE_DEFAULT + THREAD_EXTRA_STACKSIZE_PRINTF) |
|
#endif |
|
|
|
/** |
|
* @brief Minimum stack size |
|
*/ |
|
#ifndef THREAD_STACKSIZE_MINIMUM |
|
#define THREAD_STACKSIZE_MINIMUM (sizeof(thread_t)) |
|
#endif |
|
|
|
/** |
|
* @def THREAD_PRIORITY_MIN |
|
* @brief Least priority a thread can have |
|
*/ |
|
#define THREAD_PRIORITY_MIN (SCHED_PRIO_LEVELS-1) |
|
|
|
/** |
|
* @def THREAD_PRIORITY_IDLE |
|
* @brief Priority of the idle thread |
|
*/ |
|
#define THREAD_PRIORITY_IDLE (THREAD_PRIORITY_MIN) |
|
|
|
/** |
|
* @def THREAD_PRIORITY_MAIN |
|
* @brief Priority of the main thread |
|
*/ |
|
#define THREAD_PRIORITY_MAIN (THREAD_PRIORITY_MIN - (SCHED_PRIO_LEVELS/2)) |
|
|
|
/** |
|
* @name Optional flags for controlling a threads initial state |
|
* @{ |
|
*/ |
|
/** |
|
* @brief Set the new thread to sleeping. It must be woken up manually. |
|
**/ |
|
#define THREAD_CREATE_SLEEPING (1) |
|
|
|
/** |
|
* @brief Currently not implemented |
|
*/ |
|
#define THREAD_AUTO_FREE (2) |
|
|
|
/** |
|
* @brief Do not automatically call thread_yield() after creation: the newly |
|
* created thread might not run immediately. Purely for optimization. |
|
* Any other context switch (i.e. an interrupt) can still start the |
|
* thread at any time! |
|
*/ |
|
#define THREAD_CREATE_WOUT_YIELD (4) |
|
|
|
/** |
|
* @brief Write markers into the thread's stack to measure stack usage (for |
|
* debugging and profiling purposes) |
|
*/ |
|
#define THREAD_CREATE_STACKTEST (8) |
|
/** @} */ |
|
|
|
/** |
|
* @brief Creates a new thread. |
|
* |
|
* For an in-depth discussion of thread priorities, behavior and and flags, |
|
* see @ref core_thread. |
|
* |
|
* @note Avoid assigning the same priority to two or more threads. |
|
* @note Creating threads from within an ISR is currently supported, however it |
|
* is considered to be a bad programming practice and we strongly |
|
* discourage you from doing so. |
|
* |
|
* @param[out] stack start address of the preallocated stack memory |
|
* @param[in] stacksize the size of the thread's stack in bytes |
|
* @param[in] priority priority of the new thread, lower mean higher priority |
|
* @param[in] flags optional flags for the creation of the new thread |
|
* @param[in] task_func pointer to the code that is executed in the new thread |
|
* @param[in] arg the argument to the function |
|
* @param[in] name a human readable descriptor for the thread |
|
* |
|
* @return PID of newly created task on success |
|
* @return -EINVAL, if @p priority is greater than or equal to |
|
* @ref SCHED_PRIO_LEVELS |
|
* @return -EOVERFLOW, if there are too many threads running already |
|
*/ |
|
kernel_pid_t thread_create(char *stack, |
|
int stacksize, |
|
char priority, |
|
int flags, |
|
thread_task_func_t task_func, |
|
void *arg, |
|
const char *name); |
|
|
|
/** |
|
* @brief Retreive a thread control block by PID. |
|
* @details This is a bound-checked variant of accessing `sched_threads[pid]` directly. |
|
* If you know that the PID is valid, then don't use this function. |
|
* @param[in] pid Thread to retreive. |
|
* @return `NULL` if the PID is invalid or there is no such thread. |
|
*/ |
|
volatile thread_t *thread_get(kernel_pid_t pid); |
|
|
|
/** |
|
* @brief Returns the status of a process |
|
* |
|
* @param[in] pid the PID of the thread to get the status from |
|
* |
|
* @return status of the thread |
|
* @return `STATUS_NOT_FOUND` if pid is unknown |
|
*/ |
|
int thread_getstatus(kernel_pid_t pid); |
|
|
|
/** |
|
* @brief Puts the current thread into sleep mode. Has to be woken up externally. |
|
*/ |
|
void thread_sleep(void); |
|
|
|
/** |
|
* @brief Lets current thread yield. |
|
* |
|
* @details The current thread will resume operation immediately, |
|
* if there is no other ready thread with the same or a higher priority. |
|
* |
|
* Differently from thread_yield_higher() the current thread will be put to the |
|
* end of the thread's in its priority class. |
|
* |
|
* @see thread_yield_higher() |
|
*/ |
|
void thread_yield(void); |
|
|
|
/** |
|
* @brief Lets current thread yield in favor of a higher prioritized thread. |
|
* |
|
* @details The current thread will resume operation immediately, |
|
* if there is no other ready thread with a higher priority. |
|
* |
|
* Differently from thread_yield() the current thread will be scheduled next |
|
* in its own priority class, i.e. it stays the first thread in its |
|
* priority class. |
|
* |
|
* @see thread_yield() |
|
*/ |
|
void thread_yield_higher(void); |
|
|
|
/** |
|
* @brief Wakes up a sleeping thread. |
|
* |
|
* @param[in] pid the PID of the thread to be woken up |
|
* |
|
* @return `1` on success |
|
* @return `STATUS_NOT_FOUND` if pid is unknown or not sleeping |
|
*/ |
|
int thread_wakeup(kernel_pid_t pid); |
|
|
|
/** |
|
* @brief Returns the process ID of the currently running thread |
|
* |
|
* @return obviously you are not a golfer. |
|
*/ |
|
static inline kernel_pid_t thread_getpid(void) |
|
{ |
|
extern volatile kernel_pid_t sched_active_pid; |
|
return sched_active_pid; |
|
} |
|
|
|
/** |
|
* @brief Gets called upon thread creation to set CPU registers |
|
* |
|
* @param[in] task_func First function to call within the thread |
|
* @param[in] arg Argument to supply to task_func |
|
* @param[in] stack_start Start address of the stack |
|
* @param[in] stack_size Stack size |
|
* |
|
* @return stack pointer |
|
*/ |
|
char *thread_stack_init(thread_task_func_t task_func, void *arg, void *stack_start, int stack_size); |
|
|
|
/** |
|
* @brief Add thread to list, sorted by priority (internal) |
|
* |
|
* This will add @p thread to @p list sorted by the thread priority. |
|
* It reuses the thread's rq_entry field. |
|
* Used internally by msg and mutex implementations. |
|
* |
|
* @note Only use for threads *not on any runqueue* and with interrupts |
|
* disabled. |
|
* |
|
* @param[in] list ptr to list root node |
|
* @param[in] thread thread to add |
|
*/ |
|
void thread_add_to_list(list_node_t *list, thread_t *thread); |
|
|
|
#ifdef DEVELHELP |
|
/** |
|
* @brief Returns the name of a process |
|
* |
|
* @param[in] pid the PID of the thread to get the name from |
|
* |
|
* @return the threads name |
|
* @return `NULL` if pid is unknown |
|
*/ |
|
const char *thread_getname(kernel_pid_t pid); |
|
|
|
/** |
|
* @brief Measures the stack usage of a stack |
|
* |
|
* Only works if the thread was created with the flag THREAD_CREATE_STACKTEST. |
|
* |
|
* @param[in] stack the stack you want to measure. try `sched_active_thread->stack_start` |
|
* |
|
* @return the amount of unused space of the thread's stack |
|
*/ |
|
uintptr_t thread_measure_stack_free(char *stack); |
|
#endif /* DEVELHELP */ |
|
|
|
/** |
|
* @brief Prints human readable, ps-like thread information for debugging purposes |
|
*/ |
|
void thread_print_stack(void); |
|
|
|
#ifdef __cplusplus |
|
} |
|
#endif |
|
|
|
/** @} */ |
|
#endif /* THREAD_H */
|
|
|