Skip to content
C++ Basics: Wait for State with a Condition-Variable Predicate

C++ Basics: Wait for State with a Condition-Variable Predicate

November 3, 2026

Protect the condition with a mutex and recheck it whenever a wait returns.

Prepare a scratch directory

Tested on Linux x86_64 with GCC 14.2.0. Install a C++20-capable GCC compiler and its standard-library development files, with g++ on PATH. You also need Bash, a text editor, and mktemp, rm, and rmdir. No third-party library is required.

Keep the same terminal open. Stop if setup fails before saving the source:

start_directory="$PWD"
work_directory="$(mktemp -d)" || exit 1
cd -- "$work_directory" || exit 1

Run the example

Save this as condition-variable-predicate-demo.cpp:

#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>

int main()
{
    std::mutex state_mutex;
    std::condition_variable state_changed;
    bool ready = false;
    int payload = 0;
    int observed = 0;

    std::jthread consumer([&] {
        std::unique_lock lock(state_mutex);
        state_changed.wait(lock, [&] { return ready; });
        observed = payload;
    });
    {
        std::lock_guard lock(state_mutex);
        payload = 42;
        ready = true;
    }
    state_changed.notify_one();
    consumer.join();
    std::cout << "Received: " << observed << '\n';
}

Compile and run it. The second command runs only after compilation succeeds:

g++ -std=c++20 -Wall -Wextra -Werror -pedantic -pthread condition-variable-predicate-demo.cpp -o condition-variable-predicate-demo &&
./condition-variable-predicate-demo

Observed output:

Received: 42

Read the result

The consumer reads ready while holding the same mutex used by the producer. The wait releases the mutex while sleeping and reacquires it before checking the predicate again. If the producer finishes first, the predicate is already true and the consumer does not need a remembered notification. Main reads observed only after joining the consumer.

Apply it carefully

A notification is not stored work. Keep the actual readiness state under the mutex, and use the predicate overload to handle spurious wakeups. This one-item example never resets ready. A reusable queue needs a predicate such as nonempty or closed, and a shutdown path that wakes blocked consumers.

The C++ working draft documents the relevant rules. This example uses C++20 facilities; the current draft also contains newer additions.

Clean up

Remove only the source and executable created here, then remove the empty practice directory:

rm -- condition-variable-predicate-demo.cpp condition-variable-predicate-demo
cd -- "$start_directory" || exit 1
rmdir -- "$work_directory"
unset start_directory work_directory

If the directory is not empty, inspect its remaining contents before removing anything else.

Acknowledgements

This post was created with assistance from AI.

Last updated on