1 | #ifndef BOOST_THREAD_PTHREAD_THREAD_DATA_HPP |
2 | #define BOOST_THREAD_PTHREAD_THREAD_DATA_HPP |
3 | // Distributed under the Boost Software License, Version 1.0. (See |
4 | // accompanying file LICENSE_1_0.txt or copy at |
5 | // http://www.boost.org/LICENSE_1_0.txt) |
6 | // (C) Copyright 2007 Anthony Williams |
7 | // (C) Copyright 2011-2012 Vicente J. Botet Escriba |
8 | |
9 | #include <boost/thread/detail/config.hpp> |
10 | #include <boost/thread/exceptions.hpp> |
11 | #include <boost/thread/lock_guard.hpp> |
12 | #include <boost/thread/lock_types.hpp> |
13 | #include <boost/thread/mutex.hpp> |
14 | #include <boost/thread/pthread/condition_variable_fwd.hpp> |
15 | #include <boost/thread/pthread/pthread_helpers.hpp> |
16 | |
17 | #include <boost/shared_ptr.hpp> |
18 | #include <boost/enable_shared_from_this.hpp> |
19 | #include <boost/assert.hpp> |
20 | #include <boost/thread/detail/platform_time.hpp> |
21 | #ifdef BOOST_THREAD_USES_CHRONO |
22 | #include <boost/chrono/system_clocks.hpp> |
23 | #endif |
24 | |
25 | #include <map> |
26 | #include <vector> |
27 | #include <utility> |
28 | |
29 | #if defined(__ANDROID__) |
30 | # ifndef PAGE_SIZE |
31 | # define PAGE_SIZE 4096 |
32 | # endif |
33 | #endif |
34 | |
35 | #include <pthread.h> |
36 | #include <unistd.h> |
37 | |
38 | #include <boost/config/abi_prefix.hpp> |
39 | |
40 | namespace boost |
41 | { |
42 | class thread_attributes { |
43 | public: |
44 | thread_attributes() BOOST_NOEXCEPT { |
45 | int res = pthread_attr_init(attr: &val_); |
46 | BOOST_VERIFY(!res && "pthread_attr_init failed" ); |
47 | } |
48 | ~thread_attributes() { |
49 | int res = pthread_attr_destroy(attr: &val_); |
50 | BOOST_VERIFY(!res && "pthread_attr_destroy failed" ); |
51 | } |
52 | // stack |
53 | void set_stack_size(std::size_t size) BOOST_NOEXCEPT { |
54 | if (size==0) return; |
55 | #ifdef BOOST_THREAD_USES_GETPAGESIZE |
56 | std::size_t page_size = getpagesize(); |
57 | #else |
58 | std::size_t page_size = ::sysconf( _SC_PAGESIZE); |
59 | #endif |
60 | #ifdef PTHREAD_STACK_MIN |
61 | if (size<static_cast<std::size_t>(PTHREAD_STACK_MIN)) size=PTHREAD_STACK_MIN; |
62 | #endif |
63 | size = ((size+page_size-1)/page_size)*page_size; |
64 | int res = pthread_attr_setstacksize(attr: &val_, stacksize: size); |
65 | BOOST_VERIFY(!res && "pthread_attr_setstacksize failed" ); |
66 | } |
67 | |
68 | std::size_t get_stack_size() const BOOST_NOEXCEPT { |
69 | std::size_t size; |
70 | int res = pthread_attr_getstacksize(attr: &val_, stacksize: &size); |
71 | BOOST_VERIFY(!res && "pthread_attr_getstacksize failed" ); |
72 | return size; |
73 | } |
74 | #define BOOST_THREAD_DEFINES_THREAD_ATTRIBUTES_NATIVE_HANDLE |
75 | |
76 | typedef pthread_attr_t native_handle_type; |
77 | native_handle_type* native_handle() BOOST_NOEXCEPT { |
78 | return &val_; |
79 | } |
80 | const native_handle_type* native_handle() const BOOST_NOEXCEPT { |
81 | return &val_; |
82 | } |
83 | |
84 | private: |
85 | pthread_attr_t val_; |
86 | }; |
87 | |
88 | class thread; |
89 | |
90 | namespace detail |
91 | { |
92 | struct shared_state_base; |
93 | struct tss_cleanup_function; |
94 | struct thread_exit_callback_node; |
95 | struct tss_data_node |
96 | { |
97 | typedef void(*cleanup_func_t)(void*); |
98 | typedef void(*cleanup_caller_t)(cleanup_func_t, void*); |
99 | |
100 | cleanup_caller_t caller; |
101 | cleanup_func_t func; |
102 | void* value; |
103 | |
104 | tss_data_node(cleanup_caller_t caller_,cleanup_func_t func_,void* value_): |
105 | caller(caller_),func(func_),value(value_) |
106 | {} |
107 | }; |
108 | |
109 | struct thread_data_base; |
110 | typedef boost::shared_ptr<thread_data_base> thread_data_ptr; |
111 | |
112 | struct BOOST_THREAD_DECL thread_data_base: |
113 | enable_shared_from_this<thread_data_base> |
114 | { |
115 | thread_data_ptr self; |
116 | pthread_t thread_handle; |
117 | boost::mutex data_mutex; |
118 | boost::condition_variable done_condition; |
119 | bool done; |
120 | bool join_started; |
121 | bool joined; |
122 | boost::detail::thread_exit_callback_node* thread_exit_callbacks; |
123 | std::map<void const*,boost::detail::tss_data_node> tss_data; |
124 | |
125 | //#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
126 | // These data must be at the end so that the access to the other fields doesn't change |
127 | // when BOOST_THREAD_PROVIDES_INTERRUPTIONS is defined. |
128 | // Another option is to have them always |
129 | pthread_mutex_t* cond_mutex; |
130 | pthread_cond_t* current_cond; |
131 | //#endif |
132 | typedef std::vector<std::pair<condition_variable*, mutex*> |
133 | //, hidden_allocator<std::pair<condition_variable*, mutex*> > |
134 | > notify_list_t; |
135 | notify_list_t notify; |
136 | |
137 | //#ifndef BOOST_NO_EXCEPTIONS |
138 | typedef std::vector<shared_ptr<shared_state_base> > async_states_t; |
139 | async_states_t async_states_; |
140 | //#endif |
141 | //#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
142 | // These data must be at the end so that the access to the other fields doesn't change |
143 | // when BOOST_THREAD_PROVIDES_INTERRUPTIONS is defined. |
144 | // Another option is to have them always |
145 | bool interrupt_enabled; |
146 | bool interrupt_requested; |
147 | //#endif |
148 | thread_data_base(): |
149 | thread_handle(0), |
150 | done(false),join_started(false),joined(false), |
151 | thread_exit_callbacks(0), |
152 | //#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
153 | cond_mutex(0), |
154 | current_cond(0), |
155 | //#endif |
156 | notify() |
157 | //#ifndef BOOST_NO_EXCEPTIONS |
158 | , async_states_() |
159 | //#endif |
160 | //#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
161 | , interrupt_enabled(true) |
162 | , interrupt_requested(false) |
163 | //#endif |
164 | {} |
165 | virtual ~thread_data_base(); |
166 | |
167 | typedef pthread_t native_handle_type; |
168 | |
169 | virtual void run()=0; |
170 | virtual void notify_all_at_thread_exit(condition_variable* cv, mutex* m) |
171 | { |
172 | notify.push_back(x: std::pair<condition_variable*, mutex*>(cv, m)); |
173 | } |
174 | |
175 | //#ifndef BOOST_NO_EXCEPTIONS |
176 | void make_ready_at_thread_exit(shared_ptr<shared_state_base> as) |
177 | { |
178 | async_states_.push_back(x: as); |
179 | } |
180 | //#endif |
181 | }; |
182 | |
183 | BOOST_THREAD_DECL thread_data_base* get_current_thread_data(); |
184 | |
185 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
186 | class interruption_checker |
187 | { |
188 | thread_data_base* const thread_info; |
189 | pthread_mutex_t* m; |
190 | bool set; |
191 | bool done; |
192 | |
193 | void check_for_interruption() |
194 | { |
195 | #ifndef BOOST_NO_EXCEPTIONS |
196 | if(thread_info->interrupt_requested) |
197 | { |
198 | thread_info->interrupt_requested=false; |
199 | throw thread_interrupted(); // BOOST_NO_EXCEPTIONS protected |
200 | } |
201 | #endif |
202 | } |
203 | |
204 | void operator=(interruption_checker&); |
205 | public: |
206 | explicit interruption_checker(pthread_mutex_t* cond_mutex,pthread_cond_t* cond): |
207 | thread_info(detail::get_current_thread_data()),m(cond_mutex), |
208 | set(thread_info && thread_info->interrupt_enabled), done(false) |
209 | { |
210 | if(set) |
211 | { |
212 | lock_guard<mutex> guard(thread_info->data_mutex); |
213 | check_for_interruption(); |
214 | thread_info->cond_mutex=cond_mutex; |
215 | thread_info->current_cond=cond; |
216 | BOOST_VERIFY(!posix::pthread_mutex_lock(m)); |
217 | } |
218 | else |
219 | { |
220 | BOOST_VERIFY(!posix::pthread_mutex_lock(m)); |
221 | } |
222 | } |
223 | void unlock_if_locked() |
224 | { |
225 | if ( ! done) { |
226 | if (set) |
227 | { |
228 | BOOST_VERIFY(!posix::pthread_mutex_unlock(m)); |
229 | lock_guard<mutex> guard(thread_info->data_mutex); |
230 | thread_info->cond_mutex=NULL; |
231 | thread_info->current_cond=NULL; |
232 | } |
233 | else |
234 | { |
235 | BOOST_VERIFY(!posix::pthread_mutex_unlock(m)); |
236 | } |
237 | done = true; |
238 | } |
239 | } |
240 | |
241 | ~interruption_checker() BOOST_NOEXCEPT_IF(false) |
242 | { |
243 | unlock_if_locked(); |
244 | } |
245 | }; |
246 | #endif |
247 | } |
248 | |
249 | namespace this_thread |
250 | { |
251 | void BOOST_THREAD_DECL yield() BOOST_NOEXCEPT; |
252 | |
253 | namespace hidden |
254 | { |
255 | inline bool always_false() |
256 | { |
257 | return false; |
258 | } |
259 | } |
260 | |
261 | #if defined BOOST_THREAD_USES_DATETIME |
262 | #ifdef __DECXXX |
263 | /// Workaround of DECCXX issue of incorrect template substitution |
264 | template<> |
265 | #endif |
266 | inline void sleep(system_time const& abs_time) |
267 | { |
268 | mutex mx; |
269 | unique_lock<mutex> lock(mx); |
270 | condition_variable cond; |
271 | cond.timed_wait(m&: lock, abs_time, pred: hidden::always_false); |
272 | } |
273 | |
274 | template<typename TimeDuration> |
275 | void sleep(TimeDuration const& rel_time) |
276 | { |
277 | mutex mx; |
278 | unique_lock<mutex> lock(mx); |
279 | condition_variable cond; |
280 | cond.timed_wait(lock, rel_time, hidden::always_false); |
281 | } |
282 | #endif |
283 | |
284 | #ifdef BOOST_THREAD_USES_CHRONO |
285 | template <class Clock, class Duration> |
286 | void sleep_until(const chrono::time_point<Clock, Duration>& t) |
287 | { |
288 | mutex mut; |
289 | unique_lock<mutex> lk(mut); |
290 | condition_variable cv; |
291 | cv.wait_until(lk, t, hidden::always_false); |
292 | } |
293 | |
294 | template <class Rep, class Period> |
295 | void sleep_for(const chrono::duration<Rep, Period>& d) |
296 | { |
297 | mutex mut; |
298 | unique_lock<mutex> lk(mut); |
299 | condition_variable cv; |
300 | cv.wait_for(lk, d, hidden::always_false); |
301 | } |
302 | #endif |
303 | |
304 | namespace no_interruption_point |
305 | { |
306 | #if defined BOOST_THREAD_SLEEP_FOR_IS_STEADY |
307 | // Use pthread_delay_np or nanosleep when available |
308 | // because they do not provide an interruption point. |
309 | |
310 | namespace hidden |
311 | { |
312 | void BOOST_THREAD_DECL sleep_for_internal(const detail::platform_duration& ts); |
313 | } |
314 | |
315 | #if defined BOOST_THREAD_USES_DATETIME |
316 | #ifdef __DECXXX |
317 | /// Workaround of DECCXX issue of incorrect template substitution |
318 | template<> |
319 | #endif |
320 | inline void sleep(system_time const& abs_time) |
321 | { |
322 | const detail::real_platform_timepoint ts(abs_time); |
323 | detail::platform_duration d(ts - detail::real_platform_clock::now()); |
324 | while (d > detail::platform_duration::zero()) |
325 | { |
326 | d = (std::min)(a: d, b: detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); |
327 | hidden::sleep_for_internal(ts: d); |
328 | d = ts - detail::real_platform_clock::now(); |
329 | } |
330 | } |
331 | |
332 | template<typename TimeDuration> |
333 | void sleep(TimeDuration const& rel_time) |
334 | { |
335 | hidden::sleep_for_internal(ts: detail::platform_duration(rel_time)); |
336 | } |
337 | #endif |
338 | |
339 | #ifdef BOOST_THREAD_USES_CHRONO |
340 | template <class Rep, class Period> |
341 | void sleep_for(const chrono::duration<Rep, Period>& d) |
342 | { |
343 | hidden::sleep_for_internal(ts: detail::platform_duration(d)); |
344 | } |
345 | |
346 | template <class Duration> |
347 | void sleep_until(const chrono::time_point<chrono::steady_clock, Duration>& t) |
348 | { |
349 | sleep_for(t - chrono::steady_clock::now()); |
350 | } |
351 | |
352 | template <class Clock, class Duration> |
353 | void sleep_until(const chrono::time_point<Clock, Duration>& t) |
354 | { |
355 | typedef typename common_type<Duration, typename Clock::duration>::type common_duration; |
356 | common_duration d(t - Clock::now()); |
357 | while (d > common_duration::zero()) |
358 | { |
359 | d = (std::min)(d, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS))); |
360 | hidden::sleep_for_internal(ts: detail::platform_duration(d)); |
361 | d = t - Clock::now(); |
362 | } |
363 | } |
364 | #endif |
365 | |
366 | #else // BOOST_THREAD_SLEEP_FOR_IS_STEADY |
367 | // When pthread_delay_np and nanosleep are not available, |
368 | // fall back to using the interruptible sleep functions. |
369 | |
370 | #if defined BOOST_THREAD_USES_DATETIME |
371 | #ifdef __DECXXX |
372 | /// Workaround of DECCXX issue of incorrect template substitution |
373 | template<> |
374 | #endif |
375 | inline void sleep(system_time const& abs_time) |
376 | { |
377 | this_thread::sleep(abs_time); |
378 | } |
379 | |
380 | template<typename TimeDuration> |
381 | void sleep(TimeDuration const& rel_time) |
382 | { |
383 | this_thread::sleep(rel_time); |
384 | } |
385 | #endif |
386 | |
387 | #ifdef BOOST_THREAD_USES_CHRONO |
388 | template <class Clock, class Duration> |
389 | void sleep_until(const chrono::time_point<Clock, Duration>& t) |
390 | { |
391 | this_thread::sleep_until(t); |
392 | } |
393 | |
394 | template <class Rep, class Period> |
395 | void sleep_for(const chrono::duration<Rep, Period>& d) |
396 | { |
397 | this_thread::sleep_for(d); |
398 | } |
399 | #endif |
400 | |
401 | #endif // BOOST_THREAD_SLEEP_FOR_IS_STEADY |
402 | } // no_interruption_point |
403 | } // this_thread |
404 | } |
405 | |
406 | #include <boost/config/abi_suffix.hpp> |
407 | |
408 | #endif |
409 | |