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