1 // -*- C++ -*- header.
2 
3 // Copyright (C) 2015-2018 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library.  This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
9 // any later version.
10 
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
19 
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23 // <http://www.gnu.org/licenses/>.
24 
25 /** @file bits/atomic_futex.h
26  *  This is an internal header file, included by other library headers.
27  *  Do not attempt to use it directly.
28  */
29 
30 #ifndef _GLIBCXX_ATOMIC_FUTEX_H
31 #define _GLIBCXX_ATOMIC_FUTEX_H 1
32 
33 #pragma GCC system_header
34 
35 #include <bits/c++config.h>
36 #include <atomic>
37 #include <chrono>
38 #if ! (defined(_GLIBCXX_HAVE_LINUX_FUTEX) && ATOMIC_INT_LOCK_FREE > 1)
39 #include <mutex>
40 #include <condition_variable>
41 #endif
42 
43 #ifndef _GLIBCXX_ALWAYS_INLINE
44 #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__))
45 #endif
46 
47 namespace std _GLIBCXX_VISIBILITY(default)
48 {
49 _GLIBCXX_BEGIN_NAMESPACE_VERSION
50 
51 #if defined(_GLIBCXX_HAS_GTHREADS) && defined(_GLIBCXX_USE_C99_STDINT_TR1)
52 #if defined(_GLIBCXX_HAVE_LINUX_FUTEX) && ATOMIC_INT_LOCK_FREE > 1
53   struct __atomic_futex_unsigned_base
54   {
55     // Returns false iff a timeout occurred.
56     bool
57     _M_futex_wait_until(unsigned *__addr, unsigned __val, bool __has_timeout,
58 	chrono::seconds __s, chrono::nanoseconds __ns);
59 
60     // This can be executed after the object has been destroyed.
61     static void _M_futex_notify_all(unsigned* __addr);
62   };
63 
64   template <unsigned _Waiter_bit = 0x80000000>
65   class __atomic_futex_unsigned : __atomic_futex_unsigned_base
66   {
67     typedef chrono::system_clock __clock_t;
68 
69     // This must be lock-free and at offset 0.
70     atomic<unsigned> _M_data;
71 
72   public:
73     explicit
74     __atomic_futex_unsigned(unsigned __data) : _M_data(__data)
75     { }
76 
77     _GLIBCXX_ALWAYS_INLINE unsigned
78     _M_load(memory_order __mo)
79     {
80       return _M_data.load(__mo) & ~_Waiter_bit;
81     }
82 
83   private:
84     // If a timeout occurs, returns a current value after the timeout;
85     // otherwise, returns the operand's value if equal is true or a different
86     // value if equal is false.
87     // The assumed value is the caller's assumption about the current value
88     // when making the call.
89     unsigned
90     _M_load_and_test_until(unsigned __assumed, unsigned __operand,
91 	bool __equal, memory_order __mo, bool __has_timeout,
92 	chrono::seconds __s, chrono::nanoseconds __ns)
93     {
94       for (;;)
95 	{
96 	  // Don't bother checking the value again because we expect the caller
97 	  // to have done it recently.
98 	  // memory_order_relaxed is sufficient because we can rely on just the
99 	  // modification order (store_notify uses an atomic RMW operation too),
100 	  // and the futex syscalls synchronize between themselves.
101 	  _M_data.fetch_or(_Waiter_bit, memory_order_relaxed);
102 	  bool __ret = _M_futex_wait_until((unsigned*)(void*)&_M_data,
103 					   __assumed | _Waiter_bit,
104 					   __has_timeout, __s, __ns);
105 	  // Fetch the current value after waiting (clears _Waiter_bit).
106 	  __assumed = _M_load(__mo);
107 	  if (!__ret || ((__operand == __assumed) == __equal))
108 	    return __assumed;
109 	  // TODO adapt wait time
110 	}
111     }
112 
113     // Returns the operand's value if equal is true or a different value if
114     // equal is false.
115     // The assumed value is the caller's assumption about the current value
116     // when making the call.
117     unsigned
118     _M_load_and_test(unsigned __assumed, unsigned __operand,
119 	bool __equal, memory_order __mo)
120     {
121       return _M_load_and_test_until(__assumed, __operand, __equal, __mo,
122 				    false, {}, {});
123     }
124 
125     // If a timeout occurs, returns a current value after the timeout;
126     // otherwise, returns the operand's value if equal is true or a different
127     // value if equal is false.
128     // The assumed value is the caller's assumption about the current value
129     // when making the call.
130     template<typename _Dur>
131     unsigned
132     _M_load_and_test_until_impl(unsigned __assumed, unsigned __operand,
133 	bool __equal, memory_order __mo,
134 	const chrono::time_point<__clock_t, _Dur>& __atime)
135     {
136       auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
137       auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
138       // XXX correct?
139       return _M_load_and_test_until(__assumed, __operand, __equal, __mo,
140 	  true, __s.time_since_epoch(), __ns);
141     }
142 
143   public:
144 
145     _GLIBCXX_ALWAYS_INLINE unsigned
146     _M_load_when_not_equal(unsigned __val, memory_order __mo)
147     {
148       unsigned __i = _M_load(__mo);
149       if ((__i & ~_Waiter_bit) != __val)
150 	return (__i & ~_Waiter_bit);
151       // TODO Spin-wait first.
152       return _M_load_and_test(__i, __val, false, __mo);
153     }
154 
155     _GLIBCXX_ALWAYS_INLINE void
156     _M_load_when_equal(unsigned __val, memory_order __mo)
157     {
158       unsigned __i = _M_load(__mo);
159       if ((__i & ~_Waiter_bit) == __val)
160 	return;
161       // TODO Spin-wait first.
162       _M_load_and_test(__i, __val, true, __mo);
163     }
164 
165     // Returns false iff a timeout occurred.
166     template<typename _Rep, typename _Period>
167       _GLIBCXX_ALWAYS_INLINE bool
168       _M_load_when_equal_for(unsigned __val, memory_order __mo,
169 	  const chrono::duration<_Rep, _Period>& __rtime)
170       {
171 	return _M_load_when_equal_until(__val, __mo,
172 					__clock_t::now() + __rtime);
173       }
174 
175     // Returns false iff a timeout occurred.
176     template<typename _Clock, typename _Duration>
177       _GLIBCXX_ALWAYS_INLINE bool
178       _M_load_when_equal_until(unsigned __val, memory_order __mo,
179 	  const chrono::time_point<_Clock, _Duration>& __atime)
180       {
181 	// DR 887 - Sync unknown clock to known clock.
182 	const typename _Clock::time_point __c_entry = _Clock::now();
183 	const __clock_t::time_point __s_entry = __clock_t::now();
184 	const auto __delta = __atime - __c_entry;
185 	const auto __s_atime = __s_entry + __delta;
186 	return _M_load_when_equal_until(__val, __mo, __s_atime);
187       }
188 
189     // Returns false iff a timeout occurred.
190     template<typename _Duration>
191     _GLIBCXX_ALWAYS_INLINE bool
192     _M_load_when_equal_until(unsigned __val, memory_order __mo,
193 	const chrono::time_point<__clock_t, _Duration>& __atime)
194     {
195       unsigned __i = _M_load(__mo);
196       if ((__i & ~_Waiter_bit) == __val)
197 	return true;
198       // TODO Spin-wait first.  Ignore effect on timeout.
199       __i = _M_load_and_test_until_impl(__i, __val, true, __mo, __atime);
200       return (__i & ~_Waiter_bit) == __val;
201     }
202 
203     _GLIBCXX_ALWAYS_INLINE void
204     _M_store_notify_all(unsigned __val, memory_order __mo)
205     {
206       unsigned* __futex = (unsigned *)(void *)&_M_data;
207       if (_M_data.exchange(__val, __mo) & _Waiter_bit)
208 	_M_futex_notify_all(__futex);
209     }
210   };
211 
212 #else // ! (_GLIBCXX_HAVE_LINUX_FUTEX && ATOMIC_INT_LOCK_FREE > 1)
213 
214   // If futexes are not available, use a mutex and a condvar to wait.
215   // Because we access the data only within critical sections, all accesses
216   // are sequentially consistent; thus, we satisfy any provided memory_order.
217   template <unsigned _Waiter_bit = 0x80000000>
218   class __atomic_futex_unsigned
219   {
220     typedef chrono::system_clock __clock_t;
221 
222     unsigned _M_data;
223     mutex _M_mutex;
224     condition_variable _M_condvar;
225 
226   public:
227     explicit
228     __atomic_futex_unsigned(unsigned __data) : _M_data(__data)
229     { }
230 
231     _GLIBCXX_ALWAYS_INLINE unsigned
232     _M_load(memory_order __mo)
233     {
234       unique_lock<mutex> __lock(_M_mutex);
235       return _M_data;
236     }
237 
238     _GLIBCXX_ALWAYS_INLINE unsigned
239     _M_load_when_not_equal(unsigned __val, memory_order __mo)
240     {
241       unique_lock<mutex> __lock(_M_mutex);
242       while (_M_data == __val)
243 	_M_condvar.wait(__lock);
244       return _M_data;
245     }
246 
247     _GLIBCXX_ALWAYS_INLINE void
248     _M_load_when_equal(unsigned __val, memory_order __mo)
249     {
250       unique_lock<mutex> __lock(_M_mutex);
251       while (_M_data != __val)
252 	_M_condvar.wait(__lock);
253     }
254 
255     template<typename _Rep, typename _Period>
256       _GLIBCXX_ALWAYS_INLINE bool
257       _M_load_when_equal_for(unsigned __val, memory_order __mo,
258 	  const chrono::duration<_Rep, _Period>& __rtime)
259       {
260 	unique_lock<mutex> __lock(_M_mutex);
261 	return _M_condvar.wait_for(__lock, __rtime,
262 				   [&] { return _M_data == __val;});
263       }
264 
265     template<typename _Clock, typename _Duration>
266       _GLIBCXX_ALWAYS_INLINE bool
267       _M_load_when_equal_until(unsigned __val, memory_order __mo,
268 	  const chrono::time_point<_Clock, _Duration>& __atime)
269       {
270 	unique_lock<mutex> __lock(_M_mutex);
271 	return _M_condvar.wait_until(__lock, __atime,
272 				     [&] { return _M_data == __val;});
273       }
274 
275     _GLIBCXX_ALWAYS_INLINE void
276     _M_store_notify_all(unsigned __val, memory_order __mo)
277     {
278       unique_lock<mutex> __lock(_M_mutex);
279       _M_data = __val;
280       _M_condvar.notify_all();
281     }
282   };
283 
284 #endif // _GLIBCXX_HAVE_LINUX_FUTEX && ATOMIC_INT_LOCK_FREE > 1
285 #endif // _GLIBCXX_HAS_GTHREADS && _GLIBCXX_USE_C99_STDINT_TR1
286 
287 _GLIBCXX_END_NAMESPACE_VERSION
288 } // namespace std
289 
290 #endif
291