CodeNebula
C++20stladvanced

std::stop_token / std::stop_source (C++20 Addition)

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

Explanation

Cooperative cancellation mechanism. A `stop_source` can request a stop; associated `stop_token` objects can check whether a stop has been requested. `stop_callback` registers functions to run when stop is requested.

Real-World Scenario

Cancel a long-running database query from a UI thread when the user clicks "Cancel".

Quick Snippet

snippet.cpp
#include <stop_token>
#include <thread>

void search(std::stop_token token, const std::string& query) {
    for (auto& page : database) {
        if (token.stop_requested()) return;  // cancelled
        // process page
    }
}

std::stop_source source;
std::jthread worker(search, source.get_token(), "SELECT *");
// Later: source.request_stop();

Full Example

stop_token.cpp
// CodeNebula - C++20 Example: std::stop_token, stop_source, stop_callback
// Compile: g++ -std=c++20 -Wall -Wextra -pthread -o example stop_token.cpp

#include <chrono>
#include <iostream>
#include <thread>

using namespace std::chrono_literals;

void worker_function(std::stop_token stoken, const char* name) {
    int iterations = 0;
    while (!stoken.stop_requested()) {
        ++iterations;
        std::this_thread::sleep_for(20ms);
    }
    std::cout << name << " ran " << iterations << " iterations before stop\n";
}

int main() {
    std::cout << "=== std::stop_source and stop_token ===\n";

    // Create a stop_source (the controller)
    std::stop_source source;

    // Get tokens (read-only views) from the source
    std::stop_token token1 = source.get_token();
    std::stop_token token2 = source.get_token();

    std::cout << "stop_possible: " << token1.stop_possible() << "\n";
    std::cout << "stop_requested: " << token1.stop_requested() << "\n";

    // Launch threads with shared stop tokens
    std::jthread t1(worker_function, token1, "Worker-A");
    std::jthread t2(worker_function, token2, "Worker-B");

    std::this_thread::sleep_for(100ms);

    // One request stops all threads sharing this source
    std::cout << "Requesting stop...\n";
    source.request_stop();
    std::cout << "stop_requested: " << token1.stop_requested() << "\n";

    t1.join();
    t2.join();

    std::cout << "\n=== std::stop_callback ===\n";

    std::stop_source src2;
    std::stop_token tok2 = src2.get_token();

    // Register callbacks that fire when stop is requested
    std::stop_callback cb1(tok2, [] {
        std::cout << "Callback 1: cleanup resources\n";
    });
    std::stop_callback cb2(tok2, [] {
        std::cout << "Callback 2: notify observers\n";
    });

    std::cout << "Requesting stop with callbacks...\n";
    src2.request_stop();  // Both callbacks fire here

    std::cout << "\n=== Empty stop_source (no associated state) ===\n";
    std::stop_source empty{std::nostopstate};
    std::cout << "stop_possible (empty): " << empty.stop_possible() << "\n";
    std::stop_token empty_tok = empty.get_token();
    std::cout << "token stop_possible: " << empty_tok.stop_possible() << "\n";

    std::cout << "\n=== Callback with jthread ===\n";
    {
        std::jthread t([](std::stop_token st) {
            std::stop_callback on_stop(st, [] {
                std::cout << "Thread received stop notification via callback\n";
            });
            while (!st.stop_requested()) {
                std::this_thread::sleep_for(10ms);
            }
        });
        std::this_thread::sleep_for(50ms);
        t.request_stop();
    }
    std::cout << "Done\n";

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