I don't understand how work call_once
- by SABROG
Please help me understand how work call_once
Here is thread-safe code. I don't understand why this need Thread Local Storage and global_epoch variables. Variable _fast_pthread_once_per_thread_epoch can be changed to constant/enum like {FAST_PTHREAD_ONCE_INIT, BEING_INITIALIZED, FINISH_INITIALIZED}. Why needed count calls in global_epoch? I think this code can be rewriting with logc:
if flag FINISH_INITIALIZED do nothing, else go to block with mutexes and this all.
#ifndef FAST_PTHREAD_ONCE_H
#define FAST_PTHREAD_ONCE_H
#include
#include
typedef sig_atomic_t fast_pthread_once_t;
#define FAST_PTHREAD_ONCE_INIT SIG_ATOMIC_MAX
extern __thread fast_pthread_once_t _fast_pthread_once_per_thread_epoch;
#ifdef __cplusplus
extern "C" {
#endif
extern void fast_pthread_once( pthread_once_t *once, void (*func)(void) );
inline static void fast_pthread_once_inline(
fast_pthread_once_t *once, void (*func)(void) )
{
fast_pthread_once_t x = *once; /* unprotected access */
if ( x _fast_pthread_once_per_thread_epoch ) {
fast_pthread_once( once, func );
}
}
#ifdef __cplusplus
}
#endif
#endif FAST_PTHREAD_ONCE_H
Source fast_pthread_once.c
The source is written in C. The lines of the primary function are numbered for reference in the subsequent correctness argument.
#include "fast_pthread_once.h"
#include
static pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER;
/* protects global_epoch and all fast_pthread_once_t writes */
static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
/* signalled whenever a fast_pthread_once_t is finalized */
#define BEING_INITIALIZED (FAST_PTHREAD_ONCE_INIT - 1)
static fast_pthread_once_t global_epoch = 0; /* under mu */
__thread fast_pthread_once_t _fast_pthread_once_per_thread_epoch;
static void check( int x )
{
if ( x == 0 )
abort();
}
void fast_pthread_once( fast_pthread_once_t *once, void (*func)(void) )
{
/*01*/ fast_pthread_once_t x = *once; /* unprotected access */
/*02*/ if ( x _fast_pthread_once_per_thread_epoch ) {
/*03*/ check( pthread_mutex_lock(µ) == 0 );
/*04*/ if ( *once == FAST_PTHREAD_ONCE_INIT ) {
/*05*/ *once = BEING_INITIALIZED;
/*06*/ check( pthread_mutex_unlock(µ) == 0 );
/*07*/ (*func)();
/*08*/ check( pthread_mutex_lock(µ) == 0 );
/*09*/ global_epoch++;
/*10*/ *once = global_epoch;
/*11*/ check( pthread_cond_broadcast(&cv;) == 0 );
/*12*/ } else {
/*13*/ while ( *once == BEING_INITIALIZED ) {
/*14*/ check( pthread_cond_wait(&cv;, µ) == 0 );
/*15*/ }
/*16*/ }
/*17*/ _fast_pthread_once_per_thread_epoch = global_epoch;
/*18*/ check (pthread_mutex_unlock(µ) == 0);
}
}
This code from BOOST:
#ifndef BOOST_THREAD_PTHREAD_ONCE_HPP
#define BOOST_THREAD_PTHREAD_ONCE_HPP
// once.hpp
//
// (C) Copyright 2007-8 Anthony Williams
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include
#include
#include
#include "pthread_mutex_scoped_lock.hpp"
#include
#include
#include
namespace boost
{
struct once_flag
{
boost::uintmax_t epoch;
};
namespace detail
{
BOOST_THREAD_DECL boost::uintmax_t& get_once_per_thread_epoch();
BOOST_THREAD_DECL extern boost::uintmax_t once_global_epoch;
BOOST_THREAD_DECL extern pthread_mutex_t once_epoch_mutex;
BOOST_THREAD_DECL extern pthread_cond_t once_epoch_cv;
}
#define BOOST_ONCE_INITIAL_FLAG_VALUE 0
#define BOOST_ONCE_INIT {BOOST_ONCE_INITIAL_FLAG_VALUE}
// Based on Mike Burrows fast_pthread_once algorithm as described in
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2444.html
template
void call_once(once_flag& flag,Function f)
{
static boost::uintmax_t const uninitialized_flag=BOOST_ONCE_INITIAL_FLAG_VALUE;
static boost::uintmax_t const being_initialized=uninitialized_flag+1;
boost::uintmax_t const epoch=flag.epoch;
boost::uintmax_t& this_thread_epoch=detail::get_once_per_thread_epoch();
if(epoch
#endif
I right understand, boost don't use atomic operation, so code from boost not thread-safe?