1 /*
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)
6  */
7 /*!
8  * \file   windows/ipc_sync_wrappers.hpp
9  * \author Andrey Semashev
10  * \date   23.01.2016
11  *
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.
14  */
15 
16 #ifndef BOOST_LOG_WINDOWS_IPC_SYNC_WRAPPERS_HPP_INCLUDED_
17 #define BOOST_LOG_WINDOWS_IPC_SYNC_WRAPPERS_HPP_INCLUDED_
18 
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>
28 #include <cstddef>
29 #include <limits>
30 #include <string>
31 #include <utility>
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>
46 
47 namespace boost {
48 
49 BOOST_LOG_OPEN_NAMESPACE
50 
51 namespace ipc {
52 
53 namespace aux {
54 
55 // TODO: Port to Boost.Atomic when it supports extended atomic ops
56 #if defined(BOOST_MSVC) && (_MSC_VER >= 1400) && !defined(UNDER_CE)
57 
58 #if _MSC_VER == 1400
59 extern "C" unsigned char _interlockedbittestandset(long *a, long b);
60 extern "C" unsigned char _interlockedbittestandreset(long *a, long b);
61 #else
62 extern "C" unsigned char _interlockedbittestandset(volatile long *a, long b);
63 extern "C" unsigned char _interlockedbittestandreset(volatile long *a, long b);
64 #endif
65 
66 #pragma intrinsic(_interlockedbittestandset)
67 #pragma intrinsic(_interlockedbittestandreset)
68 
bit_test_and_set(boost::atomic<uint32_t> & x,uint32_t bit)69 BOOST_FORCEINLINE bool bit_test_and_set(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT
70 {
71     return _interlockedbittestandset(reinterpret_cast< long* >(&x.storage()), static_cast< long >(bit)) != 0;
72 }
73 
bit_test_and_reset(boost::atomic<uint32_t> & x,uint32_t bit)74 BOOST_FORCEINLINE bool bit_test_and_reset(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT
75 {
76     return _interlockedbittestandreset(reinterpret_cast< long* >(&x.storage()), static_cast< long >(bit)) != 0;
77 }
78 
79 #elif (defined(BOOST_MSVC) || defined(BOOST_INTEL_WIN)) && defined(_M_IX86)
80 
81 BOOST_FORCEINLINE bool bit_test_and_set(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT
82 {
83     boost::atomic< uint32_t >::storage_type* p = &x.storage();
84     bool ret;
85     __asm
86     {
87         mov eax, bit
88         mov edx, p
89         lock bts [edx], eax
90         setc ret
91     };
92     return ret;
93 }
94 
95 BOOST_FORCEINLINE bool bit_test_and_reset(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT
96 {
97     boost::atomic< uint32_t >::storage_type* p = &x.storage();
98     bool ret;
99     __asm
100     {
101         mov eax, bit
102         mov edx, p
103         lock btr [edx], eax
104         setc ret
105     };
106     return ret;
107 }
108 
109 #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
110 
111 #if !defined(__CUDACC__)
112 #define BOOST_LOG_DETAIL_ASM_CLOBBER_CC_COMMA "cc",
113 #else
114 #define BOOST_LOG_DETAIL_ASM_CLOBBER_CC_COMMA
115 #endif
116 
117 BOOST_FORCEINLINE bool bit_test_and_set(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT
118 {
119     bool res;
120     __asm__ __volatile__
121     (
122         "lock; bts %[bit_number], %[storage]\n\t"
123         "setc %[result]\n\t"
124         : [storage] "+m" (x.storage()), [result] "=q" (res)
125         : [bit_number] "Kq" (bit)
126         : BOOST_LOG_DETAIL_ASM_CLOBBER_CC_COMMA "memory"
127     );
128     return res;
129 }
130 
131 BOOST_FORCEINLINE bool bit_test_and_reset(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT
132 {
133     bool res;
134     __asm__ __volatile__
135     (
136         "lock; btr %[bit_number], %[storage]\n\t"
137         "setc %[result]\n\t"
138         : [storage] "+m" (x.storage()), [result] "=q" (res)
139         : [bit_number] "Kq" (bit)
140         : BOOST_LOG_DETAIL_ASM_CLOBBER_CC_COMMA "memory"
141     );
142     return res;
143 }
144 
145 #else
146 
147 BOOST_FORCEINLINE bool bit_test_and_set(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT
148 {
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;
152 }
153 
154 BOOST_FORCEINLINE bool bit_test_and_reset(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT
155 {
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;
159 }
160 
161 #endif
162 
163 //! Interprocess event object
164 class interprocess_event
165 {
166 private:
167     auto_handle m_event;
168 
169 public:
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);
173 
get_handle() const174     boost::winapi::HANDLE_ get_handle() const BOOST_NOEXCEPT { return m_event.get(); }
175 
set()176     void set()
177     {
178         if (BOOST_UNLIKELY(!boost::winapi::SetEvent(m_event.get())))
179         {
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));
182         }
183     }
184 
set_noexcept()185     void set_noexcept() BOOST_NOEXCEPT
186     {
187         BOOST_VERIFY(!!boost::winapi::SetEvent(m_event.get()));
188     }
189 
reset()190     void reset()
191     {
192         if (BOOST_UNLIKELY(!boost::winapi::ResetEvent(m_event.get())))
193         {
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));
196         }
197     }
198 
wait()199     void wait()
200     {
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))
203         {
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));
206         }
207     }
208 
wait(boost::winapi::HANDLE_ abort_handle)209     bool wait(boost::winapi::HANDLE_ abort_handle)
210     {
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))
214         {
215             // Wait was interrupted
216             return false;
217         }
218         else if (BOOST_UNLIKELY(retval != boost::winapi::wait_object_0))
219         {
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));
222         }
223 
224         return true;
225     }
226 
swap(interprocess_event & that)227     void swap(interprocess_event& that) BOOST_NOEXCEPT
228     {
229         m_event.swap(that.m_event);
230     }
231 };
232 
233 //! Interprocess semaphore object
234 class interprocess_semaphore
235 {
236 private:
237     typedef boost::winapi::DWORD_ NTSTATUS_;
238     struct semaphore_basic_information
239     {
240         boost::winapi::ULONG_ current_count; // current semaphore count
241         boost::winapi::ULONG_ maximum_count; // max semaphore count
242     };
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);
245 
246 private:
247     auto_handle m_sem;
248 
249     static boost::atomic< is_semaphore_zero_count_t > is_semaphore_zero_count;
250     static nt_query_semaphore_t nt_query_semaphore;
251 
252 public:
253     void create_or_open(const wchar_t* name, permissions const& perms = permissions());
254     void open(const wchar_t* name);
255 
get_handle() const256     boost::winapi::HANDLE_ get_handle() const BOOST_NOEXCEPT { return m_sem.get(); }
257 
post(uint32_t count)258     void post(uint32_t count)
259     {
260         BOOST_ASSERT(count <= static_cast< uint32_t >((std::numeric_limits< boost::winapi::LONG_ >::max)()));
261 
262         if (BOOST_UNLIKELY(!boost::winapi::ReleaseSemaphore(m_sem.get(), static_cast< boost::winapi::LONG_ >(count), NULL)))
263         {
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));
266         }
267     }
268 
is_zero_count() const269     bool is_zero_count() const
270     {
271         return is_semaphore_zero_count.load(boost::memory_order_acquire)(m_sem.get());
272     }
273 
wait()274     void wait()
275     {
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))
278         {
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));
281         }
282     }
283 
wait(boost::winapi::HANDLE_ abort_handle)284     bool wait(boost::winapi::HANDLE_ abort_handle)
285     {
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))
289         {
290             // Wait was interrupted
291             return false;
292         }
293         else if (BOOST_UNLIKELY(retval != boost::winapi::wait_object_0))
294         {
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));
297         }
298 
299         return true;
300     }
301 
swap(interprocess_semaphore & that)302     void swap(interprocess_semaphore& that) BOOST_NOEXCEPT
303     {
304         m_sem.swap(that.m_sem);
305     }
306 
307 private:
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);
311 };
312 
313 //! Interprocess mutex. Implementation adopted from Boost.Sync.
314 class interprocess_mutex
315 {
316 public:
317     //! Shared state that should be visible to all processes using the mutex
318     struct shared_state
319     {
320         boost::atomic< uint32_t > m_lock_state;
321 
shared_stateboost::ipc::aux::interprocess_mutex::shared_state322         shared_state() BOOST_NOEXCEPT : m_lock_state(0u)
323         {
324         }
325     };
326 
327     struct auto_unlock
328     {
auto_unlockboost::ipc::aux::interprocess_mutex::auto_unlock329         explicit auto_unlock(interprocess_mutex& mutex) BOOST_NOEXCEPT : m_mutex(mutex) {}
~auto_unlockboost::ipc::aux::interprocess_mutex::auto_unlock330         ~auto_unlock() { m_mutex.unlock(); }
331 
332         BOOST_DELETED_FUNCTION(auto_unlock(auto_unlock const&))
333         BOOST_DELETED_FUNCTION(auto_unlock& operator=(auto_unlock const&))
334 
335     private:
336         interprocess_mutex& m_mutex;
337     };
338 
339     struct optional_unlock
340     {
optional_unlockboost::ipc::aux::interprocess_mutex::optional_unlock341         optional_unlock() BOOST_NOEXCEPT : m_mutex(NULL) {}
optional_unlockboost::ipc::aux::interprocess_mutex::optional_unlock342         explicit optional_unlock(interprocess_mutex& mutex) BOOST_NOEXCEPT : m_mutex(&mutex) {}
~optional_unlockboost::ipc::aux::interprocess_mutex::optional_unlock343         ~optional_unlock() { if (m_mutex) m_mutex->unlock(); }
344 
disengageboost::ipc::aux::interprocess_mutex::optional_unlock345         interprocess_mutex* disengage() BOOST_NOEXCEPT
346         {
347             interprocess_mutex* p = m_mutex;
348             m_mutex = NULL;
349             return p;
350         }
351 
engageboost::ipc::aux::interprocess_mutex::optional_unlock352         void engage(interprocess_mutex& mutex) BOOST_NOEXCEPT
353         {
354             BOOST_ASSERT(!m_mutex);
355             m_mutex = &mutex;
356         }
357 
358         BOOST_DELETED_FUNCTION(optional_unlock(optional_unlock const&))
359         BOOST_DELETED_FUNCTION(optional_unlock& operator=(optional_unlock const&))
360 
361     private:
362         interprocess_mutex* m_mutex;
363     };
364 
365 private:
366     interprocess_event m_event;
367     shared_state* m_shared_state;
368 
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;
375 #else
376     // MSVC 8-11, inclusively, fail to link if these constants are declared as static constants instead of an enum
377     enum
378     {
379         lock_flag_bit = 31u,
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
384     };
385 #endif
386 
387 public:
interprocess_mutex()388     interprocess_mutex() BOOST_NOEXCEPT : m_shared_state(NULL)
389     {
390     }
391 
create(const wchar_t * name,shared_state * shared,permissions const & perms=permissions ())392     void create(const wchar_t* name, shared_state* shared, permissions const& perms = permissions())
393     {
394         m_event.create(name, false, perms);
395         m_shared_state = shared;
396     }
397 
open(const wchar_t * name,shared_state * shared)398     void open(const wchar_t* name, shared_state* shared)
399     {
400         m_event.open(name);
401         m_shared_state = shared;
402     }
403 
try_lock()404     bool try_lock()
405     {
406         return !bit_test_and_set(m_shared_state->m_lock_state, lock_flag_bit);
407     }
408 
lock()409     void lock()
410     {
411         if (BOOST_UNLIKELY(!try_lock()))
412             lock_slow();
413     }
414 
lock(boost::winapi::HANDLE_ abort_handle)415     bool lock(boost::winapi::HANDLE_ abort_handle)
416     {
417         if (BOOST_LIKELY(try_lock()))
418             return true;
419         return lock_slow(abort_handle);
420     }
421 
unlock()422     void unlock() BOOST_NOEXCEPT
423     {
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))
426         {
427             if (!bit_test_and_set(m_shared_state->m_lock_state, event_set_flag_bit))
428             {
429                 m_event.set_noexcept();
430             }
431         }
432     }
433 
434     BOOST_DELETED_FUNCTION(interprocess_mutex(interprocess_mutex const&))
435     BOOST_DELETED_FUNCTION(interprocess_mutex& operator=(interprocess_mutex const&))
436 
437 private:
438     void lock_slow();
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);
442 };
443 
444 //! A simple clock that corresponds to GetTickCount/GetTickCount64 timeline
445 struct tick_count_clock
446 {
447 #if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6
448     typedef boost::winapi::ULONGLONG_ time_point;
449 #else
450     typedef boost::winapi::DWORD_ time_point;
451 #endif
452 
nowboost::ipc::aux::tick_count_clock453     static time_point now() BOOST_NOEXCEPT
454     {
455 #if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6
456         return boost::winapi::GetTickCount64();
457 #else
458         return boost::winapi::GetTickCount();
459 #endif
460     }
461 };
462 
463 //! Interprocess condition variable
464 class interprocess_condition_variable
465 {
466 private:
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;
471 
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;
477 
478     //! Information about a semaphore object
479     struct semaphore_info :
480         public semaphore_info_list_hook_t,
481         public semaphore_info_set_hook_t
482     {
483         struct order_by_id
484         {
485             typedef bool result_type;
486 
operator ()boost::ipc::aux::interprocess_condition_variable::semaphore_info::order_by_id487             result_type operator() (semaphore_info const& left, semaphore_info const& right) const BOOST_NOEXCEPT
488             {
489                 return left.m_id < right.m_id;
490             }
operator ()boost::ipc::aux::interprocess_condition_variable::semaphore_info::order_by_id491             result_type operator() (semaphore_info const& left, uint32_t right) const BOOST_NOEXCEPT
492             {
493                 return left.m_id < right;
494             }
operator ()boost::ipc::aux::interprocess_condition_variable::semaphore_info::order_by_id495             result_type operator() (uint32_t left, semaphore_info const& right) const BOOST_NOEXCEPT
496             {
497                 return left < right.m_id;
498             }
499         };
500 
501         //! The semaphore
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;
507         //! The semaphore id
508         const uint32_t m_id;
509 
semaphore_infoboost::ipc::aux::interprocess_condition_variable::semaphore_info510         explicit semaphore_info(uint32_t id) BOOST_NOEXCEPT : m_last_check_for_zero(0u), m_id(id)
511         {
512         }
513 
514         //! Checks if the semaphore is in 'non-zero' state for too long
check_non_zero_timeoutboost::ipc::aux::interprocess_condition_variable::semaphore_info515         bool check_non_zero_timeout(tick_count_clock::time_point now) BOOST_NOEXCEPT
516         {
517             if (!m_checked_for_zero)
518             {
519                 m_last_check_for_zero = now;
520                 m_checked_for_zero = true;
521                 return false;
522             }
523 
524             return (now - m_last_check_for_zero) >= 2000u;
525         }
526 
527         BOOST_DELETED_FUNCTION(semaphore_info(semaphore_info const&))
528         BOOST_DELETED_FUNCTION(semaphore_info& operator=(semaphore_info const&))
529     };
530 
531     typedef boost::intrusive::list<
532         semaphore_info,
533         boost::intrusive::base_hook< semaphore_info_list_hook_t >,
534         boost::intrusive::constant_time_size< false >
535     > semaphore_info_list;
536 
537     typedef boost::intrusive::set<
538         semaphore_info,
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;
543 
544 public:
545     struct shared_state
546     {
547         //! Number of waiters blocked on the semaphore if >0, 0 if no threads are blocked, <0 when the blocked threads were signalled
548         int32_t m_waiters;
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;
553 
shared_stateboost::ipc::aux::interprocess_condition_variable::shared_state554         shared_state() BOOST_NOEXCEPT :
555             m_waiters(0),
556             m_generation(0u),
557             m_semaphore_id(0u)
558         {
559         }
560     };
561 
562 private:
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
572     permissions m_perms;
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;
577 
578 public:
interprocess_condition_variable()579     interprocess_condition_variable() BOOST_NOEXCEPT :
580         m_current_semaphore(NULL),
581         m_shared_state(NULL),
582         m_next_semaphore_id(0u)
583     {
584     }
585 
~interprocess_condition_variable()586     ~interprocess_condition_variable()
587     {
588         m_semaphore_info_set.clear();
589         m_semaphore_info_list.clear_and_dispose(boost::checked_deleter< semaphore_info >());
590     }
591 
init(const wchar_t * name,shared_state * shared,permissions const & perms=permissions ())592     void init(const wchar_t* name, shared_state* shared, permissions const& perms = permissions())
593     {
594         m_perms = perms;
595         m_shared_state = shared;
596 
597         m_semaphore_name = name;
598         // Reserve space for generate_semaphore_name()
599         m_semaphore_name.append(L".sem00000000");
600 
601         m_current_semaphore = get_semaphore(m_shared_state->m_semaphore_id);
602     }
603 
notify_all()604     void notify_all()
605     {
606         const int32_t waiters = m_shared_state->m_waiters;
607         if (waiters > 0)
608         {
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);
612 
613             m_current_semaphore->m_semaphore.post(waiters);
614             m_shared_state->m_waiters = -waiters;
615         }
616     }
617 
618     bool wait(interprocess_mutex::optional_unlock& lock, boost::winapi::HANDLE_ abort_handle);
619 
620     BOOST_DELETED_FUNCTION(interprocess_condition_variable(interprocess_condition_variable const&))
621     BOOST_DELETED_FUNCTION(interprocess_condition_variable& operator=(interprocess_condition_variable const&))
622 
623 private:
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();
628 
629     //! Marks the semaphore info as unused and moves to the end of list
630     void mark_unused(semaphore_info& info) BOOST_NOEXCEPT;
631 
632     //! Generates semaphore name according to id
633     void generate_semaphore_name(uint32_t id) BOOST_NOEXCEPT;
634 
635     //! Returns \c true if \a left is less than \a right considering possible integer overflow
is_overflow_less(uint32_t left,uint32_t right)636     static bool is_overflow_less(uint32_t left, uint32_t right) BOOST_NOEXCEPT
637     {
638         return ((left - right) & 0x80000000u) != 0u;
639     }
640 };
641 
642 } // namespace aux
643 
644 } // namespace ipc
645 
646 BOOST_LOG_CLOSE_NAMESPACE // namespace log
647 
648 } // namespace boost
649 
650 #include <boost/log/detail/footer.hpp>
651 
652 #endif // BOOST_LOG_WINDOWS_IPC_SYNC_WRAPPERS_HPP_INCLUDED_
653