You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
205 lines
6.6 KiB
205 lines
6.6 KiB
#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
|
|
|