2 * Copyright Andrey Semashev 2016.
3 * Distributed under the Boost Software License, Version 1.0.
4 * (See accompanying file LICENSE_1_0.txt or copy at
5 * http://www.boost.org/LICENSE_1_0.txt)
8 * \file windows/ipc_sync_wrappers.hpp
9 * \author Andrey Semashev
12 * \brief This header is the Boost.Log library implementation, see the library documentation
13 * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html.
16 #ifndef BOOST_LOG_WINDOWS_IPC_SYNC_WRAPPERS_HPP_INCLUDED_
17 #define BOOST_LOG_WINDOWS_IPC_SYNC_WRAPPERS_HPP_INCLUDED_
19 #include <boost/log/detail/config.hpp>
20 #include <boost/winapi/access_rights.hpp>
21 #include <boost/winapi/handles.hpp>
22 #include <boost/winapi/event.hpp>
23 #include <boost/winapi/semaphore.hpp>
24 #include <boost/winapi/wait.hpp>
25 #include <boost/winapi/dll.hpp>
26 #include <boost/winapi/time.hpp>
27 #include <boost/winapi/get_last_error.hpp>
32 #include <boost/assert.hpp>
33 #include <boost/throw_exception.hpp>
34 #include <boost/checked_delete.hpp>
35 #include <boost/memory_order.hpp>
36 #include <boost/atomic/atomic.hpp>
37 #include <boost/intrusive/options.hpp>
38 #include <boost/intrusive/set.hpp>
39 #include <boost/intrusive/set_hook.hpp>
40 #include <boost/intrusive/list.hpp>
41 #include <boost/intrusive/list_hook.hpp>
42 #include <boost/log/exceptions.hpp>
43 #include <boost/log/utility/permissions.hpp>
44 #include "windows/auto_handle.hpp"
45 #include <boost/log/detail/header.hpp>
49 BOOST_LOG_OPEN_NAMESPACE
55 // TODO: Port to Boost.Atomic when it supports extended atomic ops
56 #if defined(BOOST_MSVC) && (_MSC_VER >= 1400) && !defined(UNDER_CE)
59 extern "C" unsigned char _interlockedbittestandset(long *a, long b);
60 extern "C" unsigned char _interlockedbittestandreset(long *a, long b);
62 extern "C" unsigned char _interlockedbittestandset(volatile long *a, long b);
63 extern "C" unsigned char _interlockedbittestandreset(volatile long *a, long b);
66 #pragma intrinsic(_interlockedbittestandset)
67 #pragma intrinsic(_interlockedbittestandreset)
69 BOOST_FORCEINLINE bool bit_test_and_set(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT
71 return _interlockedbittestandset(reinterpret_cast< long* >(&x.storage()), static_cast< long >(bit)) != 0;
74 BOOST_FORCEINLINE bool bit_test_and_reset(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT
76 return _interlockedbittestandreset(reinterpret_cast< long* >(&x.storage()), static_cast< long >(bit)) != 0;
79 #elif (defined(BOOST_MSVC) || defined(BOOST_INTEL_WIN)) && defined(_M_IX86)
81 BOOST_FORCEINLINE bool bit_test_and_set(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT
83 boost::atomic< uint32_t >::storage_type* p = &x.storage();
95 BOOST_FORCEINLINE bool bit_test_and_reset(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT
97 boost::atomic< uint32_t >::storage_type* p = &x.storage();
109 #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
111 #if !defined(__CUDACC__)
112 #define BOOST_LOG_DETAIL_ASM_CLOBBER_CC_COMMA "cc",
114 #define BOOST_LOG_DETAIL_ASM_CLOBBER_CC_COMMA
117 BOOST_FORCEINLINE bool bit_test_and_set(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT
122 "lock; bts %[bit_number], %[storage]\n\t"
124 : [storage] "+m" (x.storage()), [result] "=q" (res)
125 : [bit_number] "Kq" (bit)
126 : BOOST_LOG_DETAIL_ASM_CLOBBER_CC_COMMA "memory"
131 BOOST_FORCEINLINE bool bit_test_and_reset(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT
136 "lock; btr %[bit_number], %[storage]\n\t"
138 : [storage] "+m" (x.storage()), [result] "=q" (res)
139 : [bit_number] "Kq" (bit)
140 : BOOST_LOG_DETAIL_ASM_CLOBBER_CC_COMMA "memory"
147 BOOST_FORCEINLINE bool bit_test_and_set(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT
149 const uint32_t mask = uint32_t(1u) << bit;
150 uint32_t old_val = x.fetch_or(mask, boost::memory_order_acq_rel);
151 return (old_val & mask) != 0u;
154 BOOST_FORCEINLINE bool bit_test_and_reset(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT
156 const uint32_t mask = uint32_t(1u) << bit;
157 uint32_t old_val = x.fetch_and(~mask, boost::memory_order_acq_rel);
158 return (old_val & mask) != 0u;
163 //! Interprocess event object
164 class interprocess_event
170 void create(const wchar_t* name, bool manual_reset, permissions const& perms = permissions());
171 void create_or_open(const wchar_t* name, bool manual_reset, permissions const& perms = permissions());
172 void open(const wchar_t* name);
174 boost::winapi::HANDLE_ get_handle() const BOOST_NOEXCEPT { return m_event.get(); }
178 if (BOOST_UNLIKELY(!boost::winapi::SetEvent(m_event.get())))
180 const boost::winapi::DWORD_ err = boost::winapi::GetLastError();
181 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to set an interprocess event object", (err));
185 void set_noexcept() BOOST_NOEXCEPT
187 BOOST_VERIFY(!!boost::winapi::SetEvent(m_event.get()));
192 if (BOOST_UNLIKELY(!boost::winapi::ResetEvent(m_event.get())))
194 const boost::winapi::DWORD_ err = boost::winapi::GetLastError();
195 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to reset an interprocess event object", (err));
201 const boost::winapi::DWORD_ retval = boost::winapi::WaitForSingleObject(m_event.get(), boost::winapi::infinite);
202 if (BOOST_UNLIKELY(retval != boost::winapi::wait_object_0))
204 const boost::winapi::DWORD_ err = boost::winapi::GetLastError();
205 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to block on an interprocess event object", (err));
209 bool wait(boost::winapi::HANDLE_ abort_handle)
211 boost::winapi::HANDLE_ handles[2u] = { m_event.get(), abort_handle };
212 const boost::winapi::DWORD_ retval = boost::winapi::WaitForMultipleObjects(2u, handles, false, boost::winapi::infinite);
213 if (retval == (boost::winapi::wait_object_0 + 1u))
215 // Wait was interrupted
218 else if (BOOST_UNLIKELY(retval != boost::winapi::wait_object_0))
220 const boost::winapi::DWORD_ err = boost::winapi::GetLastError();
221 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to block on an interprocess event object", (err));
227 void swap(interprocess_event& that) BOOST_NOEXCEPT
229 m_event.swap(that.m_event);
233 //! Interprocess semaphore object
234 class interprocess_semaphore
237 typedef boost::winapi::DWORD_ NTSTATUS_;
238 struct semaphore_basic_information
240 boost::winapi::ULONG_ current_count; // current semaphore count
241 boost::winapi::ULONG_ maximum_count; // max semaphore count
243 typedef NTSTATUS_ (__stdcall *nt_query_semaphore_t)(boost::winapi::HANDLE_ h, unsigned int info_class, semaphore_basic_information* pinfo, boost::winapi::ULONG_ info_size, boost::winapi::ULONG_* ret_len);
244 typedef bool (*is_semaphore_zero_count_t)(boost::winapi::HANDLE_ h);
249 static boost::atomic< is_semaphore_zero_count_t > is_semaphore_zero_count;
250 static nt_query_semaphore_t nt_query_semaphore;
253 void create_or_open(const wchar_t* name, permissions const& perms = permissions());
254 void open(const wchar_t* name);
256 boost::winapi::HANDLE_ get_handle() const BOOST_NOEXCEPT { return m_sem.get(); }
258 void post(uint32_t count)
260 BOOST_ASSERT(count <= static_cast< uint32_t >((std::numeric_limits< boost::winapi::LONG_ >::max)()));
262 if (BOOST_UNLIKELY(!boost::winapi::ReleaseSemaphore(m_sem.get(), static_cast< boost::winapi::LONG_ >(count), NULL)))
264 const boost::winapi::DWORD_ err = boost::winapi::GetLastError();
265 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to post on an interprocess semaphore object", (err));
269 bool is_zero_count() const
271 return is_semaphore_zero_count.load(boost::memory_order_acquire)(m_sem.get());
276 const boost::winapi::DWORD_ retval = boost::winapi::WaitForSingleObject(m_sem.get(), boost::winapi::infinite);
277 if (BOOST_UNLIKELY(retval != boost::winapi::wait_object_0))
279 const boost::winapi::DWORD_ err = boost::winapi::GetLastError();
280 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to block on an interprocess semaphore object", (err));
284 bool wait(boost::winapi::HANDLE_ abort_handle)
286 boost::winapi::HANDLE_ handles[2u] = { m_sem.get(), abort_handle };
287 const boost::winapi::DWORD_ retval = boost::winapi::WaitForMultipleObjects(2u, handles, false, boost::winapi::infinite);
288 if (retval == (boost::winapi::wait_object_0 + 1u))
290 // Wait was interrupted
293 else if (BOOST_UNLIKELY(retval != boost::winapi::wait_object_0))
295 const boost::winapi::DWORD_ err = boost::winapi::GetLastError();
296 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to block on an interprocess semaphore object", (err));
302 void swap(interprocess_semaphore& that) BOOST_NOEXCEPT
304 m_sem.swap(that.m_sem);
308 static bool is_semaphore_zero_count_init(boost::winapi::HANDLE_ h);
309 static bool is_semaphore_zero_count_nt_query_semaphore(boost::winapi::HANDLE_ h);
310 static bool is_semaphore_zero_count_emulated(boost::winapi::HANDLE_ h);
313 //! Interprocess mutex. Implementation adopted from Boost.Sync.
314 class interprocess_mutex
317 //! Shared state that should be visible to all processes using the mutex
320 boost::atomic< uint32_t > m_lock_state;
322 shared_state() BOOST_NOEXCEPT : m_lock_state(0u)
329 explicit auto_unlock(interprocess_mutex& mutex) BOOST_NOEXCEPT : m_mutex(mutex) {}
330 ~auto_unlock() { m_mutex.unlock(); }
332 BOOST_DELETED_FUNCTION(auto_unlock(auto_unlock const&))
333 BOOST_DELETED_FUNCTION(auto_unlock& operator=(auto_unlock const&))
336 interprocess_mutex& m_mutex;
339 struct optional_unlock
341 optional_unlock() BOOST_NOEXCEPT : m_mutex(NULL) {}
342 explicit optional_unlock(interprocess_mutex& mutex) BOOST_NOEXCEPT : m_mutex(&mutex) {}
343 ~optional_unlock() { if (m_mutex) m_mutex->unlock(); }
345 interprocess_mutex* disengage() BOOST_NOEXCEPT
347 interprocess_mutex* p = m_mutex;
352 void engage(interprocess_mutex& mutex) BOOST_NOEXCEPT
354 BOOST_ASSERT(!m_mutex);
358 BOOST_DELETED_FUNCTION(optional_unlock(optional_unlock const&))
359 BOOST_DELETED_FUNCTION(optional_unlock& operator=(optional_unlock const&))
362 interprocess_mutex* m_mutex;
366 interprocess_event m_event;
367 shared_state* m_shared_state;
369 #if !defined(BOOST_MSVC) || _MSC_VER >= 1800
370 static BOOST_CONSTEXPR_OR_CONST uint32_t lock_flag_bit = 31u;
371 static BOOST_CONSTEXPR_OR_CONST uint32_t event_set_flag_bit = 30u;
372 static BOOST_CONSTEXPR_OR_CONST uint32_t lock_flag_value = 1u << lock_flag_bit;
373 static BOOST_CONSTEXPR_OR_CONST uint32_t event_set_flag_value = 1u << event_set_flag_bit;
374 static BOOST_CONSTEXPR_OR_CONST uint32_t waiter_count_mask = event_set_flag_value - 1u;
376 // MSVC 8-11, inclusively, fail to link if these constants are declared as static constants instead of an enum
380 event_set_flag_bit = 30u,
381 lock_flag_value = 1u << lock_flag_bit,
382 event_set_flag_value = 1u << event_set_flag_bit,
383 waiter_count_mask = event_set_flag_value - 1u
388 interprocess_mutex() BOOST_NOEXCEPT : m_shared_state(NULL)
392 void create(const wchar_t* name, shared_state* shared, permissions const& perms = permissions())
394 m_event.create(name, false, perms);
395 m_shared_state = shared;
398 void open(const wchar_t* name, shared_state* shared)
401 m_shared_state = shared;
406 return !bit_test_and_set(m_shared_state->m_lock_state, lock_flag_bit);
411 if (BOOST_UNLIKELY(!try_lock()))
415 bool lock(boost::winapi::HANDLE_ abort_handle)
417 if (BOOST_LIKELY(try_lock()))
419 return lock_slow(abort_handle);
422 void unlock() BOOST_NOEXCEPT
424 const uint32_t old_count = m_shared_state->m_lock_state.fetch_add(lock_flag_value, boost::memory_order_release);
425 if ((old_count & event_set_flag_value) == 0u && (old_count > lock_flag_value))
427 if (!bit_test_and_set(m_shared_state->m_lock_state, event_set_flag_bit))
429 m_event.set_noexcept();
434 BOOST_DELETED_FUNCTION(interprocess_mutex(interprocess_mutex const&))
435 BOOST_DELETED_FUNCTION(interprocess_mutex& operator=(interprocess_mutex const&))
439 bool lock_slow(boost::winapi::HANDLE_ abort_handle);
440 void mark_waiting_and_try_lock(uint32_t& old_state);
441 void clear_waiting_and_try_lock(uint32_t& old_state);
444 //! A simple clock that corresponds to GetTickCount/GetTickCount64 timeline
445 struct tick_count_clock
447 #if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6
448 typedef boost::winapi::ULONGLONG_ time_point;
450 typedef boost::winapi::DWORD_ time_point;
453 static time_point now() BOOST_NOEXCEPT
455 #if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6
456 return boost::winapi::GetTickCount64();
458 return boost::winapi::GetTickCount();
463 //! Interprocess condition variable
464 class interprocess_condition_variable
467 typedef boost::intrusive::list_base_hook<
468 boost::intrusive::tag< struct for_sem_order_by_usage >,
469 boost::intrusive::link_mode< boost::intrusive::safe_link >
470 > semaphore_info_list_hook_t;
472 typedef boost::intrusive::set_base_hook<
473 boost::intrusive::tag< struct for_sem_lookup_by_id >,
474 boost::intrusive::link_mode< boost::intrusive::safe_link >,
475 boost::intrusive::optimize_size< true >
476 > semaphore_info_set_hook_t;
478 //! Information about a semaphore object
479 struct semaphore_info :
480 public semaphore_info_list_hook_t,
481 public semaphore_info_set_hook_t
485 typedef bool result_type;
487 result_type operator() (semaphore_info const& left, semaphore_info const& right) const BOOST_NOEXCEPT
489 return left.m_id < right.m_id;
491 result_type operator() (semaphore_info const& left, uint32_t right) const BOOST_NOEXCEPT
493 return left.m_id < right;
495 result_type operator() (uint32_t left, semaphore_info const& right) const BOOST_NOEXCEPT
497 return left < right.m_id;
502 interprocess_semaphore m_semaphore;
503 //! Timestamp of the moment when the semaphore was checked for zero count and it was not zero. In milliseconds since epoch.
504 tick_count_clock::time_point m_last_check_for_zero;
505 //! The flag indicates that the semaphore has been checked for zero count and it was not zero
506 bool m_checked_for_zero;
510 explicit semaphore_info(uint32_t id) BOOST_NOEXCEPT : m_last_check_for_zero(0u), m_id(id)
514 //! Checks if the semaphore is in 'non-zero' state for too long
515 bool check_non_zero_timeout(tick_count_clock::time_point now) BOOST_NOEXCEPT
517 if (!m_checked_for_zero)
519 m_last_check_for_zero = now;
520 m_checked_for_zero = true;
524 return (now - m_last_check_for_zero) >= 2000u;
527 BOOST_DELETED_FUNCTION(semaphore_info(semaphore_info const&))
528 BOOST_DELETED_FUNCTION(semaphore_info& operator=(semaphore_info const&))
531 typedef boost::intrusive::list<
533 boost::intrusive::base_hook< semaphore_info_list_hook_t >,
534 boost::intrusive::constant_time_size< false >
535 > semaphore_info_list;
537 typedef boost::intrusive::set<
539 boost::intrusive::base_hook< semaphore_info_set_hook_t >,
540 boost::intrusive::compare< semaphore_info::order_by_id >,
541 boost::intrusive::constant_time_size< false >
542 > semaphore_info_set;
547 //! Number of waiters blocked on the semaphore if >0, 0 if no threads are blocked, <0 when the blocked threads were signalled
549 //! The semaphore generation
550 uint32_t m_generation;
551 //! Id of the semaphore which is used to block threads on
552 uint32_t m_semaphore_id;
554 shared_state() BOOST_NOEXCEPT :
563 //! The list of semaphores used for blocking. The list is in the order at which the semaphores are considered to be picked for being used.
564 semaphore_info_list m_semaphore_info_list;
565 //! The list of semaphores used for blocking. Used for searching for a particular semaphore by id.
566 semaphore_info_set m_semaphore_info_set;
567 //! The semaphore that is currently being used for blocking
568 semaphore_info* m_current_semaphore;
569 //! A string storage for formatting a semaphore name
570 std::wstring m_semaphore_name;
571 //! Permissions used to create new semaphores
573 //! Process-shared state
574 shared_state* m_shared_state;
575 //! The next id for creating a new semaphore
576 uint32_t m_next_semaphore_id;
579 interprocess_condition_variable() BOOST_NOEXCEPT :
580 m_current_semaphore(NULL),
581 m_shared_state(NULL),
582 m_next_semaphore_id(0u)
586 ~interprocess_condition_variable()
588 m_semaphore_info_set.clear();
589 m_semaphore_info_list.clear_and_dispose(boost::checked_deleter< semaphore_info >());
592 void init(const wchar_t* name, shared_state* shared, permissions const& perms = permissions())
595 m_shared_state = shared;
597 m_semaphore_name = name;
598 // Reserve space for generate_semaphore_name()
599 m_semaphore_name.append(L".sem00000000");
601 m_current_semaphore = get_semaphore(m_shared_state->m_semaphore_id);
606 const int32_t waiters = m_shared_state->m_waiters;
609 const uint32_t id = m_shared_state->m_semaphore_id;
610 if (m_current_semaphore->m_id != id)
611 m_current_semaphore = get_semaphore(id);
613 m_current_semaphore->m_semaphore.post(waiters);
614 m_shared_state->m_waiters = -waiters;
618 bool wait(interprocess_mutex::optional_unlock& lock, boost::winapi::HANDLE_ abort_handle);
620 BOOST_DELETED_FUNCTION(interprocess_condition_variable(interprocess_condition_variable const&))
621 BOOST_DELETED_FUNCTION(interprocess_condition_variable& operator=(interprocess_condition_variable const&))
624 //! Finds or opens a semaphore with the specified id
625 semaphore_info* get_semaphore(uint32_t id);
626 //! Finds or creates a semaphore with zero counter
627 semaphore_info* get_unused_semaphore();
629 //! Marks the semaphore info as unused and moves to the end of list
630 void mark_unused(semaphore_info& info) BOOST_NOEXCEPT;
632 //! Generates semaphore name according to id
633 void generate_semaphore_name(uint32_t id) BOOST_NOEXCEPT;
635 //! Returns \c true if \a left is less than \a right considering possible integer overflow
636 static bool is_overflow_less(uint32_t left, uint32_t right) BOOST_NOEXCEPT
638 return ((left - right) & 0x80000000u) != 0u;
646 BOOST_LOG_CLOSE_NAMESPACE // namespace log
650 #include <boost/log/detail/footer.hpp>
652 #endif // BOOST_LOG_WINDOWS_IPC_SYNC_WRAPPERS_HPP_INCLUDED_