mirror of
https://github.com/QuantumLeaps/qpc.git
synced 2025-01-28 07:03:10 +08:00
460 lines
15 KiB
XML
460 lines
15 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<model version="5.1.0" links="1">
|
|
<documentation>Dining Philosopher Problem example
|
|
NOTE: Requries QP5.</documentation>
|
|
<!--${qpc}-->
|
|
<framework name="qpc"/>
|
|
<!--${Events}-->
|
|
<package name="Events" stereotype="0x01">
|
|
<!--${Events::TableEvt}-->
|
|
<class name="TableEvt" superclass="qpc::QEvt">
|
|
<!--${Events::TableEvt::philoNum}-->
|
|
<attribute name="philoNum" type="uint8_t" visibility="0x00" properties="0x00"/>
|
|
</class>
|
|
</package>
|
|
<!--${AOs}-->
|
|
<package name="AOs" stereotype="0x02">
|
|
<!--${AOs::Philo}-->
|
|
<class name="Philo" superclass="qpc::QActive">
|
|
<!--${AOs::Philo::timeEvt}-->
|
|
<attribute name="timeEvt" type="QTimeEvt" visibility="0x02" properties="0x00"/>
|
|
<!--${AOs::Philo::SM}-->
|
|
<statechart properties="0x01">
|
|
<!--${AOs::Philo::SM::initial}-->
|
|
<initial target="../1">
|
|
<action>static uint8_t registered = (uint8_t)0; /* starts off with 0, per C-standard */
|
|
(void)par; /* unused parameter */
|
|
if (registered == (uint8_t)0) {
|
|
registered = (uint8_t)1;
|
|
|
|
QS_OBJ_DICTIONARY(&l_philo[0].timeEvt);
|
|
QS_OBJ_DICTIONARY(&l_philo[1].timeEvt);
|
|
QS_OBJ_DICTIONARY(&l_philo[2].timeEvt);
|
|
QS_OBJ_DICTIONARY(&l_philo[3].timeEvt);
|
|
QS_OBJ_DICTIONARY(&l_philo[4].timeEvt);
|
|
|
|
QS_FUN_DICTIONARY(&Philo_initial);
|
|
QS_FUN_DICTIONARY(&Philo_thinking);
|
|
QS_FUN_DICTIONARY(&Philo_hungry);
|
|
QS_FUN_DICTIONARY(&Philo_eating);
|
|
}
|
|
|
|
QActive_subscribe(&me->super, EAT_SIG);
|
|
QActive_subscribe(&me->super, TEST_SIG);</action>
|
|
<initial_glyph conn="2,3,5,1,20,5,-3">
|
|
<action box="0,-2,6,2"/>
|
|
</initial_glyph>
|
|
</initial>
|
|
<!--${AOs::Philo::SM::thinking}-->
|
|
<state name="thinking">
|
|
<entry>QTimeEvt_armX(&me->timeEvt, THINK_TIME, 0U);</entry>
|
|
<exit>QTimeEvt_disarm(&me->timeEvt);</exit>
|
|
<!--${AOs::Philo::SM::thinking::TIMEOUT}-->
|
|
<tran trig="TIMEOUT" target="../../2">
|
|
<tran_glyph conn="2,13,3,1,20,12,-3">
|
|
<action box="0,-2,6,2"/>
|
|
</tran_glyph>
|
|
</tran>
|
|
<!--${AOs::Philo::SM::thinking::EAT, DONE}-->
|
|
<tran trig="EAT, DONE">
|
|
<action>/* EAT or DONE must be for other Philos than this one */
|
|
Q_ASSERT_ID(10, Q_EVT_CAST(TableEvt)->philoNum != PHILO_ID(me));</action>
|
|
<tran_glyph conn="2,17,3,-1,13">
|
|
<action box="0,-2,14,2"/>
|
|
</tran_glyph>
|
|
</tran>
|
|
<state_glyph node="2,5,17,16">
|
|
<entry box="1,2,5,2"/>
|
|
<exit box="1,4,6,2"/>
|
|
</state_glyph>
|
|
</state>
|
|
<!--${AOs::Philo::SM::hungry}-->
|
|
<state name="hungry">
|
|
<entry>TableEvt *pe = Q_NEW(TableEvt, HUNGRY_SIG);
|
|
pe->philoNum = PHILO_ID(me);
|
|
QACTIVE_POST(AO_Table, &pe->super, me);</entry>
|
|
<!--${AOs::Philo::SM::hungry::EAT}-->
|
|
<tran trig="EAT">
|
|
<!--${AOs::Philo::SM::hungry::EAT::[Q_EVT_CAST(TableEvt)->philoNum=~}-->
|
|
<choice target="../../../3">
|
|
<guard>Q_EVT_CAST(TableEvt)->philoNum == PHILO_ID(me)</guard>
|
|
<choice_glyph conn="15,30,5,1,7,13,-3">
|
|
<action box="1,0,19,4"/>
|
|
</choice_glyph>
|
|
</choice>
|
|
<tran_glyph conn="2,30,3,-1,13">
|
|
<action box="0,-2,14,2"/>
|
|
</tran_glyph>
|
|
</tran>
|
|
<!--${AOs::Philo::SM::hungry::DONE}-->
|
|
<tran trig="DONE">
|
|
<action>/* DONE must be for other Philos than this one */
|
|
Q_ASSERT_ID(20, Q_EVT_CAST(TableEvt)->philoNum != PHILO_ID(me));</action>
|
|
<tran_glyph conn="2,36,3,-1,14">
|
|
<action box="0,-2,14,2"/>
|
|
</tran_glyph>
|
|
</tran>
|
|
<state_glyph node="2,23,17,16">
|
|
<entry box="1,2,5,2"/>
|
|
</state_glyph>
|
|
</state>
|
|
<!--${AOs::Philo::SM::eating}-->
|
|
<state name="eating">
|
|
<entry>QTimeEvt_armX(&me->timeEvt, EAT_TIME, 0U);</entry>
|
|
<exit>TableEvt *pe = Q_NEW(TableEvt, DONE_SIG);
|
|
pe->philoNum = PHILO_ID(me);
|
|
QF_PUBLISH(&pe->super, me);
|
|
QTimeEvt_disarm(&me->timeEvt);</exit>
|
|
<!--${AOs::Philo::SM::eating::TIMEOUT}-->
|
|
<tran trig="TIMEOUT" target="../../1">
|
|
<tran_glyph conn="2,51,3,1,22,-41,-5">
|
|
<action box="0,-2,6,2"/>
|
|
</tran_glyph>
|
|
</tran>
|
|
<!--${AOs::Philo::SM::eating::EAT, DONE}-->
|
|
<tran trig="EAT, DONE">
|
|
<action>/* EAT or DONE must be for other Philos than this one */
|
|
Q_ASSERT_ID(30, Q_EVT_CAST(TableEvt)->philoNum != PHILO_ID(me));</action>
|
|
<tran_glyph conn="2,55,3,-1,13">
|
|
<action box="0,-2,14,2"/>
|
|
</tran_glyph>
|
|
</tran>
|
|
<state_glyph node="2,41,17,18">
|
|
<entry box="1,2,5,2"/>
|
|
<exit box="1,4,5,2"/>
|
|
</state_glyph>
|
|
</state>
|
|
<state_diagram size="37,61"/>
|
|
</statechart>
|
|
</class>
|
|
<!--${AOs::Table}-->
|
|
<class name="Table" superclass="qpc::QActive">
|
|
<!--${AOs::Table::fork[N_PHILO]}-->
|
|
<attribute name="fork[N_PHILO]" type="uint8_t" visibility="0x02" properties="0x00"/>
|
|
<!--${AOs::Table::isHungry[N_PHILO]}-->
|
|
<attribute name="isHungry[N_PHILO]" type="uint8_t" visibility="0x02" properties="0x00"/>
|
|
<!--${AOs::Table::SM}-->
|
|
<statechart properties="0x03">
|
|
<!--${AOs::Table::SM::initial}-->
|
|
<initial target="../1/1">
|
|
<action>uint8_t n;
|
|
(void)par; /* unused parameter */
|
|
|
|
QActive_subscribe(&me->super, DONE_SIG);
|
|
QActive_subscribe(&me->super, PAUSE_SIG);
|
|
QActive_subscribe(&me->super, SERVE_SIG);
|
|
QActive_subscribe(&me->super, TEST_SIG);
|
|
|
|
for (n = 0U; n < N_PHILO; ++n) {
|
|
me->fork[n] = FREE;
|
|
me->isHungry[n] = 0U;
|
|
BSP_displayPhilStat(n, "thinking");
|
|
}</action>
|
|
<initial_glyph conn="3,3,5,1,44,18,-9">
|
|
<action box="0,-2,6,2"/>
|
|
</initial_glyph>
|
|
</initial>
|
|
<!--${AOs::Table::SM::active}-->
|
|
<state name="active">
|
|
<!--${AOs::Table::SM::active::EAT}-->
|
|
<tran trig="EAT">
|
|
<action>Q_ERROR_ID(60);</action>
|
|
<tran_glyph conn="2,15,3,-1,14">
|
|
<action box="0,-2,17,4"/>
|
|
</tran_glyph>
|
|
</tran>
|
|
<!--${AOs::Table::SM::active::serving}-->
|
|
<state name="serving">
|
|
<entry brief="give pending permissions to eat">uint8_t n;
|
|
for (n = 0U; n < N_PHILO; ++n) { /* give permissions to eat... */
|
|
if ((me->isHungry[n] != 0U)
|
|
&& (me->fork[LEFT(n)] == FREE)
|
|
&& (me->fork[n] == FREE))
|
|
{
|
|
TableEvt *te;
|
|
|
|
me->fork[LEFT(n)] = USED;
|
|
me->fork[n] = USED;
|
|
te = Q_NEW(TableEvt, EAT_SIG);
|
|
te->philoNum = n;
|
|
QF_PUBLISH(&te->super, me);
|
|
me->isHungry[n] = 0U;
|
|
BSP_displayPhilStat(n, "eating ");
|
|
}
|
|
}</entry>
|
|
<!--${AOs::Table::SM::active::serving::HUNGRY}-->
|
|
<tran trig="HUNGRY">
|
|
<action>uint8_t n, m;
|
|
|
|
n = Q_EVT_CAST(TableEvt)->philoNum;
|
|
/* phil ID must be in range and he must be not hungry */
|
|
Q_ASSERT_ID(40, (n < N_PHILO) && (me->isHungry[n] == 0U));
|
|
|
|
BSP_displayPhilStat(n, "hungry ");
|
|
m = LEFT(n);</action>
|
|
<!--${AOs::Table::SM::active::serving::HUNGRY::[bothfree]}-->
|
|
<choice>
|
|
<guard brief="both free">(me->fork[m] == FREE) && (me->fork[n] == FREE)</guard>
|
|
<action>TableEvt *pe;
|
|
me->fork[m] = USED;
|
|
me->fork[n] = USED;
|
|
pe = Q_NEW(TableEvt, EAT_SIG);
|
|
pe->philoNum = n;
|
|
QF_PUBLISH(&pe->super, me);
|
|
BSP_displayPhilStat(n, "eating ");</action>
|
|
<choice_glyph conn="19,26,5,-1,10">
|
|
<action box="1,0,10,2"/>
|
|
</choice_glyph>
|
|
</choice>
|
|
<!--${AOs::Table::SM::active::serving::HUNGRY::[else]}-->
|
|
<choice>
|
|
<guard>else</guard>
|
|
<action>me->isHungry[n] = 1U;</action>
|
|
<choice_glyph conn="19,26,4,-1,5,10">
|
|
<action box="1,5,6,2"/>
|
|
</choice_glyph>
|
|
</choice>
|
|
<tran_glyph conn="4,26,3,-1,15">
|
|
<action box="0,-2,8,2"/>
|
|
</tran_glyph>
|
|
</tran>
|
|
<!--${AOs::Table::SM::active::serving::DONE}-->
|
|
<tran trig="DONE">
|
|
<action>uint8_t n, m;
|
|
TableEvt *pe;
|
|
|
|
n = Q_EVT_CAST(TableEvt)->philoNum;
|
|
/* phil ID must be in range and he must be not hungry */
|
|
Q_ASSERT_ID(50, (n < N_PHILO) && (me->isHungry[n] == 0U));
|
|
|
|
BSP_displayPhilStat(n, "thinking");
|
|
m = LEFT(n);
|
|
/* both forks of Phil[n] must be used */
|
|
Q_ASSERT_ID(51, (me->fork[n] == USED) && (me->fork[m] == USED));
|
|
|
|
me->fork[m] = FREE;
|
|
me->fork[n] = FREE;
|
|
m = RIGHT(n); /* check the right neighbor */
|
|
|
|
if ((me->isHungry[m] != 0U) && (me->fork[m] == FREE)) {
|
|
me->fork[n] = USED;
|
|
me->fork[m] = USED;
|
|
me->isHungry[m] = 0U;
|
|
pe = Q_NEW(TableEvt, EAT_SIG);
|
|
pe->philoNum = m;
|
|
QF_PUBLISH(&pe->super, me);
|
|
BSP_displayPhilStat(m, "eating ");
|
|
}
|
|
m = LEFT(n); /* check the left neighbor */
|
|
n = LEFT(m); /* left fork of the left neighbor */
|
|
if ((me->isHungry[m] != 0U) && (me->fork[n] == FREE)) {
|
|
me->fork[m] = USED;
|
|
me->fork[n] = USED;
|
|
me->isHungry[m] = 0U;
|
|
pe = Q_NEW(TableEvt, EAT_SIG);
|
|
pe->philoNum = m;
|
|
QF_PUBLISH(&pe->super, me);
|
|
BSP_displayPhilStat(m, "eating ");
|
|
}</action>
|
|
<tran_glyph conn="4,34,3,-1,15">
|
|
<action box="0,-2,6,2"/>
|
|
</tran_glyph>
|
|
</tran>
|
|
<!--${AOs::Table::SM::active::serving::PAUSE}-->
|
|
<tran trig="PAUSE" target="../../2">
|
|
<tran_glyph conn="4,40,3,1,37,7,-3">
|
|
<action box="0,-2,7,2"/>
|
|
</tran_glyph>
|
|
</tran>
|
|
<state_glyph node="4,19,34,24">
|
|
<entry box="1,2,27,2"/>
|
|
</state_glyph>
|
|
</state>
|
|
<!--${AOs::Table::SM::active::paused}-->
|
|
<state name="paused">
|
|
<entry>BSP_displayPaused(1U);</entry>
|
|
<exit>BSP_displayPaused(0U);</exit>
|
|
<!--${AOs::Table::SM::active::paused::SERVE}-->
|
|
<tran trig="SERVE" target="../../1">
|
|
<tran_glyph conn="4,57,3,1,39,-23,-5">
|
|
<action box="0,-2,7,2"/>
|
|
</tran_glyph>
|
|
</tran>
|
|
<!--${AOs::Table::SM::active::paused::HUNGRY}-->
|
|
<tran trig="HUNGRY">
|
|
<action>uint8_t n = Q_EVT_CAST(TableEvt)->philoNum;
|
|
/* philo ID must be in range and he must be not hungry */
|
|
Q_ASSERT_ID(60, (n < N_PHILO) && (me->isHungry[n] == 0U));
|
|
me->isHungry[n] = 1U;
|
|
BSP_displayPhilStat(n, "hungry ");</action>
|
|
<tran_glyph conn="4,60,3,-1,15">
|
|
<action box="0,-2,6,2"/>
|
|
</tran_glyph>
|
|
</tran>
|
|
<!--${AOs::Table::SM::active::paused::DONE}-->
|
|
<tran trig="DONE">
|
|
<action>uint8_t n, m;
|
|
|
|
n = Q_EVT_CAST(TableEvt)->philoNum;
|
|
/* phil ID must be in range and he must be not hungry */
|
|
Q_ASSERT_ID(70, (n < N_PHILO) && (me->isHungry[n] == 0U));
|
|
|
|
BSP_displayPhilStat(n, "thinking");
|
|
m = LEFT(n);
|
|
/* both forks of Phil[n] must be used */
|
|
Q_ASSERT_ID(71, (me->fork[n] == USED) && (me->fork[m] == USED));
|
|
|
|
me->fork[m] = FREE;
|
|
me->fork[n] = FREE;</action>
|
|
<tran_glyph conn="4,63,3,-1,15">
|
|
<action box="0,-2,6,2"/>
|
|
</tran_glyph>
|
|
</tran>
|
|
<state_glyph node="4,45,34,20">
|
|
<entry box="1,2,18,4"/>
|
|
<exit box="1,6,18,4"/>
|
|
</state_glyph>
|
|
</state>
|
|
<state_glyph node="2,5,43,62"/>
|
|
</state>
|
|
<state_diagram size="49,69"/>
|
|
</statechart>
|
|
</class>
|
|
<!--${AOs::AO_Philo[N_PHILO]}-->
|
|
<attribute name="AO_Philo[N_PHILO]" type="QActive * const" visibility="0x00" properties="0x00">
|
|
<documentation>/* opaque pointers to the Philo AOs */</documentation>
|
|
<code>= {
|
|
&l_philo[0].super,
|
|
&l_philo[1].super,
|
|
&l_philo[2].super,
|
|
&l_philo[3].super,
|
|
&l_philo[4].super
|
|
};</code>
|
|
</attribute>
|
|
<!--${AOs::AO_Table}-->
|
|
<attribute name="AO_Table" type="QActive * const" visibility="0x00" properties="0x00">
|
|
<documentation>/* opaque pointer to the Table AO */</documentation>
|
|
<code>= &l_table.super;</code>
|
|
</attribute>
|
|
<!--${AOs::Philo_ctor}-->
|
|
<operation name="Philo_ctor" type="void" visibility="0x00" properties="0x00">
|
|
<code>uint8_t n;
|
|
Philo *me;
|
|
for (n = 0U; n < N_PHILO; ++n) {
|
|
me = &l_philo[n];
|
|
QActive_ctor(&me->super, Q_STATE_CAST(&Philo_initial));
|
|
QTimeEvt_ctorX(&me->timeEvt, &me->super, TIMEOUT_SIG, 0U);
|
|
}</code>
|
|
</operation>
|
|
<!--${AOs::Table_ctor}-->
|
|
<operation name="Table_ctor" type="void" visibility="0x00" properties="0x00">
|
|
<code>uint8_t n;
|
|
Table *me = &l_table;
|
|
|
|
QActive_ctor(&me->super, Q_STATE_CAST(&Table_initial));
|
|
|
|
for (n = 0U; n < N_PHILO; ++n) {
|
|
me->fork[n] = FREE;
|
|
me->isHungry[n] = 0U;
|
|
}</code>
|
|
</operation>
|
|
</package>
|
|
<!--${src}-->
|
|
<directory name="src">
|
|
<!--${src::dpp.h}-->
|
|
<file name="dpp.h">
|
|
<text>#ifndef DPP_H
|
|
#define DPP_H
|
|
|
|
enum DPPSignals {
|
|
EAT_SIG = Q_USER_SIG, /* published by Table to let a philosopher eat */
|
|
DONE_SIG, /* published by Philosopher when done eating */
|
|
PAUSE_SIG, /* published by BSP to pause serving forks */
|
|
SERVE_SIG, /* published by BSP to serve re-start serving forks */
|
|
TEST_SIG, /* published by BSP to test the application */
|
|
MAX_PUB_SIG, /* the last published signal */
|
|
|
|
HUNGRY_SIG, /* posted direclty to Table from hungry Philo */
|
|
TIMEOUT_SIG, /* used by Philosophers for time events */
|
|
MAX_SIG /* the last signal */
|
|
};
|
|
|
|
$declare${Events::TableEvt}
|
|
|
|
/* number of philosophers */
|
|
#define N_PHILO ((uint8_t)5)
|
|
|
|
$declare${AOs::Philo_ctor}
|
|
$declare${AOs::AO_Philo[N_PHILO]}
|
|
|
|
$declare${AOs::Table_ctor}
|
|
$declare${AOs::AO_Table}
|
|
|
|
#ifdef QXK_H
|
|
void Test1_ctor(void);
|
|
extern QXThread * const XT_Test1;
|
|
void Test2_ctor(void);
|
|
extern QXThread * const XT_Test2;
|
|
#endif /* QXK_H */
|
|
|
|
#endif /* DPP_H */
|
|
</text>
|
|
</file>
|
|
<!--${src::philo.c}-->
|
|
<file name="philo.c">
|
|
<text>#include "qpc.h"
|
|
#include "dpp.h"
|
|
#include "bsp.h"
|
|
|
|
Q_DEFINE_THIS_MODULE("philo")
|
|
|
|
/* Active object class -----------------------------------------------------*/
|
|
$declare${AOs::Philo}
|
|
|
|
/* Local objects -----------------------------------------------------------*/
|
|
static Philo l_philo[N_PHILO]; /* storage for all Philos */
|
|
|
|
#define THINK_TIME \
|
|
(QTimeEvtCtr)((BSP_random() % BSP_TICKS_PER_SEC) + (BSP_TICKS_PER_SEC/2U))
|
|
#define EAT_TIME \
|
|
(QTimeEvtCtr)((BSP_random() % BSP_TICKS_PER_SEC) + BSP_TICKS_PER_SEC)
|
|
|
|
/* helper macro to provide the ID of Philo "me_" */
|
|
#define PHILO_ID(me_) ((uint8_t)((me_) - l_philo))
|
|
|
|
/* Global objects ----------------------------------------------------------*/
|
|
$define${AOs::AO_Philo[N_PHILO]}
|
|
|
|
/* Philo definition --------------------------------------------------------*/
|
|
$define${AOs::Philo_ctor}
|
|
$define${AOs::Philo}</text>
|
|
</file>
|
|
<!--${src::table.c}-->
|
|
<file name="table.c">
|
|
<text>#include "qpc.h"
|
|
#include "dpp.h"
|
|
#include "bsp.h"
|
|
|
|
Q_DEFINE_THIS_MODULE("table")
|
|
|
|
/* Active object class -----------------------------------------------------*/
|
|
$declare${AOs::Table}
|
|
|
|
#define RIGHT(n_) ((uint8_t)(((n_) + (N_PHILO - 1U)) % N_PHILO))
|
|
#define LEFT(n_) ((uint8_t)(((n_) + 1U) % N_PHILO))
|
|
#define FREE ((uint8_t)0)
|
|
#define USED ((uint8_t)1)
|
|
|
|
/* Local objects -----------------------------------------------------------*/
|
|
static Table l_table; /* the single instance of the Table active object */
|
|
|
|
/* Global-scope objects ----------------------------------------------------*/
|
|
$define${AOs::AO_Table}
|
|
|
|
/*..........................................................................*/
|
|
$define${AOs::Table_ctor}
|
|
$define${AOs::Table}</text>
|
|
</file>
|
|
</directory>
|
|
</model>
|