liveBook · Manning

Call_once

As explained in C++ Concurrency in Action, Second Edition

`std::call_once` is a C++ standard library function template that ensures a callable is executed exactly once for a given `std::once_flag`.

std::call_once coordinates one-time initialization across threads. Pair a std::once_flag with each initialization, then pass the flag and a function or other callable to std::call_once. When the call returns, the initialized data is available with the required synchronization. section 3.3.1

std::shared_ptr<some_resource> resource_ptr;
std::once_flag resource_flag;                       #1
void init_resource()
{
    resource_ptr.reset(new some_resource);
}
void foo()
{
    std::call_once(resource_flag,init_resource);    #2
    resource_ptr->do_something();
}

A shared resource is initialized by one callable; each caller uses call_once before accessing it. section 3.3.1

The same flag can protect a class member initialized on the first call to either of two methods. This avoids manually checking a shared pointer outside the lock, which can introduce a data race and undefined behavior. Where one-time initialization fits, call_once typically has lower overhead than explicit mutex locking, especially after initialization is complete. section 3.3.1

Listing 3.12. Thread-safe lazy initialization of a class member using std::call_once
class X
{
private:
    connection_info connection_details;
    connection_handle connection;
    std::once_flag connection_init_flag;
    void open_connection()
    {
        connection=connection_manager.open(connection_details);
    }
public:
    X(connection_info const& connection_details_):
        connection_details(connection_details_)
    {}
    void send_data(data_packet const& data)                             #1
    {
        std::call_once(connection_init_flag,&X::open_connection,this);  #2
        connection.send_data(data);
    }
    data_packet receive_data()                                          #3
    {
        std::call_once(connection_init_flag,&X::open_connection,this);  #2
        return connection.receive_data();
    }
};


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":14,\"ch\":72},{\"line\":14,\"ch\":74}],[{\"line\":16,\"ch\":72},{\"line\":16,\"ch\":74}],[{\"line\":21,\"ch\":72},{\"line\":21,\"ch\":74}],[{\"line\":19,\"ch\":72},{\"line\":19,\"ch\":74}],[{\"line\":16,\"ch\":72},{\"line\":16,\"ch\":74}],[{\"line\":21,\"ch\":72},{\"line\":21,\"ch\":74}]]"}
!@%STYLE%@!

Both send_data and receive_data use the same flag to ensure the connection is opened before either method uses it. section 3.3.1

How C++ Concurrency in Action, Second Edition covers it

How deep it goes
Explains flag association, callable forms, synchronization, and resource and member initialization; defers detailed memory-model treatment.
Examples
Uses a shared resource pointer and a class connection initialized before send or receive operations.
What you can do after
Apply it via std::once_flag and std::call_once to initialize shared resources or class members safely.
How it connects
Connects one-time initialization with mutexes, data races, lazy initialization, and shared data.

Also explained in

Compare how 3 books explain call_once →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of C++ Concurrency in Action, Second Edition. The section links point to the parts of the book it draws on.