medfall

A super great game engine
Log | Files | Refs

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