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