summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2018-01-04 14:59:41 +0100
committerRichard Braun <rbraun@sceen.net>2018-01-04 14:59:41 +0100
commitf0265a0d2b5da3214d5c69c5490ef78a90244a7b (patch)
tree221a1635a27ea6bc13fad1c0288b56ea8311f50a
parent9437f135da9fab16180fc64cdd64e2a3bb3d5b7a (diff)
thread: discuss compiler barriers
-rw-r--r--src/cpu.h6
-rw-r--r--src/thread.c45
2 files changed, 51 insertions, 0 deletions
diff --git a/src/cpu.h b/src/cpu.h
index 6326a58..0ca61d6 100644
--- a/src/cpu.h
+++ b/src/cpu.h
@@ -82,6 +82,9 @@ typedef void (*cpu_irq_handler_fn_t)(void *arg);
/*
* Enable/disable interrupts.
+ *
+ * These functions imply a compiler barrier.
+ * See thread_preempt_disable() in thread.c.
*/
void cpu_intr_enable(void);
void cpu_intr_disable(void);
@@ -90,6 +93,9 @@ void cpu_intr_disable(void);
* Disable/restore interrupts.
*
* Calls to these functions can safely nest.
+ *
+ * These functions imply a compiler barrier.
+ * See thread_preempt_disable() in thread.c.
*/
uint32_t cpu_intr_save(void);
void cpu_intr_restore(uint32_t eflags);
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);
}