|
![]() |
|
![]() |
|
![]() |
|
![]() |
The Quantum Framework |
|
|
|
![]() |
void QActive::start ( unsigned prio, // priority of execution thread (constant) QEvent* qSto[], // address of event queue space unsigned qLen, // size of event queue int stkSto[], // thread execution stack space unsigned stkLen // size of thread execution stack ) { myPrio = prio; QF::add(this); // create event queue "myEqueue" of length "qLen" // create execution thread "myThread" with priority "prio" and stack size "stkLen" // postCondition: assert proper creation of myQueue and myThread }
void QActive::run() { QHsm::init(); // initial transition for (;;) { QEvent *e = myEqueue->get(); // get event; blocks if queue empty dispatch(e); // dispatch event to the QHsm QF::propagate(e); // propapagate event to next subscriber } }
QF::init(QSubscrList subscr[], unsigned maxSignal) { locSubscrList = subscr; // point to provided storage locMaxSignal = maxSignal; // remember boundary of lookup table osInit(); // OS-dependent QF initialization }
///////////////////////////////////////////////////////////////////// // Quantum DPP package-scope declarations (C++ version) // Copyright (c) 2002 Miro Samek, Palo Alto, CA. // All Rights Reserved. ///////////////////////////////////////////////////////////////////// #ifndef qdpp_h #define qdpp_h enum DPPSignals { HUNGRY_SIG = Q_USER_SIG,//sent by philosopher when becoming hungry DONE_SIG, // sent by philosopher when done eating EAT0_SIG, // sent by Table to let philosopher 0 eat EAT1_SIG, // sent by Table to let philosopher 1 eat EAT2_SIG, // sent by Table to let philosopher 2 eat EAT3_SIG, // sent by Table to let philosopher 3 eat EAT4_SIG, // sent by Table to let philosopher 4 eat TIMEOUT_SIG, // timeout philosophers use to end thinking or eating MAX_SIG }; struct TableEvt : public QEvent { int philNum; // philosopher number }; // recall QEvent Base Class class Philosopher : public QActive { public: Philosopher(int n) : QActive((QPseudoState)initial), myNum(n) {} protected: void initial(QEvent const *e); // initial pseudostate QSTATE thinking(QEvent const *e); QSTATE hungry(QEvent const *e); QSTATE eating(QEvent const *e); private: int myNum; // number of this philosopher QTimer myTimer; // to timeout thining or eating }; enum { N = 5 }; // NOTE: this is now coupled with the # EAT? signals class Table : public QActive { public: Table() : QActive((QPseudoState)initial) {} private: void initial(QEvent const *e); // initial pseudostate QSTATE serving(QEvent const *e); private: int myFork[N]; // FREE or USED int isHungry[N]; // TRUE or FALSE }; #endif // qdpp_h ///////////////////////////////////////////////////////////////////// // Quantum Dining Philosophers // Copyright (c) 2002 Miro Samek, Palo Alto, CA. // All Rights Reserved. ///////////////////////////////////////////////////////////////////// #include#include #include "qassert.h" #include "port.h" DEFINE_THIS_FILE; enum { THINK_TIME = 7, EAT_TIME = 5 }; void Philosopher::initial(QEvent const *) { QF::subscribe(this, EAT0_SIG + myNum); Q_INIT(&Philosopher::thinking); } QSTATE Philosopher::thinking(QEvent const *e) { switch (e->sig) { case Q_ENTRY_SIG: myTimer.fireIn(this, TIMEOUT_SIG, THINK_TIME); return 0; case TIMEOUT_SIG: Q_TRAN(&Philosopher::hungry); return 0; } return (QSTATE)&Philosopher::top; } QSTATE Philosopher::hungry(QEvent const *e) { TableEvt *pe; switch (e->sig) { case Q_ENTRY_SIG: pe = Q_NEW(TableEvt, HUNGRY_SIG); // recall QEvent Base Class pe->philNum = myNum; QF::publish(pe); return 0; case EAT0_SIG: case EAT1_SIG: case EAT2_SIG: case EAT3_SIG: case EAT4_SIG: ASSERT(e->sig == EAT0_SIG + myNum); Q_TRAN(&Philosopher::eating); return 0; } return (QSTATE)&Philosopher::top; } QSTATE Philosopher::eating(QEvent const *e) { TableEvt *pe; switch (e->sig) { case Q_ENTRY_SIG: myTimer.fireIn(this, TIMEOUT_SIG, EAT_TIME); return 0; case TIMEOUT_SIG: Q_TRAN(&Philosopher::thinking); return 0; case Q_EXIT_SIG: pe = Q_NEW(TableEvt, DONE_SIG); pe->philNum = myNum; QF::publish(pe); return 0; } return (QSTATE)&Philosopher::top; } #define RIGHT(n_) (((n_) + (N - 1)) % N) #define LEFT(n_) (((n_) + 1) % N) enum { FREE = 0, USED = !0 }; void Table::initial(QEvent const *) { QF::subscribe(this, HUNGRY_SIG); QF::subscribe(this, DONE_SIG); for (unsigned n = 0; n < N; ++n) { myFork[n] = FREE; isHungry[n] = 0; } Q_INIT(&Table::serving); } QSTATE Table::serving(QEvent const *e) { unsigned n, m; QEvent *pe; switch (e->sig) { case HUNGRY_SIG: n = ((TableEvt *)e)->philNum; ASSERT(n < N && !isHungry[n]); printf("Philospher %1d is hungry\n", n); m = LEFT(n); if (myFork[m] == FREE && myFork[n] == FREE) // seat philosopher - no waiting! { myFork[m] = myFork[n] = USED; pe = Q_NEW(QEvent, EAT0_SIG + n); QF::publish(pe); printf("Philospher %1d is eating\n", n); } else // philosopher must wait { isHungry[n] = 1; } return 0; case DONE_SIG: n = ((TableEvt *)e)->philNum; ASSERT(n < N); printf("Philospher %1d is thinking\n", n); myFork[LEFT(n)] = myFork[n] = FREE; m = RIGHT(n); // check the right neighbor if (isHungry[m] && myFork[m] == FREE) { myFork[n] = myFork[m] = USED; isHungry[m] = 0; pe = Q_NEW(QEvent, EAT0_SIG + m); QF::publish(pe); printf("Philospher %1d is eating\n", m); } m = LEFT(n); // check the left neighbor n = LEFT(m); if (isHungry[m] && myFork[n] == FREE) { myFork[m] = myFork[n] = USED; isHungry[m] = 0; pe = Q_NEW(QEvent, EAT0_SIG + m); QF::publish(pe); printf("Philospher %1d is eating\n", m); } return 0; } return (QSTATE)&Table::top; }
class QF { public: static void init(QSubscrList subscr[], unsigned maxSignal); static void cleanup(); static void poolInit(QEvent* poolSto, unsigned nEvts, unsigned evtSize); static void tick(); static const char* getVersion(); static QEvent* create(unsigned evtSize, QSignal sig); #define Q_NEW(evtT_, sig_) ((evtT_ *)QF::create(sizeof(evtT_), (sig_))) static void subscribe(QActive* a, QSignal sig); static void unsubscribe(QActive* a, QSignal sig); static void publish(QEvent* e); // publish event static void background(); // for foreground/background systems only private: // internal interface for QActive only static void osInit(); static void osCleanup(); static void add(QActive* a); static void remove(QActive* a); static void propagate(QEvent* e); // propagate to next subsrciber static void annihilate(QEvent* e); friend class QActive; };
class QActive : public QHsm // Quantum Active Object base class { public: int start(unsigned prio, QEvent* qSto[], unsigned qLen, int stkSto[], unsigned stkLen); void postFIFO(QEvent* e); // post event directly (FIFO enqueuing) void postLIFO(QEvent* e); // post event directly (LIFO enqueuing) void run(); // run() is active throughout lifetime of the object protected: QActive(QPseudoState initial); // protected ctor virtual ~QActive(); // virtual xtor void stop(); // stops the thread; nothing happens thereafter! private: int enqueue(QEvent* e); // intended to use only by friend class QF private: // data members... QF_EQUEUE(myEqueue) // OS-dependent event-queue primitive QF_THREAD(myThread) // OS-dependent thread primitive unsigned char myPrio; // priority of the active object friend class QF; };
The start method is the initializer for an active object. Note that the parameters each represent important decisions or processes that must preceed invocation:
int start ( unsigned prio, // priority of active object QEvent * qSto[], // event queue (must allocate first) unsigned qLen, // size of event queue int stkSto[], // runtime stack unsigned stkLen // size of runtime stack );
The method run() is active throughout the life of the active object, analogous to main() in a standard single thread program. Note that there are two ways to post events directly to the object, LIFO (standard buffering) and FIFO (jump to the front of the queue).
class QTimer { public: QTimer() : myActive(0) {} // default ctor void fireIn(QActive* act, QSignal sig, unsigned nTicks); void fireEvery(QActive* act, QSignal sig, unsigned nTicks); void disarm(); void rearm(unsigned nTicks); private: void arm(QActive* act, QSignal sig, unsigned nTicks); private: QTimer * myNext; // to link timers in the list QEvent myToutEvt; // timeout event instance to send QActive * myActive; unsigned short myCtr; unsigned short myInterval; friend class QF; };
Timer objects can be used as count-down timers via the fireIn() method,
which fires after the number of ticks specified in the parameter nTicks
and notifies by posting the signal specified in sig to the object
specified in act.
The event pools are classic free list implementation, using a fixed block of allocated memory. (Note, there may be faster models, in principle, but this is insignificant for relatively small pools.) Note that these pools must be allocated as part of the startup, and event pool size is another tactical decision that must be made. |
![]() Event pool data structure |
The event queues are classic circular array implementation. Note that the items in the event queues are pointers to event base class, hence can hold the address of any event from any pool. Again, a significant tactical decision is to determine the sizes of the event queues. The event pools are differentiated by the size of the event type. The event queues are associated with active objects. |
![]() Event queue data structure |
Online Documentation |
|
Book CD Directory Tree |
![]() Directory structure for multiplatform deployment of the QP libraries and QF applications |