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