]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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> | |
b32b8144 FG |
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> | |
7c673cae FG |
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 | ||
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 | ||
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 | ||
b32b8144 | 174 | boost::winapi::HANDLE_ get_handle() const BOOST_NOEXCEPT { return m_event.get(); } |
7c673cae FG |
175 | |
176 | void set() | |
177 | { | |
b32b8144 | 178 | if (BOOST_UNLIKELY(!boost::winapi::SetEvent(m_event.get()))) |
7c673cae | 179 | { |
b32b8144 | 180 | const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); |
7c673cae FG |
181 | BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to set an interprocess event object", (err)); |
182 | } | |
183 | } | |
184 | ||
185 | void set_noexcept() BOOST_NOEXCEPT | |
186 | { | |
b32b8144 | 187 | BOOST_VERIFY(!!boost::winapi::SetEvent(m_event.get())); |
7c673cae FG |
188 | } |
189 | ||
190 | void reset() | |
191 | { | |
b32b8144 | 192 | if (BOOST_UNLIKELY(!boost::winapi::ResetEvent(m_event.get()))) |
7c673cae | 193 | { |
b32b8144 | 194 | const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); |
7c673cae FG |
195 | BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to reset an interprocess event object", (err)); |
196 | } | |
197 | } | |
198 | ||
199 | void wait() | |
200 | { | |
b32b8144 FG |
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)) | |
7c673cae | 203 | { |
b32b8144 | 204 | const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); |
7c673cae FG |
205 | BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to block on an interprocess event object", (err)); |
206 | } | |
207 | } | |
208 | ||
b32b8144 | 209 | bool wait(boost::winapi::HANDLE_ abort_handle) |
7c673cae | 210 | { |
b32b8144 FG |
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)) | |
7c673cae FG |
214 | { |
215 | // Wait was interrupted | |
216 | return false; | |
217 | } | |
b32b8144 | 218 | else if (BOOST_UNLIKELY(retval != boost::winapi::wait_object_0)) |
7c673cae | 219 | { |
b32b8144 | 220 | const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); |
7c673cae FG |
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 | ||
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: | |
b32b8144 | 237 | typedef boost::winapi::DWORD_ NTSTATUS_; |
7c673cae FG |
238 | struct semaphore_basic_information |
239 | { | |
b32b8144 FG |
240 | boost::winapi::ULONG_ current_count; // current semaphore count |
241 | boost::winapi::ULONG_ maximum_count; // max semaphore count | |
7c673cae | 242 | }; |
b32b8144 FG |
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); | |
7c673cae FG |
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 | ||
b32b8144 | 256 | boost::winapi::HANDLE_ get_handle() const BOOST_NOEXCEPT { return m_sem.get(); } |
7c673cae FG |
257 | |
258 | void post(uint32_t count) | |
259 | { | |
b32b8144 | 260 | BOOST_ASSERT(count <= static_cast< uint32_t >((std::numeric_limits< boost::winapi::LONG_ >::max)())); |
7c673cae | 261 | |
b32b8144 | 262 | if (BOOST_UNLIKELY(!boost::winapi::ReleaseSemaphore(m_sem.get(), static_cast< boost::winapi::LONG_ >(count), NULL))) |
7c673cae | 263 | { |
b32b8144 | 264 | const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); |
7c673cae FG |
265 | BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to post on an interprocess semaphore object", (err)); |
266 | } | |
267 | } | |
268 | ||
269 | bool is_zero_count() const | |
270 | { | |
271 | return is_semaphore_zero_count.load(boost::memory_order_acquire)(m_sem.get()); | |
272 | } | |
273 | ||
274 | void wait() | |
275 | { | |
b32b8144 FG |
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)) | |
7c673cae | 278 | { |
b32b8144 | 279 | const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); |
7c673cae FG |
280 | BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to block on an interprocess semaphore object", (err)); |
281 | } | |
282 | } | |
283 | ||
b32b8144 | 284 | bool wait(boost::winapi::HANDLE_ abort_handle) |
7c673cae | 285 | { |
b32b8144 FG |
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)) | |
7c673cae FG |
289 | { |
290 | // Wait was interrupted | |
291 | return false; | |
292 | } | |
b32b8144 | 293 | else if (BOOST_UNLIKELY(retval != boost::winapi::wait_object_0)) |
7c673cae | 294 | { |
b32b8144 | 295 | const boost::winapi::DWORD_ err = boost::winapi::GetLastError(); |
7c673cae FG |
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 | ||
302 | void swap(interprocess_semaphore& that) BOOST_NOEXCEPT | |
303 | { | |
304 | m_sem.swap(that.m_sem); | |
305 | } | |
306 | ||
307 | private: | |
b32b8144 FG |
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); | |
7c673cae FG |
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 | ||
322 | shared_state() BOOST_NOEXCEPT : m_lock_state(0u) | |
323 | { | |
324 | } | |
325 | }; | |
326 | ||
327 | struct auto_unlock | |
328 | { | |
329 | explicit auto_unlock(interprocess_mutex& mutex) BOOST_NOEXCEPT : m_mutex(mutex) {} | |
330 | ~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 | { | |
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(); } | |
344 | ||
345 | interprocess_mutex* disengage() BOOST_NOEXCEPT | |
346 | { | |
347 | interprocess_mutex* p = m_mutex; | |
348 | m_mutex = NULL; | |
349 | return p; | |
350 | } | |
351 | ||
352 | 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: | |
388 | interprocess_mutex() BOOST_NOEXCEPT : m_shared_state(NULL) | |
389 | { | |
390 | } | |
391 | ||
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 | ||
398 | void open(const wchar_t* name, shared_state* shared) | |
399 | { | |
400 | m_event.open(name); | |
401 | m_shared_state = shared; | |
402 | } | |
403 | ||
404 | bool try_lock() | |
405 | { | |
406 | return !bit_test_and_set(m_shared_state->m_lock_state, lock_flag_bit); | |
407 | } | |
408 | ||
409 | void lock() | |
410 | { | |
411 | if (BOOST_UNLIKELY(!try_lock())) | |
412 | lock_slow(); | |
413 | } | |
414 | ||
b32b8144 | 415 | bool lock(boost::winapi::HANDLE_ abort_handle) |
7c673cae FG |
416 | { |
417 | if (BOOST_LIKELY(try_lock())) | |
418 | return true; | |
419 | return lock_slow(abort_handle); | |
420 | } | |
421 | ||
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(); | |
b32b8144 | 439 | bool lock_slow(boost::winapi::HANDLE_ abort_handle); |
7c673cae FG |
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 | |
b32b8144 | 448 | typedef boost::winapi::ULONGLONG_ time_point; |
7c673cae | 449 | #else |
b32b8144 | 450 | typedef boost::winapi::DWORD_ time_point; |
7c673cae FG |
451 | #endif |
452 | ||
453 | static time_point now() BOOST_NOEXCEPT | |
454 | { | |
455 | #if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 | |
b32b8144 | 456 | return boost::winapi::GetTickCount64(); |
7c673cae | 457 | #else |
b32b8144 | 458 | return boost::winapi::GetTickCount(); |
7c673cae FG |
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 | ||
487 | result_type operator() (semaphore_info const& left, semaphore_info const& right) const BOOST_NOEXCEPT | |
488 | { | |
489 | return left.m_id < right.m_id; | |
490 | } | |
491 | result_type operator() (semaphore_info const& left, uint32_t right) const BOOST_NOEXCEPT | |
492 | { | |
493 | return left.m_id < right; | |
494 | } | |
495 | 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 | ||
510 | 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 | |
515 | 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 | ||
554 | 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: | |
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 | ||
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 | ||
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 | ||
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 | ||
b32b8144 | 618 | bool wait(interprocess_mutex::optional_unlock& lock, boost::winapi::HANDLE_ abort_handle); |
7c673cae FG |
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 | |
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_ |