rt 0.17.0

A real-time operating system capable of full preemption
Documentation
#include <rt/timer.h>

#include <rt/container.h>
#include <rt/queue.h>
#include <rt/task.h>
#include <rt/tick.h>

static void timer_command_push(struct rt_timer *timer, enum rt_timer_op op,
                               unsigned long period)
{
    const struct rt_timer_command timer_cmd = {
        .timer = timer,
        .period = period,
        .op = op,
    };
    rt_queue_push(timer->queue, &timer_cmd);
}

static bool timer_command_trypush(struct rt_timer *timer, enum rt_timer_op op,
                                  unsigned long period)
{
    const struct rt_timer_command timer_cmd = {
        .timer = timer,
        .period = period,
        .op = op,
    };
    return rt_queue_trypush(timer->queue, &timer_cmd);
}

static bool timer_command_timedpush(struct rt_timer *timer, enum rt_timer_op op,
                                    unsigned long period,
                                    unsigned long block_ticks)
{
    const struct rt_timer_command timer_cmd = {
        .timer = timer,
        .period = period,
        .op = op,
    };
    return rt_queue_timedpush(timer->queue, &timer_cmd, block_ticks);
}

void rt_timer_start(struct rt_timer *timer)
{
    timer_command_push(timer, RT_TIMER_OP_START, 0);
}

bool rt_timer_trystart(struct rt_timer *timer)
{
    return timer_command_trypush(timer, RT_TIMER_OP_START, 0);
}

bool rt_timer_timedstart(struct rt_timer *timer, unsigned long block_ticks)
{
    return timer_command_timedpush(timer, RT_TIMER_OP_START, 0, block_ticks);
}

void rt_timer_stop(struct rt_timer *timer)
{
    timer_command_push(timer, RT_TIMER_OP_STOP, 0);
}

bool rt_timer_trystop(struct rt_timer *timer)
{
    return timer_command_trypush(timer, RT_TIMER_OP_STOP, 0);
}

bool rt_timer_timedstop(struct rt_timer *timer, unsigned long block_ticks)
{
    return timer_command_timedpush(timer, RT_TIMER_OP_STOP, 0, block_ticks);
}

void rt_timer_change_period(struct rt_timer *timer, unsigned long period)
{
    timer_command_push(timer, RT_TIMER_OP_CHANGE_PERIOD, period);
}

bool rt_timer_trychange_period(struct rt_timer *timer, unsigned long period)
{
    return timer_command_trypush(timer, RT_TIMER_OP_CHANGE_PERIOD, period);
}

bool rt_timer_timedchange_period(struct rt_timer *timer, unsigned long period,
                                 unsigned long block_ticks)
{
    return timer_command_timedpush(timer, RT_TIMER_OP_CHANGE_PERIOD, period,
                                   block_ticks);
}

static inline struct rt_timer *timer_from_list(const struct rt_list *l)
{
    return rt_container_of(l, struct rt_timer, list);
}

static inline void timer_call(const struct rt_timer *timer)
{
    if (timer->has_arg)
    {
        timer->fn.with_arg(timer->arg);
    }
    else
    {
        timer->fn.no_arg();
    }
}

static void timer_start(struct rt_list *list, struct rt_timer *timer,
                        unsigned long base_tick)
{
    struct rt_list *next;
    for (next = rt_list_front(list); next != list; next = next->next)
    {
        if ((timer->tick - base_tick) <
            (timer_from_list(next)->tick - base_tick))
        {
            break;
        }
    }
    rt_list_push_back(next, &timer->list);
}

static inline void timer_stop(struct rt_timer *timer)
{
    rt_list_remove(&timer->list);
}

void rt_timer_daemon(uintptr_t arg)
{
    rt_task_drop_privilege();
    struct rt_queue *const queue = (struct rt_queue *)arg;
    RT_LIST(timer_list);
    unsigned long expired_tick = 0;
    for (;;)
    {
        const unsigned long ticks_to_advance = rt_tick_count() - expired_tick;
        /* A million ticks isn't cool... You know what's cool?
         * This timeout will be used to wait for commands when there are no
         * timers pending. This ensures that expired_tick is periodically
         * updated so that when a timer does start, its expiration order will
         * be calculated relative to a tick that is within a bounded number of
         * real-world ticks to the current tick, and that this number did not
         * overflow. */
        unsigned long command_timeout = 1000000000UL;
        while (!rt_list_is_empty(&timer_list))
        {
            struct rt_timer *const timer =
                timer_from_list(rt_list_front(&timer_list));
            unsigned long ticks_to_next_expiry = timer->tick - expired_tick;
            if (ticks_to_next_expiry > ticks_to_advance)
            {
                /* The earlist timer trigger hasn't occurred yet. Calculate how
                 * many more ticks we need to wait for that to happen, so we
                 * can wait for commands with that timeout. */
                ticks_to_next_expiry -= ticks_to_advance;
                if (command_timeout > ticks_to_next_expiry)
                {
                    command_timeout = ticks_to_next_expiry;
                }
                break;
            }
            timer_call(timer);
            timer_stop(timer);
            if (timer->periodic)
            {
                timer->tick += timer->period;
                timer_start(&timer_list, timer, expired_tick);
            }
        }
        expired_tick += ticks_to_advance;
        struct rt_timer_command cmd;
        if (rt_queue_timedpop(queue, &cmd, command_timeout))
        {
            switch (cmd.op)
            {
            case RT_TIMER_OP_START:
                cmd.timer->tick = rt_tick_count() + cmd.timer->period;
                timer_stop(cmd.timer);
                timer_start(&timer_list, cmd.timer, expired_tick);
                break;
            case RT_TIMER_OP_STOP:
                timer_stop(cmd.timer);
                break;
            case RT_TIMER_OP_CHANGE_PERIOD:
                cmd.timer->period = cmd.period;
                break;
            }
        }
    }
}