206 lines
6.6 KiB
C++
206 lines
6.6 KiB
C++
#ifndef BOOST_THREAD_WIN32_ONCE_HPP
|
|
#define BOOST_THREAD_WIN32_ONCE_HPP
|
|
|
|
// once.hpp
|
|
//
|
|
// (C) Copyright 2005-7 Anthony Williams
|
|
// (C) Copyright 2005 John Maddock
|
|
//
|
|
// 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 <cstring>
|
|
#include <cstddef>
|
|
#include <boost/assert.hpp>
|
|
#include <boost/static_assert.hpp>
|
|
#include <boost/detail/interlocked.hpp>
|
|
#include <boost/thread/win32/thread_primitives.hpp>
|
|
#include <boost/thread/win32/interlocked_read.hpp>
|
|
|
|
#include <boost/config/abi_prefix.hpp>
|
|
|
|
#ifdef BOOST_NO_STDC_NAMESPACE
|
|
namespace std
|
|
{
|
|
using ::memcpy;
|
|
using ::ptrdiff_t;
|
|
}
|
|
#endif
|
|
|
|
namespace boost
|
|
{
|
|
struct once_flag
|
|
{
|
|
long status;
|
|
long count;
|
|
};
|
|
|
|
#define BOOST_ONCE_INIT {0,0}
|
|
|
|
namespace detail
|
|
{
|
|
#ifdef BOOST_NO_ANSI_APIS
|
|
typedef wchar_t once_char_type;
|
|
#else
|
|
typedef char once_char_type;
|
|
#endif
|
|
unsigned const once_mutex_name_fixed_length=54;
|
|
unsigned const once_mutex_name_length=once_mutex_name_fixed_length+
|
|
sizeof(void*)*2+sizeof(unsigned long)*2+1;
|
|
|
|
template <class I>
|
|
void int_to_string(I p, once_char_type* buf)
|
|
{
|
|
for(unsigned i=0; i < sizeof(I)*2; ++i,++buf)
|
|
{
|
|
#ifdef BOOST_NO_ANSI_APIS
|
|
once_char_type const a=L'A';
|
|
#else
|
|
once_char_type const a='A';
|
|
#endif
|
|
*buf = a + static_cast<once_char_type>((p >> (i*4)) & 0x0f);
|
|
}
|
|
*buf = 0;
|
|
}
|
|
|
|
inline void name_once_mutex(once_char_type* mutex_name,void* flag_address)
|
|
{
|
|
#ifdef BOOST_NO_ANSI_APIS
|
|
static const once_char_type fixed_mutex_name[]=L"Local\\{C15730E2-145C-4c5e-B005-3BC753F42475}-once-flag";
|
|
#else
|
|
static const once_char_type fixed_mutex_name[]="Local\\{C15730E2-145C-4c5e-B005-3BC753F42475}-once-flag";
|
|
#endif
|
|
BOOST_STATIC_ASSERT(sizeof(fixed_mutex_name) ==
|
|
(sizeof(once_char_type)*(once_mutex_name_fixed_length+1)));
|
|
|
|
std::memcpy(mutex_name,fixed_mutex_name,sizeof(fixed_mutex_name));
|
|
detail::int_to_string(reinterpret_cast<std::ptrdiff_t>(flag_address),
|
|
mutex_name + once_mutex_name_fixed_length);
|
|
detail::int_to_string(win32::GetCurrentProcessId(),
|
|
mutex_name + once_mutex_name_fixed_length + sizeof(void*)*2);
|
|
}
|
|
|
|
inline void* open_once_event(once_char_type* mutex_name,void* flag_address)
|
|
{
|
|
if(!*mutex_name)
|
|
{
|
|
name_once_mutex(mutex_name,flag_address);
|
|
}
|
|
|
|
#ifdef BOOST_NO_ANSI_APIS
|
|
return ::boost::detail::win32::OpenEventW(
|
|
#else
|
|
return ::boost::detail::win32::OpenEventA(
|
|
#endif
|
|
::boost::detail::win32::synchronize |
|
|
::boost::detail::win32::event_modify_state,
|
|
false,
|
|
mutex_name);
|
|
}
|
|
|
|
inline void* create_once_event(once_char_type* mutex_name,void* flag_address)
|
|
{
|
|
if(!*mutex_name)
|
|
{
|
|
name_once_mutex(mutex_name,flag_address);
|
|
}
|
|
#ifdef BOOST_NO_ANSI_APIS
|
|
return ::boost::detail::win32::CreateEventW(
|
|
#else
|
|
return ::boost::detail::win32::CreateEventA(
|
|
#endif
|
|
0,::boost::detail::win32::manual_reset_event,
|
|
::boost::detail::win32::event_initially_reset,
|
|
mutex_name);
|
|
}
|
|
}
|
|
|
|
|
|
template<typename Function>
|
|
void call_once(once_flag& flag,Function f)
|
|
{
|
|
// Try for a quick win: if the procedure has already been called
|
|
// just skip through:
|
|
long const function_complete_flag_value=0xc15730e2;
|
|
long const running_value=0x7f0725e3;
|
|
long status;
|
|
bool counted=false;
|
|
detail::win32::handle_manager event_handle;
|
|
detail::once_char_type mutex_name[detail::once_mutex_name_length];
|
|
mutex_name[0]=0;
|
|
|
|
while((status=::boost::detail::interlocked_read_acquire(&flag.status))
|
|
!=function_complete_flag_value)
|
|
{
|
|
status=BOOST_INTERLOCKED_COMPARE_EXCHANGE(&flag.status,running_value,0);
|
|
if(!status)
|
|
{
|
|
try
|
|
{
|
|
if(!event_handle)
|
|
{
|
|
event_handle=detail::open_once_event(mutex_name,&flag);
|
|
}
|
|
if(event_handle)
|
|
{
|
|
::boost::detail::win32::ResetEvent(event_handle);
|
|
}
|
|
f();
|
|
if(!counted)
|
|
{
|
|
BOOST_INTERLOCKED_INCREMENT(&flag.count);
|
|
counted=true;
|
|
}
|
|
BOOST_INTERLOCKED_EXCHANGE(&flag.status,function_complete_flag_value);
|
|
if(!event_handle &&
|
|
(::boost::detail::interlocked_read_acquire(&flag.count)>1))
|
|
{
|
|
event_handle=detail::create_once_event(mutex_name,&flag);
|
|
}
|
|
if(event_handle)
|
|
{
|
|
::boost::detail::win32::SetEvent(event_handle);
|
|
}
|
|
break;
|
|
}
|
|
catch(...)
|
|
{
|
|
BOOST_INTERLOCKED_EXCHANGE(&flag.status,0);
|
|
if(!event_handle)
|
|
{
|
|
event_handle=detail::open_once_event(mutex_name,&flag);
|
|
}
|
|
if(event_handle)
|
|
{
|
|
::boost::detail::win32::SetEvent(event_handle);
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
if(!counted)
|
|
{
|
|
BOOST_INTERLOCKED_INCREMENT(&flag.count);
|
|
counted=true;
|
|
status=::boost::detail::interlocked_read_acquire(&flag.status);
|
|
if(status==function_complete_flag_value)
|
|
{
|
|
break;
|
|
}
|
|
if(!event_handle)
|
|
{
|
|
event_handle=detail::create_once_event(mutex_name,&flag);
|
|
continue;
|
|
}
|
|
}
|
|
BOOST_VERIFY(!::boost::detail::win32::WaitForSingleObject(
|
|
event_handle,::boost::detail::win32::infinite));
|
|
}
|
|
}
|
|
}
|
|
|
|
#include <boost/config/abi_suffix.hpp>
|
|
|
|
#endif
|