CodeNebula
C++20stladvanced

std::barrier (C++20 Addition)

C++20 · The Big Four · Compiler flag: -std=c++20

Explanation

A reusable synchronization primitive where a fixed number of threads repeatedly synchronize at a barrier point. Each phase can optionally invoke a completion function.

Real-World Scenario

Synchronize simulation steps in a parallel physics engine where all threads must finish one time step before any begins the next.

Quick Snippet

snippet.cpp
#include <barrier>
#include <thread>
#include <vector>

auto on_phase_complete = []() noexcept { /* swap buffers */ };
std::barrier sync(4, on_phase_complete);

auto simulate = [&](int id) {
    for (int step = 0; step < 100; ++step) {
        compute_physics(id);
        sync.arrive_and_wait();  // wait for all 4 threads
    }
};

Full Example

barrier.cpp
// CodeNebula - C++20 Example: std::barrier (Reusable Synchronization)
// Compile: g++ -std=c++20 -Wall -Wextra -pthread -o example barrier.cpp

#include <barrier>
#include <iostream>
#include <string>
#include <thread>
#include <vector>

int main() {
    std::cout << "=== std::barrier - reusable synchronization point ===\n";
    std::cout << "(Unlike latch, barrier can be used for multiple phases)\n\n";

    constexpr int num_threads = 3;
    constexpr int num_phases = 3;
    int phase_counter = 0;

    // Completion function runs once per phase when all threads arrive
    auto on_completion = [&phase_counter]() noexcept {
        ++phase_counter;
        std::cout << "--- Phase " << phase_counter << " complete ---\n";
    };

    std::barrier sync_point(num_threads, on_completion);

    auto worker = [&](int id) {
        for (int phase = 0; phase < num_phases; ++phase) {
            // Do phase work
            std::cout << "Thread " << id << " working on phase " << (phase + 1) << "\n";
            std::this_thread::sleep_for(std::chrono::milliseconds(10 * (id + 1)));

            // Wait for all threads to complete this phase
            sync_point.arrive_and_wait();
        }
    };

    std::vector<std::jthread> threads;
    for (int i = 0; i < num_threads; ++i) {
        threads.emplace_back(worker, i);
    }

    for (auto& t : threads) t.join();
    std::cout << "All " << num_phases << " phases completed.\n";

    std::cout << "\n=== Barrier with arrive_and_drop ===\n";
    std::cout << "(Thread leaves the barrier, reducing expected count)\n\n";

    int active_count = 3;
    std::barrier shrinking_barrier(3, [&active_count]() noexcept {
        std::cout << "  [Barrier: " << active_count << " threads completed phase]\n";
    });

    auto optional_worker = [&](int id, int phases_to_do) {
        for (int p = 0; p < phases_to_do; ++p) {
            std::cout << "Thread " << id << " phase " << (p + 1) << "\n";
            std::this_thread::sleep_for(std::chrono::milliseconds(15));
            shrinking_barrier.arrive_and_wait();
        }
        if (phases_to_do < 3) {
            // Drop out early - reduces expected count for future phases
            std::cout << "Thread " << id << " dropping out\n";
            --active_count;
            shrinking_barrier.arrive_and_drop();
        }
    };

    std::jthread t0(optional_worker, 0, 3);  // Participates in all 3 phases
    std::jthread t1(optional_worker, 1, 3);  // Participates in all 3 phases
    std::jthread t2(optional_worker, 2, 1);  // Drops out after phase 1

    t0.join();
    t1.join();
    t2.join();
    std::cout << "Done with shrinking barrier example.\n";

    return 0;
}
Run command
g++ -std=c++20 -Wall -Wextra -pthread -o example barrier.cpp