-
Notifications
You must be signed in to change notification settings - Fork 8.2k
extend dlist to detect unlinked nodes and avoid double-remove bugs #12248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ee84022
ecea4d9
123b697
ba1f4a2
677f9da
f364e2a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -133,20 +133,22 @@ static inline int register_event(struct k_poll_event *event, | |
| /* must be called with interrupts locked */ | ||
| static inline void clear_event_registration(struct k_poll_event *event) | ||
| { | ||
| bool remove = false; | ||
|
|
||
| event->poller = NULL; | ||
|
|
||
| switch (event->type) { | ||
| case K_POLL_TYPE_SEM_AVAILABLE: | ||
| __ASSERT(event->sem != NULL, "invalid semaphore\n"); | ||
| sys_dlist_remove(&event->_node); | ||
| remove = true; | ||
| break; | ||
| case K_POLL_TYPE_DATA_AVAILABLE: | ||
| __ASSERT(event->queue != NULL, "invalid queue\n"); | ||
| sys_dlist_remove(&event->_node); | ||
| remove = true; | ||
| break; | ||
| case K_POLL_TYPE_SIGNAL: | ||
| __ASSERT(event->signal != NULL, "invalid poll signal\n"); | ||
| sys_dlist_remove(&event->_node); | ||
| remove = true; | ||
| break; | ||
| case K_POLL_TYPE_IGNORE: | ||
| /* nothing to do */ | ||
|
|
@@ -155,6 +157,9 @@ static inline void clear_event_registration(struct k_poll_event *event) | |
| __ASSERT(false, "invalid event type\n"); | ||
| break; | ||
| } | ||
| if (remove && sys_dnode_is_linked(&event->_node)) { | ||
| sys_dlist_remove(&event->_node); | ||
| } | ||
|
||
| } | ||
|
|
||
| /* must be called with interrupts locked */ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -591,9 +591,13 @@ void _priq_dumb_remove(sys_dlist_t *pq, struct k_thread *thread) | |
|
|
||
| struct k_thread *_priq_dumb_best(sys_dlist_t *pq) | ||
| { | ||
| struct k_thread *t = NULL; | ||
|
||
| sys_dnode_t *n = sys_dlist_peek_head(pq); | ||
|
|
||
| return CONTAINER_OF(n, struct k_thread, base.qnode_dlist); | ||
| if (n != NULL) { | ||
| t = CONTAINER_OF(n, struct k_thread, base.qnode_dlist); | ||
| } | ||
| return t; | ||
| } | ||
|
|
||
| bool _priq_rb_lessthan(struct rbnode *a, struct rbnode *b) | ||
|
|
@@ -648,9 +652,13 @@ void _priq_rb_remove(struct _priq_rb *pq, struct k_thread *thread) | |
|
|
||
| struct k_thread *_priq_rb_best(struct _priq_rb *pq) | ||
| { | ||
| struct k_thread *t = NULL; | ||
| struct rbnode *n = rb_get_min(&pq->tree); | ||
|
|
||
| return CONTAINER_OF(n, struct k_thread, base.qnode_rb); | ||
| if (n != NULL) { | ||
| t = CONTAINER_OF(n, struct k_thread, base.qnode_rb); | ||
| } | ||
| return t; | ||
| } | ||
|
|
||
| #ifdef CONFIG_SCHED_MULTIQ | ||
|
|
@@ -683,10 +691,14 @@ struct k_thread *_priq_mq_best(struct _priq_mq *pq) | |
| return NULL; | ||
| } | ||
|
|
||
| struct k_thread *t = NULL; | ||
| sys_dlist_t *l = &pq->queues[__builtin_ctz(pq->bitmask)]; | ||
| sys_dnode_t *n = sys_dlist_peek_head(l); | ||
|
|
||
| return CONTAINER_OF(n, struct k_thread, base.qnode_dlist); | ||
| if (n != NULL) { | ||
| t = CONTAINER_OF(n, struct k_thread, base.qnode_dlist); | ||
| } | ||
| return t; | ||
| } | ||
|
|
||
| int _unpend_all(_wait_q_t *wait_q) | ||
|
|
@@ -884,7 +896,7 @@ void _impl_k_wakeup(k_tid_t thread) | |
| return; | ||
| } | ||
|
|
||
| if (_abort_thread_timeout(thread) == _INACTIVE) { | ||
| if (_abort_thread_timeout(thread) < 0) { | ||
| irq_unlock(key); | ||
| return; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,15 +46,11 @@ static struct _timeout *next(struct _timeout *t) | |
|
|
||
| static void remove_timeout(struct _timeout *t) | ||
| { | ||
| if (t->node.next != NULL && t->node.prev != NULL) { | ||
| if (next(t) != NULL) { | ||
| next(t)->dticks += t->dticks; | ||
| } | ||
|
|
||
| sys_dlist_remove(&t->node); | ||
| if (next(t) != NULL) { | ||
| next(t)->dticks += t->dticks; | ||
| } | ||
| t->node.next = t->node.prev = NULL; | ||
| t->dticks = _INACTIVE; | ||
|
|
||
| sys_dlist_remove(&t->node); | ||
| } | ||
|
|
||
| static s32_t elapsed(void) | ||
|
|
@@ -64,7 +60,7 @@ static s32_t elapsed(void) | |
|
|
||
| void _add_timeout(struct _timeout *to, _timeout_func_t fn, s32_t ticks) | ||
| { | ||
| __ASSERT(to->dticks < 0, ""); | ||
| __ASSERT(!sys_dnode_is_linked(&to->node), ""); | ||
| to->fn = fn; | ||
| ticks = max(1, ticks); | ||
|
|
||
|
|
@@ -96,10 +92,10 @@ void _add_timeout(struct _timeout *to, _timeout_func_t fn, s32_t ticks) | |
|
|
||
| int _abort_timeout(struct _timeout *to) | ||
| { | ||
| int ret = _INACTIVE; | ||
| int ret = -EINVAL; | ||
|
||
|
|
||
| LOCKED(&timeout_lock) { | ||
| if (to->dticks != _INACTIVE) { | ||
| if (sys_dnode_is_linked(&to->node)) { | ||
| remove_timeout(to); | ||
| ret = 0; | ||
| } | ||
|
|
@@ -112,7 +108,7 @@ s32_t z_timeout_remaining(struct _timeout *timeout) | |
| { | ||
| s32_t ticks = 0; | ||
|
|
||
| if (timeout->dticks == _INACTIVE) { | ||
| if (_is_inactive_timeout(timeout)) { | ||
| return 0; | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not particularly relevant, but this was always a bad API (a true return indicates a false state) that never got fixed up during all the refactoring. I wouldn't cry to see this patch change it to z_timeout_active() or whatever.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was partly because of the legacy of
_EXPIRED: timeouts could be in three states, not two. This function tests that the state is exactly what_INACTIVEindicated.A true result indicates that the predicate identified by the function name is true. Why is that a bad API? We use
sys_dlist_is_empty(), notsys_dlist_is_nonempty().The whole timeout API uses prefix underscore. While I'd rather that wasn't the case (violates C reserved identifiers for the non-static functions) I wouldn't want to change just one function, nor change all of them, in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that not incorrect, exactly, but it's not innapropriate to argue against the ellision of unommitted negatives either.
No seriously, double negatives in API choices are bad, especially in predicates. The base adjective for that state is "active". True should mean "active", false should mean "inactive".