rt/include/rt/task.h

197 lines
6.5 KiB
C

#ifndef RT_TASK_H
#define RT_TASK_H
#include <rt/assert.h>
#include <rt/context.h>
#include <rt/cycle.h>
#include <rt/list.h>
#include <rt/mpu.h>
#include <rt/stack.h>
#include <rt/syscall.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#ifndef RT_TASK_READY_CLZ_ENABLE
#if ((defined(__arm__) || defined(__aarch64__)) && \
defined(__ARM_FEATURE_CLZ)) || \
defined(__x86_64__)
#define RT_TASK_READY_CLZ_ENABLE 1
#else
#define RT_TASK_READY_CLZ_ENABLE 0
#endif
#endif
#if RT_TASK_READY_CLZ_ENABLE
#define RT_TASK_MAX_PRIORITY ((sizeof(unsigned) * CHAR_BIT) - 1)
#else
#define RT_TASK_MAX_PRIORITY (UINT_MAX - 1)
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifndef RT_TASK_CYCLE_ENABLE
#define RT_TASK_CYCLE_ENABLE 0
#endif
#if RT_TASK_CYCLE_ENABLE && !RT_CYCLE_ENABLE
#error "To use task cycle counts, the cycle counter must be enabled."
#endif
/*
* Yield the core to another task of the same priority. If the current task is
* still the highest priority, it will continue executing.
*/
void rt_task_yield(void);
/*
* Sleep the current task for a given number of ticks.
*/
void rt_task_sleep(unsigned long ticks);
/*
* Sleep the current task until *last_wake_tick + period.
* *last_wake_tick will be set to the next wakeup tick.
*/
void rt_task_sleep_periodic(unsigned long *last_wake_tick,
unsigned long period);
/*
* Exit from the current task. This will be called automatically when a task
* function returns.
*/
__attribute__((noreturn)) void rt_task_exit(void);
/*
* Get the name of the current task.
*/
const char *rt_task_name(void);
/*
* Get the current task.
*/
struct rt_task *rt_task_self(void);
/*
* On architectures that have privilege levels, make the current task
* unprivileged.
*/
void rt_task_drop_privilege(void);
enum rt_task_state
{
RT_TASK_STATE_INIT,
RT_TASK_STATE_RUNNING,
RT_TASK_STATE_READY,
RT_TASK_STATE_BLOCKED,
RT_TASK_STATE_BLOCKED_TIMEOUT,
RT_TASK_STATE_ASLEEP,
RT_TASK_STATE_EXITED,
};
struct rt_task
{
struct rt_list list;
struct rt_list sleep_list;
void *ctx;
struct rt_list *list_head;
unsigned priority;
unsigned base_priority;
enum rt_task_state state;
unsigned long wake_tick;
#if RT_MPU_ENABLE
struct rt_mpu_config mpu_config;
#endif
struct rt_mutex *blocking_mutex;
union
{
struct rt_sem **sem;
struct rt_mutex **mutex;
} timeout_ptr;
struct rt_list mutex_list;
#if RT_TASK_CYCLE_ENABLE
uint64_t total_cycles;
uint32_t start_cycle;
#endif
const char *name;
};
/*
* Add a task to the ready list. This function may only be called in the system
* call handler or before rt_start.
*/
void rt_task_ready(struct rt_task *task);
#define RT_TASK_INIT(name_, name_str, priority_, ...) \
{ \
.list = RT_LIST_INIT(name_.list), \
.sleep_list = RT_LIST_INIT(name_.sleep_list), .priority = (priority_), \
.base_priority = (priority_), .state = RT_TASK_STATE_INIT, \
.mutex_list = RT_LIST_INIT(name_.mutex_list), .name = (name_str), \
}
#define RT_CAT_(a, b) a##b
#define RT_CAT(a, b) RT_CAT_(a, b)
#define RT_TASK_CONSTRUCTOR_PRIORITY 1000
/* Use a constructor with a priority based on __COUNTER__ so that tasks created
* in the same file with equal priority are added to the ready list in the
* order they appear in the file. Use % 60000 to ensure that the overall
* constructor priority does not wrap around at 2^16 and is always greater than
* the priority of the list initialization constructor. */
#define RT_TASK_COMMON(fn, stack_size, priority, name, ctx_init, ...) \
RT_TASK_COMMON_(fn, stack_size, priority, name, ctx_init, __COUNTER__, \
__VA_ARGS__)
#define RT_TASK_COMMON_(fn, stack_size, priority, name, ctx_init, counter, \
...) \
__attribute__((constructor(RT_TASK_CONSTRUCTOR_PRIORITY + \
(counter % 60000)))) static void \
RT_CAT(fn##_task_init_, counter)(void) \
{ \
static RT_STACK(fn##_task_stack, stack_size); \
RT_MPU_PRIV_DATA(fn##_task) \
static struct rt_task fn##_task = \
RT_TASK_INIT(fn##_task, name, priority); \
RT_MPU_CONFIG_INIT(&fn##_task.mpu_config, \
RT_MPU_REGION(fn##_task_stack, \
sizeof fn##_task_stack, \
RT_MPU_STACK_ATTR), \
__VA_ARGS__); \
fn##_task.ctx = ctx_init; \
rt_task_ready(&fn##_task); \
} \
rt_static_assert((intmax_t)(priority) <= (intmax_t)RT_TASK_MAX_PRIORITY, \
"the priority of task \"" name "\", " #priority \
", is higher than the maximum allowed")
/*
* Statically create a task that runs fn on a stack of at least stack_size,
* with the given priority. To create new tasks after rt is running, use
* rt_task_init*.
*/
#define RT_TASK(fn, stack_size, priority, ...) \
RT_TASK_COMMON(fn, stack_size, priority, #fn, \
rt_context_init((fn), fn##_task_stack, \
sizeof fn##_task_stack), \
__VA_ARGS__)
#define RT_TASK_ARG(fn, arg, stack_size, priority, ...) \
RT_TASK_COMMON(fn, stack_size, priority, #fn "(" #arg ")", \
rt_context_init_arg((fn), (arg), fn##_task_stack, \
sizeof fn##_task_stack), \
__VA_ARGS__)
#ifdef __cplusplus
}
#endif
#endif /* RT_TASK_H */