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