sequence points
code reordering
critical sections
concurrency
programming concepts

Do sequence points prevent code reordering across critical section boundaries?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Sequence points, or in modern C++ terminology sequencing rules, control the order of evaluations within one thread of execution. They do not, by themselves, create the cross-thread visibility guarantees you rely on for critical sections; that job belongs to synchronization primitives such as mutex lock and unlock operations.

Sequence Points Are Not a Concurrency Primitive

Older C and C++ discussions often talk about sequence points. Modern C++ standards instead describe expressions as being sequenced before, sequenced after, or unsequenced relative to one another.

That matters for questions such as:

  • does one side effect happen before another in the same thread
  • is an expression well-defined
  • can the compiler reorder local computations without changing observable behavior

Those rules are important, but they are not the same thing as inter-thread synchronization.

If two threads access shared data, sequence points inside one thread do not magically tell the other thread when writes become visible.

What Actually Protects a Critical Section

A critical section is about synchronization between threads. In C++, that usually means:

  • 'std::mutex'
  • lock and unlock operations
  • atomics with explicit memory ordering when locks are not used

Example with a mutex:

cpp
1#include <iostream>
2#include <mutex>
3#include <thread>
4
5std::mutex m;
6int shared_value = 0;
7
8void writer() {
9    std::lock_guard<std::mutex> lock(m);
10    shared_value = 42;
11}
12
13void reader() {
14    std::lock_guard<std::mutex> lock(m);
15    std::cout << shared_value << '\n';
16}

The important ordering guarantee here comes from the mutex operations. Unlocking in one thread synchronizes with locking in another thread on the same mutex. That creates the happens-before relationship that makes the write visible safely.

Reordering and the As-If Rule

Compilers and CPUs are free to reorder operations as long as they preserve the program's required observable behavior. This is why local code motion is possible even when source order looks straightforward.

Inside a correctly synchronized critical section, the implementation must preserve the semantics required by the lock. It cannot move a shared write that is meant to happen inside the critical section so that it becomes visible as if it occurred outside the lock's synchronization effects.

But that guarantee comes from synchronization semantics, not from sequence points.

A useful mental model is:

  • sequencing rules constrain expression evaluation inside one thread
  • lock and unlock semantics constrain visibility and ordering between threads

Why Sequence Points Alone Are Not Enough

Consider unsynchronized shared access:

cpp
1#include <thread>
2#include <iostream>
3
4int shared_value = 0;
5bool ready = false;
6
7void writer() {
8    shared_value = 42;
9    ready = true;
10}
11
12void reader() {
13    while (!ready) {
14    }
15    std::cout << shared_value << '\n';
16}

This code has a data race. Even though each statement ends a full expression, the program is not made safe by that fact. The compiler and CPU are allowed to behave in ways that break your intuition because no synchronization primitive establishes ordering between the threads.

That is the core answer to the question: sequence points do not turn unsynchronized shared memory into a safe critical section.

Mutexes Versus Atomics

Mutexes are the usual critical-section tool, but atomics can also establish ordering when used correctly.

For example:

cpp
1#include <atomic>
2#include <iostream>
3#include <thread>
4
5std::atomic<bool> ready{false};
6int shared_value = 0;
7
8void writer() {
9    shared_value = 42;
10    ready.store(true, std::memory_order_release);
11}
12
13void reader() {
14    while (!ready.load(std::memory_order_acquire)) {
15    }
16    std::cout << shared_value << '\n';
17}

Here, acquire and release semantics create the cross-thread ordering. Again, this is a memory-model guarantee, not a sequence-point guarantee.

Critical Section Boundaries Are Defined by Synchronization

When people ask whether code can be reordered across critical section boundaries, the right question is:

"Would that reordering violate the synchronization semantics of lock and unlock?"

If yes, the implementation may not perform it in a way that changes the required behavior.

So code inside a critical section is not protected because there is a semicolon after each statement or because expressions are sequenced. It is protected because the synchronization object creates ordering and visibility constraints that the implementation must respect.

Common Pitfalls

The biggest mistake is mixing single-thread evaluation rules with multi-thread synchronization rules. They solve different problems.

Another mistake is assuming that source order alone is enough to communicate with another thread. Without mutexes or atomics, source order in one thread does not create a reliable visibility guarantee in another.

Developers also sometimes treat volatile as a threading tool in C++. It is not a substitute for mutexes or atomics.

Finally, old terminology about sequence points can obscure the modern C++ memory model. For concurrency questions, think in terms of data races, happens-before, and synchronization operations.

Summary

  • Sequence points or sequencing rules are about evaluation order within one thread.
  • Critical section safety comes from synchronization primitives such as mutexes and atomics.
  • Lock and unlock operations, not semicolons, create the cross-thread ordering guarantees.
  • Unsynchronized shared access remains a data race even if expressions are individually well sequenced.
  • For concurrency reasoning in C++, use the memory-model concepts of happens-before and synchronization.

Course illustration
Course illustration

All Rights Reserved.