Why would a context switch on a single core behave differently compared to 2 threads on different cores ? (except any cache coherency issues)
The threads on separate cores may act at exactly the same time. You still have issues on a single core.
Somewhere here on Stackoverflow is also stated that memory barriers are not required on single core processors.
This information maybe taken out of context (or not provide enough context).
Wikipedia's Memory barrier and Memory ordering pages have sections Out-of-order execution versus compiler reordering optimizations and Compile time/Run time ordering. There are many places in a pipeline where the ordering of memory may matter. In some cases, this may be taken care of by the compiler, by the OS, or by our own code.
Compiler memory barriers apply to a single CPU. They are especially useful with hardware where the ordering and timing of writes and reads matter.
Linux defines some more types of memory barriers,
- Write/Store.
- Data dependency.
- Read/Load.
- General memory barriers.
Mainly these map fairly well to DMB (DSB and IMB are more for code modification).
The more advances ARM CPUs have multiple load/store units. In theory some non-preemptive threading switch Note1 (especially with aliased memory) could cause some issue with a multi-threaded single CPU application. However, it would be fairly hard to construct this case.
For the most part, good memory ordering is handled by the CPU by scheduling instructions. A common case where it does matter with a single CPU is for system level programmers altering CP15 registers. For instance, an ISB should be issued when turning on the MMU. The same may be true for certain hardware/device registers. Finally, a program loader will need barriers as well as cache operations, even on single CPU systems.
UnixSmurf wrote these blogs on memory access ordering,
The topic is complex and you have to be specific about the types of barriers you are discussing.
Note1: I say non preemptive as if an interrupt occurs, the single CPU will probably ensure that all outstanding memory requests are complete. With a non preemptive switch, you do something like longjmp to change threads. In theory, you could change contexts before all writes had completed. The system would only need a DMB in the yield() to avoid it.