//------------------------------------------------------------------------------
// emEngine.h
//
// Copyright (C) 2005-2008,2010,2016 Oliver Hamann.
//
// Homepage: http://eaglemode.sourceforge.net/
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License version 3 as published by the
// Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
// more details.
//
// You should have received a copy of the GNU General Public License version 3
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------
#ifndef emEngine_h
#define emEngine_h
#ifndef emSignal_h
#include <emCore/emSignal.h>
#endif
//==============================================================================
//================================== emEngine ==================================
//==============================================================================
class emEngine : public emUncopyable {
public:
// The classes emEngine, emSignal and emScheduler can be used for some
// kind of CPU sharing and event handling, within one thread of
// execution.
//
// The central point is the virtual method Cycle of emEngine. Derived
// classes have to overload it for doing something useful in it.
//
// Polling
// -------
//
// One possibility is that Cycle could be called by the scheduler on
// every time slice. The size of a time slice is defined by the actual
// derivative of emScheduler. It should be about 10 millisecs.
//
// Such a polling can be made by waking up the engine once through
// calling WakeUp, and by always returning true from the Cycle call.
//
// Not polling
// -----------
//
// For sparing CPU cycles, polling should be used in rare cases only.
// Best is to let the engine sleep most times, and to wake it up
// whenever there is something useful to do.
//
// After calling WakeUp on an engine, Cycle is called by the scheduler
// within the current time slice. The Cycle call should return false for
// falling asleep again.
//
// Through the wake-up mechanism, Cycle can be called multiple times
// within one time slice. For example, this allows to have chains of
// signals without any delay through the time slices.
//
// Signals
// -------
//
// emSignal can be used for sending a unary event to the engines without
// having references to them. But all receiving engines must share the
// same scheduler, and the sender must have a reference to that
// scheduler. Very often, the sender is even an engine.
//
// An engine can receive a signal event by calling IsSignaled from
// within Cycle. To avoid polling, an engine can be woken up by signals.
// Therefore any number of signals can be connected to an engine with
// AddWakeUpSignal.
//
// Here is an example of how a sender can send two different signals A
// and B to receivers:
//
// class MySender : public emEngine {
// private:
// emSignal A;
// emSignal B;
// public:
// inline MySender(emScheduler & scheduler) : emEngine(scheduler) {}
// inline const emSignal & GetSignalA() const { return A; }
// inline const emSignal & GetSignalB() const { return B; }
// inline void SendA() { Signal(A); }
// inline void SendB() { Signal(B); }
// };
//
// class MyReceiver : public emEngine {
// private:
// MySender & Sender;
// public:
// inline MyReceiver(MySender & sender)
// : emEngine(sender.GetScheduler()), Sender(sender)
// {
// AddWakeUpSignal(Sender.GetSignalA());
// AddWakeUpSignal(Sender.GetSignalB());
// }
// protected:
// virtual bool Cycle();
// };
//
// bool MyReceiver::Cycle()
// {
// if (IsSignaled(Sender.GetSignalA())) {
// //...got signal A...
// }
// if (IsSignaled(Sender.GetSignalB())) {
// //...got signal B...
// }
// return false;
// }
//
// Minimum fairness
// ----------------
//
// A call to Cycle should not block for a too long time. If an engine
// wants to perform a long-time job, it should divide that job into
// multiple time slices. The method IsTimeSliceAtEnd can be helpful
// here. Example:
//
// bool JobEngine::Cycle()
// {
// while (!JobFinished()) {
// WorkOnTheJobALittleBit();
// if (IsTimeSliceAtEnd()) break;
// }
// return !JobFinished();
// }
//
// The above example provides just minimum fairness, because if there
// are two such job engines, one would always eat the whole time slice
// and the other would perform WorkOnTheJobALittleBit only once per time
// slice.
//
// ??? More fairness for parallel jobs may be supported in the future.
// ??? Maybe the return value of Cycle should be replaced by a method.
//
// There is a helper for serializing jobs: emPriSchedAgent
emEngine(emScheduler & scheduler);
// Construct this as a sleeping engine.
// Arguments:
// scheduler - The scheduler which has to serve this engine.
virtual ~emEngine();
// Destructor.
emScheduler & GetScheduler() const;
// Get the scheduler.
void WakeUp();
// Wake up this engine. This means that Cycle will be called by
// the scheduler within the current time slice. Even if WakeUp
// is called from within Cycle, Cycle will be called again
// within the current time slice.
void AddWakeUpSignal(const emSignal & signal);
// Wake up this engine whenever the given signal is signaled.
// Waking up through a signal is like calling WakeUp. Adding the
// same signal more than once does not cost any additional
// resources. But the number of additions is counted.
void RemoveWakeUpSignal(const emSignal & signal);
// Remove the given signal from the set of signals waking up
// this engine. If a signal has been added multiple times, it
// would have to be removed for the same number of times, before
// it would really be disconnected from the engine (this is for
// solving conflicts between base classes and derived classes:
// the derived class does not need to know whether the base
// class has added a certain signal for its own purpose). There
// is no need to remove signals before destruction of signals or
// engines. This is solved by the destructors more efficient.
int GetWakeUpSignalRefs(const emSignal & signal) const;
// Get number of references to the given signal (how often the
// signal has been added and not removed).
void Signal(emSignal & signal);
// Signal the given signal. This is just a short cut for
// signal.Signal(GetScheduler()). It does not matter on which
// emEngine the call is made, as long as they belong to the same
// scheduler.
bool IsSignaled(const emSignal & signal) const;
// Ask whether the given signal has been signaled. This must be
// called only from within the Cycle method of this engine. The
// return value is true, if the signal has been signaled between
// the beginning of the previous call to Cycle and the beginning
// of the current call to Cycle.
bool IsTimeSliceAtEnd() const;
// Returns true if the current time slice is at its end.
enum PriorityType {
VERY_LOW_PRIORITY = 0,
LOW_PRIORITY = 1,
DEFAULT_PRIORITY = 2,
HIGH_PRIORITY = 3,
VERY_HIGH_PRIORITY = 4
};
void SetEnginePriority(PriorityType priority);
PriorityType GetEnginePriority() const;
// Set or get the priority. If two engines are to be called
// within the same time slice, the one with the higher priority
// is called first. And if the priority is equal, it's the order
// of waking up the engines. VERY_LOW_PRIORITY and
// VERY_HIGH_PRIORITY are meant to be used by driver
// implementations only. A GUI driver should perform input at
// VERY_HIGH_PRIORITY and output at VERY_LOW_PRIORITY.
protected:
virtual bool Cycle() = 0;
// This is the virtual Cycle method mentioned all above. It is
// only to be called by the scheduler. There is no problem with
// doing a 'delete this' within Cycle, from the schedulers point
// of view.
// The meaning of the return value is:
// false: Fall asleep if this engine has not been woken up
// since the beginning of this call to Cycle.
// true: Wake up this engine on next time slice if the engine
// will not be woken up earlier or if it has not been
// woken up since the beginning of this call to Cycle.
private:
friend class emScheduler;
friend class emSignal;
void WakeUpImp();
static void RemoveLink(emSignal::Link * link);
emScheduler & Scheduler;
// The scheduler.
emScheduler::EngineRingNode RNode;
// A node for this engine in a list of awake engines, garbage
// when sleeping.
emSignal::Link * SLFirst;
// First element in the list of connected signals.
emInt8 AwakeState;
// -1 <=> Sleeping.
// emScheduler::TimeSlice <=> Busy in current time slice
// emScheduler::TimeSlice^1 <=> Busy in next time slice
emUInt8 Priority;
// The priority (0-4).
emUInt64 Clock;
// State of emScheduler::Clock after last call to Cycle().
};
inline emScheduler & emEngine::GetScheduler() const
{
return Scheduler;
}
inline void emEngine::WakeUp()
{
if (AwakeState!=Scheduler.TimeSlice) WakeUpImp();
}
inline void emEngine::Signal(emSignal & signal)
{
if (!signal.RNode.Next) {
signal.RNode.Next=Scheduler.PSList.Next;
Scheduler.PSList.Next=&signal.RNode;
}
}
inline bool emEngine::IsSignaled(const emSignal & signal) const
{
return signal.Clock>Clock;
}
inline bool emEngine::IsTimeSliceAtEnd() const
{
return Scheduler.IsTimeSliceAtEnd();
}
inline emEngine::PriorityType emEngine::GetEnginePriority() const
{
return (PriorityType)Priority;
}
#endif