1 /*
2  * Distributed under the Boost Software License, Version 1.0.
3  *    (See accompanying file LICENSE_1_0.txt or copy at
4  *          http://www.boost.org/LICENSE_1_0.txt)
5  *
6  * (C) Copyright 2007-2008 Anthony Williams
7  * (C) Copyright 2012-2013 Vicente J. Botet Escriba
8  * (C) Copyright 2013 Andrey Semashev
9  */
10 /*!
11  * \file   detail/mutexes/timed_mutex_windows.hpp
12  *
13  * \brief  This header is the Boost.Sync library implementation, see the library documentation
14  *         at http://www.boost.org/doc/libs/release/libs/sync/doc/html/index.html.
15  */
16 
17 #ifndef BOOST_SYNC_DETAIL_MUTEXES_TIMED_MUTEX_WINDOWS_HPP_INCLUDED_
18 #define BOOST_SYNC_DETAIL_MUTEXES_TIMED_MUTEX_WINDOWS_HPP_INCLUDED_
19 
20 #include <cstddef>
21 #include <boost/cstdint.hpp>
22 #include <boost/assert.hpp>
23 #include <boost/utility/enable_if.hpp>
24 #include <boost/detail/winapi/synchronization.hpp>
25 #include <boost/sync/exceptions/lock_error.hpp>
26 #include <boost/sync/detail/config.hpp>
27 #include <boost/sync/detail/throw_exception.hpp>
28 #include <boost/sync/detail/interlocked.hpp>
29 #include <boost/sync/detail/time_traits.hpp>
30 #include <boost/sync/detail/time_units.hpp>
31 #include <boost/sync/detail/waitable_timer.hpp>
32 #include <boost/sync/detail/mutexes/basic_mutex_windows.hpp>
33 
34 #include <boost/sync/detail/header.hpp>
35 
36 #ifdef BOOST_HAS_PRAGMA_ONCE
37 #pragma once
38 #endif
39 
40 namespace boost {
41 
42 namespace sync {
43 
44 BOOST_SYNC_DETAIL_OPEN_ABI_NAMESPACE {
45 
46 class timed_mutex
47 {
48 public:
49     typedef void _is_condition_variable_compatible;
50 
51 private:
52     sync::detail::windows::basic_mutex m_mutex;
53 
54 public:
55 #if !defined(BOOST_NO_CXX11_CONSTEXPR)
56 #define BOOST_SYNC_DEFINES_TIMED_MUTEX_CONSTEXPR_CONSTRUCTOR
57 #endif
58 
59     BOOST_CONSTEXPR timed_mutex() BOOST_NOEXCEPT : m_mutex()
60     {
61     }
62 
63     void lock()
64     {
65         m_mutex.lock();
66     }
67 
68     void unlock() BOOST_NOEXCEPT
69     {
70         m_mutex.unlock();
71     }
72 
73     bool try_lock()
74     {
75         return m_mutex.try_lock();
76     }
77 
78     template< typename Time >
79     typename enable_if_c< sync::detail::time_traits< Time >::is_specialized, bool >::type timed_lock(Time const& t)
80     {
81         if (m_mutex.try_lock())
82             return true;
83 
84         return priv_timed_lock(sync::detail::time_traits< Time >::to_sync_unit(t));
85     }
86 
87     template< typename Duration >
88     typename detail::enable_if_tag< Duration, detail::time_duration_tag, bool >::type try_lock_for(Duration const& rel_time)
89     {
90         if (m_mutex.try_lock())
91             return true;
92 
93         return priv_timed_lock(sync::detail::time_traits< Duration >::to_sync_unit(rel_time));
94     }
95 
96     template< typename TimePoint >
97     typename detail::enable_if_tag< TimePoint, detail::time_point_tag, bool >::type try_lock_until(TimePoint const& abs_time)
98     {
99         if (m_mutex.try_lock())
100             return true;
101 
102         return priv_timed_lock(sync::detail::time_traits< TimePoint >::to_sync_unit(abs_time));
103     }
104 
105     BOOST_DELETED_FUNCTION(timed_mutex(timed_mutex const&))
106     BOOST_DELETED_FUNCTION(timed_mutex& operator= (timed_mutex const&))
107 
108 private:
109     bool priv_timed_lock(sync::detail::system_time_point const& t)
110     {
111         long old_count = m_mutex.m_active_count;
112         m_mutex.mark_waiting_and_try_lock(old_count);
113         if ((old_count & sync::detail::windows::basic_mutex::lock_flag_value) == 0)
114             return true;
115 
116         try
117         {
118             boost::detail::winapi::HANDLE_ handles[2];
119             handles[0] = m_mutex.get_event();
120             handles[1] = sync::detail::windows::get_waitable_timer();
121 
122             if (!boost::detail::winapi::SetWaitableTimer(handles[1], reinterpret_cast< const boost::detail::winapi::LARGE_INTEGER_* >(&t.get()), 0, NULL, NULL, false))
123             {
124                 const boost::detail::winapi::DWORD_ err = boost::detail::winapi::GetLastError();
125                 BOOST_SYNC_DETAIL_THROW(lock_error, (err)("timed_mutex::timed_lock failed to set a timeout"));
126             }
127 
128             while (true)
129             {
130                 const boost::detail::winapi::DWORD_ res = boost::detail::winapi::WaitForMultipleObjects(sizeof(handles) / sizeof(*handles), handles, false, boost::detail::winapi::infinite);
131                 if (res == boost::detail::winapi::wait_failed)
132                 {
133                     const boost::detail::winapi::DWORD_ err = boost::detail::winapi::GetLastError();
134                     BOOST_SYNC_DETAIL_THROW(lock_error, (err)("timed_mutex::timed_lock failed in WaitForMultipleObjects"));
135                 }
136 
137                 switch (res)
138                 {
139                 case boost::detail::winapi::wait_object_0: // event was notified
140                     m_mutex.clear_waiting_and_try_lock(old_count);
141                     if ((old_count & sync::detail::windows::basic_mutex::lock_flag_value) == 0)
142                         return true;
143                     break;
144 
145                 case boost::detail::winapi::wait_object_0 + 1: // timeout has expired
146                     BOOST_ATOMIC_INTERLOCKED_EXCHANGE_ADD(&m_mutex.m_active_count, -1);
147                     return false;
148 
149                 default:
150                     BOOST_ASSERT(false);
151                 }
152             }
153         }
154         catch (...)
155         {
156             BOOST_ATOMIC_INTERLOCKED_EXCHANGE_ADD(&m_mutex.m_active_count, -1);
157             throw;
158         }
159     }
160 
161     bool priv_timed_lock(sync::detail::system_duration t)
162     {
163         long old_count = m_mutex.m_active_count;
164         m_mutex.mark_waiting_and_try_lock(old_count);
165         if ((old_count & sync::detail::windows::basic_mutex::lock_flag_value) == 0)
166             return true;
167 
168         try
169         {
170             const boost::detail::winapi::HANDLE_ evt = m_mutex.get_event();
171             sync::detail::system_duration::native_type time_left = t.get();
172             while (time_left > 0)
173             {
174                 const boost::detail::winapi::DWORD_ dur = time_left > boost::detail::winapi::max_non_infinite_wait ?
175                     boost::detail::winapi::max_non_infinite_wait : static_cast< boost::detail::winapi::DWORD_ >(time_left);
176                 const boost::detail::winapi::DWORD_ res = boost::detail::winapi::WaitForSingleObject(evt, dur);
177                 switch (res)
178                 {
179                 case boost::detail::winapi::wait_object_0:
180                     m_mutex.clear_waiting_and_try_lock(old_count);
181                     if ((old_count & sync::detail::windows::basic_mutex::lock_flag_value) == 0)
182                         return true;
183                     break;
184 
185                 case boost::detail::winapi::wait_timeout:
186                     time_left -= dur;
187                     break;
188 
189                 default:
190                     {
191                         const boost::detail::winapi::DWORD_ err = boost::detail::winapi::GetLastError();
192                         BOOST_SYNC_DETAIL_THROW(lock_error, (err)("timed_mutex::timed_lock failed in WaitForSingleObject"));
193                     }
194                 }
195             }
196 
197             BOOST_ATOMIC_INTERLOCKED_EXCHANGE_ADD(&m_mutex.m_active_count, -1);
198 
199             return false;
200         }
201         catch (...)
202         {
203             BOOST_ATOMIC_INTERLOCKED_EXCHANGE_ADD(&m_mutex.m_active_count, -1);
204             throw;
205         }
206     }
207 
208     template< typename TimePoint >
209     bool priv_timed_lock(sync::detail::chrono_time_point< TimePoint > const& t)
210     {
211         typedef TimePoint time_point;
212         typedef typename time_point::clock clock;
213         typedef typename time_point::duration duration;
214         time_point now = clock::now();
215         while (now < t.get())
216         {
217             if (priv_timed_lock(sync::detail::time_traits< duration >::to_sync_unit(t.get() - now)))
218                 return true;
219             now = clock::now();
220         }
221         return false;
222     }
223 };
224 
225 } // namespace winnt
226 
227 } // namespace sync
228 
229 } // namespace boost
230 
231 #include <boost/sync/detail/footer.hpp>
232 
233 #endif // BOOST_SYNC_DETAIL_MUTEXES_TIMED_MUTEX_WINDOWS_HPP_INCLUDED_
234