1 // The MIT License (MIT) 2 // 3 // Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 23 #pragma once 24 25 #ifndef __MT_EVENT_USER__ 26 #define __MT_EVENT_USER__ 27 28 namespace MT 29 { 30 // 31 // 32 // 33 class Event 34 { 35 static const int NOT_SIGNALED = 0; 36 static const int SIGNALED = 1; 37 38 ::MW_CRITICAL_SECTION criticalSection; 39 ::MW_CONDITION_VARIABLE condition; 40 41 EventReset::Type resetType; 42 43 volatile uint32 numOfWaitingThreads; 44 volatile int32 value; 45 volatile bool isInitialized; 46 47 private: 48 AutoResetIfNeed()49 void AutoResetIfNeed() 50 { 51 if (resetType == EventReset::MANUAL) 52 { 53 return; 54 } 55 value = NOT_SIGNALED; 56 } 57 58 59 public: 60 61 MT_NOCOPYABLE(Event); 62 Event()63 Event() 64 : numOfWaitingThreads(0) 65 , isInitialized(false) 66 { 67 } 68 Event(EventReset::Type resetType_,bool initialState)69 Event(EventReset::Type resetType_, bool initialState) 70 : numOfWaitingThreads(0) 71 , isInitialized(false) 72 { 73 Create(resetType_, initialState); 74 } 75 ~Event()76 ~Event() 77 { 78 if (isInitialized) 79 { 80 ::DeleteCriticalSection(&criticalSection); 81 isInitialized = false; 82 } 83 } 84 Create(EventReset::Type resetType_,bool initialState)85 void Create(EventReset::Type resetType_, bool initialState) 86 { 87 MT_ASSERT (!isInitialized, "Event already initialized"); 88 resetType = resetType_; 89 90 ::InitializeCriticalSectionAndSpinCount( &criticalSection, 16 ); 91 ::InitializeConditionVariable( &condition ); 92 93 value = initialState ? SIGNALED : NOT_SIGNALED; 94 isInitialized = true; 95 numOfWaitingThreads = 0; 96 } 97 Signal()98 void Signal() 99 { 100 MT_ASSERT (isInitialized, "Event not initialized"); 101 ::EnterCriticalSection( &criticalSection ); 102 value = SIGNALED; 103 if (numOfWaitingThreads > 0) 104 { 105 if (resetType == EventReset::MANUAL) 106 { 107 ::WakeAllConditionVariable( &condition ); 108 } else 109 { 110 ::WakeConditionVariable( &condition ); 111 } 112 } 113 ::LeaveCriticalSection( &criticalSection ); 114 } 115 Reset()116 void Reset() 117 { 118 MT_ASSERT (isInitialized, "Event not initialized"); 119 MT_ASSERT(resetType == EventReset::MANUAL, "Can't reset, auto reset event"); 120 121 ::EnterCriticalSection( &criticalSection ); 122 value = NOT_SIGNALED; 123 ::LeaveCriticalSection( &criticalSection ); 124 } 125 Wait(uint32 milliseconds)126 bool Wait(uint32 milliseconds) 127 { 128 MT_ASSERT (isInitialized, "Event not initialized"); 129 130 ::EnterCriticalSection( &criticalSection ); 131 // early exit if event already signaled 132 if ( value != NOT_SIGNALED ) 133 { 134 AutoResetIfNeed(); 135 ::LeaveCriticalSection( &criticalSection ); 136 return true; 137 } 138 139 numOfWaitingThreads++; 140 141 for(;;) 142 { 143 MW_BOOL ret = ::SleepConditionVariableCS(&condition, &criticalSection, (MW_DWORD)milliseconds); 144 145 #if defined(MT_DEBUG) || defined(MT_INSTRUMENTED_BUILD) 146 if (ret == 0) 147 { 148 MW_DWORD err = ::GetLastError(); 149 MT_USED_IN_ASSERT(err); 150 MT_ASSERT(err == MW_ERROR_TIMEOUT, "Unexpected return value from SleepConditionVariable"); 151 } 152 #endif 153 154 /* 155 https://msdn.microsoft.com/en-us/library/windows/desktop/ms686301(v=vs.85).aspx 156 157 Condition variables are subject to spurious wakeups (those not associated with an explicit wake) and stolen wakeups (another thread manages to run before the woken thread). 158 Therefore, you should recheck a predicate (typically in a while loop) after a sleep operation returns. 159 */ 160 if (value == SIGNALED || ret == 0) 161 { 162 break; 163 } 164 } 165 166 numOfWaitingThreads--; 167 bool isSignaled = (value != NOT_SIGNALED); 168 if (isSignaled) 169 { 170 AutoResetIfNeed(); 171 } 172 173 ::LeaveCriticalSection( &criticalSection ); 174 return isSignaled; 175 } 176 177 }; 178 179 } 180 181 182 #endif 183