-
Notifications
You must be signed in to change notification settings - Fork 2.1k
core: add thread flags #4103
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
core: add thread flags #4103
Conversation
Wow, do you remember us celebrating when we could minimize tcb size by a couple of bytes a year ago or so? I have to admit that I haven't understood the whole idea of this PR, but trading more RAM against improved runtime seems a to be a bad compromise on IoT devices. |
@kaspar030 Ping, a kind reminder for rebase. |
2aac8a0
to
aa83865
Compare
|
A common use case is what we're usually using mutexes for: wait for an event that might happen in an isr, eg., arrival of a network packet or a byte on a serial line. Currently, a thread usually waits in a loop: mutex_lock();
while (1) {
mutex_lock());
/* handle event */
mutex_unlock();
} and an ISR would unlock the mutex whenever an event occurs. You can find that kind of logic (sometimes using messages) all over the place. While it kinda works, it has several disadvantages:
The main concept behind this PR is that events can be atomically flagged and waited for in any combination: while(1) {
flag = thread_flag_wait_any(); /* waits for flag */
switch(flag) {
case FLAG_MSG:
msg_receive(&msg);
case FLAG_TAPDEV_ISR:
handle_isr();
}
} As is, no flags for messages or mutexes are generated, but that's easy to add.
Of course! ;) I will try to move the needed struct out of tcb_t. I had that in an early version, but it was less efficient... |
* | ||
* @return The value of *var* before the operation. | ||
*/ | ||
static inline int atomic_or(atomic_int_t *var, int val) |
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.
Maybe make this file a PR on its own, and also add xor
and not
to complete the family?
Thanks for the explanation. |
aa83865
to
33ac75f
Compare
I've changed the implementation to use tcb->wait_data for the wait mask, and also changed the flags type to uint16_t. That way, on 32bit platforms, sizeof(tcb_t) doesn't get bigger due to alignment luck, and gains 2 byte on other platforms. Also, I've changed core/msg.c to set some flags and xtimer_msg_receive_timeout() to make use of them. |
Nice! |
@OlegHahm, would you like to review this? Should I split the the msg changes? |
char *sp; /**< thread's stack pointer */ | ||
uint16_t status; /**< thread's status */ | ||
|
||
kernel_pid_t pid; /**< thread's process id */ | ||
uint16_t priority; /**< thread's priority */ | ||
|
||
thread_flags_t flags; /**< currently set flags */ |
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.
Couldn't this be optional, too, if we have a pseudomodule anyway?
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.
It could. But I've also changed msg.c to use thread flags internally, which makes some things a lot nicer.
e.g., currently in xtimer_receive_msg_timeout, there's a race between receiving the message and deleting the timer, and if the thread has a message queue, that might lead to the timer message to be queued right after the actual received message.
This PR's xtimer_receive_msg_timeout() implementation uses a thread flag to pass the timeout information, solving the race problem.
The pseudomodule basically only disables the thread flags user API.
edit sorry, the messaging changes are unrelated to the xtimer changes.
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.
Ok, I see. I guess it makes sense.
I can give it a try.
Would make it easier to review. |
9283d7f
to
450fe77
Compare
|
@@ -1,7 +1,7 @@ | |||
/* | |||
* Copyright (C) 2013 Freie Universität Berlin | |||
* | |||
* This file is subject to the terms and conditions of the GNU Lesser | |||
* This file is subject to the terms and thread_conditions of the GNU Lesser |
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.
ups
let me rebase and have another look. IMHO it should be ready. |
666a0c0
to
bde6f47
Compare
@haukepetersen IMHO ready to merge. |
thread_flags_t thread_flags_wait_all(thread_flags_t mask); | ||
|
||
/** | ||
* @brief Wait for any flags in mask to become set (blocking), one at a time |
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.
most or least significant first? Please document
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.
addressed
Code looks good and works as expected (at least the test application) ACK when addressed, rebased and squashed. |
Needs a rebase after #4923 got merged. |
22ca025
to
6d15f62
Compare
6d15f62
to
37bbdac
Compare
|
Dammit, removed the "waiting for pr" label too late... |
@cgundogan would you take another look? |
@cgundogan Ah, not needed, there are already two ACKs. ;) |
if you don't want my personal ACK (; |
Don't want to overuse your ACKs, I know reviewing is hard! :) |
{ | ||
unsigned state = irq_disable(); | ||
mask &= thread->flags; | ||
thread->flags &= ~mask; |
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 am not 100% sure, but I think
mask &= thread->flags;
thread->flags &= ~mask;
can be reduced to thread->flags &= ~mask;
only?
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.
No, as the function needs to return the actually cleared flags.
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.
dammit, I didn't look that far..
Murdock is fine -> go. |
_thread_flags_wait_any(mask); | ||
thread_t *me = (thread_t*) sched_active_thread; | ||
unsigned tmp = me->flags & mask; | ||
return _thread_flags_clear_atomic(me, thread_flags_clear(1 << bitarithm_lsb(tmp))); |
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.
If you are trying to unset all bits, but not the least significant one, then you may use tmp &= -tmp
, or tmp &= ~tmp + 1
(equivalent) instead of 1 << bitarithm_lsb(tmp)
.
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.
Well, I'm trying to unset only the least signigicant bit. :)
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.
1 << bitarithm_lsb(tmp)
isn't this the same as unsetting all bits but not the lsb?
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.
Ah yes.
>>> a = 0x1110; a &= -a; print(bin(a))
0b10000
>>>
Is this due to pythons signed bit handling?
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.
yes, this only works if the number is unsigned. The number gets negated and 1 is added to the result for negative unsigned numbers .. AFAIK
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.
See #5200. Saves a whopping 32 bytes of code. ;)
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.
(16byte on 32bit ARM).
This PR adds functionality to set multiple (32) "flags" per thread, and also wait for them in any combination, efficiently.
While adding 8 bytes to each TCB, this method allows faster conditional sleep than mutexes and is way more flexible. It is possible to implement mutex_wait and msg_wait with these flags, making it easy to wait for, e.g., a button to be pressed, a mutex to become free and a timer to be shot, at the same time.
This is WIP, but as people are asking (#4102), I decided to share.