1 // semaphore.hpp, osx/ios dispatch semaphores
2 //
3 // Copyright (C) 2013 Tim Blechmann
4 // Copyright (C) 2013 Andrey Semashev
5 //
6 // Distributed under the Boost Software License, Version 1.0. (See
7 // accompanying file LICENSE_1_0.txt or copy at
8 // http://www.boost.org/LICENSE_1_0.txt)
9 
10 #ifndef BOOST_SYNC_DETAIL_SEMAPHORE_SEMAPHORE_DISPATCH_HPP_INCLUDED_
11 #define BOOST_SYNC_DETAIL_SEMAPHORE_SEMAPHORE_DISPATCH_HPP_INCLUDED_
12 
13 #include <time.h>
14 #include <cstddef>
15 #include <dispatch/dispatch.h>
16 
17 #include <boost/cstdint.hpp>
18 #include <boost/utility/enable_if.hpp>
19 #include <boost/sync/detail/config.hpp>
20 #include <boost/sync/detail/time_traits.hpp>
21 #include <boost/sync/detail/time_units.hpp>
22 #include <boost/sync/detail/throw_exception.hpp>
23 #include <boost/sync/detail/system_error.hpp>
24 #include <boost/sync/exceptions/resource_error.hpp>
25 #include <boost/sync/detail/header.hpp>
26 
27 #ifdef BOOST_HAS_PRAGMA_ONCE
28 #pragma once
29 #endif
30 
31 namespace boost {
32 
33 namespace sync  {
34 
35 BOOST_SYNC_DETAIL_OPEN_ABI_NAMESPACE {
36 
37 class semaphore
38 {
39     BOOST_DELETED_FUNCTION(semaphore(semaphore const&))
40     BOOST_DELETED_FUNCTION(semaphore& operator=(semaphore const&))
41 
42 public:
43     explicit semaphore(unsigned int i = 0)
44     {
45         // Note that dispatch_release requires the semaphore counter to be equal to the one specified for dispatch_semaphore_create.
46         // Otherwise dispatch_release just crashes. This is probably a bug in libdispatch, but in order to work around it, we have to
47         // set the counter manually in a loop, after creating the semaphore.
48         m_sem = dispatch_semaphore_create(0);
49         if (m_sem == NULL)
50             BOOST_SYNC_DETAIL_THROW(resource_error, (sync::detail::system_ns::errc::not_enough_memory)("semaphore constructor failed in dispatch_semaphore_create"));
51 
52         for (; i > 0; --i)
53             dispatch_semaphore_signal(m_sem);
54     }
55 
56     ~semaphore()
57     {
58         dispatch_release(m_sem);
59     }
60 
61     void post()
62     {
63         dispatch_semaphore_signal(m_sem);
64     }
65 
66     void wait()
67     {
68         dispatch_semaphore_wait(m_sem, DISPATCH_TIME_FOREVER);
69     }
70 
71     bool try_wait()
72     {
73         return dispatch_semaphore_wait(m_sem, DISPATCH_TIME_NOW) == 0;
74     }
75 
76     template< typename Time >
77     typename enable_if_c< sync::detail::time_traits< Time >::is_specialized, bool >::type timed_wait(Time const& t)
78     {
79         return priv_timed_wait(sync::detail::time_traits< Time >::to_sync_unit(t));
80     }
81 
82     template< typename Duration >
83     typename enable_if< detail::is_time_tag_of< Duration, detail::time_duration_tag >, bool >::type wait_for(Duration const& duration)
84     {
85         return priv_timed_wait(sync::detail::time_traits< Duration >::to_sync_unit(duration));
86     }
87 
88     template< typename TimePoint >
89     typename enable_if< detail::is_time_tag_of< TimePoint, detail::time_point_tag >, bool >::type wait_until(TimePoint const& abs_time)
90     {
91         return priv_timed_wait(sync::detail::time_traits< TimePoint >::to_sync_unit(abs_time));
92     }
93 
94 private:
95     bool priv_timed_wait(sync::detail::system_duration dur)
96     {
97         dispatch_time_t timeout = DISPATCH_TIME_NOW;
98 
99         sync::detail::system_duration::native_type time_left = dur.get();
100         if (time_left > 0)
101         {
102             enum
103             {
104                 nanoseconds_fraction = 1000000000u,
105                 conversion_ratio = static_cast< uint64_t >(nanoseconds_fraction) >= sync::detail::system_duration::subsecond_fraction ?
106                     nanoseconds_fraction / sync::detail::system_duration::subsecond_fraction :
107                     sync::detail::system_duration::subsecond_fraction / nanoseconds_fraction
108             };
109 
110             const int64_t nanoseconds_left = static_cast< uint64_t >(nanoseconds_fraction) >= sync::detail::system_duration::subsecond_fraction ?
111                 time_left / conversion_ratio : time_left * conversion_ratio;
112 
113             timeout = dispatch_time(DISPATCH_TIME_NOW, nanoseconds_left);
114         }
115 
116         return dispatch_semaphore_wait(m_sem, timeout) == 0;
117     }
118 
119     bool priv_timed_wait(sync::detail::system_time_point const& t)
120     {
121         struct timespec timespec_timeout = t.get();
122         dispatch_time_t timeout = dispatch_walltime( &timespec_timeout, 0 );
123         return dispatch_semaphore_wait(m_sem, timeout) == 0;
124     }
125 
126     template< typename TimePoint >
127     bool priv_timed_wait(sync::detail::chrono_time_point< TimePoint > const& t)
128     {
129         typedef TimePoint time_point;
130         typedef typename time_point::clock clock;
131         typedef typename time_point::duration duration;
132         time_point now = clock::now();
133         while (now < t.get())
134         {
135             if (priv_timed_wait(sync::detail::time_traits< duration >::to_sync_unit(t.get() - now)))
136                 return true;
137             now = clock::now();
138         }
139         return false;
140     }
141 
142 private:
143     dispatch_semaphore_t m_sem;
144 };
145 
146 } // namespace posix
147 
148 } // namespace sync
149 
150 } // namespace boost
151 
152 #include <boost/sync/detail/footer.hpp>
153 
154 #endif // BOOST_SYNC_DETAIL_SEMAPHORE_SEMAPHORE_DISPATCH_HPP_INCLUDED_
155