• Alternative for call_once

    From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++ on Sun Aug 16 19:20:36 2026
    From Newsgroup: comp.lang.c++

    // header

    #include <atomic>

    struct once_mutex
    {
    once_mutex() noexcept = default;
    once_mutex( const once_mutex & ) = delete;
    once_mutex &operator =( const once_mutex & ) = delete;
    bool lock() noexcept;
    void commit( bool success = true ) noexcept;
    operator bool() const noexcept;
    private:
    std::atomic_schar m_lock = 0;
    };

    struct once_guard
    {
    once_guard() noexcept = default;
    once_guard( once_mutex &, bool autoSucess = false ) noexcept;
    ~once_guard() noexcept;
    once_guard( once_guard && ) noexcept;
    once_guard &operator =( once_guard && ) noexcept;
    bool lock( once_mutex &, bool autoSucess = false ) noexcept;
    void commit( bool success = true ) noexcept;
    operator bool() const noexcept;
    private:
    once_mutex *m_pMtx = nullptr;
    bool m_autoSuccess = false;
    };

    // cpp

    #include <atomic>
    #include "once_mutex.hpp"

    using namespace std;

    bool once_mutex::lock() noexcept
    {
    for( signed char ref = m_lock.load( memory_order_acquire ); ; )
    if( ref > 0 ) [[likely]]
    return false;
    else if( ref < 0 )
    {
    m_lock.wait( ref, memory_order_relaxed );
    ref = m_lock.load( memory_order_acquire );
    }
    else if( m_lock.compare_exchange_strong( ref, -1, memory_order_relaxed, memory_order_relaxed ) )
    return true;
    }

    void once_mutex::commit( bool success ) noexcept
    {
    for( signed char ref = m_lock.load( memory_order_relaxed ); ; )
    if( ref >= 0 )
    return;
    else if( m_lock.compare_exchange_strong( ref, (char)success, memory_order_release, memory_order_relaxed ) )
    break;
    if( success )
    m_lock.notify_all();
    else
    m_lock.notify_one();
    }

    once_mutex::operator bool() const noexcept
    {
    return m_lock.load( memory_order_relaxed ) > 0;
    }

    once_guard::once_guard( once_mutex &om, bool autoSucess ) noexcept
    {
    if( !om.lock() )
    return;
    m_pMtx = &om;
    m_autoSuccess = autoSucess;
    }

    once_guard::~once_guard() noexcept
    {
    if( m_pMtx )
    m_pMtx->commit( m_autoSuccess );
    }

    once_guard::once_guard( once_guard &&om ) noexcept :
    m_pMtx( om.m_pMtx )
    {
    om.m_pMtx = nullptr;
    }

    once_guard &once_guard::operator =( once_guard &&om ) noexcept
    {
    commit();
    m_pMtx = om.m_pMtx;
    om.m_pMtx = nullptr;
    return *this;
    }

    bool once_guard::lock( once_mutex &mtx, bool autoSucess ) noexcept
    {
    commit( autoSucess );
    if( !mtx.lock() )
    return false;
    m_pMtx = &mtx;
    m_autoSuccess = autoSucess;
    return true;
    }

    void once_guard::commit( bool success ) noexcept
    {
    if( !m_pMtx )
    return;
    m_pMtx->commit( success );
    m_pMtx = nullptr;
    }

    once_guard::operator bool() const noexcept
    {
    return (bool)m_pMtx;
    }
    --- Synchronet 3.22a-Linux NewsLink 1.2