summaryrefslogtreecommitdiff
path: root/src/thread.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/thread.c')
-rw-r--r--src/thread.c45
1 files changed, 45 insertions, 0 deletions
diff --git a/src/thread.c b/src/thread.c
index e361473..0d63be8 100644
--- a/src/thread.c
+++ b/src/thread.c
@@ -241,6 +241,48 @@ thread_preempt_disable(void)
thread->preempt_level++;
assert(thread->preempt_level != 0);
+ /*
+ * This is a compiler barrier. It tells the compiler not to reorder
+ * the instructions it emits across this point.
+ *
+ * Reordering is one of the most effective ways to optimize generated
+ * machine code, and the C specification allows compilers to optimize
+ * very aggressively, which is why C shouldn't be thought of as a
+ * "portable assembler" any more.
+ *
+ * Sometimes, especially when building critical sections, strong control
+ * over ordering is required, and this is when compiler barriers should
+ * be used. In this kernel, disabling preemption is one of the primary
+ * ways to create critical sections. The following barrier makes sure
+ * that no instructions from the critical section leak out before it.
+ *
+ * When using GCC or a compatible compiler such as Clang, compiler
+ * barriers are implemented with an inline assembly expression that
+ * includes the "memory" clobber. This clobber tells the compiler that
+ * memory may change in unexpected ways. All memory accesses started
+ * before the barrier must complete before the barrier, and all memory
+ * accesses that complete after the barrier must start after the barrier.
+ * See barrier() in macros.h.
+ *
+ * When calling assembly functions, there is usually no need to add
+ * an explicit compiler barrier, because the compiler, not knowing
+ * what the external function does, normally assumes memory may change,
+ * i.e. that the function has unexpected side effects. In C code however,
+ * the compiler has better knowledge about side effects. That knowledge
+ * comes from the amount of code in a single compilation unit. The
+ * traditional compilation unit when building with optimizations is the
+ * source file, but with link-time optimizations (LTO), this can be the
+ * whole program. This is why specifying barriers in C code is required
+ * for a reliable behaviour.
+ *
+ * Also note that compiler barriers only affect the machine code
+ * generated by the compiler. Processors also internally perform
+ * various kinds of reordering. When confined to a single processor,
+ * this can safely be ignored because the processor model guarantees
+ * that the state seen by software matches program order. This
+ * assumption breaks on multiprocessor systems though, where memory
+ * barriers are also required to enforce ordering across processors.
+ */
barrier();
}
@@ -249,6 +291,7 @@ thread_preempt_enable_no_yield(void)
{
struct thread *thread;
+ /* See thread_preempt_disable() */
barrier();
thread = thread_self();
@@ -465,6 +508,8 @@ thread_runq_schedule(struct thread_runq *runq)
* assume that memory may have changed in completely unexpected ways,
* which is equivalent to the inline assembly expression used to
* implement compiler barriers with GCC (see barrier() in macros.h).
+ *
+ * See thread_preempt_disable() for a description of compiler barriers.
*/
thread_switch_context(prev, next);
}