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__ 26 #define __MT_EVENT__ 27 28 #include <sys/time.h> 29 #include <sched.h> 30 #include <errno.h> 31 32 33 namespace MT 34 { 35 // 36 // 37 // 38 class Event 39 { 40 static const int NOT_SIGNALED = 0; 41 static const int SIGNALED = 1; 42 43 44 pthread_mutex_t mutex; 45 pthread_cond_t condition; 46 47 EventReset::Type resetType; 48 49 volatile uint32 numOfWaitingThreads; 50 volatile int32 value; 51 volatile bool isInitialized; 52 53 private: 54 AutoResetIfNeed()55 void AutoResetIfNeed() 56 { 57 if (resetType == EventReset::MANUAL) 58 { 59 return; 60 } 61 value = NOT_SIGNALED; 62 } 63 64 public: 65 66 MT_NOCOPYABLE(Event); 67 68 Event()69 Event() 70 : numOfWaitingThreads(0) 71 , isInitialized(false) 72 { 73 } 74 Event(EventReset::Type resetType,bool initialState)75 Event(EventReset::Type resetType, bool initialState) 76 : numOfWaitingThreads(0) 77 , isInitialized(false) 78 { 79 Create(resetType, initialState); 80 } 81 ~Event()82 ~Event() 83 { 84 if (isInitialized) 85 { 86 int res = pthread_cond_destroy( &condition ); 87 MT_USED_IN_ASSERT(res); 88 MT_ASSERT(res == 0, "pthread_cond_destroy - failed"); 89 90 res = pthread_mutex_destroy( &mutex ); 91 MT_USED_IN_ASSERT(res); 92 MT_ASSERT(res == 0, "pthread_mutex_destroy - failed"); 93 } 94 } 95 Create(EventReset::Type _resetType,bool initialState)96 void Create(EventReset::Type _resetType, bool initialState) 97 { 98 MT_ASSERT (!isInitialized, "Event already initialized"); 99 100 resetType = _resetType; 101 102 int res = pthread_mutex_init( &mutex, nullptr ); 103 MT_USED_IN_ASSERT(res); 104 MT_ASSERT(res == 0, "pthread_mutex_init - failed"); 105 106 res = pthread_cond_init( &condition, nullptr ); 107 MT_USED_IN_ASSERT(res); 108 MT_ASSERT(res == 0, "pthread_cond_init - failed"); 109 110 value = initialState ? SIGNALED : NOT_SIGNALED; 111 isInitialized = true; 112 numOfWaitingThreads = 0; 113 } 114 Signal()115 void Signal() 116 { 117 MT_ASSERT (isInitialized, "Event not initialized"); 118 119 int res = pthread_mutex_lock( &mutex ); 120 MT_USED_IN_ASSERT(res); 121 MT_ASSERT(res == 0, "pthread_mutex_lock - failed"); 122 123 value = SIGNALED; 124 if (numOfWaitingThreads > 0) 125 { 126 if (resetType == EventReset::MANUAL) 127 { 128 res = pthread_cond_broadcast( &condition ); 129 MT_USED_IN_ASSERT(res); 130 MT_ASSERT(res == 0, "pthread_cond_broadcast - failed"); 131 } else 132 { 133 res = pthread_cond_signal( &condition ); 134 MT_USED_IN_ASSERT(res); 135 MT_ASSERT(res == 0, "pthread_cond_signal - failed"); 136 } 137 } 138 139 res = pthread_mutex_unlock( &mutex ); 140 MT_USED_IN_ASSERT(res); 141 MT_ASSERT(res == 0, "pthread_mutex_unlock - failed"); 142 } 143 Reset()144 void Reset() 145 { 146 MT_ASSERT (isInitialized, "Event not initialized"); 147 MT_ASSERT(resetType == EventReset::MANUAL, "Can't reset, auto reset event"); 148 149 int res = pthread_mutex_lock( &mutex ); 150 MT_USED_IN_ASSERT(res); 151 MT_ASSERT(res == 0, "pthread_mutex_lock - failed"); 152 153 value = NOT_SIGNALED; 154 155 res = pthread_mutex_unlock( &mutex ); 156 MT_USED_IN_ASSERT(res); 157 MT_ASSERT(res == 0, "pthread_mutex_unlock - failed"); 158 159 } 160 Wait(uint32 milliseconds)161 bool Wait(uint32 milliseconds) 162 { 163 MT_ASSERT (isInitialized, "Event not initialized"); 164 165 int res = pthread_mutex_lock( &mutex ); 166 MT_USED_IN_ASSERT(res); 167 MT_ASSERT(res == 0, "pthread_mutex_lock - failed"); 168 169 // early exit if event already signaled 170 if ( value != NOT_SIGNALED ) 171 { 172 AutoResetIfNeed(); 173 res = pthread_mutex_unlock( &mutex ); 174 MT_USED_IN_ASSERT(res); 175 MT_ASSERT(res == 0, "pthread_mutex_unlock - failed"); 176 return true; 177 } 178 179 numOfWaitingThreads++; 180 181 //convert milliseconds to global posix time 182 struct timespec ts; 183 184 struct timeval tv; 185 gettimeofday(&tv, NULL); 186 187 uint64_t nanoseconds = ((uint64_t) tv.tv_sec) * 1000 * 1000 * 1000 + (uint64_t)milliseconds * 1000 * 1000 + ((uint64_t) tv.tv_usec) * 1000; 188 189 ts.tv_sec = (time_t)(nanoseconds / 1000 / 1000 / 1000); 190 ts.tv_nsec = (long)(nanoseconds - ((uint64_t) ts.tv_sec) * 1000 * 1000 * 1000); 191 192 int ret = 0; 193 while(true) 194 { 195 ret = pthread_cond_timedwait( &condition, &mutex, &ts ); 196 MT_ASSERT(ret == 0 || ret == ETIMEDOUT || ret == EINTR, "Unexpected return value"); 197 198 /* 199 200 http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_cond_timedwait.html 201 202 It is important to note that when pthread_cond_wait() and pthread_cond_timedwait() return without error, the associated predicate may still be false. 203 Similarly, when pthread_cond_timedwait() returns with the timeout error, the associated predicate may be true due to an unavoidable race between 204 the expiration of the timeout and the predicate state change. 205 206 */ 207 if (value == SIGNALED || ret == ETIMEDOUT) 208 { 209 break; 210 } 211 } 212 213 numOfWaitingThreads--; 214 bool isSignaled = (value == SIGNALED); 215 216 if (isSignaled) 217 { 218 AutoResetIfNeed(); 219 } 220 221 res = pthread_mutex_unlock( &mutex ); 222 MT_USED_IN_ASSERT(res); 223 MT_ASSERT(res == 0, "pthread_mutex_unlock - failed"); 224 225 return isSignaled; 226 } 227 228 }; 229 230 } 231 232 233 #endif 234