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:
thread_attributes()44         thread_attributes() BOOST_NOEXCEPT {
45             int res = pthread_attr_init(&val_);
46             BOOST_VERIFY(!res && "pthread_attr_init failed");
47         }
~thread_attributes()48         ~thread_attributes() {
49           int res = pthread_attr_destroy(&val_);
50           BOOST_VERIFY(!res && "pthread_attr_destroy failed");
51         }
52         // stack
set_stack_size(std::size_t size)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 #if PTHREAD_STACK_MIN > 0
61           if (size<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(&val_, size);
65           BOOST_VERIFY(!res && "pthread_attr_setstacksize failed");
66         }
67 
get_stack_size() const68         std::size_t get_stack_size() const BOOST_NOEXCEPT {
69             std::size_t size;
70             int res = pthread_attr_getstacksize(&val_, &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;
native_handle()77         native_handle_type* native_handle() BOOST_NOEXCEPT {
78           return &val_;
79         }
native_handle() const80         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 
tss_data_nodeboost::detail::tss_data_node104             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
thread_data_baseboost::detail::thread_data_base148             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;
notify_all_at_thread_exitboost::detail::thread_data_base170             virtual void notify_all_at_thread_exit(condition_variable* cv, mutex* m)
171             {
172               notify.push_back(std::pair<condition_variable*, mutex*>(cv, m));
173             }
174 
175 //#ifndef BOOST_NO_EXCEPTIONS
make_ready_at_thread_exitboost::detail::thread_data_base176             void make_ready_at_thread_exit(shared_ptr<shared_state_base> as)
177             {
178               async_states_.push_back(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 
check_for_interruption()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:
interruption_checker(pthread_mutex_t * cond_mutex,pthread_cond_t * cond)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             }
unlock_if_locked()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 
BOOST_NOEXCEPT_IF(false)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         {
always_false()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
sleep(system_time const & abs_time)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(lock, abs_time, hidden::always_false);
272         }
273 
274         template<typename TimeDuration>
sleep(TimeDuration const & rel_time)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>
sleep_until(const chrono::time_point<Clock,Duration> & t)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>
sleep_for(const chrono::duration<Rep,Period> & d)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
sleep(system_time const & abs_time)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)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS));
327               hidden::sleep_for_internal(d);
328               d = ts - detail::real_platform_clock::now();
329             }
330           }
331 
332           template<typename TimeDuration>
sleep(TimeDuration const & rel_time)333           void sleep(TimeDuration const& rel_time)
334           {
335             hidden::sleep_for_internal(detail::platform_duration(rel_time));
336           }
337 #endif
338 
339 #ifdef BOOST_THREAD_USES_CHRONO
340           template <class Rep, class Period>
sleep_for(const chrono::duration<Rep,Period> & d)341           void sleep_for(const chrono::duration<Rep, Period>& d)
342           {
343             hidden::sleep_for_internal(detail::platform_duration(d));
344           }
345 
346           template <class Duration>
sleep_until(const chrono::time_point<chrono::steady_clock,Duration> & t)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>
sleep_until(const chrono::time_point<Clock,Duration> & t)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(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