Modernes C++ programmieren

Okt 20, 2024

listings-Chap29-README-onepage.md

Listings of Chap29.docx

This is the list of listings on one page. You can also view a linked summary.

Listing 29.1: How to start a thread.

Book listing lst-0001-book.cpp:

// https://godbolt.org/z/qnadYPWh1 
#include <iostream>
#include <thread>
using std::cout; using std::endl;

long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }
void task1() { auto r = fib(40); cout << "fib(40)=" << r << endl; }
void task2() { auto r = fib(41); cout << "fib(41)=" << r << endl; }
void task3() { auto r = fib(42); cout << "fib(42)=" << r << endl; }

struct BackgroundTask {
    void operator()() const {
        task1();
        task2();
        task3();
    }
};

int main() {
    BackgroundTask backgroundTask{}; // Initialization, does not compute anything yet
    std::jthread myThread{ backgroundTask }; // Computation starts
}

Godbolt Listing lst-0001-godb.cpp, https://godbolt.org/z/qnadYPWh1:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/qnadYPWh1 
#include <iostream>
#include <thread>
using std::cout; using std::endl;

long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }
void task1() { auto r = fib(40); cout << "fib(40)=" << r << endl; }
void task2() { auto r = fib(41); cout << "fib(41)=" << r << endl; }
void task3() { auto r = fib(42); cout << "fib(42)=" << r << endl; }

struct BackgroundTask {
    void operator()() const {
        task1();
        task2();
        task3();
    }
};

int main() {
    BackgroundTask backgroundTask{}; // Initialization, does not compute anything yet
    std::jthread myThread{ backgroundTask }; // Computation starts
}

GodboltId:nPGY47r4K

Book listing lst-0003-book.cpp:

// https://godbolt.org/z/nPGY47r4K 
std::jthread myThread{ [] {
    task1();
    task2();
    task3();
} };

Godbolt Listing lst-0003-godb.cpp, https://godbolt.org/z/nPGY47r4K:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/nPGY47r4K 
std::jthread myThread{ [] {
    task1();
    task2();
    task3();
} };

Listing 29.2: With “stop_token”, the outside world communicates into the thread.

Book listing lst-0004-book.cpp:

// https://godbolt.org/z/6cqd7Tsae 
struct BackgroundTask {
    void operator()(std::stop_token st) const { // Token for communication
        task1();
        if(st.stop_requested()) return;
        task2();
        if(st.stop_requested()) return;
        task3();
    }
};
int main() {
    BackgroundTask backgroundTask{};
    std::jthread myThread{ backgroundTask };
    std::this_thread::sleep_for(std::chrono::milliseconds(100)); // wait 100ms
    myThread.request_stop(); // request the thread to stop
}

Godbolt Listing lst-0004-godb.cpp, https://godbolt.org/z/6cqd7Tsae:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/6cqd7Tsae 
struct BackgroundTask {
    void operator()(std::stop_token st) const { // Token for communication
        task1();
        if(st.stop_requested()) return;
        task2();
        if(st.stop_requested()) return;
        task3();
    }
};
int main() {
    BackgroundTask backgroundTask{};
    std::jthread myThread{ backgroundTask };
    std::this_thread::sleep_for(std::chrono::milliseconds(100)); // wait 100ms
    myThread.request_stop(); // request the thread to stop
}

GodboltId:Tzs93MzfK

Book listing lst-0005-book.cpp:

// https://godbolt.org/z/Tzs93MzfK 
std::thread myThread{ [] {  // pure thread
    task1();
    task2();
    task3();
} };
myThread.join();   // waits for the thread to finish

Godbolt Listing lst-0005-godb.cpp, https://godbolt.org/z/Tzs93MzfK:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/Tzs93MzfK 
std::thread myThread{ [] {  // pure thread
    task1();
    task2();
    task3();
} };
myThread.join();   // waits for the thread to finish

GodboltId:E49on6zn3

Book listing lst-0006-book.cpp:

// https://godbolt.org/z/E49on6zn3
#include <iostream>
#include <thread>
#include <vector>
#include <exception>
using std::cout; using std::endl;

long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }
void task1() { auto r = fib(40); cout << "fib(40)=" << r << endl; }

void main_program() {
    try {
        std::thread th{ &task1 };
        std::vector data{ 0,1,2 };
        data.at(666);                     // (ERR) triggers out_of_range
        th.join();                        // would wait
    } catch(std::runtime_error &ex) {     // (ERR) does not match out_of_range
         /*...*/
    }
}

int main() {
  try {
    main_program();
  } catch( ... ) {                        // so far, so good, looks safe
    std::cout << "An error has occurred\n"; // you won't see this
  }
}

Godbolt Listing lst-0006-godb.cpp, https://godbolt.org/z/E49on6zn3:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23 -Wno-unused-result; libs:boost@184
// https://godbolt.org/z/E49on6zn3
#include <iostream>
#include <thread>
#include <vector>
#include <exception>
using std::cout; using std::endl;

long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }
void task1() { auto r = fib(40); cout << "fib(40)=" << r << endl; }

void main_program() {
    try {
        std::thread th{ &task1 };
        std::vector data{ 0,1,2 };
        data.at(666);                     // (ERR) triggers out_of_range
        th.join();                        // would wait
    } catch(std::runtime_error &ex) {     // (ERR) does not match out_of_range
         /*...*/
    }
}

int main() {
  try {
    main_program();
  } catch( ... ) {                        // so far, so good, looks safe
    std::cout << "An error has occurred\n"; // you won't see this
  }
}

GodboltId:T4c1GnjY4

Book listing lst-0007-book.cpp:

// https://godbolt.org/z/T4c1GnjY4
#include <iostream>
#include <thread>
#include <vector>
#include <exception>
using std::cout; using std::endl;

long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }
void task1() { auto r = fib(40); cout << "fib(40)=" << r << endl; }

void main_program() {
    std::thread th{ &task1 };
    try {
        std::vector data{ 0,1,2 };
        data.at(666);                 // (ERR) triggers out_of_range
    } catch(std::runtime_error &ex) { // does not match out_of_range
         /* ... */                    // handle specific error here
    } catch( ... ) {
        th.join();
        throw;                        // continue error handling outside
    }
    th.join();                        // waits after Okay or specific error
}

int main() {
    try {
        main_program();
    } catch( ... ) {
        std::cout << "An error has occurred\n";
    }
}

Godbolt Listing lst-0007-godb.cpp, https://godbolt.org/z/T4c1GnjY4:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23 -Wno-unused-result; libs:boost@184
// https://godbolt.org/z/T4c1GnjY4
#include <iostream>
#include <thread>
#include <vector>
#include <exception>
using std::cout; using std::endl;

long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }
void task1() { auto r = fib(40); cout << "fib(40)=" << r << endl; }

void main_program() {
    std::thread th{ &task1 };
    try {
        std::vector data{ 0,1,2 };
        data.at(666);                 // (ERR) triggers out_of_range
    } catch(std::runtime_error &ex) { // does not match out_of_range
         /* ... */                    // handle specific error here
    } catch( ... ) {
        th.join();
        throw;                        // continue error handling outside
    }
    th.join();                        // waits after Okay or specific error
}

int main() {
    try {
        main_program();
    } catch( ... ) {
        std::cout << "An error has occurred\n";
    }
}

GodboltId:8TdzEGf3d

Book listing lst-0008-book.cpp:

// https://godbolt.org/z/8TdzEGf3d 
void mainProgram() {
    std::jthread th{ &task1 };
    std::vector data{ 0,1,2 };
    data.at(666);                  // (ERR) triggers out_of_range
}

Godbolt Listing lst-0008-godb.cpp, https://godbolt.org/z/8TdzEGf3d:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/8TdzEGf3d 
void mainProgram() {
    std::jthread th{ &task1 };
    std::vector data{ 0,1,2 };
    data.at(666);                  // (ERR) triggers out_of_range
}

Listing 29.3: Add parameters to the thread function in the constructor.

Book listing lst-0009-book.cpp:

// https://godbolt.org/z/bMdrWrd4c
#include <iostream>
#include <thread>
using std::cout; using std::endl;

long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }
void runFib(long n) {
    auto r = fib(n);
    cout << "fib("<<n<<")=" << r << endl;
}
long ack(long m, long n) { // Ackermann function
    if(m==0) return n+1;
    if(n==0) return ack(m-1, 1);
    return ack(m - 1, ack(m, n-1));
}
void runAck(long m, long n) {
    auto r = ack(m, n);
    cout << "ack("<<m<<','<<n<<")=" << r << endl;
}

int main() {
    std::jthread f40{ runFib, 40 };
    std::jthread f41{ runFib, 41 };
    std::jthread f42{ runFib, 42 };

    f40.join(); f41.join(); f42.join();

    std::thread a1{ runAck, 4, 0 };
    std::thread a2{ runAck, 4, 1 };
    std::thread a3{ runAck, 2, 700 };
    std::thread a4{ runAck, 3, 10 };
}

Godbolt Listing lst-0009-godb.cpp, https://godbolt.org/z/bMdrWrd4c:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/bMdrWrd4c
#include <iostream>
#include <thread>
using std::cout; using std::endl;

long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }
void runFib(long n) {
    auto r = fib(n);
    cout << "fib("<<n<<")=" << r << endl;
}
long ack(long m, long n) { // Ackermann function
    if(m==0) return n+1;
    if(n==0) return ack(m-1, 1);
    return ack(m - 1, ack(m, n-1));
}
void runAck(long m, long n) {
    auto r = ack(m, n);
    cout << "ack("<<m<<','<<n<<")=" << r << endl;
}

int main() {
    std::jthread f40{ runFib, 40 };
    std::jthread f41{ runFib, 41 };
    std::jthread f42{ runFib, 42 };

    f40.join(); f41.join(); f42.join();

    std::thread a1{ runAck, 4, 0 };
    std::thread a2{ runAck, 4, 1 };
    std::thread a3{ runAck, 2, 700 };
    std::thread a4{ runAck, 3, 10 };
}

Listing 29.4: Parameters are copied into the thread.

Book listing lst-0010-book.cpp:

// https://godbolt.org/z/fbnbKzrs1 
#include <iostream>
#include <thread>
#include <chrono>
using namespace std::chrono; // seconds, suffix s

void delayPrint(seconds s, const std::string& msg) {
    std::this_thread::sleep_for(s);
    std::cout << msg << std::endl;
}
int main() {
    std::jthread m1{ delayPrint, 1s, "On your marks" };
    std::jthread m2{ delayPrint, 2s, std::string{"set"} };
    std::string go = "go";
    std::jthread m3{ delayPrint, 3s, go };
}

Godbolt Listing lst-0010-godb.cpp, https://godbolt.org/z/fbnbKzrs1:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/fbnbKzrs1 
#include <iostream>
#include <thread>
#include <chrono>
using namespace std::chrono; // seconds, suffix s

void delayPrint(seconds s, const std::string& msg) {
    std::this_thread::sleep_for(s);
    std::cout << msg << std::endl;
}
int main() {
    std::jthread m1{ delayPrint, 1s, "On your marks" };
    std::jthread m2{ delayPrint, 2s, std::string{"set"} };
    std::string go = "go";
    std::jthread m3{ delayPrint, 3s, go };
}

Listing 29.5: Use caution with raw pointers as parameters.

Book listing lst-0011-book.cpp:

// https://godbolt.org/z/hvq5dbzde 
#include <iostream>
#include <thread>
#include <chrono>
using namespace std::chrono; // seconds, suffix s

void delayPrint(seconds s, const char* msg) {  // (ERR) raw pointer
    std::this_thread::sleep_for(s);
    std::cout << msg << std::endl;             // (ERR) this won't work
}

void run() {
    const char risk[] = "This won't end well...";
    std::jthread t{ delayPrint, 1s, risk };    // (ERR) raw pointer
    t.detach();
    // here the scope of 'risk' is left
}
int main() {
    run();
    std::this_thread::sleep_for(2s);           // wait another 2 seconds
}

Godbolt Listing lst-0011-godb.cpp, https://godbolt.org/z/hvq5dbzde:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/hvq5dbzde 
#include <iostream>
#include <thread>
#include <chrono>
using namespace std::chrono; // seconds, suffix s

void delayPrint(seconds s, const char* msg) {  // (ERR) raw pointer
    std::this_thread::sleep_for(s);
    std::cout << msg << std::endl;             // (ERR) this won't work
}

void run() {
    const char risk[] = "This won't end well...";
    std::jthread t{ delayPrint, 1s, risk };    // (ERR) raw pointer
    t.detach();
    // here the scope of 'risk' is left
}
int main() {
    run();
    std::this_thread::sleep_for(2s);           // wait another 2 seconds
}

Listing 29.6: Forcing a reference with ref.

Book listing lst-0012-book.cpp:

// https://godbolt.org/z/GnsWYvecv 
#include <iostream>
#include <thread>
#include <chrono>
using namespace std::chrono; // seconds, suffix s
struct State {
    int counter;
};
void showState(const State& state) {
    for(auto i : { 5,4,3,2,1 }) {
        std::cout << "counter: " << state.counter << std::endl;
        std::this_thread::sleep_for(1s);
    }
}
int main() {
    State state { 4 };
    std::jthread th{showState, std::ref(state)}; // remains reference to state
    std::this_thread::sleep_for(1s);
    state.counter = 501;
    std::this_thread::sleep_for(1s);
    state.counter = 87;
    std::this_thread::sleep_for(1s);
    state.counter = 2;
}

Godbolt Listing lst-0012-godb.cpp, https://godbolt.org/z/GnsWYvecv:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/GnsWYvecv 
#include <iostream>
#include <thread>
#include <chrono>
using namespace std::chrono; // seconds, suffix s
struct State {
    int counter;
};
void showState(const State& state) {
    for(auto i : { 5,4,3,2,1 }) {
        std::cout << "counter: " << state.counter << std::endl;
        std::this_thread::sleep_for(1s);
    }
}
int main() {
    State state { 4 };
    std::jthread th{showState, std::ref(state)}; // remains reference to state
    std::this_thread::sleep_for(1s);
    state.counter = 501;
    std::this_thread::sleep_for(1s);
    state.counter = 87;
    std::this_thread::sleep_for(1s);
    state.counter = 2;
}

Listing 29.7: Using “move” to transfer inputs to the thread.

Book listing lst-0013-book.cpp:

// https://godbolt.org/z/Pjzqsv9nz
// … includes …
#include <thread>
using namespace std::chrono; // seconds, suffix s
struct Image {
    std::vector<char> data_; // Copying expensive
    explicit Image() : data_(1'000'000) {}
};
void showImage(Image img) {
    std::cout << img.data_.size() << '\n';
}
void showIptr(std::unique_ptr<int> iptr) {
    std::cout << *iptr << '\n';
}
int main() {
    // expensive to copy, but good to move:
    Image image{};
    std::cout << image.data_.size() << std::endl;    // Output: 1000000
    std::jthread th1{ showImage, std::move(image) }; // Output: 1000000
    std::this_thread::sleep_for(1s);
    std::cout << image.data_.size() << std::endl;    // Output: 0
    th1.join();  // explicitly wait until the thread is done
    // impossible to copy, but good to move:
    auto iptr = std::make_unique<int>( 657 );
    std::cout << (bool)iptr << std::endl;            // Output: 1 for true
    std::jthread th2{ showIptr, std::move(iptr) };   // Output: 657
    std::this_thread::sleep_for(1s);
    std::cout << (bool)iptr.get() << std::endl;      // Output: 0 for false
}

Godbolt Listing lst-0013-godb.cpp, https://godbolt.org/z/Pjzqsv9nz:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23 -Wno-unused-result; libs:boost@184
// https://godbolt.org/z/Pjzqsv9nz
// … includes …
#include <thread>
using namespace std::chrono; // seconds, suffix s
struct Image {
    std::vector<char> data_; // Copying expensive
    explicit Image() : data_(1'000'000) {}
};
void showImage(Image img) {
    std::cout << img.data_.size() << '\n';
}
void showIptr(std::unique_ptr<int> iptr) {
    std::cout << *iptr << '\n';
}
int main() {
    // expensive to copy, but good to move:
    Image image{};
    std::cout << image.data_.size() << std::endl;    // Output: 1000000
    std::jthread th1{ showImage, std::move(image) }; // Output: 1000000
    std::this_thread::sleep_for(1s);
    std::cout << image.data_.size() << std::endl;    // Output: 0
    th1.join();  // explicitly wait until the thread is done
    // impossible to copy, but good to move:
    auto iptr = std::make_unique<int>( 657 );
    std::cout << (bool)iptr << std::endl;            // Output: 1 for true
    std::jthread th2{ showIptr, std::move(iptr) };   // Output: 657
    std::this_thread::sleep_for(1s);
    std::cout << (bool)iptr.get() << std::endl;      // Output: 0 for false
}

Listing 29.8: Returning a thread.

Book listing lst-0014-book.cpp:

// https://godbolt.org/z/Kv84eb96E 
#include <iostream>
#include <thread>
#include <chrono>
using namespace std::chrono; // seconds, suffix s
auto makeThread(std::string who) {
    return std::jthread{ [who] {
        std::this_thread::sleep_for(1s);
        std::cout << "Good luck, " << who << std::endl;
    } };
}
int main() {
    auto th = makeThread("Jim"); // Output: Good luck, Jim
    th.join();
}

Godbolt Listing lst-0014-godb.cpp, https://godbolt.org/z/Kv84eb96E:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/Kv84eb96E 
#include <iostream>
#include <thread>
#include <chrono>
using namespace std::chrono; // seconds, suffix s
auto makeThread(std::string who) {
    return std::jthread{ [who] {
        std::this_thread::sleep_for(1s);
        std::cout << "Good luck, " << who << std::endl;
    } };
}
int main() {
    auto th = makeThread("Jim"); // Output: Good luck, Jim
    th.join();
}

Listing 29.9: Moving a thread.

Book listing lst-0015-book.cpp:

// https://godbolt.org/z/8cMTrEWz6
#include <thread>
#include <chrono>
using namespace std::chrono; // seconds, suffix s
void yourMission(std::jthread job) {
    job.join();
}
int main() {
     std::jthread th{ [] {
        std::this_thread::sleep_for(1s);
        std::cout << "should you choose to accept it" << std::endl;
    } };
    yourMission( std::move(th) );  // Transfer responsibility
}

Godbolt Listing lst-0015-godb.cpp, https://godbolt.org/z/8cMTrEWz6:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23 -Wno-unused-result; libs:boost@184
// https://godbolt.org/z/8cMTrEWz6
#include <thread>
#include <chrono>
using namespace std::chrono; // seconds, suffix s
void yourMission(std::jthread job) {
    job.join();
}
int main() {
     std::jthread th{ [] {
        std::this_thread::sleep_for(1s);
        std::cout << "should you choose to accept it" << std::endl;
    } };
    yourMission( std::move(th) );  // Transfer responsibility
}

Listing 29.10: Threads in containers.

Book listing lst-0016-book.cpp:

// https://godbolt.org/z/YrEPnYMaP 
#include <iostream>
#include <thread>
#include <vector>
using std::cout; using std::endl;
long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }
void runFib(long n) { auto r = fib(n); cout << "fib("<<n<<")=" << r << endl; }
int main() {
    std::vector<std::jthread> threads;
    // starten
    for( auto n : { 38, 39, 40, 41, 42, 43, }) {
        threads.emplace_back( runFib, n );
    }
}

Godbolt Listing lst-0016-godb.cpp, https://godbolt.org/z/YrEPnYMaP:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/YrEPnYMaP 
#include <iostream>
#include <thread>
#include <vector>
using std::cout; using std::endl;
long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }
void runFib(long n) { auto r = fib(n); cout << "fib("<<n<<")=" << r << endl; }
int main() {
    std::vector<std::jthread> threads;
    // starten
    for( auto n : { 38, 39, 40, 41, 42, 43, }) {
        threads.emplace_back( runFib, n );
    }
}

Listing 29.11: Finding out the hardware concurrency.

Book listing lst-0019-book.cpp:

// https://godbolt.org/z/zojG68dWq
#include <thread>
#include <iostream>
#include <vector>
#include <chrono>  // steady_clock
using std::cout; using std::endl; using namespace std::chrono;
long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }

int main() {
  cout << std::thread::hardware_concurrency() << '\n';
  for(int nthreads : { 1,2,3,4,5,6 }) {
    cout << "Threads: ";
    const auto start = steady_clock::now();

    std::vector<std::jthread> threads;
    for(int ti = 1; ti <= nthreads; ++ti) {
      threads.emplace_back( std::jthread{fib, 40});
      cout << ti << "... "; cout.flush();
    }
    for(auto &th : threads) th.join(); // explicitly join before timing

    const auto now = steady_clock::now();
    cout << "  Time:  " << duration_cast<milliseconds>(
        now-start).count()<<"ms\n";
  }
}

Godbolt Listing lst-0019-godb.cpp, https://godbolt.org/z/zojG68dWq:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23 -Wno-unused-result; libs:boost@184
// https://godbolt.org/z/zojG68dWq
#include <thread>
#include <iostream>
#include <vector>
#include <chrono>  // steady_clock
using std::cout; using std::endl; using namespace std::chrono;
long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }

int main() {
  cout << std::thread::hardware_concurrency() << '\n';
  for(int nthreads : { 1,2,3,4,5,6 }) {
    cout << "Threads: ";
    const auto start = steady_clock::now();

    std::vector<std::jthread> threads;
    for(int ti = 1; ti <= nthreads; ++ti) {
      threads.emplace_back( std::jthread{fib, 40});
      cout << ti << "... "; cout.flush();
    }
    for(auto &th : threads) th.join(); // explicitly join before timing

    const auto now = steady_clock::now();
    cout << "  Time:  " << duration_cast<milliseconds>(
        now-start).count()<<"ms\n";
  }
}

Listing 29.12: Each thread has an identifier.

Book listing lst-0020-book.cpp:

// https://godbolt.org/z/TM6c7cdss 
#include <thread>
#include <iostream>
int main() {
    std::cout << "Main: " << std::this_thread::get_id() << '\n';
    std::jthread th{ []{
        std::cout << "Thread: " << std::this_thread::get_id() << '\n';
    }};
}

Godbolt Listing lst-0020-godb.cpp, https://godbolt.org/z/TM6c7cdss:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/TM6c7cdss 
#include <thread>
#include <iostream>
int main() {
    std::cout << "Main: " << std::this_thread::get_id() << '\n';
    std::jthread th{ []{
        std::cout << "Thread: " << std::this_thread::get_id() << '\n';
    }};
}

Listing 29.13: A classic data race.

Book listing lst-0021-book.cpp:

// https://godbolt.org/z/7GEs9a4n6 
#include <thread>
#include <iostream>

int count = 0; // is simultaneously modified

void run() {
    for(int i=0; i<1'000'000; ++i) {
        count += 1;   // unprotected
    }
}

int main() {
    std::cout << "Start: " << count << '\n'; // Output: Start: 0
    std::thread th1{ run };
    std::thread th2{ run };
    std::thread th3{ run };
    th1.join(); th2.join(); th3.join();
    std::cout << "End: " << count << '\n';   // Output certainly not: 3000000
}

Godbolt Listing lst-0021-godb.cpp, https://godbolt.org/z/7GEs9a4n6:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/7GEs9a4n6 
#include <thread>
#include <iostream>

int count = 0; // is simultaneously modified

void run() {
    for(int i=0; i<1'000'000; ++i) {
        count += 1;   // unprotected
    }
}

int main() {
    std::cout << "Start: " << count << '\n'; // Output: Start: 0
    std::thread th1{ run };
    std::thread th2{ run };
    std::thread th3{ run };
    th1.join(); th2.join(); th3.join();
    std::cout << "End: " << count << '\n';   // Output certainly not: 3000000
}

Listing 29.14: Benign data races are also undefined.

Book listing lst-0022-book.cpp:

// https://godbolt.org/z/v3b6sxEab 
#include <thread>
/* exact count not so important */
int count = 0; // is concurrently modified
void run() {
    for(int i=0; i<1'000; ++i) {
        count += 1; // unprotected
        if(count > 1000) return;  // (ERR) Termination condition
        for(int j=0; j<1'000; ++j)
            ;
    }
}

int main() {
    std::thread th1{ run };
    std::thread th2{ run };
    std::thread th3{ run };
    th1.join(); th2.join(); th3.join();
}

Godbolt Listing lst-0022-godb.cpp, https://godbolt.org/z/v3b6sxEab:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/v3b6sxEab 
#include <thread>
/* exact count not so important */
int count = 0; // is concurrently modified
void run() {
    for(int i=0; i<1'000; ++i) {
        count += 1; // unprotected
        if(count > 1000) return;  // (ERR) Termination condition
        for(int j=0; j<1'000; ++j)
            ;
    }
}

int main() {
    std::thread th1{ run };
    std::thread th2{ run };
    std::thread th3{ run };
    th1.join(); th2.join(); th3.join();
}

Listing 29.15: Latches can count down and wait.

Book listing lst-0023-book.cpp:

// https://godbolt.org/z/rrfn4d5WP 
#include <thread>
#include <latch>
#include <iostream>
long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }

int main() {
    std::latch la{ 3 };                             // we expect 3 threads
    std::jthread th1{ [&la] { fib(39); la.count_down(); } };
    std::jthread th2{ [&la] { fib(38); la.count_down(); } };
    std::jthread th3{ [&la] { fib(40); la.count_down(); } };
    fib(37); // main thread
    std::cout << "Main thread: done\n";
    la.wait();                                      // waits until la == 0
    std::cout << "Rest done\n";
}

Godbolt Listing lst-0023-godb.cpp, https://godbolt.org/z/rrfn4d5WP:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/rrfn4d5WP 
#include <thread>
#include <latch>
#include <iostream>
long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }

int main() {
    std::latch la{ 3 };                             // we expect 3 threads
    std::jthread th1{ [&la] { fib(39); la.count_down(); } };
    std::jthread th2{ [&la] { fib(38); la.count_down(); } };
    std::jthread th3{ [&la] { fib(40); la.count_down(); } };
    fib(37); // main thread
    std::cout << "Main thread: done\n";
    la.wait();                                      // waits until la == 0
    std::cout << "Rest done\n";
}

Listing 29.16: A barrier waits for a specific number of threads.

Book listing lst-0024-book.cpp:

// https://godbolt.org/z/515dMo9Kx
#include <thread>
#include <barrier>
#include <iostream>
#include <vector>
long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }

constexpr int anz = 8;          // 8 workers
constexpr int max_n = 32;       // calculate up to 32
std::vector<long> results(anz); // Buffer: space for 8 results

void output() {                 // Signaling function prints buffer
    for (auto n : results) std::cout << n << ' ';
    std::cout << '\n';
}

std::barrier ba{anz, output};   // always output after 8

void worker(std::stop_token st, int idx) {
    // n = 0, 9, 17, 25, … ; 1, 10, 18, 26, …
    for(int n = idx; n<max_n; n += anz) {
        if(st.stop_requested()) return;
        results[idx] = fib(n);  // write result to buffer
        ba.arrive_and_wait();   // wait until 8 threads are here
    }
}

int main() {
    std::vector<std::jthread> threads;     // 8 Threads
    for (int idx=0; idx<anz; ++idx) {
        threads.emplace_back(worker, idx); // create thread with index
    }
    for (auto& t : threads) t.join();      // wait until all are done
}

Godbolt Listing lst-0024-godb.cpp, https://godbolt.org/z/515dMo9Kx:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23 -Wno-unused-result; libs:boost@184
// https://godbolt.org/z/515dMo9Kx
#include <thread>
#include <barrier>
#include <iostream>
#include <vector>
long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }

constexpr int anz = 8;          // 8 workers
constexpr int max_n = 32;       // calculate up to 32
std::vector<long> results(anz); // Buffer: space for 8 results

void output() {                 // Signaling function prints buffer
    for (auto n : results) std::cout << n << ' ';
    std::cout << '\n';
}

std::barrier ba{anz, output};   // always output after 8

void worker(std::stop_token st, int idx) {
    // n = 0, 9, 17, 25, … ; 1, 10, 18, 26, …
    for(int n = idx; n<max_n; n += anz) {
        if(st.stop_requested()) return;
        results[idx] = fib(n);  // write result to buffer
        ba.arrive_and_wait();   // wait until 8 threads are here
    }
}

int main() {
    std::vector<std::jthread> threads;     // 8 Threads
    for (int idx=0; idx<anz; ++idx) {
        threads.emplace_back(worker, idx); // create thread with index
    }
    for (auto& t : threads) t.join();      // wait until all are done
}

Listing 29.17: A mutex together with a simple lock.

Book listing lst-0025-book.cpp:

// https://godbolt.org/z/3fz5a4vYv 
#include <mutex> // mutex, lock_guard
#include <list>
#include <algorithm> // find
using std::lock_guard; using std::mutex; using std::find;
class MxIntList {
    std::list <int> data_;
    mutable mutex mx_;
public:
    void add(int value) {
        lock_guard guard{mx_};  // protects until the end of the method
        data_.push_back(value);
    }
    bool contains(int searchVal) const {
        lock_guard guard{mx_};  // protects until the end of the method
        return find(data_.begin(), data_.end(), searchVal) != data_.end();
    }
};

Godbolt Listing lst-0025-godb.cpp, https://godbolt.org/z/3fz5a4vYv:

//#(execute) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/3fz5a4vYv 
#include <mutex> // mutex, lock_guard
#include <list>
#include <algorithm> // find
using std::lock_guard; using std::mutex; using std::find;
class MxIntList {
    std::list <int> data_;
    mutable mutex mx_;
public:
    void add(int value) {
        lock_guard guard{mx_};  // protects until the end of the method
        data_.push_back(value);
    }
    bool contains(int searchVal) const {
        lock_guard guard{mx_};  // protects until the end of the method
        return find(data_.begin(), data_.end(), searchVal) != data_.end();
    }
};

Listing 29.18: The interface to a multithreaded stack.

Book listing lst-0026-book.cpp:

// https://godbolt.org/z/4j7njd763 
#include <vector>

template<class T>
class MxStack {
public:
    bool isEmpty() const;
    void push(const T&);
    void pop();
    const T& top() const;
};

Godbolt Listing lst-0026-godb.cpp, https://godbolt.org/z/4j7njd763:

//#(execute) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/4j7njd763 
#include <vector>

template<class T>
class MxStack {
public:
    bool isEmpty() const;
    void push(const T&);
    void pop();
    const T& top() const;
};

Listing 29.19: Problematic code for the “MxStack”.

Book listing lst-0027-book.cpp:

// https://godbolt.org/z/1boeTMMvf 
MxStack<int> mxs{};
// …
// more code
// …
if( ! mxs.isEmpty()) {            // (ERR) not safe
    const auto value = mxs.top(); // (ERR) not safe
    mxs.pop();                    // (ERR) not safe
    // …
    // more code
    // …
}

Godbolt Listing lst-0027-godb.cpp, https://godbolt.org/z/1boeTMMvf:

//#(execute) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/1boeTMMvf 
MxStack<int> mxs{};
// …
// more code
// …
if( ! mxs.isEmpty()) {            // (ERR) not safe
    const auto value = mxs.top(); // (ERR) not safe
    mxs.pop();                    // (ERR) not safe
    // …
    // more code
    // …
}

Listing 29.20: A possible way two threads could execute the previous code.

Book listing lst-0028-book.cpp:

/* Thread 1 */                         /* Thread 2 */
if( ! mxs.isEmpty()) {
                                    if( ! mxs.isEmpty()) {
    const auto value = mxs.top();
                                        const auto value = mxs.top();
    mxs.pop();
    // … more code …
                                        mxs.pop();
                                        // … more code …

Listing 29.21: A very simple thread-safe stack.

Book listing lst-0029-book.cpp:

// https://godbolt.org/z/vcb9qP8jK
#include <vector>
#include <thread>
#include <mutex>
#include <iostream>
#include <numeric>  // iota
#include <concepts> // copyable 

/* T: noexcept copyable and assignable */
template<std::copyable T>
class MxStack {
    std::vector<T> data_;
    std::mutex mx_;

public:
    MxStack() : data_{} {}

    bool isEmpty() const { return data_.empty(); }

    void push(const T& val) {
        std::lock_guard<std::mutex> g{mx_};
        data_.push_back(val);
    }

    T pop() {
        std::lock_guard g{mx_};
        if(data_.empty())
            throw std::length_error{"empty stack"};
        T tmp{std::move(data_.back())};
        data_.pop_back();
        return tmp;
    }
};

int main() {
    // Prepare stack
    MxStack<int> mxs{};
    for(int i=1; i<=1'000'000; ++i) mxs.push(i);
    // Define computation
    auto sumIt = [&mxs](long &sum) {
        int val{};
        try {
            while( ! mxs.isEmpty()) {
                sum += mxs.pop(); // might still throw
            }
        } catch(std::length_error &ex) {}
    };
    // Compute
    long sum1 = 0;           // for partial result
    std::jthread th1{sumIt, std::ref(sum1)};
    long sum2 = 0;           // for partial result
    std::thread th2{sumIt, std::ref(sum2)};
    th1.join(); th2.join();
    long sum = sum1 + sum2; // Total result
    // Result
    std::cout << "Expected result: "
        << (1'000'000L*1'000'001)/2 << '\n'; // Output: 500000500000
    std::cout << "Actual: "
        << sum << '\n';                      // Output: 500000500000
}

Godbolt Listing lst-0029-godb.cpp, https://godbolt.org/z/vcb9qP8jK:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23 -Wno-unused-result; libs:boost@184
// https://godbolt.org/z/vcb9qP8jK
#include <vector>
#include <thread>
#include <mutex>
#include <iostream>
#include <numeric>  // iota
#include <concepts> // copyable 

/* T: noexcept copyable and assignable */
template<std::copyable T>
class MxStack {
    std::vector<T> data_;
    std::mutex mx_;

public:
    MxStack() : data_{} {}

    bool isEmpty() const { return data_.empty(); }

    void push(const T& val) {
        std::lock_guard<std::mutex> g{mx_};
        data_.push_back(val);
    }

    T pop() {
        std::lock_guard g{mx_};
        if(data_.empty())
            throw std::length_error{"empty stack"};
        T tmp{std::move(data_.back())};
        data_.pop_back();
        return tmp;
    }
};

int main() {
    // Prepare stack
    MxStack<int> mxs{};
    for(int i=1; i<=1'000'000; ++i) mxs.push(i);
    // Define computation
    auto sumIt = [&mxs](long &sum) {
        int val{};
        try {
            while( ! mxs.isEmpty()) {
                sum += mxs.pop(); // might still throw
            }
        } catch(std::length_error &ex) {}
    };
    // Compute
    long sum1 = 0;           // for partial result
    std::jthread th1{sumIt, std::ref(sum1)};
    long sum2 = 0;           // for partial result
    std::thread th2{sumIt, std::ref(sum2)};
    th1.join(); th2.join();
    long sum = sum1 + sum2; // Total result
    // Result
    std::cout << "Expected result: "
        << (1'000'000L*1'000'001)/2 << '\n'; // Output: 500000500000
    std::cout << "Actual: "
        << sum << '\n';                      // Output: 500000500000
}

Listing 29.22: “swap” for “MxStack”.

Book listing lst-0032-book.cpp:

// https://godbolt.org/z/9PMcz1cqP 
friend void swap(MxStack& re, MxStack& li) {
    if(&re==&li) return; // Same address? Swapping with itself is unnecessary
    std::lock( re.mx_, li.mx_ );   // multiple locks simultaneously
    std::lock_guard lkre{re.mx_, std::adopt_lock}; // already locked
    std::lock_guard lkli{li.mx_, std::adopt_lock}; // already locked
    std::swap(li.data_, re.data_); // perform swap
}

Godbolt Listing lst-0032-godb.cpp, https://godbolt.org/z/9PMcz1cqP:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/9PMcz1cqP 
friend void swap(MxStack& re, MxStack& li) {
    if(&re==&li) return; // Same address? Swapping with itself is unnecessary
    std::lock( re.mx_, li.mx_ );   // multiple locks simultaneously
    std::lock_guard lkre{re.mx_, std::adopt_lock}; // already locked
    std::lock_guard lkli{li.mx_, std::adopt_lock}; // already locked
    std::swap(li.data_, re.data_); // perform swap
}

Listing 29.23: Mutexes can be transferred.

Book listing lst-0033-book.cpp:

// https://godbolt.org/z/e7rraPGx4 
#include <thread>
#include <mutex>
#include <vector>
#include <numeric> // accumulate, iota
using std::mutex; using std::unique_lock;

std::vector<int> myData;              // shared data
mutex myMutex;                        // Mutex for the data
unique_lock<mutex> prepareData() {
    unique_lock lk1{myMutex};        // lock
    myData.resize(1000);
    std::iota(myData.begin(), myData.end(), 1); // 1..1000
    return lk1;                                 // Transfer lock
}
int processData() {
    unique_lock lk2 = prepareData();         // Lock transferred
    return std::accumulate(myData.begin(), myData.end(), 0);
}

Godbolt Listing lst-0033-godb.cpp, https://godbolt.org/z/e7rraPGx4:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/e7rraPGx4 
#include <thread>
#include <mutex>
#include <vector>
#include <numeric> // accumulate, iota
using std::mutex; using std::unique_lock;

std::vector<int> myData;              // shared data
mutex myMutex;                        // Mutex for the data
unique_lock<mutex> prepareData() {
    unique_lock lk1{myMutex};        // lock
    myData.resize(1000);
    std::iota(myData.begin(), myData.end(), 1); // 1..1000
    return lk1;                                 // Transfer lock
}
int processData() {
    unique_lock lk2 = prepareData();         // Lock transferred
    return std::accumulate(myData.begin(), myData.end(), 0);
}

Listing 29.24: Late initialization with a single thread.

Book listing lst-0034-book.cpp:

// https://godbolt.org/z/T7vehq58b 
std::shared_ptr<BigData> bigData{};
BigData& getBigData() {
    if(!bigData) bigData.reset(new BigData{});
    return *bigData;
}
int useBigData() {
    auto bigData = getBigData();
    // bigData->…
}

Godbolt Listing lst-0034-godb.cpp, https://godbolt.org/z/T7vehq58b:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/T7vehq58b 
std::shared_ptr<BigData> bigData{};
BigData& getBigData() {
    if(!bigData) bigData.reset(new BigData{});
    return *bigData;
}
int useBigData() {
    auto bigData = getBigData();
    // bigData->…
}

Listing 29.25: Late initialization with multiple threads.

Book listing lst-0035-book.cpp:

// https://godbolt.org/z/3vcnzfqfM 
#include <mutex> // once_flag, call_once
std::shared_ptr<BigData> bigData{};
std::once_flag bigDataInitFlag;
void initBigData() {
    bigData = std::make_shared<BigData>();
}
int useBigData() {
    std::call_once(bigDataInitFlag, initBigData);
    // bigData->…
}

Godbolt Listing lst-0035-godb.cpp, https://godbolt.org/z/3vcnzfqfM:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/3vcnzfqfM 
#include <mutex> // once_flag, call_once
std::shared_ptr<BigData> bigData{};
std::once_flag bigDataInitFlag;
void initBigData() {
    bigData = std::make_shared<BigData>();
}
int useBigData() {
    std::call_once(bigDataInitFlag, initBigData);
    // bigData->…
}

Listing 29.26: Late initialization can also be useful within a thread.

Book listing lst-0036-book.cpp:

// https://godbolt.org/z/TcsrefEPh 
#include <mutex> // once_flag, call_once
#include <memory>
struct Connection {
    void csend(const char *data) {} // dummy
    const char* crecv() {} // dummy
};
class Sender {
    std::shared_ptr<Connection> conn_;
    std::once_flag connInitFlag_;
    void open() {
      conn_.reset( new Connection{} );
    }
public:
    void send(const char* data) {
      std::call_once(connInitFlag_, &Sender::open, this); // method pointer
      conn_->csend(data);
    }
    const char* recv() {
      std::call_once(connInitFlag_, [this] {this->open();} ); // lambda
      return conn_->crecv();
    }
};

Godbolt Listing lst-0036-godb.cpp, https://godbolt.org/z/TcsrefEPh:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/TcsrefEPh 
#include <mutex> // once_flag, call_once
#include <memory>
struct Connection {
    void csend(const char *data) {} // dummy
    const char* crecv() {} // dummy
};
class Sender {
    std::shared_ptr<Connection> conn_;
    std::once_flag connInitFlag_;
    void open() {
      conn_.reset( new Connection{} );
    }
public:
    void send(const char* data) {
      std::call_once(connInitFlag_, &Sender::open, this); // method pointer
      conn_->csend(data);
    }
    const char* recv() {
      std::call_once(connInitFlag_, [this] {this->open();} ); // lambda
      return conn_->crecv();
    }
};

Listing 29.27: Precautions are necessary when a mutex needs to be locked multiple times.

Book listing lst-0037-book.cpp:

// https://godbolt.org/z/KPaPoh4Gn
#include <mutex> // recursive_mutex
#include <iostream>
struct MulDiv {
    std::recursive_mutex mx_;
    int value_;
    explicit MulDiv(int value) : value_{value} {}
    void mul(int x) {
        std::lock_guard lk1(mx_);  // inner
        value_ *= x;
    }
    void div(int x) {
        std::lock_guard lk2(mx_);  // inner
        value_ /= x;
    }
    void muldiv(int x, int y){
        std::lock_guard lk3(mx_);  // outer
        mul(x);
        div(y);
    }
};
int main() {
   MulDiv m{42}; // 3*7*2 *5
   m.muldiv(5, 15);
   std::cout << m.value_ << '\n';  // Output: 14
}

Godbolt Listing lst-0037-godb.cpp, https://godbolt.org/z/KPaPoh4Gn:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23 -Wno-unused-result; libs:boost@184
// https://godbolt.org/z/KPaPoh4Gn
#include <mutex> // recursive_mutex
#include <iostream>
struct MulDiv {
    std::recursive_mutex mx_;
    int value_;
    explicit MulDiv(int value) : value_{value} {}
    void mul(int x) {
        std::lock_guard lk1(mx_);  // inner
        value_ *= x;
    }
    void div(int x) {
        std::lock_guard lk2(mx_);  // inner
        value_ /= x;
    }
    void muldiv(int x, int y){
        std::lock_guard lk3(mx_);  // outer
        mul(x);
        div(y);
    }
};
int main() {
   MulDiv m{42}; // 3*7*2 *5
   m.muldiv(5, 15);
   std::cout << m.value_ << '\n';  // Output: 14
}

Listing 29.28: Memory only for the current thread.

Book listing lst-0038-book.cpp:

// https://godbolt.org/z/1j8W7GjTf 
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
thread_local unsigned int usage = 0;
static std::mutex cout_mutex;
void use(const std::string thread_name) {
    ++usage;
    std::lock_guard lock(cout_mutex); // Protect output
    std::cout << thread_name << ": " << usage << '\n';
}
int main() {
    std::jthread a{use, "a"}, b{use, "b"};
    use("main");
}

Godbolt Listing lst-0038-godb.cpp, https://godbolt.org/z/1j8W7GjTf:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/1j8W7GjTf 
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
thread_local unsigned int usage = 0;
static std::mutex cout_mutex;
void use(const std::string thread_name) {
    ++usage;
    std::lock_guard lock(cout_mutex); // Protect output
    std::cout << thread_name << ": " << usage << '\n';
}
int main() {
    std::jthread a{use, "a"}, b{use, "b"};
    use("main");
}

Listing 29.29: Two threads communicate via a condition variable.

Book listing lst-0040-book.cpp:

// https://godbolt.org/z/c9x6xT8dr
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <deque>
#include <iostream>
#include <syncstream>   // osyncstream
std::deque<int> g_data{};          // Data exchange between threads
std::condition_variable g_condvar; // notify
std::mutex g_mx;                   // protects g_data during changes
void produce(int limit) {
  std::vector primes{2};           // prior primes as test-divisors
  for(int candidate=3; candidate < limit; candidate+=2) {
    for(int divisor : primes) {
      if(divisor*divisor > candidate) {     // candidate is prime
        std::lock_guard lk{g_mx};           // protect data
        g_data.push_back(candidate);        // fill
        g_condvar.notify_one();             // notify
        primes.push_back(candidate);        // for internal calculations
        break; // next prime candidate
      } else if(candidate % divisor == 0) { // not prime
        break;                              // next prime candidate
      } else {
        // next divisor to check
      }
    }
  }
  // notify all work done
  std::lock_guard lk{g_mx};            // protect data
  g_data.push_back(0);                 // fill with end marker
  g_condvar.notify_all();              // notify
}
void consume(char l, char r) {
  while(true) {                        // forever
    std::unique_lock lk{g_mx};
    g_condvar.wait(lk, []{ return !g_data.empty();});
    int prim = g_data.front();         // fetch data
    if(prim == 0) return;              // done; leave 0 for other consumers
    g_data.pop_front();
    lk.unlock();                       // release lock
    std::osyncstream osync{std::cout}; // synchronize output
    osync << l << prim << r <<' ';
  }
}
int main() {
  // a producer:
  std::jthread thProd{produce, 1'000};
  // three consumers
  std::jthread thCon1{consume, '[', ']' };
  std::jthread thCon2{consume, '<', '>' };
  std::jthread thCon3{consume, '{', '}' };
  // wait and finish
  thProd.join();
  thCon1.join(); thCon2.join(); thCon3.join();
  std::cout << '\n';
}

Godbolt Listing lst-0040-godb.cpp, https://godbolt.org/z/c9x6xT8dr:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23 -Wno-unused-result; libs:boost@184
// https://godbolt.org/z/c9x6xT8dr
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <deque>
#include <iostream>
#include <syncstream>   // osyncstream
std::deque<int> g_data{};          // Data exchange between threads
std::condition_variable g_condvar; // notify
std::mutex g_mx;                   // protects g_data during changes
void produce(int limit) {
  std::vector primes{2};           // prior primes as test-divisors
  for(int candidate=3; candidate < limit; candidate+=2) {
    for(int divisor : primes) {
      if(divisor*divisor > candidate) {     // candidate is prime
        std::lock_guard lk{g_mx};           // protect data
        g_data.push_back(candidate);        // fill
        g_condvar.notify_one();             // notify
        primes.push_back(candidate);        // for internal calculations
        break; // next prime candidate
      } else if(candidate % divisor == 0) { // not prime
        break;                              // next prime candidate
      } else {
        // next divisor to check
      }
    }
  }
  // notify all work done
  std::lock_guard lk{g_mx};            // protect data
  g_data.push_back(0);                 // fill with end marker
  g_condvar.notify_all();              // notify
}
void consume(char l, char r) {
  while(true) {                        // forever
    std::unique_lock lk{g_mx};
    g_condvar.wait(lk, []{ return !g_data.empty();});
    int prim = g_data.front();         // fetch data
    if(prim == 0) return;              // done; leave 0 for other consumers
    g_data.pop_front();
    lk.unlock();                       // release lock
    std::osyncstream osync{std::cout}; // synchronize output
    osync << l << prim << r <<' ';
  }
}
int main() {
  // a producer:
  std::jthread thProd{produce, 1'000};
  // three consumers
  std::jthread thCon1{consume, '[', ']' };
  std::jthread thCon2{consume, '<', '>' };
  std::jthread thCon3{consume, '{', '}' };
  // wait and finish
  thProd.join();
  thCon1.join(); thCon2.join(); thCon3.join();
  std::cout << '\n';
}

Listing 29.30: “async” can easily initiate asynchronous computations.

Book listing lst-0045-book.cpp:

// https://godbolt.org/z/Gh9Y5srd7 
#include <iostream>
#include <future>  // async
using std::cout; using std::endl;
long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }

int main() {
    auto f40 = std::async(fib, 40);
    auto f41 = std::async(fib, 41);
    auto f42 = std::async(fib, 42);
    auto f43 = std::async(fib, 43);
    /* ... at this point, further calculations can be made ... */
    cout << "fib(40): " << f40.get() << endl; // Output: fib(40): 102334155
    cout << "fib(41): " << f41.get() << endl; // Output: fib(41): 165580141
    cout << "fib(42): " << f42.get() << endl; // Output: fib(42): 267914296
    cout << "fib(43): " << f43.get() << endl; // Output: fib(43): 433494437
}

Godbolt Listing lst-0045-godb.cpp, https://godbolt.org/z/Gh9Y5srd7:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/Gh9Y5srd7 
#include <iostream>
#include <future>  // async
using std::cout; using std::endl;
long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }

int main() {
    auto f40 = std::async(fib, 40);
    auto f41 = std::async(fib, 41);
    auto f42 = std::async(fib, 42);
    auto f43 = std::async(fib, 43);
    /* ... at this point, further calculations can be made ... */
    cout << "fib(40): " << f40.get() << endl; // Output: fib(40): 102334155
    cout << "fib(41): " << f41.get() << endl; // Output: fib(41): 165580141
    cout << "fib(42): " << f42.get() << endl; // Output: fib(42): 267914296
    cout << "fib(43): " << f43.get() << endl; // Output: fib(43): 433494437
}

Listing 29.31: It is okay not to retrieve a result.

Book listing lst-0046-book.cpp:

// https://godbolt.org/z/7Mea1Y3Mn 
#include <iostream>
#include <future>  // async
using std::cout; using std::endl;
long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }

int main() {
    auto f40 = std::async(fib, 40);
    auto f41 = std::async(fib, 41);
    auto f42 = std::async(fib, 42);
    auto f43 = std::async(fib, 43);
    cout << "fib(40): " << f40.get() << endl; // Output: fib(40): 102334155
} // also waits for f41, f42, and f43.

Godbolt Listing lst-0046-godb.cpp, https://godbolt.org/z/7Mea1Y3Mn:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/7Mea1Y3Mn 
#include <iostream>
#include <future>  // async
using std::cout; using std::endl;
long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }

int main() {
    auto f40 = std::async(fib, 40);
    auto f41 = std::async(fib, 41);
    auto f42 = std::async(fib, 42);
    auto f43 = std::async(fib, 43);
    cout << "fib(40): " << f40.get() << endl; // Output: fib(40): 102334155
} // also waits for f41, f42, and f43.

Listing 29.32: How to enforce deferred execution.

Book listing lst-0047-book.cpp:

// https://godbolt.org/z/P9MjGe6Mn 
#include <iostream>
#include <future>  // async
#include <vector>
using std::cout; using std::endl;
long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }
int main() {
    // Prepare tasks
    std::vector< std::future<long> > fibs;
    for(int n=0; n<50; ++n) {
        auto fut = std::async(std::launch::deferred, fib, n);
        fibs.push_back( std::move(fut) );
    }
    // only retrieve the required result
    cout << "fib(42): " << fibs[42].get() << endl; // Output: fib(42): 267914296
}

Godbolt Listing lst-0047-godb.cpp, https://godbolt.org/z/P9MjGe6Mn:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/P9MjGe6Mn 
#include <iostream>
#include <future>  // async
#include <vector>
using std::cout; using std::endl;
long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }
int main() {
    // Prepare tasks
    std::vector< std::future<long> > fibs;
    for(int n=0; n<50; ++n) {
        auto fut = std::async(std::launch::deferred, fib, n);
        fibs.push_back( std::move(fut) );
    }
    // only retrieve the required result
    cout << "fib(42): " << fibs[42].get() << endl; // Output: fib(42): 267914296
}

Listing 29.33: Waiting for a certain time with “async”.

Book listing lst-0048-book.cpp:

// https://godbolt.org/z/o9oYrvMcj 
#include <iostream>
#include <future>  // async
#include <chrono>
using std::cout; using std::endl; using namespace std::chrono;
long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }
int main() {
    auto f43 = std::async(fib, 43);
    while(true) {
        auto ready = f43.wait_for(500ms);
        if(ready==std::future_status::timeout) {
            std::cout << "not yet..." << std::endl;
        } else {
            break;
        }
    }
    // pick up, is immediately there
    cout << "fib(43): " << f43.get() << endl; // Output: fib(43): 701408733
}

Godbolt Listing lst-0048-godb.cpp, https://godbolt.org/z/o9oYrvMcj:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/o9oYrvMcj 
#include <iostream>
#include <future>  // async
#include <chrono>
using std::cout; using std::endl; using namespace std::chrono;
long fib(long n) { return n<=1 ? n : fib(n-1)+fib(n-2); }
int main() {
    auto f43 = std::async(fib, 43);
    while(true) {
        auto ready = f43.wait_for(500ms);
        if(ready==std::future_status::timeout) {
            std::cout << "not yet..." << std::endl;
        } else {
            break;
        }
    }
    // pick up, is immediately there
    cout << "fib(43): " << f43.get() << endl; // Output: fib(43): 701408733
}

Listing 29.34: Waiting for a lock with a “timed_mutex”.

Book listing lst-0049-book.cpp:

// https://godbolt.org/z/xzY8hj9oP 
#include <chrono>
#include <future>
#include <mutex>
#include <vector>
#include <iostream>
std::timed_mutex mtx;
long fibX(long n) { return n < 2L ? 1L : fibX(n-1L) + fibX(n-2L); }
long fibCall(long n) {
    using namespace std::chrono; // Suffixes
    if(mtx.try_lock_for(1000ms)) {
        auto res = fibX(n);
        mtx.unlock();
        return res;
    } else {
        return 0L;
    }
}

int main() {
    std::vector< std::future<long> > fs;
    for(long n=1; n<= 42; ++n) {
        fs.emplace_back( std::async(std::launch::async, fibCall, n) );
    }
    for(auto &f : fs) {
        std::cout << f.get() << " ";
    }
    std::cout << std::endl;
}

Godbolt Listing lst-0049-godb.cpp, https://godbolt.org/z/xzY8hj9oP:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/xzY8hj9oP 
#include <chrono>
#include <future>
#include <mutex>
#include <vector>
#include <iostream>
std::timed_mutex mtx;
long fibX(long n) { return n < 2L ? 1L : fibX(n-1L) + fibX(n-2L); }
long fibCall(long n) {
    using namespace std::chrono; // Suffixes
    if(mtx.try_lock_for(1000ms)) {
        auto res = fibX(n);
        mtx.unlock();
        return res;
    } else {
        return 0L;
    }
}

int main() {
    std::vector< std::future<long> > fs;
    for(long n=1; n<= 42; ++n) {
        fs.emplace_back( std::async(std::launch::async, fibCall, n) );
    }
    for(auto &f : fs) {
        std::cout << f.get() << " ";
    }
    std::cout << std::endl;
}

Listing 29.35: Exceptions only arrive at “get” in the outer thread.

Book listing lst-0050-book.cpp:

// https://godbolt.org/z/rYM9xqsGa
#include <future> // async
#include <vector>
#include <algorithm> // max
#include <iostream>

int calculateHeight(int count, int maxCount, int scale) {
  if(maxCount == 0)
      throw std::logic_error("All heights 0");
  return (count * scale) / maxCount;
}

void bar(const std::vector<int> &counts) {
  // Start calculation
  auto maxCount = *std::max_element(counts.begin(), counts.end());
  std::vector< std::future<int> > futs;
  for(int count : counts) {
    futs.push_back(
          std::async(std::launch::async,
              calculateHeight, count, maxCount, 200) );
  }

  // Collect results
  for(auto &fut : futs) {
    std::cout << fut.get() << ' ';                 // triggers exception
  }
  std::cout << '\n';
}

int main() {
  try {
    bar(std::vector {10,23,13,0,33,4 });         // Output: 60 139 78 0 200 24
    bar(std::vector { 0, 0, 0, 0 });             // triggers exception
  } catch(std::exception &ex) {
    std::cout << "Error: " << ex.what() << '\n'; // Output: Error: All heights 0
  }
}

Godbolt Listing lst-0050-godb.cpp, https://godbolt.org/z/rYM9xqsGa:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23 -Wno-unused-result; libs:boost@184
// https://godbolt.org/z/rYM9xqsGa
#include <future> // async
#include <vector>
#include <algorithm> // max
#include <iostream>

int calculateHeight(int count, int maxCount, int scale) {
  if(maxCount == 0)
      throw std::logic_error("All heights 0");
  return (count * scale) / maxCount;
}

void bar(const std::vector<int> &counts) {
  // Start calculation
  auto maxCount = *std::max_element(counts.begin(), counts.end());
  std::vector< std::future<int> > futs;
  for(int count : counts) {
    futs.push_back(
          std::async(std::launch::async,
              calculateHeight, count, maxCount, 200) );
  }

  // Collect results
  for(auto &fut : futs) {
    std::cout << fut.get() << ' ';                 // triggers exception
  }
  std::cout << '\n';
}

int main() {
  try {
    bar(std::vector {10,23,13,0,33,4 });         // Output: 60 139 78 0 200 24
    bar(std::vector { 0, 0, 0, 0 });             // triggers exception
  } catch(std::exception &ex) {
    std::cout << "Error: " << ex.what() << '\n'; // Output: Error: All heights 0
  }
}

Listing 29.36: Only the “get” needs to be encapsulated in “try”.

Book listing lst-0051-book.cpp:

// https://godbolt.org/z/1WG5arvWT
#include <future> // async
#include <iostream>
using namespace std;
int calcHeight(int count, int maxCount, int scale) {
  if(maxCount == 0)
      throw logic_error("maxCount is 0");
  return (count * scale) / maxCount;
}

int main() {
  auto fut = async(launch::async, calcHeight,0,0,200); // (ERR) throws
  try {
    cout << fut.get() << '\n';              // triggers exception
  } catch(exception &ex) {
    cout << "Error: " << ex.what() << '\n'; // Output: Error: maxCount is 0
  }
}

Godbolt Listing lst-0051-godb.cpp, https://godbolt.org/z/1WG5arvWT:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/1WG5arvWT
#include <future> // async
#include <iostream>
using namespace std;
int calcHeight(int count, int maxCount, int scale) {
  if(maxCount == 0)
      throw logic_error("maxCount is 0");
  return (count * scale) / maxCount;
}

int main() {
  auto fut = async(launch::async, calcHeight,0,0,200); // (ERR) throws
  try {
    cout << fut.get() << '\n';              // triggers exception
  } catch(exception &ex) {
    cout << "Error: " << ex.what() << '\n'; // Output: Error: maxCount is 0
  }
}

Listing 29.37: A “future” and “promise” working together.

Book listing lst-0053-book.cpp:

// https://godbolt.org/z/7E1E6aezE
#include <future>
#include <thread>
#include <iostream>
#include <exception>
int ack(int m, int n); // Ackermann function
void longComputation(std::promise<int> intPromise) {
  try {
    int result = ack(3,12);
    intPromise.set_value(result);                       // report result
  } catch (std::exception &e) {
    intPromise.set_exception(make_exception_ptr(e));    // report exception
  } catch ( ... ) {
    intPromise.set_exception(std::current_exception()); // Exc. w/o name
  }
}
int main () {
  std::promise<int> intPromise;                         // Create promise
  std::future<int> intFuture = intPromise.get_future(); // Request future
  std::jthread th{ longComputation,                     // start
             std::move(intPromise) };                   // Pass promise
  th.detach();                                          // let it run
  // might throw an exception:
  int result = intFuture.get();                         // Request result
  std::cout << result << std::endl;
}

Godbolt Listing lst-0053-godb.cpp, https://godbolt.org/z/7E1E6aezE:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23 -Wno-unused-result; libs:boost@184
// https://godbolt.org/z/7E1E6aezE
#include <future>
#include <thread>
#include <iostream>
#include <exception>
int ack(int m, int n); // Ackermann function
void longComputation(std::promise<int> intPromise) {
  try {
    int result = ack(3,12);
    intPromise.set_value(result);                       // report result
  } catch (std::exception &e) {
    intPromise.set_exception(make_exception_ptr(e));    // report exception
  } catch ( ... ) {
    intPromise.set_exception(std::current_exception()); // Exc. w/o name
  }
}
int main () {
  std::promise<int> intPromise;                         // Create promise
  std::future<int> intFuture = intPromise.get_future(); // Request future
  std::jthread th{ longComputation,                     // start
             std::move(intPromise) };                   // Pass promise
  th.detach();                                          // let it run
  // might throw an exception:
  int result = intFuture.get();                         // Request result
  std::cout << result << std::endl;
}

Listing 29.38: Preparing a packaged task for later execution.

Book listing lst-0054-book.cpp:

// https://godbolt.org/z/9s94nbrx3 
#include <future>
#include <thread>
#include <iostream>
int ack(int m, int n); // Ackermann function
int main () {
    std::packaged_task<int(void)> task1 {  // Signature of the remaining function
    []{ return ack(3,11);}  };             // prepare ack(3,11)
  auto f1 = task1.get_future();            // Communication channel
  std::jthread th1 { move(task1) };        // in new thread
  std::cout << "  ack(3,11):" << f1.get()  // Retrieve result
      << '\n';                             // Output: ack(3,11):16381
}

Godbolt Listing lst-0054-godb.cpp, https://godbolt.org/z/9s94nbrx3:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/9s94nbrx3 
#include <future>
#include <thread>
#include <iostream>
int ack(int m, int n); // Ackermann function
int main () {
    std::packaged_task<int(void)> task1 {  // Signature of the remaining function
    []{ return ack(3,11);}  };             // prepare ack(3,11)
  auto f1 = task1.get_future();            // Communication channel
  std::jthread th1 { move(task1) };        // in new thread
  std::cout << "  ack(3,11):" << f1.get()  // Retrieve result
      << '\n';                             // Output: ack(3,11):16381
}

Listing 29.39: Provide a packaged task with arguments later.

Book listing lst-0055-book.cpp:

// https://godbolt.org/z/bq1hn5rjo 
#include <future>
#include <thread>
#include <iostream>
int ack(int m, int n); // Ackermann function
int main () {
  std::packaged_task<int(int,int)> task2 { &ack }; // different signature
  auto f2 = task2.get_future();
  std::jthread th2 { move(task2), 3, 12 };         // Parameters here
  std::cout << "  ack(3,12):" << f2.get() << '\n'; // Output: ack(3,12):32765
  th2.join();
}

Godbolt Listing lst-0055-godb.cpp, https://godbolt.org/z/bq1hn5rjo:

//#(compile) c++; compiler:g132; options:-O3 -std=c++23; libs:-
// https://godbolt.org/z/bq1hn5rjo 
#include <future>
#include <thread>
#include <iostream>
int ack(int m, int n); // Ackermann function
int main () {
  std::packaged_task<int(int,int)> task2 { &ack }; // different signature
  auto f2 = task2.get_future();
  std::jthread th2 { move(task2), 3, 12 };         // Parameters here
  std::cout << "  ack(3,12):" << f2.get() << '\n'; // Output: ack(3,12):32765
  th2.join();
}

Listing 29.40: How to determine if the operations are lock-free.

Book listing lst-0056-book.cpp:

// https://godbolt.org/z/dG318o9ez
#include <iostream>
#include <atomic>

struct CArray { int a[100]; };
struct Simple { int x, y; };

int main() {
    std::atomic<CArray> carray{};
    std::cout << (carray.is_lock_free() ? "lock-free" : "locks")
        << '\n';                               // Output: locks
    std::atomic<Simple> simple{};
    std::cout << (simple.is_lock_free() ? "lock-free" : "locks")
        << '\n';                               // Output: lock-free
}

Godbolt Listing lst-0056-godb.cpp, https://godbolt.org/z/dG318o9ez:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23 -latomic; libs:boost@184
// https://godbolt.org/z/dG318o9ez
#include <iostream>
#include <atomic>

struct CArray { int a[100]; };
struct Simple { int x, y; };

int main() {
    std::atomic<CArray> carray{};
    std::cout << (carray.is_lock_free() ? "lock-free" : "locks")
        << '\n';                               // Output: locks
    std::atomic<Simple> simple{};
    std::cout << (simple.is_lock_free() ? "lock-free" : "locks")
        << '\n';                               // Output: lock-free
}

Listing 29.41: The “SpinlockMutex” class prevents sleeping while waiting.

Book listing lst-0057-book.cpp:

// https://godbolt.org/z/EnjPr5r76
#include <atomic>
class SpinlockMutex {
  std::atomic_flag flag_;
public:
  SpinlockMutex()
  : flag_{ATOMIC_FLAG_INIT}
  {}
  void lock() {                               // e.g., called by lock_guard
    while(flag_.test_and_set(std::memory_order_acquire)) // mostly read
      { /* nothing */ }
  }
  void unlock() {
    flag_.clear(std::memory_order_release);              // write operation
  }
};

Godbolt Listing lst-0057-godb.cpp, https://godbolt.org/z/EnjPr5r76:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23 -latomic; libs:-
// https://godbolt.org/z/EnjPr5r76
#include <atomic>
class SpinlockMutex {
  std::atomic_flag flag_;
public:
  SpinlockMutex()
  : flag_{ATOMIC_FLAG_INIT}
  {}
  void lock() {                               // e.g., called by lock_guard
    while(flag_.test_and_set(std::memory_order_acquire)) // mostly read
      { /* nothing */ }
  }
  void unlock() {
    flag_.clear(std::memory_order_release);              // write operation
  }
};

Listing 29.42: Your coroutine returns a “generator”.

Book listing lst-0058-book.cpp:

// https://godbolt.org/z/Gfazq6GE7 
#include <generator>
#include <iostream>
#include <vector>

std::generator<int> fib(int n) { // generates int values
  int a = 0, b = 1;
  while (--n) {
    co_yield b;                  // makes this function a coroutine
    auto tmp = a;
    a = b;
    b += tmp;
  }
}
int main() {
  for (auto i : fib(10)) std::cout << i << ' ';
  std::cout << '\n';              // Output: 1 1 2 3 5 8 13 21 34 55
}

Godbolt Listing lst-0058-godb.cpp, https://godbolt.org/z/Gfazq6GE7:

//#(compile) c++; compiler:gsnapshot; options:"-std=c++23"; libs:-
// https://godbolt.org/z/Gfazq6GE7 
#include <generator>
#include <iostream>
#include <vector>

std::generator<int> fib(int n) { // generates int values
  int a = 0, b = 1;
  while (--n) {
    co_yield b;                  // makes this function a coroutine
    auto tmp = a;
    a = b;
    b += tmp;
  }
}
int main() {
  for (auto i : fib(10)) std::cout << i << ' ';
  std::cout << '\n';              // Output: 1 1 2 3 5 8 13 21 34 55
}

Listing 29.43: A simple example of a coroutine return type with its “promise_type”.

Book listing lst-0059-book.cpp:

// https://godbolt.org/z/8enx43Ksb
#include <coroutine>
#include <iostream>
struct Routine { // Coroutine return type
  struct promise_type;                     // defined further ahead
  Routine(auto h) : hdl_(h) {}             // Constructor
  ~Routine() { if(hdl_) hdl_.destroy(); }  // Destructor
  Routine(const Routine&) = delete;        // no copies
  Routine(Routine&&) = delete;             // no moves
  // Interface for the caller:
  bool more() {                            // continue coroutine
    if(!hdl_ || hdl_.done()) return false; // - nothing more to do
    hdl_.resume();                         // - blocking call
    return !hdl_.done();                   // - signals done or not
  }
private:
  using handle_type_ = std::coroutine_handle<promise_type>;
  handle_type_ hdl_;     // to control the coroutine
};
struct Routine::promise_type {             // necessary promise_type
  auto get_return_object() {               // initializes the handle
    return Routine{ handle_type_::from_promise(*this) };
  }
  auto initial_suspend() { return std::suspend_never{}; } // start immediately
  auto final_suspend() noexcept { return std::suspend_always{}; } // normal case
  void unhandled_exception() {}            // no exception handling
  void return_void() {}                    // Coroutine has no return
};
Routine counter() {  // Return type is our wrapper class with the promise_type
  for (unsigned i = 0;; ++i) {
    co_await std::suspend_always{};        // Pause coroutine
    std::cout << "counter: " << i << std::endl;
  }
}

int main() {
  Routine routine = counter();             // Create and start coroutine
  for (int i = 0; i < 3; ++i) {
      std::cout << "Hello main!\n";
      routine.more();                      // continue coroutine
  }
}

Godbolt Listing lst-0059-godb.cpp, https://godbolt.org/z/8enx43Ksb:

//#(compile) c++; compiler:g141; options:-O1 -std=c++23; libs:-
// https://godbolt.org/z/8enx43Ksb
#include <coroutine>
#include <iostream>
struct Routine { // Coroutine return type
  struct promise_type;                     // defined further ahead
  Routine(auto h) : hdl_(h) {}             // Constructor
  ~Routine() { if(hdl_) hdl_.destroy(); }  // Destructor
  Routine(const Routine&) = delete;        // no copies
  Routine(Routine&&) = delete;             // no moves
  // Interface for the caller:
  bool more() {                            // continue coroutine
    if(!hdl_ || hdl_.done()) return false; // - nothing more to do
    hdl_.resume();                         // - blocking call
    return !hdl_.done();                   // - signals done or not
  }
private:
  using handle_type_ = std::coroutine_handle<promise_type>;
  handle_type_ hdl_;     // to control the coroutine
};
struct Routine::promise_type {             // necessary promise_type
  auto get_return_object() {               // initializes the handle
    return Routine{ handle_type_::from_promise(*this) };
  }
  auto initial_suspend() { return std::suspend_never{}; } // start immediately
  auto final_suspend() noexcept { return std::suspend_always{}; } // normal case
  void unhandled_exception() {}            // no exception handling
  void return_void() {}                    // Coroutine has no return
};
Routine counter() {  // Return type is our wrapper class with the promise_type
  for (unsigned i = 0;; ++i) {
    co_await std::suspend_always{};        // Pause coroutine
    std::cout << "counter: " << i << std::endl;
  }
}

int main() {
  Routine routine = counter();             // Create and start coroutine
  for (int i = 0; i < 3; ++i) {
      std::cout << "Hello main!\n";
      routine.more();                      // continue coroutine
  }
}