A once_flag which is failable
From
Bonita Montero@3:633/10 to
All on Sat Oct 25 11:52:56 2025
I was annoyed that the initialition-lambda of once_flag can be
bypassed only if you throw an exception. So I wrote xonce_flag
that optionally accepts a bool from the initialization-lambda
and when it is false the initialization stops.
"defer" is similary to experimental::scope_exit.
#pragma once
#include <atomic>
#include "defer.h"
struct xonce_flag
{
xonce_flag() = default;
xonce_flag( xonce_flag const & );
private:
template<typename Fn>
friend bool xcall_once( xonce_flag &onceFlag, Fn fn );
std::atomic<int8_t> m_flag = 0;
};
xonce_flag::xonce_flag( xonce_flag const &other ) :
m_flag( other.m_flag.load( std::memory_order_relaxed ) )
{
}
template<typename Fn>
bool xcall_once( xonce_flag &once, Fn fn )
{
using namespace std;
auto load = [&] { return once.m_flag.load( memory_order_acquire ); };
for( int8_t flag = load(); flag <= 0; ) [[likely]]
{
if( flag < 0 )
{
once.m_flag.wait( flag, memory_order_relaxed );
flag = load();
continue;
}
if( !once.m_flag.compare_exchange_strong( flag, -1, memory_order_acquire, memory_order_relaxed ) )
continue;
defer finally( [&]
{
once.m_flag.store( 0, memory_order_relaxed );
once.m_flag.notify_all();
} );
if constexpr( !requires { { fn() } -> convertible_to<bool>; } )
fn();
else
if( !fn() )
return false;
finally.disable();
once.m_flag.store( 1, memory_order_release );
once.m_flag.notify_all();
return true;
}
return true;
}
--- PyGate Linux v1.5
* Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)