c++multithreadinggccruntime-errorstd-call-once

I can't reproduce the function memoization from Functional Programming in C++


The following code should be almost a copy of what the book Functional Programming in C++ presents at the end of Chapter 6 section 1:

#include <iostream>
#include <utility>
#include <mutex>

template<typename F>
class lazy_val {
  private:
    F m_computation;
    mutable decltype(m_computation()) m_cache;
    mutable std::once_flag m_value_flag;
  public:
    lazy_val(F f)
        : m_computation(f)
    {}
    operator const decltype(m_computation())& () const {
        std::call_once(m_value_flag, [this](){
            m_cache = m_computation();
        });
        return m_cache; // the execution never gets to this line
    }
};

int expensive() {
    std::cout << "expensive call...\n";
    return 7;
}

int main() {
    std::cout << "non memoized" << '\n';
    std::cout << expensive() << '\n';
    std::cout << expensive() << '\n';
    std::cout << expensive() << '\n';
    const auto expensive_memo = lazy_val(expensive);
    std::cout << "memoized" << '\n';
    std::cout << expensive_memo << '\n'; // crash happens here
    std::cout << expensive_memo << '\n';
    std::cout << expensive_memo << '\n';
}

However, when I execute it (the compilation is goes fine), I get this error:

non memoized
expensive call...
7
expensive call...
7
expensive call...
7
memoized
terminate called after throwing an instance of 'std::system_error'
  what():  Unknown error -1
Aborted (core dumped)

If ignore concurrency issues and only rely on a mutable bool m_cache_initialized; initialized to false, and a if (!m_cache_initialized) { m_cache = m_computation(); m_cache_initialized = true; }, then everything works.

This makes me think that the problem is with how I use std::call_once/std::once_flag in my code. However I don't see how it is different from what is shown in the book (constructor in Listing 6.2, but without the line initializing the m_cache_initialized to false, and the rest of the class is at the bottom of page 125 and top of 126).


Solution

  • You must be using Linux and gcc. An "undocumented" feature of this implementation is that any C++ code that uses any thread-related stuff must be explicitly linked with -lpthread.

    I reproduced your exact crash on Linux, after compiling (with -std=c++17) and linking without -lpthread. The shown code runs fine, if explicitly linked with -lpthread.