diff --git a/Jamfile.v2 b/Jamfile.v2 index 9fbcdc6..8333597 100644 --- a/Jamfile.v2 +++ b/Jamfile.v2 @@ -2,4 +2,4 @@ import testing ; # Tests from Jamfiles in individual library test subdirectories build-project example ; # test-suite interprocess_example -build-project test ; # test-suite interprocess_test +build-project test ; # test-suite interprocess_test \ No newline at end of file diff --git a/doc/interprocess.qbk b/doc/interprocess.qbk index 25d6da8..bb1dc41 100644 --- a/doc/interprocess.qbk +++ b/doc/interprocess.qbk @@ -6716,7 +6716,8 @@ thank them: [section:release_notes_boost_1_55_00 Boost 1.55 Release] * Fixed bugs [@https://svn.boost.org/trac/boost/ticket/7164 #7164], - [@https://svn.boost.org/trac/boost/ticket/8277 #8277]. + [@https://svn.boost.org/trac/boost/ticket/8277 #8277], + [@https://svn.boost.org/trac/boost/ticket/8277 #9908]. [endsect] diff --git a/include/boost/interprocess/detail/intermodule_singleton_common.hpp b/include/boost/interprocess/detail/intermodule_singleton_common.hpp index 485f98d..60ec593 100644 --- a/include/boost/interprocess/detail/intermodule_singleton_common.hpp +++ b/include/boost/interprocess/detail/intermodule_singleton_common.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -145,7 +146,7 @@ class intermodule_singleton_common //If previous state was initializing, this means that another winner thread is //trying to initialize the singleton. Just wait until completes its work. else if(previous_module_singleton_initialized == Initializing){ - unsigned int k = 0; + spin_wait swait; while(1){ previous_module_singleton_initialized = atomic_read32(&this_module_singleton_initialized); if(previous_module_singleton_initialized >= Initialized){ @@ -153,7 +154,7 @@ class intermodule_singleton_common break; } else if(previous_module_singleton_initialized == Initializing){ - yield(k++); + swait.yield(); } else{ //This can't be happening! @@ -207,7 +208,7 @@ class intermodule_singleton_common static void initialize_global_map_handle() { //Obtain unique map name and size - unsigned k = 0; + spin_wait swait; while(1){ //Try to pass map state to initializing ::boost::uint32_t tmp = atomic_cas32(&this_module_map_initialized, Initializing, Uninitialized); @@ -220,7 +221,7 @@ class intermodule_singleton_common } //If some other thread is doing the work wait else if(tmp == Initializing){ - yield(k++); + swait.yield(); } else{ //(tmp == Uninitialized) //If not initialized try it again? diff --git a/include/boost/interprocess/detail/managed_open_or_create_impl.hpp b/include/boost/interprocess/detail/managed_open_or_create_impl.hpp index 389f9f2..948b4f4 100644 --- a/include/boost/interprocess/detail/managed_open_or_create_impl.hpp +++ b/include/boost/interprocess/detail/managed_open_or_create_impl.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -354,7 +355,7 @@ class managed_open_or_create_impl //file and know if we have really created it or just open it //drop me a e-mail! bool completed = false; - unsigned k = 0; + spin_wait swait; while(!completed){ try{ create_device(dev, id, size, perm, file_like_t()); @@ -385,7 +386,7 @@ class managed_open_or_create_impl catch(...){ throw; } - yield(k++); + swait.yield(); } } @@ -432,12 +433,12 @@ class managed_open_or_create_impl else{ if(FileBased){ offset_t filesize = 0; - unsigned k = 0; + spin_wait swait; while(filesize == 0){ if(!get_file_size(file_handle_from_mapping_handle(dev.get_mapping_handle()), filesize)){ throw interprocess_exception(error_info(system_error_code())); } - yield(k++); + swait.yield(); } if(filesize == 1){ throw interprocess_exception(error_info(corrupted_error)); @@ -449,9 +450,9 @@ class managed_open_or_create_impl boost::uint32_t *patomic_word = static_cast(region.get_address()); boost::uint32_t value = atomic_read32(patomic_word); - unsigned k = 0; + spin_wait swait; while(value == InitializingSegment || value == UninitializedSegment){ - yield(k++); + swait.yield(); value = atomic_read32(patomic_word); } diff --git a/include/boost/interprocess/detail/os_thread_functions.hpp b/include/boost/interprocess/detail/os_thread_functions.hpp index dd86ba0..32e019d 100644 --- a/include/boost/interprocess/detail/os_thread_functions.hpp +++ b/include/boost/interprocess/detail/os_thread_functions.hpp @@ -15,18 +15,36 @@ #include #include #include -#include +#include #if defined(BOOST_INTERPROCESS_WINDOWS) # include #else -# ifdef BOOST_HAS_UNISTD_H -# include -# include -# include -# include +# include +# include +# include +# include +# ifdef BOOST_INTERPROCESS_BSD_DERIVATIVE + //Some *BSD systems (OpenBSD & NetBSD) need sys/param.h before sys/sysctl.h, whereas + //others (FreeBSD & Darwin) need sys/types.h +# include +# include +# include +# endif +//According to the article "C/C++ tip: How to measure elapsed real time for benchmarking" +# if defined(CLOCK_MONOTONIC_PRECISE) //BSD +# define BOOST_INTERPROCESS_CLOCK_MONOTONIC CLOCK_MONOTONIC_PRECISE +# elif defined(CLOCK_MONOTONIC_RAW) //Linux +# define BOOST_INTERPROCESS_CLOCK_MONOTONIC CLOCK_MONOTONIC_RAW +# elif defined(CLOCK_HIGHRES) //Solaris +# define BOOST_INTERPROCESS_CLOCK_MONOTONIC CLOCK_HIGHRES +# elif defined(CLOCK_MONOTONIC) //POSIX (AIX, BSD, Linux, Solaris) +# define BOOST_INTERPROCESS_CLOCK_MONOTONIC CLOCK_MONOTONIC +# elif !defined(CLOCK_MONOTONIC) && (defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)) +# include // mach_absolute_time, mach_timebase_info_data_t +# define BOOST_INTERPROCESS_MATCH_ABSOLUTE_TIME # else -# error Unknown platform +# error "No high resolution steady clock in your system, please provide a patch" # endif #endif @@ -57,6 +75,75 @@ inline OS_thread_id_t get_invalid_thread_id() inline bool equal_thread_id(OS_thread_id_t id1, OS_thread_id_t id2) { return id1 == id2; } +//return the system tick in ns +inline unsigned long get_system_tick_ns() +{ + unsigned long curres; + winapi::set_timer_resolution(10000, 0, &curres); + //Windows API returns the value in hundreds of ns + return (curres - 1ul)*100ul; +} + +//return the system tick in us +inline unsigned long get_system_tick_us() +{ + unsigned long curres; + winapi::set_timer_resolution(10000, 0, &curres); + //Windows API returns the value in hundreds of ns + return (curres - 1ul)/10ul + 1ul; +} + +typedef unsigned __int64 OS_highres_count_t; + +inline OS_highres_count_t get_system_tick_in_highres_counts() +{ + __int64 freq; + unsigned long curres; + winapi::set_timer_resolution(10000, 0, &curres); + //Frequency in counts per second + if(!winapi::query_performance_frequency(&freq)){ + //Tick resolution in ms + return (curres-1)/10000u + 1; + } + else{ + //In femtoseconds + __int64 count_fs = __int64(1000000000000000LL - 1LL)/freq + 1LL; + __int64 tick_counts = (__int64(curres)*100000000LL - 1LL)/count_fs + 1LL; + return static_cast(tick_counts); + } +} + +inline OS_highres_count_t get_current_system_highres_count() +{ + __int64 count; + if(!winapi::query_performance_counter(&count)){ + count = winapi::get_tick_count(); + } + return count; +} + +inline void zero_highres_count(OS_highres_count_t &count) +{ count = 0; } + +inline bool is_highres_count_zero(const OS_highres_count_t &count) +{ return count == 0; } + +template +inline Ostream &ostream_highres_count(Ostream &ostream, const OS_highres_count_t &count) +{ + ostream << count; + return ostream; +} + +inline OS_highres_count_t system_highres_count_subtract(const OS_highres_count_t &l, const OS_highres_count_t &r) +{ return l - r; } + +inline bool system_highres_count_less(const OS_highres_count_t &l, const OS_highres_count_t &r) +{ return l < r; } + +inline void thread_sleep_tick() +{ winapi::sleep_tick(); } + inline void thread_yield() { winapi::sched_yield(); } @@ -98,6 +185,13 @@ inline long double get_current_process_creation_time() CreationTime.dwLowDateTime*resolution; } +inline unsigned int get_num_cores() +{ + winapi::system_info sysinfo; + winapi::get_system_info( &sysinfo ); + //in Windows dw is long which is equal in bits to int + return static_cast(sysinfo.dwNumberOfProcessors); +} #else //#if (defined BOOST_INTERPROCESS_WINDOWS) @@ -165,9 +259,132 @@ inline bool equal_thread_id(OS_thread_id_t id1, OS_thread_id_t id2) inline void thread_yield() { ::sched_yield(); } +#ifndef BOOST_INTERPROCESS_MATCH_ABSOLUTE_TIME +typedef struct timespec OS_highres_count_t; +#else +typedef unsigned long long OS_highres_count_t; +#endif + +inline unsigned long get_system_tick_ns() +{ + #ifdef _SC_CLK_TCK + long hz =::sysconf(_SC_CLK_TCK); // ticks per sec + if(hz <= 0){ //Try a typical value on error + hz = 100; + } + return 999999999ul/static_cast(hz)+1ul; + #else + #error "Can't obtain system tick value for your system, please provide a patch" + #endif +} + +inline OS_highres_count_t get_system_tick_in_highres_counts() +{ + #ifndef BOOST_INTERPROCESS_MATCH_ABSOLUTE_TIME + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = get_system_tick_ns(); + return ts; + #else + mach_timebase_info_data_t info; + mach_timebase_info(&info); + //ns + return static_cast + ( + static_cast(get_system_tick_ns()) + / (static_cast(info.numer) / info.denom) + ); + #endif +} + +//return system ticks in us +inline unsigned long get_system_tick_us() +{ + return (get_system_tick_ns()-1)/1000ul + 1ul; +} + +inline OS_highres_count_t get_current_system_highres_count() +{ + #if defined(BOOST_INTERPROCESS_CLOCK_MONOTONIC) + struct timespec count; + ::clock_gettime(BOOST_INTERPROCESS_CLOCK_MONOTONIC, &count); + return count; + #elif defined(BOOST_INTERPROCESS_MATCH_ABSOLUTE_TIME) + return ::mach_absolute_time(); + #endif +} + +#ifndef BOOST_INTERPROCESS_MATCH_ABSOLUTE_TIME + +inline void zero_highres_count(OS_highres_count_t &count) +{ count.tv_sec = 0; count.tv_nsec = 0; } + +inline bool is_highres_count_zero(const OS_highres_count_t &count) +{ return count.tv_sec == 0 && count.tv_nsec == 0; } + +template +inline Ostream &ostream_highres_count(Ostream &ostream, const OS_highres_count_t &count) +{ + ostream << count.tv_sec << "s:" << count.tv_nsec << "ns"; + return ostream; +} + +inline OS_highres_count_t system_highres_count_subtract(const OS_highres_count_t &l, const OS_highres_count_t &r) +{ + OS_highres_count_t res; + + if (l.tv_nsec < r.tv_nsec){ + res.tv_nsec = 1000000000 + l.tv_nsec - r.tv_nsec; + res.tv_sec = l.tv_sec - 1 - r.tv_sec; + } + else{ + res.tv_nsec = l.tv_nsec - r.tv_nsec; + res.tv_sec = l.tv_sec - r.tv_sec; + } + + return res; +} + +inline bool system_highres_count_less(const OS_highres_count_t &l, const OS_highres_count_t &r) +{ return l.tv_sec < r.tv_sec || (l.tv_sec == r.tv_sec && l.tv_nsec < r.tv_nsec); } + +#else + +inline void zero_highres_count(OS_highres_count_t &count) +{ count = 0; } + +inline bool is_highres_count_zero(const OS_highres_count_t &count) +{ return count == 0; } + +template +inline Ostream &ostream_highres_count(Ostream &ostream, const OS_highres_count_t &count) +{ + ostream << count ; + return ostream; +} + +inline OS_highres_count_t system_highres_count_subtract(const OS_highres_count_t &l, const OS_highres_count_t &r) +{ return l - r; } + +inline bool system_highres_count_less(const OS_highres_count_t &l, const OS_highres_count_t &r) +{ return l < r; } + +#endif + +inline void thread_sleep_tick() +{ + struct timespec rqt; + //Sleep for the half of the tick time + rqt.tv_sec = 0; + rqt.tv_nsec = get_system_tick_ns()/2; + ::nanosleep(&rqt, 0); +} + inline void thread_sleep(unsigned int ms) { - const struct timespec rqt = { ms/1000u, (ms%1000u)*1000000u }; + struct timespec rqt; + rqt.tv_sec = ms/1000u; + rqt.tv_nsec = (ms%1000u)*1000000u; ::nanosleep(&rqt, 0); } @@ -190,6 +407,39 @@ inline OS_systemwide_thread_id_t get_invalid_systemwide_thread_id() inline long double get_current_process_creation_time() { return 0.0L; } +inline unsigned int get_num_cores() +{ + #ifdef _SC_NPROCESSORS_ONLN + long cores = ::sysconf(_SC_NPROCESSORS_ONLN); + // sysconf returns -1 if the name is invalid, the option does not exist or + // does not have a definite limit. + // if sysconf returns some other negative number, we have no idea + // what is going on. Default to something safe. + if(cores <= 0){ + return 1; + } + //Check for overflow (unlikely) + else if(static_cast(cores) >= + static_cast(static_cast(-1))){ + return static_cast(-1); + } + else{ + return static_cast(cores); + } + #elif defined(BOOST_INTERPROCESS_BSD_DERIVATIVE) && defined(HW_NCPU) + int request[2] = { CTL_HW, HW_NCPU }; + int num_cores; + std::size_t result_len = sizeof(num_cores); + if ( (::sysctl (request, 2, &num_cores, &result_len, 0, 0) < 0) || (num_cores <= 0) ){ + //Return a safe value + return 1; + } + else{ + return static_cast(num_cores); + } + #endif +} + #endif //#if (defined BOOST_INTERPROCESS_WINDOWS) typedef char pid_str_t[sizeof(OS_process_id_t)*3+1]; diff --git a/include/boost/interprocess/detail/robust_emulation.hpp b/include/boost/interprocess/detail/robust_emulation.hpp index 5722589..e0866dc 100644 --- a/include/boost/interprocess/detail/robust_emulation.hpp +++ b/include/boost/interprocess/detail/robust_emulation.hpp @@ -24,6 +24,7 @@ #include #include #include +#include #include namespace boost{ @@ -228,7 +229,7 @@ inline void robust_spin_mutex::lock() //Now the logic. Try to lock, if successful mark the owner //if it fails, start recovery logic - unsigned int spin_count = 0; + spin_wait swait; while(1){ if (mtx.try_lock()){ atomic_write32(&this->owner, get_current_process_id()); @@ -236,14 +237,10 @@ inline void robust_spin_mutex::lock() } else{ //Do the dead owner checking each spin_threshold lock tries - yield(spin_count); - ++spin_count; - if(spin_count > spin_threshold){ + swait.yield(); + if(0 == (swait.count() & 255u)){ //Check if owner dead and take ownership if possible - if(!this->robust_check()){ - spin_count = 0; - } - else{ + if(this->robust_check()){ break; } } @@ -292,7 +289,7 @@ inline bool robust_spin_mutex::timed_lock if(now >= abs_time) return this->try_lock(); - unsigned k = 0; + spin_wait swait; do{ if(this->try_lock()){ break; @@ -303,7 +300,7 @@ inline bool robust_spin_mutex::timed_lock return this->try_lock(); } // relinquish current time slice - ipcdetail::yield(k++); + swait.yield(); }while (true); return true; diff --git a/include/boost/interprocess/detail/tmp_dir_helpers.hpp b/include/boost/interprocess/detail/tmp_dir_helpers.hpp index e4e867e..2af1a8a 100644 --- a/include/boost/interprocess/detail/tmp_dir_helpers.hpp +++ b/include/boost/interprocess/detail/tmp_dir_helpers.hpp @@ -58,7 +58,7 @@ namespace ipcdetail { struct ::timeval result; std::size_t result_len = sizeof result; - if (::sysctl (request, 2, &result, &result_len, NULL, 0) < 0) + if (::sysctl (request, 2, &result, &result_len, 0, 0) < 0) return; char bootstamp_str[256]; diff --git a/include/boost/interprocess/detail/win32_api.hpp b/include/boost/interprocess/detail/win32_api.hpp index 4bffb51..8d82dbf 100644 --- a/include/boost/interprocess/detail/win32_api.hpp +++ b/include/boost/interprocess/detail/win32_api.hpp @@ -883,6 +883,7 @@ extern "C" __declspec(dllimport) int __stdcall GetProcessTimes , interprocess_filetime *lpExitTime,interprocess_filetime *lpKernelTime , interprocess_filetime *lpUserTime ); extern "C" __declspec(dllimport) void __stdcall Sleep(unsigned long); +extern "C" __declspec(dllimport) unsigned long __stdcall GetTickCount(void); extern "C" __declspec(dllimport) int __stdcall SwitchToThread(); extern "C" __declspec(dllimport) unsigned long __stdcall GetLastError(); extern "C" __declspec(dllimport) void __stdcall SetLastError(unsigned long); @@ -943,7 +944,6 @@ extern "C" __declspec(dllimport) int __stdcall FreeLibrary(void *); extern "C" __declspec(dllimport) void *__stdcall GetProcAddress(void *, const char*); extern "C" __declspec(dllimport) void *__stdcall GetModuleHandleA(const char*); extern "C" __declspec(dllimport) void *__stdcall GetFileInformationByHandle(void *, interprocess_by_handle_file_information*); -extern "C" __declspec(dllimport) int __stdcall QueryPerformanceCounter(__int64 *lpPerformanceCount); //Advapi32.dll extern "C" __declspec(dllimport) long __stdcall RegOpenKeyExA(void *, const char *, unsigned long, unsigned long, void **); @@ -1013,6 +1013,12 @@ typedef long (__stdcall *NtQuerySection_t)(void*, section_information_class, int typedef long (__stdcall *NtQueryInformationFile_t)(void *,io_status_block_t *,void *, long, int); typedef long (__stdcall *NtOpenFile_t)(void*,unsigned long ,object_attributes_t*,io_status_block_t*,unsigned long,unsigned long); typedef long (__stdcall *NtClose_t) (void*); +typedef long (__stdcall *NtQueryTimerResolution_t) (unsigned long* LowestResolution, unsigned long* HighestResolution, unsigned long* CurrentResolution); +typedef long (__stdcall *NtSetTimerResolution_t) (unsigned long RequestedResolution, int Set, unsigned long* ActualResolution); + +//kernel32.dll +typedef int (__stdcall *QueryPerformanceCounter_t) (__int64 *lpPerformanceCount); +typedef int (__stdcall *QueryPerformanceFrequency_t)(__int64 *lpFrequency); } //namespace winapi { } //namespace interprocess { @@ -1047,10 +1053,13 @@ inline unsigned long make_lang_id(unsigned long p, unsigned long s) inline void sched_yield() { if(!SwitchToThread()){ - Sleep(1); + Sleep(0); } } +inline void sleep_tick() +{ Sleep(1); } + inline void sleep(unsigned long ms) { Sleep(ms); } @@ -1270,11 +1279,6 @@ inline long reg_query_value_ex(void *hKey, const char *lpValueName, unsigned lon inline long reg_close_key(void *hKey) { return RegCloseKey(hKey); } -inline bool query_performance_counter(__int64 *lpPerformanceCount) -{ - return 0 != QueryPerformanceCounter(lpPerformanceCount); -} - inline void initialize_object_attributes ( object_attributes_t *pobject_attr, unicode_string_t *name , unsigned long attr, void *rootdir, void *security_descr) @@ -1299,8 +1303,20 @@ inline void rtl_init_empty_unicode_string(unicode_string_t *ucStr, wchar_t *buf, template struct function_address_holder { - enum { NtSetInformationFile, NtQuerySystemInformation, NtQueryObject, NtQuerySemaphore, NtQuerySection, NtOpenFile, NtClose, NumFunction }; - enum { NtDll_dll, NumModule }; + enum { NtSetInformationFile + , NtQuerySystemInformation + , NtQueryObject + , NtQuerySemaphore + , NtQuerySection + , NtOpenFile + , NtClose + , NtQueryTimerResolution + , NtSetTimerResolution + , QueryPerformanceCounter + , QueryPerformanceFrequency + , NumFunction + }; + enum { NtDll_dll, Kernel32_dll, NumModule }; private: static const char *FunctionNames[NumFunction]; @@ -1314,21 +1330,26 @@ struct function_address_holder static void *get_module_from_id(unsigned int id) { BOOST_ASSERT(id < (unsigned int)NumModule); - return get_module_handle(ModuleNames[id]); + void *addr = get_module_handle(ModuleNames[id]); + BOOST_ASSERT(addr); + return addr; } static void *get_module(const unsigned int id) { BOOST_ASSERT(id < (unsigned int)NumModule); - while(ModuleStates[id] < 2){ + for(unsigned i = 0; ModuleStates[id] < 2; ++i){ if(interlocked_compare_exchange(&ModuleStates[id], 1, 0) == 0){ ModuleAddresses[id] = get_module_from_id(id); interlocked_increment(&ModuleStates[id]); break; } - else{ + else if(i & 1){ sched_yield(); } + else{ + sleep_tick(); + } } return ModuleAddresses[id]; } @@ -1336,22 +1357,27 @@ struct function_address_holder static void *get_address_from_dll(const unsigned int id) { BOOST_ASSERT(id < (unsigned int)NumFunction); - return get_proc_address(get_module(FunctionModules[id]), FunctionNames[id]); + void *addr = get_proc_address(get_module(FunctionModules[id]), FunctionNames[id]); + BOOST_ASSERT(addr); + return addr; } public: static void *get(const unsigned int id) { BOOST_ASSERT(id < (unsigned int)NumFunction); - while(FunctionStates[id] < 2){ + for(unsigned i = 0; FunctionStates[id] < 2; ++i){ if(interlocked_compare_exchange(&FunctionStates[id], 1, 0) == 0){ FunctionAddresses[id] = get_address_from_dll(id); interlocked_increment(&FunctionStates[id]); break; } - else{ + else if(i & 1){ sched_yield(); } + else{ + sleep_tick(); + } } return FunctionAddresses[id]; } @@ -1366,7 +1392,11 @@ const char *function_address_holder::FunctionNames[function_address_holde "NtQuerySemaphore", "NtQuerySection", "NtOpenFile", - "NtClose" + "NtClose", + "NtQueryTimerResolution", + "NtSetTimerResolution", + "QueryPerformanceCounter", + "QueryPerformanceFrequency" }; template @@ -1378,13 +1408,18 @@ unsigned int function_address_holder::FunctionModules[function_address_ho NtDll_dll, NtDll_dll, NtDll_dll, - NtDll_dll + NtDll_dll, + NtDll_dll, + NtDll_dll, + Kernel32_dll, + Kernel32_dll }; template const char *function_address_holder::ModuleNames[function_address_holder::NumModule] = { - "ntdll.dll" + "ntdll.dll", + "kernel32.dll" }; @@ -2065,11 +2100,8 @@ inline bool get_file_mapping_size(void *file_mapping_hnd, __int64 &size) interprocess_section_basic_information info; unsigned long ntstatus = pNtQuerySection(file_mapping_hnd, section_basic_information, &info, sizeof(info), 0); - if(ntstatus){ - return false; - } size = info.section_size; - return true; + return !ntstatus; } inline bool get_semaphore_info(void *handle, long &count, long &limit) @@ -2079,14 +2111,41 @@ inline bool get_semaphore_info(void *handle, long &count, long &limit) (winapi::NtQuerySemaphore_t)dll_func::get(winapi::dll_func::NtQuerySemaphore); unsigned int ret_len; long status = pNtQuerySemaphore(handle, winapi::semaphore_basic_information, &info, sizeof(info), &ret_len); - if(status){ - return false; - } count = info.count; limit = info.limit; - return true; + return !status; } +inline bool query_timer_resolution(unsigned long *lowres, unsigned long *highres, unsigned long *curres) +{ + winapi::NtQueryTimerResolution_t pNtQueryTimerResolution = + (winapi::NtQueryTimerResolution_t)dll_func::get(winapi::dll_func::NtQueryTimerResolution); + return !pNtQueryTimerResolution(lowres, highres, curres); +} + +inline bool set_timer_resolution(unsigned long RequestedResolution, int Set, unsigned long* ActualResolution) +{ + winapi::NtSetTimerResolution_t pNtSetTimerResolution = + (winapi::NtSetTimerResolution_t)dll_func::get(winapi::dll_func::NtSetTimerResolution); + return !pNtSetTimerResolution(RequestedResolution, Set, ActualResolution); +} + +inline bool query_performance_counter(__int64 *lpPerformanceCount) +{ + QueryPerformanceCounter_t pQueryPerformanceCounter = (QueryPerformanceCounter_t) + dll_func::get(dll_func::QueryPerformanceCounter); + return 0 != pQueryPerformanceCounter(lpPerformanceCount); +} + +inline bool query_performance_frequency(__int64 *lpFrequency) +{ + QueryPerformanceCounter_t pQueryPerformanceFrequency = (QueryPerformanceFrequency_t) + dll_func::get(dll_func::QueryPerformanceFrequency); + return 0 != pQueryPerformanceFrequency(lpFrequency); +} + +inline unsigned long get_tick_count() +{ return GetTickCount(); } } //namespace winapi } //namespace interprocess diff --git a/include/boost/interprocess/detail/yield_k.hpp b/include/boost/interprocess/detail/yield_k.hpp deleted file mode 100644 index bf05865..0000000 --- a/include/boost/interprocess/detail/yield_k.hpp +++ /dev/null @@ -1,136 +0,0 @@ -//This file was copied from boost/smart_ptr/detail and -//modified here to avoid dependencies with that library -#ifndef BOOST_INTERPROCESS_DETAIL_YIELD_K_HPP_INCLUDED -#define BOOST_INTERPROCESS_DETAIL_YIELD_K_HPP_INCLUDED - -// MS compatible compilers support #pragma once - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - -// -// yield_k.hpp -// -// Copyright (c) 2008 Peter Dimov -// -// void yield( unsigned k ); -// -// Typical use: -// -// for( unsigned k = 0; !try_lock(); ++k ) yield( k ); -// -// 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 - -// BOOST_INTERPROCESS_SMT_PAUSE - -#if defined(_MSC_VER) && _MSC_VER >= 1310 && ( defined(_M_IX86) || defined(_M_X64) ) - -extern "C" void _mm_pause(); -#pragma intrinsic( _mm_pause ) - -#define BOOST_INTERPROCESS_SMT_PAUSE _mm_pause(); - -#elif defined(__GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) - -#define BOOST_INTERPROCESS_SMT_PAUSE __asm__ __volatile__( "rep; nop" : : : "memory" ); - -#endif - -// - -#if defined (BOOST_INTERPROCESS_WINDOWS) - -#include - -namespace boost -{ -namespace interprocess -{ -namespace ipcdetail -{ - -inline void yield( unsigned k ) -{ - if( k < 4 ) - { - } -#if defined( BOOST_INTERPROCESS_SMT_PAUSE ) - else if( k < 16 ){ - BOOST_INTERPROCESS_SMT_PAUSE - } -#endif - else if( k < 32 ){ - //Try to yield to another thread running on the current processor - if(!winapi::SwitchToThread()){ - //If not yield to any thread of same or higher priority on any processor - boost::interprocess::winapi::Sleep(0); - } - } - else{ - //Yields to any thread on any processor - boost::interprocess::winapi::Sleep(1); - } -} - -} // namespace ipcdetail -} // namespace interprocess -} // namespace boost - -#else - -#include -#include - -namespace boost -{ -namespace interprocess -{ -namespace ipcdetail -{ - -inline void yield( unsigned k ) -{ - if( k < 4 ) - { - } -#if defined( BOOST_INTERPROCESS_SMT_PAUSE ) - else if( k < 16 ) - { - BOOST_INTERPROCESS_SMT_PAUSE - } -#endif - else if( k < 32 || k & 1 ) - { - sched_yield(); - } - else - { - // g++ -Wextra warns on {} or {0} - struct timespec rqtp = { 0, 0 }; - - // POSIX says that timespec has tv_sec and tv_nsec - // But it doesn't guarantee order or placement - - rqtp.tv_sec = 0; - rqtp.tv_nsec = 1000; - - nanosleep( &rqtp, 0 ); - } -} - -} // namespace ipcdetail -} // namespace interprocess -} // namespace boost - -#endif - -#include - -#endif // #ifndef BOOST_INTERPROCESS_DETAIL_YIELD_K_HPP_INCLUDED diff --git a/include/boost/interprocess/sync/file_lock.hpp b/include/boost/interprocess/sync/file_lock.hpp index 81fd817..1b4dd39 100644 --- a/include/boost/interprocess/sync/file_lock.hpp +++ b/include/boost/interprocess/sync/file_lock.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include //!\file @@ -149,7 +150,7 @@ class file_lock using namespace boost::detail; if(now >= abs_time) return false; - unsigned k = 0; + spin_wait swait; do{ if(!ipcdetail::try_acquire_file_lock(hnd, acquired)) return false; @@ -164,7 +165,7 @@ class file_lock return true; } // relinquish current time slice - ipcdetail::yield(k++); + swait.yield(); } }while (true); } @@ -178,7 +179,7 @@ class file_lock if(now >= abs_time) return false; - unsigned k = 0; + spin_wait swait; do{ if(!ipcdetail::try_acquire_file_lock_sharable(hnd, acquired)) return false; @@ -193,7 +194,7 @@ class file_lock return true; } // relinquish current time slice - ipcdetail::yield(k++); + swait.yield(); } }while (true); } diff --git a/include/boost/interprocess/sync/posix/mutex.hpp b/include/boost/interprocess/sync/posix/mutex.hpp index 13c2f48..6bc45ca 100644 --- a/include/boost/interprocess/sync/posix/mutex.hpp +++ b/include/boost/interprocess/sync/posix/mutex.hpp @@ -44,6 +44,7 @@ #ifndef BOOST_INTERPROCESS_POSIX_TIMEOUTS # include +# include #endif #include @@ -119,7 +120,7 @@ inline bool posix_mutex::timed_lock(const boost::posix_time::ptime &abs_time) //Obtain current count and target time boost::posix_time::ptime now = microsec_clock::universal_time(); - unsigned k = 0; + spin_wait swait; do{ if(this->try_lock()){ break; @@ -130,7 +131,7 @@ inline bool posix_mutex::timed_lock(const boost::posix_time::ptime &abs_time) return false; } // relinquish current time slice - ipcdetail::yield(k++); + swait.yield(); }while (true); return true; diff --git a/include/boost/interprocess/sync/posix/recursive_mutex.hpp b/include/boost/interprocess/sync/posix/recursive_mutex.hpp index 4fd06dc..ce2ad68 100644 --- a/include/boost/interprocess/sync/posix/recursive_mutex.hpp +++ b/include/boost/interprocess/sync/posix/recursive_mutex.hpp @@ -38,6 +38,7 @@ #include #ifndef BOOST_INTERPROCESS_POSIX_TIMEOUTS # include +# include #endif #include @@ -108,7 +109,7 @@ inline bool posix_recursive_mutex::timed_lock(const boost::posix_time::ptime &ab //Obtain current count and target time boost::posix_time::ptime now = microsec_clock::universal_time(); - unsigned k = 0; + spin_wait swait; do{ if(this->try_lock()){ break; @@ -119,7 +120,7 @@ inline bool posix_recursive_mutex::timed_lock(const boost::posix_time::ptime &ab return false; } // relinquish current time slice - yield(k++); + swait.yield(); }while (true); return true; diff --git a/include/boost/interprocess/sync/posix/semaphore_wrapper.hpp b/include/boost/interprocess/sync/posix/semaphore_wrapper.hpp index 3e18d60..98cb128 100644 --- a/include/boost/interprocess/sync/posix/semaphore_wrapper.hpp +++ b/include/boost/interprocess/sync/posix/semaphore_wrapper.hpp @@ -35,6 +35,7 @@ #include #else #include +#include #endif namespace boost { @@ -195,11 +196,11 @@ inline bool semaphore_timed_wait(sem_t *handle, const boost::posix_time::ptime & return false; #else //#ifdef BOOST_INTERPROCESS_POSIX_TIMEOUTS boost::posix_time::ptime now; - unsigned k = 0; + spin_wait swait; do{ if(semaphore_try_wait(handle)) return true; - yield(k++); + swait.yield(); }while((now = microsec_clock::universal_time()) < abs_time); return false; #endif //#ifdef BOOST_INTERPROCESS_POSIX_TIMEOUTS diff --git a/include/boost/interprocess/sync/spin/condition.hpp b/include/boost/interprocess/sync/spin/condition.hpp index 641bf7f..be86333 100644 --- a/include/boost/interprocess/sync/spin/condition.hpp +++ b/include/boost/interprocess/sync/spin/condition.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -140,9 +141,9 @@ inline void spin_condition::notify(boost::uint32_t command) } //Notify that all threads should execute wait logic - unsigned k = 0; + spin_wait swait; while(SLEEP != atomic_cas32(const_cast(&m_command), command, SLEEP)){ - yield(k++); + swait.yield(); } //The enter mutex will rest locked until the last waiting thread unlocks it } @@ -206,9 +207,9 @@ inline bool spin_condition::do_timed_wait(bool tout_enabled, while(1){ //The thread sleeps/spins until a spin_condition commands a notification //Notification occurred, we will lock the checking mutex so that - unsigned k = 0; + spin_wait swait; while(atomic_read32(&m_command) == SLEEP){ - yield(k++); + swait.yield(); //Check for timeout if(tout_enabled){ diff --git a/include/boost/interprocess/sync/spin/mutex.hpp b/include/boost/interprocess/sync/spin/mutex.hpp index fc43a9c..7a77549 100644 --- a/include/boost/interprocess/sync/spin/mutex.hpp +++ b/include/boost/interprocess/sync/spin/mutex.hpp @@ -22,6 +22,7 @@ #include #include #include +#include namespace boost { namespace interprocess { @@ -60,7 +61,7 @@ inline spin_mutex::~spin_mutex() inline void spin_mutex::lock(void) { - unsigned k = 0; + spin_wait swait; do{ boost::uint32_t prev_s = ipcdetail::atomic_cas32(const_cast(&m_s), 1, 0); @@ -68,7 +69,7 @@ inline void spin_mutex::lock(void) break; } // relinquish current timeslice - ipcdetail::yield(k++); + swait.yield(); }while (true); } @@ -87,7 +88,7 @@ inline bool spin_mutex::timed_lock(const boost::posix_time::ptime &abs_time) //Obtain current count and target time boost::posix_time::ptime now = microsec_clock::universal_time(); - unsigned k = 0; + spin_wait swait; do{ if(this->try_lock()){ break; @@ -98,7 +99,7 @@ inline bool spin_mutex::timed_lock(const boost::posix_time::ptime &abs_time) return false; } // relinquish current time slice - ipcdetail::yield(k++); + swait.yield(); }while (true); return true; diff --git a/include/boost/interprocess/sync/spin/semaphore.hpp b/include/boost/interprocess/sync/spin/semaphore.hpp index a209d36..4b16235 100644 --- a/include/boost/interprocess/sync/spin/semaphore.hpp +++ b/include/boost/interprocess/sync/spin/semaphore.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include namespace boost { @@ -59,11 +60,9 @@ inline void spin_semaphore::post() inline void spin_semaphore::wait() { - unsigned k = 0; + spin_wait swait; while(!ipcdetail::atomic_add_unless32(&m_count, boost::uint32_t(-1), boost::uint32_t(0))){ - while(ipcdetail::atomic_read32(&m_count) == 0){ - ipcdetail::yield(k++); - } + swait.yield(); } } @@ -81,7 +80,7 @@ inline bool spin_semaphore::timed_wait(const boost::posix_time::ptime &abs_time) //Obtain current count and target time boost::posix_time::ptime now(microsec_clock::universal_time()); - unsigned k = 0; + spin_wait swait; do{ if(this->try_wait()){ break; @@ -92,7 +91,7 @@ inline bool spin_semaphore::timed_wait(const boost::posix_time::ptime &abs_time) return this->try_wait(); } // relinquish current time slice - ipcdetail::yield(k++); + swait.yield(); }while (true); return true; } diff --git a/include/boost/interprocess/sync/spin/wait.hpp b/include/boost/interprocess/sync/spin/wait.hpp new file mode 100644 index 0000000..ae2d1be --- /dev/null +++ b/include/boost/interprocess/sync/spin/wait.hpp @@ -0,0 +1,188 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Peter Dimov 2008. +// (C) Copyright Ion Gaztanaga 2013. 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) +// +// See http://www.boost.org/libs/interprocess for documentation. +// +////////////////////////////////////////////////////////////////////////////// + +//Parts of this file come from boost/smart_ptr/detail/yield_k.hpp +//Many thanks to Peter Dimov. + +#ifndef BOOST_INTERPROCESS_SYNC_WAIT_HPP_INCLUDED +#define BOOST_INTERPROCESS_SYNC_WAIT_HPP_INCLUDED + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include +#include +#include + +//#define BOOST_INTERPROCESS_SPIN_WAIT_DEBUG +#ifdef BOOST_INTERPROCESS_SPIN_WAIT_DEBUG +#include +#endif + +// BOOST_INTERPROCESS_SMT_PAUSE + +#if defined(_MSC_VER) && _MSC_VER >= 1310 && ( defined(_M_IX86) || defined(_M_X64) ) + +extern "C" void _mm_pause(); +#pragma intrinsic( _mm_pause ) + +#define BOOST_INTERPROCESS_SMT_PAUSE _mm_pause(); + +#elif defined(__GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) + +#define BOOST_INTERPROCESS_SMT_PAUSE __asm__ __volatile__( "rep; nop" : : : "memory" ); + +#endif + +namespace boost{ +namespace interprocess{ +namespace ipcdetail { + +template +class num_core_holder; + +template<> +class num_core_holder<0> +{ + public: + static unsigned int get() + { + if(!num_cores){ + return ipcdetail::get_num_cores(); + } + else{ + return num_cores; + } + } + + private: + static unsigned int num_cores; +}; + +unsigned int num_core_holder<0>::num_cores = ipcdetail::get_num_cores(); + +} //namespace ipcdetail { + +class spin_wait +{ + public: + spin_wait() + : m_k(0u) + { + (void)m_nop_pause_limit; + (void)m_yield_only_counts; + (void)m_count_start; + } + + #ifdef BOOST_INTERPROCESS_SPIN_WAIT_DEBUG + ~spin_wait() + { + if(m_k){ + std::cout << "final m_k: " << m_k + << " system tick(us): " << ipcdetail::get_system_tick_us() << std::endl; + } + } + #endif + + unsigned int count() const + { return m_k; } + + void yield() + { + //Lazy initialization of limits + if( !m_k){ + this->init_limits(); + } + //Nop tries + if( m_k < (m_nop_pause_limit >> 2) ){ + + } + //Pause tries if the processor supports it + #if defined(BOOST_INTERPROCESS_SMT_PAUSE) + else if( m_k < m_nop_pause_limit ){ + BOOST_INTERPROCESS_SMT_PAUSE + } + #endif + //Yield/Sleep strategy + else{ + //Lazy initialization of tick information + if(m_k == m_nop_pause_limit){ + this->init_tick_info(); + } + else if( this->yield_or_sleep() ){ + ipcdetail::thread_yield(); + } + else{ + ipcdetail::thread_sleep_tick(); + } + } + ++m_k; + } + + void reset() + { + m_k = 0u; + m_count_start = ipcdetail::get_current_system_highres_count(); + } + + private: + + void init_limits() + { + unsigned int num_cores = ipcdetail::num_core_holder<0>::get(); + m_nop_pause_limit = num_cores > 1u ? 32u : 0u; + } + + void init_tick_info() + { + m_yield_only_counts = ipcdetail::get_system_tick_in_highres_counts(); + m_count_start = ipcdetail::get_current_system_highres_count(); + } + + //Returns true if yield must be called, false is sleep must be called + bool yield_or_sleep() + { + if(ipcdetail::is_highres_count_zero(m_yield_only_counts)){ //If yield-only limit was reached then yield one in every two tries + return (m_k & 1u) != 0; + } + else{ //Try to see if we've reched yield-only time limit + const ipcdetail::OS_highres_count_t now = ipcdetail::get_current_system_highres_count(); + const ipcdetail::OS_highres_count_t elapsed = ipcdetail::system_highres_count_subtract(now, m_count_start); + if(!ipcdetail::system_highres_count_less(elapsed, m_yield_only_counts)){ + #ifdef BOOST_INTERPROCESS_SPIN_WAIT_DEBUG + std::cout << "elapsed!\n" + << " m_yield_only_counts: "; + ipcdetail::ostream_highres_count(std::cout, m_yield_only_counts) + << " system tick(us): " << ipcdetail::get_system_tick_us() << '\n' + << " m_k: " << m_k << " elapsed counts: "; + ipcdetail::ostream_highres_count(std::cout, elapsed) << std::endl; + #endif + //Yield-only time reached, now it's time to sleep + ipcdetail::zero_highres_count(m_yield_only_counts); + return false; + } + } + return true; //Otherwise yield + } + + unsigned int m_k; + unsigned int m_nop_pause_limit; + ipcdetail::OS_highres_count_t m_yield_only_counts; + ipcdetail::OS_highres_count_t m_count_start; +}; + +} // namespace interprocess +} // namespace boost + +#include + +#endif // #ifndef BOOST_INTERPROCESS_SYNC_WAIT_HPP_INCLUDED diff --git a/test/robust_mutex_test.hpp b/test/robust_mutex_test.hpp index 3ec0cb5..09688c3 100644 --- a/test/robust_mutex_test.hpp +++ b/test/robust_mutex_test.hpp @@ -16,6 +16,7 @@ #include //std::system #include #include +#include #include "get_process_id_name.hpp" #include "mutex_test_template.hpp" #include @@ -66,9 +67,9 @@ int robust_mutex_test(int argc, char *argv[]) return 1; //Wait until child locks the mutexes and dies - unsigned k = 0; + spin_wait swait; while(!*go_ahead){ - ipcdetail::yield(k++); + swait.yield(); } std::cout << "... recovering mutex[0]" << std::endl; @@ -164,9 +165,9 @@ int robust_mutex_test(int argc, char *argv[]) } //Wait until child locks the 2nd mutex and dies - unsigned k = 0; + spin_wait swait; while(!*go_ahead2){ - ipcdetail::yield(k++); + swait.yield(); } //Done, now try to lock number 3 to see if robust