1f25ce65dSSergey Makeev // The MIT License (MIT) 2f25ce65dSSergey Makeev // 3f25ce65dSSergey Makeev // Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev 4f25ce65dSSergey Makeev // 5f25ce65dSSergey Makeev // Permission is hereby granted, free of charge, to any person obtaining a copy 6f25ce65dSSergey Makeev // of this software and associated documentation files (the "Software"), to deal 7f25ce65dSSergey Makeev // in the Software without restriction, including without limitation the rights 8f25ce65dSSergey Makeev // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9f25ce65dSSergey Makeev // copies of the Software, and to permit persons to whom the Software is 10f25ce65dSSergey Makeev // furnished to do so, subject to the following conditions: 11f25ce65dSSergey Makeev // 12f25ce65dSSergey Makeev // The above copyright notice and this permission notice shall be included in 13f25ce65dSSergey Makeev // all copies or substantial portions of the Software. 14f25ce65dSSergey Makeev // 15f25ce65dSSergey Makeev // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16f25ce65dSSergey Makeev // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17f25ce65dSSergey Makeev // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18f25ce65dSSergey Makeev // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19f25ce65dSSergey Makeev // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20f25ce65dSSergey Makeev // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21f25ce65dSSergey Makeev // THE SOFTWARE. 22f25ce65dSSergey Makeev 2347d53e4dSSergey Makeev #pragma once 2447d53e4dSSergey Makeev 2581ec7369SSergey Makeev #ifndef __MT_EVENT__ 2681ec7369SSergey Makeev #define __MT_EVENT__ 2781ec7369SSergey Makeev 2847d53e4dSSergey Makeev #include <sys/time.h> 2947d53e4dSSergey Makeev #include <sched.h> 3047d53e4dSSergey Makeev #include <errno.h> 3147d53e4dSSergey Makeev 3247d53e4dSSergey Makeev 3347d53e4dSSergey Makeev namespace MT 3447d53e4dSSergey Makeev { 3547d53e4dSSergey Makeev // 3647d53e4dSSergey Makeev // 3747d53e4dSSergey Makeev // 3847d53e4dSSergey Makeev class Event 3947d53e4dSSergey Makeev { 4047d53e4dSSergey Makeev static const int NOT_SIGNALED = 0; 4147d53e4dSSergey Makeev static const int SIGNALED = 1; 4247d53e4dSSergey Makeev 43897ca6d5SSergey Makeev 4447d53e4dSSergey Makeev pthread_mutex_t mutex; 4547d53e4dSSergey Makeev pthread_cond_t condition; 462f083884Ss.makeev_local 4747d53e4dSSergey Makeev EventReset::Type resetType; 482f083884Ss.makeev_local 492f083884Ss.makeev_local volatile uint32 numOfWaitingThreads; 502f083884Ss.makeev_local volatile int32 value; 512f083884Ss.makeev_local volatile bool isInitialized; 5247d53e4dSSergey Makeev 5347d53e4dSSergey Makeev private: 5447d53e4dSSergey Makeev AutoResetIfNeed()5547d53e4dSSergey Makeev void AutoResetIfNeed() 5647d53e4dSSergey Makeev { 5747d53e4dSSergey Makeev if (resetType == EventReset::MANUAL) 5847d53e4dSSergey Makeev { 5947d53e4dSSergey Makeev return; 6047d53e4dSSergey Makeev } 612f083884Ss.makeev_local value = NOT_SIGNALED; 6247d53e4dSSergey Makeev } 6347d53e4dSSergey Makeev 6447d53e4dSSergey Makeev public: 6547d53e4dSSergey Makeev 662e846c40SSergey Makeev MT_NOCOPYABLE(Event); 672e846c40SSergey Makeev 682e846c40SSergey Makeev Event()6947d53e4dSSergey Makeev Event() 70897ca6d5SSergey Makeev : numOfWaitingThreads(0) 71897ca6d5SSergey Makeev , isInitialized(false) 7247d53e4dSSergey Makeev { 7347d53e4dSSergey Makeev } 7447d53e4dSSergey Makeev Event(EventReset::Type resetType,bool initialState)7547d53e4dSSergey Makeev Event(EventReset::Type resetType, bool initialState) 76897ca6d5SSergey Makeev : numOfWaitingThreads(0) 77897ca6d5SSergey Makeev , isInitialized(false) 7847d53e4dSSergey Makeev { 7947d53e4dSSergey Makeev Create(resetType, initialState); 8047d53e4dSSergey Makeev } 8147d53e4dSSergey Makeev ~Event()8247d53e4dSSergey Makeev ~Event() 8347d53e4dSSergey Makeev { 8447d53e4dSSergey Makeev if (isInitialized) 8547d53e4dSSergey Makeev { 8671198a5eSs.makeev_local int res = pthread_cond_destroy( &condition ); 8771198a5eSs.makeev_local MT_USED_IN_ASSERT(res); 8871198a5eSs.makeev_local MT_ASSERT(res == 0, "pthread_cond_destroy - failed"); 8971198a5eSs.makeev_local 9094519253Ss.makeev_local res = pthread_mutex_destroy( &mutex ); 9171198a5eSs.makeev_local MT_USED_IN_ASSERT(res); 9271198a5eSs.makeev_local MT_ASSERT(res == 0, "pthread_mutex_destroy - failed"); 9347d53e4dSSergey Makeev } 9447d53e4dSSergey Makeev } 9547d53e4dSSergey Makeev Create(EventReset::Type _resetType,bool initialState)9647d53e4dSSergey Makeev void Create(EventReset::Type _resetType, bool initialState) 9747d53e4dSSergey Makeev { 9834a394c3SSergey Makeev MT_ASSERT (!isInitialized, "Event already initialized"); 9947d53e4dSSergey Makeev 10047d53e4dSSergey Makeev resetType = _resetType; 10147d53e4dSSergey Makeev 10271198a5eSs.makeev_local int res = pthread_mutex_init( &mutex, nullptr ); 10371198a5eSs.makeev_local MT_USED_IN_ASSERT(res); 10471198a5eSs.makeev_local MT_ASSERT(res == 0, "pthread_mutex_init - failed"); 10571198a5eSs.makeev_local 10671198a5eSs.makeev_local res = pthread_cond_init( &condition, nullptr ); 10771198a5eSs.makeev_local MT_USED_IN_ASSERT(res); 10871198a5eSs.makeev_local MT_ASSERT(res == 0, "pthread_cond_init - failed"); 10947d53e4dSSergey Makeev 1102f083884Ss.makeev_local value = initialState ? SIGNALED : NOT_SIGNALED; 11147d53e4dSSergey Makeev isInitialized = true; 112897ca6d5SSergey Makeev numOfWaitingThreads = 0; 11347d53e4dSSergey Makeev } 11447d53e4dSSergey Makeev Signal()11547d53e4dSSergey Makeev void Signal() 11647d53e4dSSergey Makeev { 11734a394c3SSergey Makeev MT_ASSERT (isInitialized, "Event not initialized"); 11847d53e4dSSergey Makeev 11971198a5eSs.makeev_local int res = pthread_mutex_lock( &mutex ); 12071198a5eSs.makeev_local MT_USED_IN_ASSERT(res); 12171198a5eSs.makeev_local MT_ASSERT(res == 0, "pthread_mutex_lock - failed"); 12271198a5eSs.makeev_local 1232f083884Ss.makeev_local value = SIGNALED; 124c88507a8Ss.makeev_local if (numOfWaitingThreads > 0) 125897ca6d5SSergey Makeev { 126897ca6d5SSergey Makeev if (resetType == EventReset::MANUAL) 127897ca6d5SSergey Makeev { 12871198a5eSs.makeev_local res = pthread_cond_broadcast( &condition ); 12971198a5eSs.makeev_local MT_USED_IN_ASSERT(res); 13071198a5eSs.makeev_local MT_ASSERT(res == 0, "pthread_cond_broadcast - failed"); 131897ca6d5SSergey Makeev } else 132897ca6d5SSergey Makeev { 13371198a5eSs.makeev_local res = pthread_cond_signal( &condition ); 13471198a5eSs.makeev_local MT_USED_IN_ASSERT(res); 13571198a5eSs.makeev_local MT_ASSERT(res == 0, "pthread_cond_signal - failed"); 136897ca6d5SSergey Makeev } 137897ca6d5SSergey Makeev } 1382f0964abSSergey Makeev 13971198a5eSs.makeev_local res = pthread_mutex_unlock( &mutex ); 14071198a5eSs.makeev_local MT_USED_IN_ASSERT(res); 14171198a5eSs.makeev_local MT_ASSERT(res == 0, "pthread_mutex_unlock - failed"); 14247d53e4dSSergey Makeev } 14347d53e4dSSergey Makeev Reset()14447d53e4dSSergey Makeev void Reset() 14547d53e4dSSergey Makeev { 14634a394c3SSergey Makeev MT_ASSERT (isInitialized, "Event not initialized"); 147c88507a8Ss.makeev_local MT_ASSERT(resetType == EventReset::MANUAL, "Can't reset, auto reset event"); 1482f083884Ss.makeev_local 14971198a5eSs.makeev_local int res = pthread_mutex_lock( &mutex ); 15071198a5eSs.makeev_local MT_USED_IN_ASSERT(res); 15171198a5eSs.makeev_local MT_ASSERT(res == 0, "pthread_mutex_lock - failed"); 15271198a5eSs.makeev_local 1532f083884Ss.makeev_local value = NOT_SIGNALED; 15471198a5eSs.makeev_local 15571198a5eSs.makeev_local res = pthread_mutex_unlock( &mutex ); 15671198a5eSs.makeev_local MT_USED_IN_ASSERT(res); 15771198a5eSs.makeev_local MT_ASSERT(res == 0, "pthread_mutex_unlock - failed"); 15871198a5eSs.makeev_local 15947d53e4dSSergey Makeev } 16047d53e4dSSergey Makeev Wait(uint32 milliseconds)16147d53e4dSSergey Makeev bool Wait(uint32 milliseconds) 16247d53e4dSSergey Makeev { 16334a394c3SSergey Makeev MT_ASSERT (isInitialized, "Event not initialized"); 16447d53e4dSSergey Makeev 16571198a5eSs.makeev_local int res = pthread_mutex_lock( &mutex ); 16671198a5eSs.makeev_local MT_USED_IN_ASSERT(res); 16771198a5eSs.makeev_local MT_ASSERT(res == 0, "pthread_mutex_lock - failed"); 1682f0964abSSergey Makeev 1692f0964abSSergey Makeev // early exit if event already signaled 1702f083884Ss.makeev_local if ( value != NOT_SIGNALED ) 1712f0964abSSergey Makeev { 1722f0964abSSergey Makeev AutoResetIfNeed(); 17371198a5eSs.makeev_local res = pthread_mutex_unlock( &mutex ); 17471198a5eSs.makeev_local MT_USED_IN_ASSERT(res); 17571198a5eSs.makeev_local MT_ASSERT(res == 0, "pthread_mutex_unlock - failed"); 1762f0964abSSergey Makeev return true; 1772f0964abSSergey Makeev } 1782f0964abSSergey Makeev 179897ca6d5SSergey Makeev numOfWaitingThreads++; 180897ca6d5SSergey Makeev 1812f083884Ss.makeev_local //convert milliseconds to global posix time 1822f083884Ss.makeev_local struct timespec ts; 1832f083884Ss.makeev_local 18447d53e4dSSergey Makeev struct timeval tv; 1852f083884Ss.makeev_local gettimeofday(&tv, NULL); 1862f083884Ss.makeev_local 1876cd27d5fSs.makeev_local uint64_t nanoseconds = ((uint64_t) tv.tv_sec) * 1000 * 1000 * 1000 + (uint64_t)milliseconds * 1000 * 1000 + ((uint64_t) tv.tv_usec) * 1000; 1882f083884Ss.makeev_local 189*bc48b7efSSergey Makeev ts.tv_sec = (time_t)(nanoseconds / 1000 / 1000 / 1000); 190*bc48b7efSSergey Makeev ts.tv_nsec = (long)(nanoseconds - ((uint64_t) ts.tv_sec) * 1000 * 1000 * 1000); 19147d53e4dSSergey Makeev 192364a200cSSergey Makeev int ret = 0; 193364a200cSSergey Makeev while(true) 194364a200cSSergey Makeev { 1952f083884Ss.makeev_local ret = pthread_cond_timedwait( &condition, &mutex, &ts ); 196364a200cSSergey Makeev MT_ASSERT(ret == 0 || ret == ETIMEDOUT || ret == EINTR, "Unexpected return value"); 197364a200cSSergey Makeev 1982f083884Ss.makeev_local /* 1992f083884Ss.makeev_local 2002f083884Ss.makeev_local http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_cond_timedwait.html 2012f083884Ss.makeev_local 2022f083884Ss.makeev_local It is important to note that when pthread_cond_wait() and pthread_cond_timedwait() return without error, the associated predicate may still be false. 2032f083884Ss.makeev_local Similarly, when pthread_cond_timedwait() returns with the timeout error, the associated predicate may be true due to an unavoidable race between 2042f083884Ss.makeev_local the expiration of the timeout and the predicate state change. 2052f083884Ss.makeev_local 2062f083884Ss.makeev_local */ 2072f083884Ss.makeev_local if (value == SIGNALED || ret == ETIMEDOUT) 208364a200cSSergey Makeev { 209364a200cSSergey Makeev break; 210364a200cSSergey Makeev } 211364a200cSSergey Makeev } 212897ca6d5SSergey Makeev 213897ca6d5SSergey Makeev numOfWaitingThreads--; 21471198a5eSs.makeev_local bool isSignaled = (value == SIGNALED); 215c88507a8Ss.makeev_local 216c88507a8Ss.makeev_local if (isSignaled) 2172f083884Ss.makeev_local { 218897ca6d5SSergey Makeev AutoResetIfNeed(); 2192f083884Ss.makeev_local } 22047d53e4dSSergey Makeev 22171198a5eSs.makeev_local res = pthread_mutex_unlock( &mutex ); 22271198a5eSs.makeev_local MT_USED_IN_ASSERT(res); 22371198a5eSs.makeev_local MT_ASSERT(res == 0, "pthread_mutex_unlock - failed"); 22447d53e4dSSergey Makeev 2252f083884Ss.makeev_local return isSignaled; 22647d53e4dSSergey Makeev } 22747d53e4dSSergey Makeev 22847d53e4dSSergey Makeev }; 22947d53e4dSSergey Makeev 23047d53e4dSSergey Makeev } 23147d53e4dSSergey Makeev 23247d53e4dSSergey Makeev 23381ec7369SSergey Makeev #endif 234