Commit 8375fb76 authored by Holman Greenhand's avatar Holman Greenhand Committed by Anas Nashif
Browse files

kernel: Allow late processing of timeouts



This change proposes to handle the case where the handle_timeouts
function is called after a number of ticks greater than the first
timeout delta of the _timeout_q list. In the current implementation if
the case occurs, after subtracting the number of ticks the
delta_ticks_from_prev field becomes negative and the first timeout is
never processed. It is therefore necessary to treat this case and to
prevent delta_ticks_from_prev from becoming negative. Moreover, the lag
produced by the initial delay must also be applied to following timeouts
by browsing the list until it was entirely consumed.

Fixes #5401

Signed-off-by: default avatarHolman Greenhand <greenhandholman@gmail.com>
parent 4b69e612
Loading
Loading
Loading
Loading
+44 −21
Original line number Diff line number Diff line
@@ -179,19 +179,17 @@ static inline void handle_timeouts(s32_t ticks)

	key = irq_lock();

	struct _timeout *head =
		(struct _timeout *)sys_dlist_peek_head(&_timeout_q);
	sys_dnode_t *next = sys_dlist_peek_head(&_timeout_q);
	struct _timeout *timeout = (struct _timeout *)next;

	K_DEBUG("head: %p, delta: %d\n",
		head, head ? head->delta_ticks_from_prev : -2112);
		timeout, timeout ? timeout->delta_ticks_from_prev : -2112);

	if (!head) {
	if (!next) {
		irq_unlock(key);
		return;
	}

	head->delta_ticks_from_prev -= ticks;

	/*
	 * Dequeue all expired timeouts from _timeout_q, relieving irq lock
	 * pressure between each of them, allowing handling of higher priority
@@ -199,32 +197,57 @@ static inline void handle_timeouts(s32_t ticks)
	 * of a timeout which delta is 0, since timeouts of 0 ticks are
	 * prohibited.
	 */
	sys_dnode_t *next = &head->node;
	struct _timeout *timeout = (struct _timeout *)next;

	_handling_timeouts = 1;

	while (timeout && timeout->delta_ticks_from_prev == 0) {
	while (next) {

		/*
		 * In the case where ticks number is greater than the first
		 * timeout delta of the list, the lag produced by this initial
		 * difference must also be applied to others timeouts in list
		 * until it was entirely consumed.
		 */

		sys_dlist_remove(next);
		s32_t tmp = timeout->delta_ticks_from_prev;

		if (timeout->delta_ticks_from_prev < ticks) {
			timeout->delta_ticks_from_prev = 0;
		} else {
			timeout->delta_ticks_from_prev -= ticks;
		}

		ticks -= tmp;

		next = sys_dlist_peek_next(&_timeout_q, next);

		if (timeout->delta_ticks_from_prev == 0) {
			sys_dnode_t *node = &timeout->node;

			sys_dlist_remove(node);

			/*
		 * 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.
			 * 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);

			sys_dlist_prepend(&expired, node);

			timeout->delta_ticks_from_prev = _EXPIRED;

		} else if (ticks <= 0) {
			break;
		}

		irq_unlock(key);
		key = irq_lock();

		next = sys_dlist_peek_head(&_timeout_q);
		timeout = (struct _timeout *)next;
	}