[PoC] core: remove "volatile" from scheduler variables.#11073
[PoC] core: remove "volatile" from scheduler variables.#11073jcarrano wants to merge 3 commits intoRIOT-OS:masterfrom
Conversation
There is no reason for these two variables to be volatile: * In application (thread) context, the "active_thread" never changes- if thread X is reading sched_active_pid, the value is always X whenever thread X is active. * In kernel context, it is the kernel that changes the value, so again, it needs not be volatile. With regards to the contents of the thread structure itself, sometimes it is important that it is re-read (for example in msg_* functions). Here, however, there are calls to extern-defined functions to yield, etc. Since the compiler cannot know if these functions alter global memory, it is forced to emit code re-read these variables, so again volatile is unncesessary (maybe if using LTO it can "smart up" and do something stupid.) If later it turns out that an explicit barrier is necessary, it can be added. For more information on this discussion, see this thread: * https://lists.riot-os.org/pipermail/devel/2019-January/006077.html And for more info on barriers vs volatile, see: * https://www.kernel.org/doc/html/v4.17/process/volatile-considered-harmful.html * https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html * https://yarchive.net/comp/linux/memory_barriers.html
sched_context_switch_request ---------------------------- sched_context_switch_request is only set and read from interrupt context, as a way to signal the kernel that after the handler has run it has to switch threads. Because the interrupt context is essentially single threaded, then all the regular guarantees of single-theaded C apply. In architectures with interrupt preemption this may not be true, but since the setting and querying the request flag happens in separate functions, it is always reread. It could still be possible that the setting and querying happen in inline functions and that could lead the compiler to think that no modification of this value is possible and optimize the read out. It is convenient, therefore, to insert a compiler barrier, that is cheap (or free) and solves the problem perfectly. sched_threads ------------- When running in the context of a thread, this array has all volatile elements, except for the one referring to the current thread. For obvious reasons, this is the only one that does not change and can safely be accessed as if it was not volatile. The contents of this thread structure, as well as the other members of the sched_threads array can change. But even in this case volatile does not solve anything: correctly accessing the members of a shared data structure needs either atomics or disabling interrupts. The latter is what's done in RIOT. In interrupt or kernel context, these structures should not change. For this reason this variable should not be volatile. The same considereations as with sched_context_switch_request apply with respect to preemmptible IRQs. Again, it may be necessary to insert barriers.
Now that the variables are no longer volatile, the definitions conflict. This makes the test compile again and hopefully validates that removing the volatile qualifier is OK.
|
@kaspar030, @keestux: You might want to have a look here. What would be the best way to test this? Are |
| static inline kernel_pid_t thread_getpid(void) | ||
| { | ||
| extern volatile kernel_pid_t sched_active_pid; | ||
| extern kernel_pid_t sched_active_pid; |
There was a problem hiding this comment.
Why is this extern declaration needed anyway? We do include sched.h ...
There was a problem hiding this comment.
You're right, it should not be needed. I'd guess it was needed once upon a time...
|
The changes look good to me. I've manually checked a few tests and they give the expected output. |
|
How about grepping for |
|
More things to weed out. |
|
What's our thought on this one? Can we drop |
|
I see that the subject of the PR is PoC (Proof of Concept?). In that case we don't care too much about completeness, just that this is the right track. I hope we can soon come to an agreement that the changes are correct and that we can continue cleaning (spring cleaning :-) |
| #define THREAD_FIRSTGROUP_NUMOF (3U) | ||
|
|
||
| extern volatile thread_t *sched_active_thread; | ||
| extern thread_t *sched_active_thread; |
There was a problem hiding this comment.
I suggest we include sched.h instead of doing this declaration. (same for tests/mutex_order)
There was a problem hiding this comment.
I think there is an API call for that which should be used instead of accessing the variable directly
There was a problem hiding this comment.
If there is an API then quite some code is ignoring that API.
There was a problem hiding this comment.
I was wrong, there only is thread_get(kernel_pid_t pid). One could use thread_get(thread_getpid()), but would be less efficient. Maybe anothrr static inline function should be added for that use case?
There was a problem hiding this comment.
One could use
thread_get(thread_getpid()), but would be less efficient. Maybe anothrr static inline function should be added for that use case?
+1 for another function. Maybe thread_get_current()? That would be a nice PR of its own.
There was a problem hiding this comment.
Would it be an abuse to make thread_get(KERNEL_PID_UNDEF) get the current thread?
There was a problem hiding this comment.
I'd say that thread_get(KERNEL_PID_UNDEF) is less readable than thread_t * thread_get_current(void).
Yes, there's quite some things. I was planning on that as the next step.
That was what I was looking for. |
|
What is the state here? Obviously the discussion around this PoC died down, so can it be closed? |
|
This PR needs some love and rigorous testing. It could very well be that the E.g. if one function would repeatedly read |
|
I think this kind of PR falls into what @maribu has been pushing for replacing by atomics, or irq_disable/enable calls, maybe you would want to take over? |
In fact, most of this fight was already won when we changed the public API to no longer show any trace of Note that if |
|
Would you be up for picking this up again @maribu? |
Contribution description
NOTE: The main purpose of this PR is to test the change in the CI.
NOTE2: The explanations are too long to paste here, read the commit messages.
This removes "volatile" from
There is no reason for these variables to be volatile:
In application (thread) context, the "active_thread" never changes- if thread X is reading sched_active_pid, the value is always X whenever thread X is active.
In kernel context, it is the kernel that changes the value, so again, it needs not be volatile.
With regards to the contents of the thread structure itself, sometimes it is important that it is re-read (for example in msg_* functions). Here, however, there are calls to extern-defined functions to yield, etc. Since the compiler cannot know if these functions alter global memory, it is forced to emit code re-read these variables, so again volatile is unncesessary (maybe if using LTO it can "smart up" and do something stupid.) If later it turns out that an explicit barrier is necessary, it can be added.
For more information on this discussion, see this thread:
And for more info on barriers vs volatile, see:
Testing procedure
Run ALL the tests.
Related Issues
#252, #10177, also see the thread in the devel mailing list.