1 //------------------------------------------------------------------------------
2 // emEngine.h
3 //
4 // Copyright (C) 2005-2008,2010,2016 Oliver Hamann.
5 //
6 // Homepage: http://eaglemode.sourceforge.net/
7 //
8 // This program is free software: you can redistribute it and/or modify it under
9 // the terms of the GNU General Public License version 3 as published by the
10 // Free Software Foundation.
11 //
12 // This program is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 // FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
15 // more details.
16 //
17 // You should have received a copy of the GNU General Public License version 3
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 //------------------------------------------------------------------------------
20 
21 #ifndef emEngine_h
22 #define emEngine_h
23 
24 #ifndef emSignal_h
25 #include <emCore/emSignal.h>
26 #endif
27 
28 
29 //==============================================================================
30 //================================== emEngine ==================================
31 //==============================================================================
32 
33 class emEngine : public emUncopyable {
34 
35 public:
36 
37 	// The classes emEngine, emSignal and emScheduler can be used for some
38 	// kind of CPU sharing and event handling, within one thread of
39 	// execution.
40 	//
41 	// The central point is the virtual method Cycle of emEngine. Derived
42 	// classes have to overload it for doing something useful in it.
43 	//
44 	// Polling
45 	// -------
46 	//
47 	// One possibility is that Cycle could be called by the scheduler on
48 	// every time slice. The size of a time slice is defined by the actual
49 	// derivative of emScheduler. It should be about 10 millisecs.
50 	//
51 	// Such a polling can be made by waking up the engine once through
52 	// calling WakeUp, and by always returning true from the Cycle call.
53 	//
54 	// Not polling
55 	// -----------
56 	//
57 	// For sparing CPU cycles, polling should be used in rare cases only.
58 	// Best is to let the engine sleep most times, and to wake it up
59 	// whenever there is something useful to do.
60 	//
61 	// After calling WakeUp on an engine, Cycle is called by the scheduler
62 	// within the current time slice. The Cycle call should return false for
63 	// falling asleep again.
64 	//
65 	// Through the wake-up mechanism, Cycle can be called multiple times
66 	// within one time slice. For example, this allows to have chains of
67 	// signals without any delay through the time slices.
68 	//
69 	// Signals
70 	// -------
71 	//
72 	// emSignal can be used for sending a unary event to the engines without
73 	// having references to them. But all receiving engines must share the
74 	// same scheduler, and the sender must have a reference to that
75 	// scheduler. Very often, the sender is even an engine.
76 	//
77 	// An engine can receive a signal event by calling IsSignaled from
78 	// within Cycle. To avoid polling, an engine can be woken up by signals.
79 	// Therefore any number of signals can be connected to an engine with
80 	// AddWakeUpSignal.
81 	//
82 	// Here is an example of how a sender can send two different signals A
83 	// and B to receivers:
84 	//
85 	// class MySender : public emEngine {
86 	// private:
87 	//   emSignal A;
88 	//   emSignal B;
89 	// public:
90 	//   inline MySender(emScheduler & scheduler) : emEngine(scheduler) {}
91 	//   inline const emSignal & GetSignalA() const { return A; }
92 	//   inline const emSignal & GetSignalB() const { return B; }
93 	//   inline void SendA() { Signal(A); }
94 	//   inline void SendB() { Signal(B); }
95 	// };
96 	//
97 	// class MyReceiver : public emEngine {
98 	// private:
99 	//   MySender & Sender;
100 	// public:
101 	//   inline MyReceiver(MySender & sender)
102 	//     : emEngine(sender.GetScheduler()), Sender(sender)
103 	//   {
104 	//     AddWakeUpSignal(Sender.GetSignalA());
105 	//     AddWakeUpSignal(Sender.GetSignalB());
106 	//   }
107 	// protected:
108 	//   virtual bool Cycle();
109 	// };
110 	//
111 	// bool MyReceiver::Cycle()
112 	// {
113 	//   if (IsSignaled(Sender.GetSignalA())) {
114 	//     //...got signal A...
115 	//   }
116 	//   if (IsSignaled(Sender.GetSignalB())) {
117 	//     //...got signal B...
118 	//   }
119 	//   return false;
120 	// }
121 	//
122 	// Minimum fairness
123 	// ----------------
124 	//
125 	// A call to Cycle should not block for a too long time. If an engine
126 	// wants to perform a long-time job, it should divide that job into
127 	// multiple time slices. The method IsTimeSliceAtEnd can be helpful
128 	// here. Example:
129 	//
130 	// bool JobEngine::Cycle()
131 	// {
132 	//   while (!JobFinished()) {
133 	//     WorkOnTheJobALittleBit();
134 	//     if (IsTimeSliceAtEnd()) break;
135 	//   }
136 	//   return !JobFinished();
137 	// }
138 	//
139 	// The above example provides just minimum fairness, because if there
140 	// are two such job engines, one would always eat the whole time slice
141 	// and the other would perform WorkOnTheJobALittleBit only once per time
142 	// slice.
143 	//
144 	// ??? More fairness for parallel jobs may be supported in the future.
145 	// ??? Maybe the return value of Cycle should be replaced by a method.
146 	//
147 	// There is a helper for serializing jobs: emPriSchedAgent
148 
149 	emEngine(emScheduler & scheduler);
150 		// Construct this as a sleeping engine.
151 		// Arguments:
152 		//   scheduler - The scheduler which has to serve this engine.
153 
154 	virtual ~emEngine();
155 		// Destructor.
156 
157 	emScheduler & GetScheduler() const;
158 		// Get the scheduler.
159 
160 	void WakeUp();
161 		// Wake up this engine. This means that Cycle will be called by
162 		// the scheduler within the current time slice. Even if WakeUp
163 		// is called from within Cycle, Cycle will be called again
164 		// within the current time slice.
165 
166 	void AddWakeUpSignal(const emSignal & signal);
167 		// Wake up this engine whenever the given signal is signaled.
168 		// Waking up through a signal is like calling WakeUp. Adding the
169 		// same signal more than once does not cost any additional
170 		// resources. But the number of additions is counted.
171 
172 	void RemoveWakeUpSignal(const emSignal & signal);
173 		// Remove the given signal from the set of signals waking up
174 		// this engine. If a signal has been added multiple times, it
175 		// would have to be removed for the same number of times, before
176 		// it would really be disconnected from the engine (this is for
177 		// solving conflicts between base classes and derived classes:
178 		// the derived class does not need to know whether the base
179 		// class has added a certain signal for its own purpose). There
180 		// is no need to remove signals before destruction of signals or
181 		// engines. This is solved by the destructors more efficient.
182 
183 	int GetWakeUpSignalRefs(const emSignal & signal) const;
184 		// Get number of references to the given signal (how often the
185 		// signal has been added and not removed).
186 
187 	void Signal(emSignal & signal);
188 		// Signal the given signal. This is just a short cut for
189 		// signal.Signal(GetScheduler()). It does not matter on which
190 		// emEngine the call is made, as long as they belong to the same
191 		// scheduler.
192 
193 	bool IsSignaled(const emSignal & signal) const;
194 		// Ask whether the given signal has been signaled. This must be
195 		// called only from within the Cycle method of this engine. The
196 		// return value is true, if the signal has been signaled between
197 		// the beginning of the previous call to Cycle and the beginning
198 		// of the current call to Cycle.
199 
200 	bool IsTimeSliceAtEnd() const;
201 		// Returns true if the current time slice is at its end.
202 
203 	enum PriorityType {
204 		VERY_LOW_PRIORITY  = 0,
205 		LOW_PRIORITY       = 1,
206 		DEFAULT_PRIORITY   = 2,
207 		HIGH_PRIORITY      = 3,
208 		VERY_HIGH_PRIORITY = 4
209 	};
210 	void SetEnginePriority(PriorityType priority);
211 	PriorityType GetEnginePriority() const;
212 		// Set or get the priority. If two engines are to be called
213 		// within the same time slice, the one with the higher priority
214 		// is called first. And if the priority is equal, it's the order
215 		// of waking up the engines. VERY_LOW_PRIORITY and
216 		// VERY_HIGH_PRIORITY are meant to be used by driver
217 		// implementations only. A GUI driver should perform input at
218 		// VERY_HIGH_PRIORITY and output at VERY_LOW_PRIORITY.
219 
220 protected:
221 
222 	virtual bool Cycle() = 0;
223 		// This is the virtual Cycle method mentioned all above. It is
224 		// only to be called by the scheduler. There is no problem with
225 		// doing a 'delete this' within Cycle, from the schedulers point
226 		// of view.
227 		// The meaning of the return value is:
228 		//  false: Fall asleep if this engine has not been woken up
229 		//         since the beginning of this call to Cycle.
230 		//   true: Wake up this engine on next time slice if the engine
231 		//         will not be woken up earlier or if it has not been
232 		//         woken up since the beginning of this call to Cycle.
233 
234 private:
235 
236 	friend class emScheduler;
237 	friend class emSignal;
238 
239 	void WakeUpImp();
240 
241 	static void RemoveLink(emSignal::Link * link);
242 
243 	emScheduler & Scheduler;
244 		// The scheduler.
245 
246 	emScheduler::EngineRingNode RNode;
247 		// A node for this engine in a list of awake engines, garbage
248 		// when sleeping.
249 
250 	emSignal::Link * SLFirst;
251 		// First element in the list of connected signals.
252 
253 	emInt8 AwakeState;
254 		// -1 <=> Sleeping.
255 		// emScheduler::TimeSlice <=> Busy in current time slice
256 		// emScheduler::TimeSlice^1 <=> Busy in next time slice
257 
258 	emUInt8 Priority;
259 		// The priority (0-4).
260 
261 	emUInt64 Clock;
262 		// State of emScheduler::Clock after last call to Cycle().
263 };
264 
GetScheduler()265 inline emScheduler & emEngine::GetScheduler() const
266 {
267 	return Scheduler;
268 }
269 
WakeUp()270 inline void emEngine::WakeUp()
271 {
272 	if (AwakeState!=Scheduler.TimeSlice) WakeUpImp();
273 }
274 
Signal(emSignal & signal)275 inline void emEngine::Signal(emSignal & signal)
276 {
277 	if (!signal.RNode.Next) {
278 		signal.RNode.Next=Scheduler.PSList.Next;
279 		Scheduler.PSList.Next=&signal.RNode;
280 	}
281 }
282 
IsSignaled(const emSignal & signal)283 inline bool emEngine::IsSignaled(const emSignal & signal) const
284 {
285 	return signal.Clock>Clock;
286 }
287 
IsTimeSliceAtEnd()288 inline bool emEngine::IsTimeSliceAtEnd() const
289 {
290 	return Scheduler.IsTimeSliceAtEnd();
291 }
292 
GetEnginePriority()293 inline emEngine::PriorityType emEngine::GetEnginePriority() const
294 {
295 	return (PriorityType)Priority;
296 }
297 
298 
299 #endif
300