medfall

A super great game engine
Log | Files | Refs

event.hpp (9754B)


      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_EVENT_HPP
     11 #define RL_EVENT_HPP
     12 #ifdef _MSC_VER
     13 #   pragma once
     14 #endif
     15 
     16 #include "../base.hpp"
     17 #include "../context_base.hpp"
     18 #include "../sync_var.hpp"
     19 #include "../waitset.hpp"
     20 #include "semaphore.hpp"
     21 
     22 
     23 namespace rl
     24 {
     25 
     26 
     27 struct event_data
     28 {
     29     virtual void set(debug_info_param info) = 0;
     30     virtual void reset(debug_info_param info) = 0;
     31     virtual void pulse(debug_info_param info) = 0;
     32     virtual sema_wakeup_reason wait(bool try_wait, bool is_timed, debug_info_param info) = 0;
     33     virtual bool is_signaled(debug_info_param info) = 0;
     34     virtual void memory_acquire(debug_info_param info) = 0;
     35     virtual void* prepare_wait(debug_info_param info) = 0;
     36     virtual ~event_data() {} // just to calm down gcc
     37 };
     38 
     39 
     40 
     41 
     42 template<thread_id_t thread_count>
     43 class event_data_impl : public event_data
     44 {
     45 public:
     46     event_data_impl(bool manual_reset, bool initial_state)
     47         : manual_reset_(manual_reset)
     48         , state_(initial_state)
     49     {
     50     }
     51 
     52     ~event_data_impl()
     53     {
     54         //!!! detect destuction with waiters
     55     }
     56 
     57 private:
     58     signature<0xdada1234> sign_;
     59     bool const manual_reset_;
     60     bool state_;
     61     waitset<thread_count> ws_;
     62     sync_var<thread_count> sync_;
     63 
     64     struct state_event
     65     {
     66         enum type
     67         {
     68             type_set,
     69             type_reset,
     70             type_pulse,
     71         };
     72 
     73         event_data_impl* addr_;
     74         type type_;
     75         bool initial_state_;
     76         bool final_state_;
     77         thread_id_t unblocked_;
     78 
     79         void output(std::ostream& s) const
     80         {
     81             s << "<" << std::hex << addr_ << std::dec << "> event: ";
     82             if (type_set == type_)
     83                 s << "set ";
     84             else if (type_reset == type_)
     85                 s << "reset ";
     86             else
     87                 s << "pulse ";
     88             s << "initial_state=" << initial_state_
     89                 << " final_state=" << final_state_;
     90             if (type_reset != type_)
     91                 s << " unblocked=" << unblocked_;
     92         }
     93 
     94     };
     95 
     96     virtual void set(debug_info_param info)
     97     {
     98         context& c = ctx();
     99         c.sched();
    100         sign_.check(info);
    101 
    102         bool initial_state = state_;
    103         thread_id_t unblocked = 0;
    104 
    105         if (state_)
    106         {
    107             //!!! probably can break if a thread waits in wfmo
    108             RL_VERIFY(false == ws_);
    109         }
    110         else
    111         {
    112             sync_.release(c.threadx_);
    113             state_ = true;
    114 
    115             if (manual_reset_)
    116             {
    117                 unblocked = ws_.unpark_all(c, info);
    118             }
    119             else
    120             {
    121                 if (ws_.unpark_one(c, info))
    122                     unblocked = 1;
    123             }
    124         }
    125 
    126         RL_HIST(state_event) {this, state_event::type_set, initial_state, state_, unblocked} RL_HIST_END();
    127     }
    128 
    129     virtual void reset(debug_info_param info)
    130     {
    131         context& c = ctx();
    132         c.sched();
    133         sign_.check(info);
    134 
    135         bool initial_state = state_;
    136 
    137         if (state_)
    138         {
    139             RL_VERIFY(false == ws_);
    140             sync_.release(c.threadx_);
    141             state_ = false;
    142         }
    143 
    144         RL_HIST(state_event) {this, state_event::type_reset, initial_state, state_, 0} RL_HIST_END();
    145     }
    146 
    147     virtual void pulse(debug_info_param info)
    148     {
    149         context& c = ctx();
    150         c.sched();
    151         sign_.check(info);
    152 
    153         //??? should I model nasty caveat described in MSDN
    154         thread_id_t unblocked = 0;
    155 
    156         if (state_)
    157         {
    158             //!!! probably can break if a thread waits in wfmo
    159             RL_VERIFY(false == ws_);
    160         }
    161         else
    162         {
    163             sync_.release(c.threadx_);
    164             state_ = true;
    165             unblocked = ws_.unpark_all(c, info);
    166             state_ = false;
    167         }
    168 
    169         RL_HIST(state_event) {this, state_event::type_pulse, state_, state_, unblocked} RL_HIST_END();
    170     }
    171 
    172     struct wait_event
    173     {
    174         event_data_impl* addr_;
    175         bool try_wait_;
    176         bool is_timed_;
    177         bool initial_state_;
    178         bool final_state_;
    179         sema_wakeup_reason reason_;
    180 
    181         void output(std::ostream& s) const
    182         {
    183             s << "<" << std::hex << addr_ << std::dec << "> event: ";
    184             if (try_wait_)
    185                 s << "try_wait ";
    186             else if (is_timed_)
    187                 s << "timed wait ";
    188             else
    189                 s << "wait ";
    190 
    191             if (reason_ == sema_wakeup_reason_success)
    192                 s << "succeeded ";
    193             else if (reason_ == sema_wakeup_reason_failed)
    194                 s << "failed ";
    195             else if (reason_ == sema_wakeup_reason_timeout)
    196                 s << "timed out ";
    197             else if (reason_ == sema_wakeup_reason_spurious)
    198                 s << "spuriously failed ";
    199 
    200             s << "initial_state=" << initial_state_
    201                 << " final_state=" << final_state_;
    202         }
    203     };
    204 
    205     virtual sema_wakeup_reason wait(bool try_wait, bool is_timed, debug_info_param info)
    206     {
    207         context& c = ctx();
    208         c.sched();
    209         sign_.check(info);
    210 
    211         bool initial_state = state_;
    212         sema_wakeup_reason reason = sema_wakeup_reason_success;
    213 
    214         for (;;)
    215         {
    216             if (state_)
    217             {
    218                 if (manual_reset_)
    219                 {
    220                     sync_.acquire(c.threadx_);
    221                 }
    222                 else
    223                 {
    224                     state_ = false;
    225                     sync_.acq_rel(c.threadx_);
    226                 }
    227                 reason = sema_wakeup_reason_success;
    228                 break;
    229             }
    230 
    231             if (try_wait)
    232             {
    233                 sync_.acquire(c.threadx_);
    234                 reason = sema_wakeup_reason_failed;
    235                 break;
    236             }
    237 
    238             unpark_reason wr = ws_.park_current(c, is_timed, false, true, info);
    239             initial_state = state_;
    240             if (unpark_reason_timeout == wr)
    241             {
    242                 sync_.acquire(c.threadx_);
    243                 reason = sema_wakeup_reason_timeout;
    244                 break;
    245             }
    246             else if (unpark_reason_normal == wr)
    247             {
    248                 RL_VERIFY(state_ == true);
    249                 if (manual_reset_)
    250                 {
    251                     sync_.acquire(c.threadx_);
    252                 }
    253                 else
    254                 {
    255                     state_ = false;
    256                     sync_.acq_rel(c.threadx_);
    257                 }
    258                 c.switch_back(info);
    259                 reason = sema_wakeup_reason_success;
    260                 break;
    261             }
    262             RL_VERIFY(false);
    263         }
    264 
    265         RL_HIST(wait_event) {this, try_wait, is_timed, initial_state, state_, reason} RL_HIST_END();
    266         return reason;
    267     }
    268 
    269     virtual bool is_signaled(debug_info_param info)
    270     {
    271         (void)info;
    272         return state_;
    273     }
    274 
    275     virtual void memory_acquire(debug_info_param info)
    276     {
    277         (void)info;
    278         sync_.acquire(ctx().threadx_);
    279     }
    280 
    281     virtual void* prepare_wait(debug_info_param info)
    282     {
    283         (void)info;
    284         return &ws_;
    285     }
    286 
    287     RL_NOCOPY(event_data_impl);
    288 };
    289 
    290 
    291 
    292 class generic_event : public win_waitable_object
    293 {
    294 public:
    295     generic_event()
    296         : impl_()
    297     {
    298     }
    299 
    300     generic_event(generic_event const&)
    301         : impl_()
    302     {
    303     }
    304 
    305     generic_event& operator = (generic_event const&)
    306     {
    307         return *this;
    308     }
    309 
    310     void init(bool manual_reset, bool initial_state, debug_info_param info)
    311     {
    312         context& c = ctx();
    313         RL_ASSERT_IMPL(0 == impl_, test_result_double_initialization_of_event, "", info);
    314         sign_.check(info);
    315         impl_ = c.event_ctor(manual_reset, initial_state);
    316     }
    317 
    318     void deinit(debug_info_param info)
    319     {
    320         context& c = ctx();
    321         check(info);
    322         c.event_dtor(impl_);
    323         impl_ = 0;
    324     }
    325 
    326     void set(debug_info_param info)
    327     {
    328         check(info);
    329         impl_->set(info);
    330     }
    331 
    332     void reset(debug_info_param info)
    333     {
    334         check(info);
    335         impl_->reset(info);
    336     }
    337 
    338     void pulse(debug_info_param info)
    339     {
    340         check(info);
    341         impl_->pulse(info);
    342     }
    343 
    344     virtual sema_wakeup_reason wait(bool try_wait, bool is_timed, debug_info_param info)
    345     {
    346         check(info);
    347         return impl_->wait(try_wait, is_timed, info);
    348     }
    349 
    350     virtual bool signal(debug_info_param info)
    351     {
    352         set(info);
    353         return true;
    354     }
    355 
    356 private:
    357     event_data* impl_;
    358     signature<0x3390eeaa> sign_;
    359 
    360     event_data* check(debug_info_param info)
    361     {
    362         RL_ASSERT_IMPL(impl_, test_result_usage_of_non_initialized_event, "", info);
    363         sign_.check(info);
    364         return impl_;
    365     }
    366 
    367     virtual bool is_signaled(debug_info_param info)
    368     {
    369         return check(info)->is_signaled(info);
    370     }
    371 
    372     virtual void memory_acquire(debug_info_param info)
    373     {
    374         check(info)->memory_acquire(info);
    375     }
    376 
    377     virtual void* prepare_wait(debug_info_param info)
    378     {
    379         return check(info)->prepare_wait(info);
    380     }
    381 };
    382 
    383 
    384 }
    385 
    386 #endif