kernel/timeout: fix handling expired timeouts in reverve queuing order

Queuing in the timeout_q of timeouts expiring on the same tick queue
them in reverse order: as soon as the new timeout finds a timeout
expiring on the same tick or later, it get prepended to that timeout:
this allows exiting the traversal of the timeout as soon as possible,
which is done with interrupts locked, thus reducing interrupt latency.
However, this has the side-effect of handling the timeouts expiring on
the same tick in the reverse order that they are queued.

For example:

    thread_c, prio 4:

        uint32_t uptime = k_uptime_get_32();

        while(uptime == k_uptime_get_32()); /* align on tick */

        k_timer_start(&timer_a, 5, 0);
        k_timer_start(&timer_b, 5, 0);

    thread_a, prio 5:

        k_timer_status_sync(&timer_a);
        printk("thread_a got timer_a\n");

    thread_b, prio 5:

        k_timer_status_sync(&timer_b);
        printk("thread_b got timer_b\n");

One could "reasonably" expect thread_a to run first, since both threads
have the same prio, and timer_a was started before timer_b, thus
inserted first in the timeout_q first (time-wise). However, thread_b
will run before thread_a, since timer_b's timeout is prepended to
timer_a's.

This patch keeps the reversing of the order when adding timeouts in the
timeout_q, thus preserving the same interrupt latency; however, when
dequeuing them and adding them to the expired queue, we now reverse that
order _again_, causing the timeouts to be handled in the expected order.

Change-Id: Id83045f63e2be88809d6089b8ae62034e4e3facb
Signed-off-by: Benjamin Walsh <walsh.benj@gmail.com>
This commit is contained in:
Benjamin Walsh 2017-02-15 20:20:06 -05:00 committed by Anas Nashif
commit 6f4bc80901

View file

@ -226,7 +226,18 @@ static inline void handle_timeouts(int32_t ticks)
while (timeout && timeout->delta_ticks_from_prev == 0) {
sys_dlist_remove(next);
sys_dlist_append(&expired, next);
/*
* Reverse the order that that were queued in the timeout_q:
* timeouts expiring on the same ticks are queued in the
* reverse order, time-wise, that they are added to shorten the
* amount of time with interrupts locked while walking the
* timeout_q. By reversing the order _again_ when building the
* expired queue, they end up being processed in the same order
* they were added, time-wise.
*/
sys_dlist_prepend(&expired, next);
timeout->delta_ticks_from_prev = _EXPIRED;
irq_unlock(key);