condition_variable.hpp (10341B)
1 /* Relacy Race Detector 2 * Copyright (c) 2008-2013, Dmitry S. Vyukov 3 * All rights reserved. 4 * This software is provided AS-IS with no warranty, either express or implied. 5 * This software is distributed under a license and may not be copied, 6 * modified or distributed except as expressly authorized under the 7 * terms of the license contained in the file LICENSE in this distribution. 8 */ 9 10 #ifndef RL_CONDITION_VARIABLE_HPP 11 #define RL_CONDITION_VARIABLE_HPP 12 #ifdef _MSC_VER 13 # pragma once 14 #endif 15 16 #include "../base.hpp" 17 #include "../context_base.hpp" 18 #include "../waitset.hpp" 19 #include "../signature.hpp" 20 21 22 namespace rl 23 { 24 25 struct mutex_wrapper 26 { 27 virtual void lock(debug_info_param info) const = 0; 28 virtual void unlock(debug_info_param info) const = 0; 29 virtual ~mutex_wrapper() {} 30 }; 31 32 template<typename mutex_t> 33 class mutex_wrapper_impl : public mutex_wrapper 34 { 35 public: 36 mutex_wrapper_impl(mutex_t& m) 37 : m_(m) 38 { 39 } 40 41 private: 42 mutex_t& m_; 43 44 virtual void lock(debug_info_param info) const 45 { 46 m_.lock(info); 47 } 48 49 virtual void unlock(debug_info_param info) const 50 { 51 m_.unlock(info); 52 } 53 54 RL_NOCOPY(mutex_wrapper_impl); 55 }; 56 57 struct pred_wrapper 58 { 59 virtual bool exec() const = 0; 60 virtual ~pred_wrapper() {} 61 }; 62 63 template<typename pred_t> 64 class pred_wrapper_impl : public pred_wrapper 65 { 66 public: 67 pred_wrapper_impl(pred_t p) 68 : p_(p) 69 { 70 } 71 72 private: 73 mutable pred_t p_; 74 75 virtual bool exec() const 76 { 77 return p_(); 78 } 79 80 RL_NOCOPY(pred_wrapper_impl); 81 }; 82 83 84 struct condvar_data 85 { 86 virtual void notify_one(debug_info_param info) = 0; 87 virtual void notify_all(debug_info_param info) = 0; 88 virtual sema_wakeup_reason wait(mutex_wrapper const& lock, bool is_timed, debug_info_param info) = 0; 89 virtual bool wait(mutex_wrapper const& lock, pred_wrapper const& pred, bool is_timed, debug_info_param info) = 0; 90 virtual ~condvar_data() {} // just to calm down gcc 91 }; 92 93 template<thread_id_t thread_count> 94 class condvar_data_impl : public condvar_data 95 { 96 public: 97 condvar_data_impl(bool allow_spurious_wakeups) 98 { 99 spurious_wakeup_limit_ = 0; 100 if (allow_spurious_wakeups && ctx().is_random_sched()) 101 spurious_wakeup_limit_ = 10; 102 } 103 104 ~condvar_data_impl() 105 { 106 //!!! detect destoy when there are blocked threads 107 } 108 109 private: 110 waitset<thread_count> ws_; 111 signature<0xc0ffe3ad> sign_; 112 int spurious_wakeup_limit_; 113 114 struct event_t 115 { 116 enum type_e 117 { 118 type_notify_one, 119 type_notify_all, 120 type_wait_enter, 121 type_wait_exit, 122 type_wait_pred_enter, 123 type_wait_pred_exit, 124 }; 125 126 condvar_data_impl const* var_addr_; 127 type_e type_; 128 thread_id_t thread_count_; 129 unpark_reason reason_; 130 131 void output(std::ostream& s) const 132 { 133 s << "<" << std::hex << var_addr_ << std::dec << "> cond_var: "; 134 switch (type_) 135 { 136 case type_notify_one: 137 s << "notify one total_blocked=" << thread_count_ << " unblocked=" << (thread_count_ ? 1 : 0); 138 break; 139 case type_notify_all: 140 s << "notify all unblocked=" << thread_count_; 141 break; 142 case type_wait_enter: s << "wait enter"; break; 143 case type_wait_exit: 144 s << "wait exit"; 145 if (unpark_reason_normal == reason_) 146 s << " due to notified"; 147 else if (unpark_reason_timeout == reason_) 148 s << " due to timeout"; 149 else if (unpark_reason_spurious == reason_) 150 s << " spuriously"; 151 break; 152 case type_wait_pred_enter: s << "wait pred enter"; break; 153 case type_wait_pred_exit: s << "wait pred exit"; break; 154 } 155 } 156 }; 157 158 virtual void notify_one(debug_info_param info) 159 { 160 context& c = ctx(); 161 //??? do I need this scheduler call? 162 c.sched(); 163 sign_.check(info); 164 RL_HIST(event_t) {this, event_t::type_notify_one, ws_.size()} RL_HIST_END(); 165 ws_.unpark_one(c, info); 166 } 167 168 virtual void notify_all(debug_info_param info) 169 { 170 context& c = ctx(); 171 //??? do I need this scheduler call? 172 c.sched(); 173 sign_.check(info); 174 RL_HIST(event_t) {this, event_t::type_notify_all, ws_.size()} RL_HIST_END(); 175 ws_.unpark_all(c, info); 176 } 177 178 virtual sema_wakeup_reason wait(mutex_wrapper const& lock, bool is_timed, debug_info_param info) 179 { 180 //!!! detect whether mutex is the same 181 context& c = ctx(); 182 sign_.check(info); 183 RL_HIST(event_t) {this, event_t::type_wait_enter} RL_HIST_END(); 184 lock.unlock(info); 185 sign_.check(info); 186 bool allow_spurious_wakeup = (spurious_wakeup_limit_ > 0); 187 unpark_reason reason = ws_.park_current(c, is_timed, allow_spurious_wakeup, false, info); 188 if (reason == unpark_reason_spurious) 189 spurious_wakeup_limit_ -= 1; 190 RL_HIST(event_t) {this, event_t::type_wait_exit, 0, reason} RL_HIST_END(); 191 lock.lock(info); 192 sign_.check(info); 193 if (reason == unpark_reason_normal) 194 return sema_wakeup_reason_success; 195 else if (reason == unpark_reason_spurious) 196 return sema_wakeup_reason_spurious; 197 else //if (reason == unpark_reason_timeout) 198 return sema_wakeup_reason_timeout; 199 } 200 201 virtual bool wait(mutex_wrapper const& lock, pred_wrapper const& pred, bool is_timed, debug_info_param info) 202 { 203 context& c = ctx(); 204 sign_.check(info); 205 RL_HIST(event_t) {this, event_t::type_wait_pred_enter} RL_HIST_END(); 206 while (!pred.exec()) 207 { 208 sema_wakeup_reason reason = wait(lock, is_timed, info); 209 if (reason == sema_wakeup_reason_timeout) 210 { 211 RL_HIST(event_t) {this, event_t::type_wait_pred_exit} RL_HIST_END(); 212 return pred.exec(); 213 } 214 } 215 RL_HIST(event_t) {this, event_t::type_wait_pred_exit} RL_HIST_END(); 216 return true; 217 } 218 }; 219 220 221 template<typename tag_t> 222 class condvar 223 { 224 public: 225 condvar() 226 : impl_() 227 { 228 } 229 230 condvar(condvar const&) 231 : impl_() 232 { 233 } 234 235 condvar& operator = (condvar const&) 236 { 237 return *this; 238 } 239 240 ~condvar() 241 { 242 } 243 244 void init(bool allow_spurious_wakeups, debug_info_param info) 245 { 246 context& c = ctx(); 247 RL_ASSERT_IMPL(0 == impl_, test_result_double_initialization_of_condvar, "", info); 248 sign_.check(info); 249 impl_ = c.condvar_ctor(allow_spurious_wakeups); 250 } 251 252 void deinit(debug_info_param info) 253 { 254 context& c = ctx(); 255 check(info); 256 c.condvar_dtor(impl_); 257 impl_ = 0; 258 } 259 260 void notify_one(debug_info_param info) 261 { 262 check(info); 263 impl_->notify_one(info); 264 } 265 266 void notify_all(debug_info_param info) 267 { 268 check(info); 269 impl_->notify_all(info); 270 } 271 272 template<typename lock_t> 273 sema_wakeup_reason wait(lock_t& lock, bool is_timed, debug_info_param info) 274 { 275 check(info); 276 mutex_wrapper_impl<lock_t> w (lock); 277 return impl_->wait(w, is_timed, info); 278 } 279 280 template<typename lock_t, typename pred_t> 281 bool wait(mutex_wrapper const& lock, pred_wrapper const& pred, bool is_timed, debug_info_param info) 282 { 283 check(info); 284 return impl_->wait(mutex_wrapper_impl<lock_t>(lock), pred_wrapper_impl<pred_t>(pred), is_timed, info); 285 } 286 287 private: 288 condvar_data* impl_; 289 signature<0xbadc0ffe> sign_; 290 291 void check(debug_info_param info) 292 { 293 RL_ASSERT_IMPL(impl_, test_result_usage_of_non_initialized_condvar, "", info); 294 sign_.check(info); 295 } 296 }; 297 298 299 300 template<typename tag_t> 301 class condition_variable_std : condvar<tag_t> 302 { 303 public: 304 condition_variable_std() 305 { 306 condvar<tag_t>::init(true, $); 307 } 308 309 ~condition_variable_std() 310 { 311 condvar<tag_t>::deinit($); 312 } 313 314 void notify_one(debug_info_param info) 315 { 316 condvar<tag_t>::notify_one(info); 317 } 318 319 void notify_all(debug_info_param info) 320 { 321 condvar<tag_t>::notify_all(info); 322 } 323 324 template<typename lock_t> 325 void wait(lock_t& lock, debug_info_param info) 326 { 327 condvar<tag_t>::wait(lock, false, info); 328 } 329 330 template<typename lock_t, typename pred_t> 331 void wait(lock_t& lock, pred_t pred, debug_info_param info) 332 { 333 condvar<tag_t>::wait(lock, pred, false, info); 334 } 335 336 template<typename lock_t, typename abs_time_t> 337 bool wait_until(lock_t& lock, abs_time_t const&, debug_info_param info) 338 { 339 return condvar<tag_t>::wait(lock, true, info); 340 } 341 342 template<typename lock_t, typename abs_time_t, typename pred_t> 343 bool wait_until(lock_t& lock, abs_time_t const&, pred_t pred, debug_info_param info) 344 { 345 return condvar<tag_t>::wait(lock, pred, true, info); 346 } 347 348 template<typename lock_t, typename rel_time_t> 349 bool wait_for(lock_t& lock, rel_time_t const&, debug_info_param info) 350 { 351 sema_wakeup_reason reason = condvar<tag_t>::wait(lock, true, info); 352 return reason == sema_wakeup_reason_success; 353 } 354 355 template<typename lock_t, typename rel_time_t, typename pred_t> 356 bool wait_for(lock_t& lock, rel_time_t const&, pred_t pred, debug_info_param info) 357 { 358 return condvar<tag_t>::wait(lock, pred, true, info); 359 } 360 361 RL_NOCOPY(condition_variable_std); 362 }; 363 364 365 struct condvar_tag_std; 366 typedef condition_variable_std<condvar_tag_std> condition_variable; 367 struct condvar_tag_std_any; 368 typedef condition_variable_std<condvar_tag_std_any> condition_variable_any; 369 370 } 371 372 #endif