Boost logo

Boost :

Subject: [boost] [msm] Review
From: Franz Alt (f.alt_at_[hidden])
Date: 2009-12-02 15:15:48


Hello Boost!

This is my review for the MSM library:

* What is your evaluation of the design?

I've used MSM quite a while. I've already used MSM 1.x and also the new
2.0 since a few weeks. In this time I've written some own state
machines, played with the samples contained with MSM 1.x/2.0 and also
made some comparisons with the Boost.Statechart library. I've run the
state machines I've build on standard desktop PCs as well as on embedded
systems (MIPS with ~300 MHz and ARM with ~50 MHz). On all platforms MSM
made a good picture and always worked as expected. I'm not a guru in UML
state charts but I've tried to use as much features of MSM as I could.

* What is your evaluation of the implementation?

I've looked into the main parts of the source code of MSM 1.x and a
little bit of MSM 2.0. I've welcome the front- and back-end at 2.0 and
the omission of the CRTP pattern (CRTP = The Curiously Recurring
Template Pattern). My experience in template metaprogramming is quite
limited but as I've tortured myself with CRTP a long time I knew that
it's handling is quite hard sometimes. The font- and back-end at 2.0
improved the readability of MSM very much. Or should I say it improves
the readability of some parts of the MSMs source code!? Because the
source code of MSM 2.0 is very hard to read and understand at some
places. But it's wonderful to see that metaprogramming stuff working ;-)

* What is your evaluation of the documentation?

I think that the documentation could be better than it is at the moment.
For my taste "one" big documentation isn't nice to read. In Boost there
are existing more nicely documentations like Boost.Proto or
Boost.Spirit. It's a matter of taste but I think it could be an
improvemet for Boost if all libraries documentations has (nearly) the
same visual style. But this shouldn't be discussed here at this review.

* What is your evaluation of the potential usefulness of the library?

I think MSM is very useful for every professional C++ developer.
Especially for state machines on embedded systems MSM could be a real
improvement with it's O(1) complexity. I've made a comparison of MSM
against Boost.Statechart and a state machine generated with IBM's
Rational Rhapsody tool. The state machine for this comparison was the
simple CD player example delivered with MSM. The results of this
comparison and the source code - expected the libraries to build the
Rhapsody example - I've attached to this mail. The conclusion at this
test was that MSM is about 5 times faster than a state machine generated
with IBMs Rational Rhapsody and a state machine with Rhapsody is 4 times
faster than a state machine generated with Boost.Statechart. This was
very surprisingly for my because I've thought that Boost.Statechart is
much faster than the generated state machine with Rhapsody. In contrast
to Boost.Statechart, MSM was approximately 20 times faster. Like I've
said before I'v used the simple CD player example. A more complex state
machine could lead to more interesting results of MSM, Boost.Statechart
and Rhapsody. But this need some extra time that I don't have at moment.

Another very important fact is that MSM uses very much compiler
ressources. The problem here is that state machines with too much states
and/or transitions could lead to the situation that the compiler runs
out of memory and the state machine couldn't be build. I think this is
only a problem of time. Further compiler versions could be more
cooperative with MSM than today compilers. I think MSM is perfectly for
small and medium static state machines and Boost.Statechart is good for
dynamic and very big state machines. But this is only a thesis from
myself and I think this should be evaluated in more detail. But this
shouldn't be part of this review.

A very big improvement for me is the eUML "language" at MSM 2.0. It's
takes getting used to use it but I think it's such a cool think that I
don't know how I've written state machines before without eUML :-) Ok, a
little bit hamed but I think writing state machines with eUML could be
such a big improvement for every application.

* Did you try to use the library? With what compiler? Did you have any
problems?

I've used the library for several tests. I've used MSVC 9 and GCC
4.3/4.4. I've detected no problems with MSM. The only "problem" was that
mistypes could lead to compiler errors with pages of (useless)
informations. But this isn't a problem of MSM. This is more a problem of
template metaprogramming.

* How much effort did you put into your evaluation? A glance? A quick
reading? In-depth study?

I've used MSM 1.x quite a long time. For this review I tried to use the
new 2.0 and some of the new features (like eUML). I've already tried to
use MSM on embedded systems like on MIPS and ARM platform. I've already
tried to make a in-depth study but the MSM source code is very hard to
understand.

* Are you knowledgeable about the problem domain?

I'm not a guru in UML state machines but I know the basics and used
state machines quite a long time.

* Do you think the library should be accepted as a Boost library? Be
sure to say this explicitly so that your other comments don't obscure
your overall opinion.

Yes. I think MSM is an enrichment for Boost. MSM beates IBMs Rational
Rhapsody! And the best, it's free, it's metaprogramming and it's Boost
:-) Writing state machines make so much fun with MSM and the knowledge
that it's O(1) is the best a software developer could wish.

Best regards

Franz


// MsmSimple.cpp : Defines the entry point for the console application.
//

#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>

namespace msm = boost::msm;
namespace mpl = boost::mpl;

#include <iostream>
#ifdef WIN32
#include "windows.h"
#else
#include <sys/time.h>
#endif

namespace test_fsm // Concrete FSM implementation
{
    // events
    struct play {};
    struct end_pause {};
    struct stop {};
    struct pause {};
    struct open_close {};
    struct cd_detected{};

    // Concrete FSM implementation
    struct player_ : public msm::front::state_machine_def<player_>
    {
        // no need for exception handling or message queue
        typedef int no_exception_thrown;
        typedef int no_message_queue;

        // The list of FSM states
        struct Empty : public msm::front::state<>
        {
            // optional entry/exit methods
            template <class Event,class FSM>
            void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Empty" << std::endl;*/}
            template <class Event,class FSM>
            void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Empty" << std::endl;*/}
        };
        struct Open : public msm::front::state<>
        {
            template <class Event,class FSM>
            void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Open" << std::endl;*/}
            template <class Event,class FSM>
            void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Open" << std::endl;*/}
        };

        struct Stopped : public msm::front::state<>
        {
            template <class Event,class FSM>
            void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Stopped" << std::endl;*/}
            template <class Event,class FSM>
            void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Stopped" << std::endl;*/}
        };

        struct Playing : public msm::front::state<>
        {
            template <class Event,class FSM>
            void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Playing" << std::endl;*/}
            template <class Event,class FSM>
            void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Playing" << std::endl;*/}
        };

        struct Paused : public msm::front::state<>
        {
            template <class Event,class FSM>
            void on_entry(Event const&,FSM& ) {/*std::cout << "entering: Paused" << std::endl;*/}
            template <class Event,class FSM>
            void on_exit(Event const&,FSM& ) {/*std::cout << "leaving: Paused" << std::endl;*/}
        };

        // the initial state of the player SM. Must be defined
        typedef Empty initial_state;
        // transition actions
        void start_playback(play const&) { }
        void open_drawer(open_close const&) { }
        void close_drawer(open_close const&) { }
        void store_cd_info(cd_detected const& cd) { }
        void stop_playback(stop const&) { }
        void pause_playback(pause const&) { }
        void resume_playback(end_pause const&) { }
        void stop_and_open(open_close const&) { }
        void stopped_again(stop const&) {}
        // guard conditions

        typedef player_ p; // makes transition table cleaner

        // Transition table for player
        struct transition_table : mpl::vector<
            // Start Event Next Action Guard
            // +---------+-------------+---------+---------------------+----------------------+
            a_row < Stopped , play , Playing , &p::start_playback >,
            a_row < Stopped , open_close , Open , &p::open_drawer >,
            a_row < Stopped , stop , Stopped , &p::stopped_again >,
            // +---------+-------------+---------+---------------------+----------------------+
            a_row < Open , open_close , Empty , &p::close_drawer >,
            // +---------+-------------+---------+---------------------+----------------------+
            a_row < Empty , open_close , Open , &p::open_drawer >,
            a_row < Empty , cd_detected , Stopped , &p::store_cd_info >,
            // +---------+-------------+---------+---------------------+----------------------+
            a_row < Playing , stop , Stopped , &p::stop_playback >,
            a_row < Playing , pause , Paused , &p::pause_playback >,
            a_row < Playing , open_close , Open , &p::stop_and_open >,
            // +---------+-------------+---------+---------------------+----------------------+
            a_row < Paused , end_pause , Playing , &p::resume_playback >,
            a_row < Paused , stop , Stopped , &p::stop_playback >,
            a_row < Paused , open_close , Open , &p::stop_and_open >
            // +---------+-------------+---------+---------------------+----------------------+
> {};

        // Replaces the default no-transition response.
        template <class FSM,class Event>
        void no_transition(Event const& e, FSM&,int state)
        {
            std::cout << "no transition from state " << state
                << " on event " << typeid(e).name() << std::endl;
        }
    };
    typedef msm::back::state_machine<player_> player;

    //
    // Testing utilities.
    //
    static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };

    void pstate(player const& p)
    {
        std::cout << " -> " << state_names[p.current_state()[0]] << std::endl;
    }

}

#ifndef WIN32
long mtime(struct timeval& tv1,struct timeval& tv2)
{
    return (tv2.tv_sec-tv1.tv_sec) *1000000 + ((tv2.tv_usec-tv1.tv_usec));
}
#endif

int main()
{
    // for timing
#ifdef WIN32
    LARGE_INTEGER res;
    ::QueryPerformanceFrequency(&res);
    LARGE_INTEGER li,li2;
#else
    struct timeval tv1,tv2;
    gettimeofday(&tv1,NULL);
#endif

    test_fsm::player p2;
    p2.start();
    // for timing
#ifdef WIN32
    ::QueryPerformanceCounter(&li);
#else
    gettimeofday(&tv1,NULL);
#endif
    for (int i=0;i</*100*//*1000000*/10000000;++i)
    {
        p2.process_event(test_fsm::open_close());
        p2.process_event(test_fsm::open_close());
        p2.process_event(test_fsm::cd_detected());
        p2.process_event(test_fsm::play());
        p2.process_event(test_fsm::pause());
        // go back to Playing
        p2.process_event(test_fsm::end_pause());
        p2.process_event(test_fsm::pause());
        p2.process_event(test_fsm::stop());
        // event leading to the same state
        p2.process_event(test_fsm::stop());
        p2.process_event(test_fsm::open_close());
        p2.process_event(test_fsm::open_close());
    }
#ifdef WIN32
    ::QueryPerformanceCounter(&li2);
#else
    gettimeofday(&tv2,NULL);
#endif
#ifdef WIN32
    std::cout << "msm took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl;
#else
    std::cout << "msm took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl;
#endif
    return 0;
}


#include <boost/statechart/event.hpp>
#include <boost/statechart/state_machine.hpp>
#include <boost/statechart/simple_state.hpp>
#include <boost/statechart/transition.hpp>
#include "boost/mpl/list.hpp"

#include <vector>

#include <iostream>
#ifdef WIN32
#include "windows.h"
#else
#include <sys/time.h>
#endif

namespace sc = boost::statechart;
namespace mpl = boost::mpl;

namespace test_sc
{

    //events
    struct play : sc::event< play > {};
    struct end_pause : sc::event< end_pause > {};
    struct stop : sc::event< stop > {};
    struct pause : sc::event< pause > {};
    struct open_close : sc::event< open_close > {};
    struct cd_detected : sc::event< cd_detected > {};

    struct Empty;
    struct Open;
    struct Stopped;
    struct Playing;
    struct Paused;
    // SM
    struct player : sc::state_machine< player, Empty >
    {
        void open_drawer(open_close const&) { /*std::cout << "player::open_drawer\n";*/ }
        void store_cd_info(cd_detected const& cd) {/*std::cout << "player::store_cd_info\n";*/ }
        void close_drawer(open_close const&) { /*std::cout << "player::close_drawer\n";*/ }
        void start_playback(play const&) { /*std::cout << "player::start_playback\n";*/ }
        void stopped_again(stop const&) {/*std::cout << "player::stopped_again\n";*/}
        void stop_playback(stop const&) { /*std::cout << "player::stop_playback\n";*/ }
        void pause_playback(pause const&) { /*std::cout << "player::pause_playback\n"; */}
        void stop_and_open(open_close const&) { /*std::cout << "player::stop_and_open\n";*/ }
        void resume_playback(end_pause const&) { /*std::cout << "player::resume_playback\n";*/ }
    };

    struct Empty : sc::simple_state< Empty, player >
    {
        Empty() { /*std::cout << "entering Empty" << std::endl;*/ } // entry
        ~Empty() { /*std::cout << "leaving Empty" << std::endl;*/ } // exit
        typedef mpl::list<
            sc::transition< open_close, Open,
            player, &player::open_drawer >,
            sc::transition< cd_detected, Stopped,
            player, &player::store_cd_info > > reactions;

    };
    struct Open : sc::simple_state< Open, player >
    {
        Open() { /*std::cout << "entering Open" << std::endl;*/ } // entry
        ~Open() { /*std::cout << "leaving Open" << std::endl;*/ } // exit
        typedef sc::transition< open_close, Empty,
            player, &player::close_drawer > reactions;

    };
    struct Stopped : sc::simple_state< Stopped, player >
    {
        Stopped() { /*std::cout << "entering Stopped" << std::endl;*/ } // entry
        ~Stopped() { /*std::cout << "leaving Stopped" << std::endl;*/ } // exit
        typedef mpl::list<
            sc::transition< play, Playing,
            player, &player::start_playback >,
            sc::transition< open_close, Open,
            player, &player::open_drawer >,
            sc::transition< stop, Stopped,
            player, &player::stopped_again > > reactions;

    };
    struct Playing : sc::simple_state< Playing, player >
    {
        Playing() { /*std::cout << "entering Playing" << std::endl;*/ } // entry
        ~Playing() { /*std::cout << "leaving Playing" << std::endl;*/ } // exit
        typedef mpl::list<
            sc::transition< stop, Stopped,
            player, &player::stop_playback >,
            sc::transition< pause, Paused,
            player, &player::pause_playback >,
            sc::transition< open_close, Open,
            player, &player::stop_and_open > > reactions;
    };
    struct Paused : sc::simple_state< Paused, player >
    {
        Paused() { /*std::cout << "entering Paused" << std::endl;*/ } // entry
        ~Paused() { /*std::cout << "leaving Paused" << std::endl;*/ } // exit
        typedef mpl::list<
            sc::transition< end_pause, Playing,
            player, &player::resume_playback >,
            sc::transition< stop, Stopped,
            player, &player::stop_playback >,
            sc::transition< open_close, Open,
            player, &player::stop_and_open > > reactions;
    };
}

#ifndef WIN32
long mtime(struct timeval& tv1,struct timeval& tv2)
{
    return (tv2.tv_sec-tv1.tv_sec) *1000000 + ((tv2.tv_usec-tv1.tv_usec));
}
#endif

int main()
{
    test_sc::player p;
    p.initiate();
    // for timing
#ifdef WIN32
    LARGE_INTEGER res;
    ::QueryPerformanceFrequency(&res);
    LARGE_INTEGER li,li2;
    ::QueryPerformanceCounter(&li);
#else
    struct timeval tv1,tv2;
    gettimeofday(&tv1,NULL);
#endif
    for (int i=0;i</*100*//*1000000*/10000000;++i)
    {
        p.process_event(test_sc::open_close());
        p.process_event(test_sc::open_close());
        p.process_event(test_sc::cd_detected());
        p.process_event(test_sc::play());
        p.process_event(test_sc::pause());
        // go back to Playing
        p.process_event(test_sc::end_pause());
        p.process_event(test_sc::pause());
        p.process_event(test_sc::stop());
        // event leading to the same state
        p.process_event(test_sc::stop());
        p.process_event(test_sc::open_close());
        p.process_event(test_sc::open_close());
    }
#ifdef WIN32
    ::QueryPerformanceCounter(&li2);
#else
    gettimeofday(&tv2,NULL);
#endif
#ifdef WIN32
    std::cout << "sc took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl;
#else
    std::cout << "sc took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl;
#endif
    return 0;
}


// player.cpp : Defines the entry point for the console application.
//

#ifdef WIN32
#include "windows.h"
#else
#include <sys/time.h>
#endif

#include "Player.h"

int main()
{
    // for timing
#ifdef WIN32
    LARGE_INTEGER res;
    ::QueryPerformanceFrequency(&res);
    LARGE_INTEGER li,li2;
#else
    struct timeval tv1,tv2;
    gettimeofday(&tv1,NULL);
#endif
    Player p;
    p.startBehavior();

    // for timing
#ifdef WIN32
    ::QueryPerformanceCounter(&li);
#else
    gettimeofday(&tv1,NULL);
#endif

    for (int i=0;i</*100*//*1000000*/10000000;++i)
    {
        open_close oc1;
        p.handleEvent(&oc1);
        open_close oc2;
        p.handleEvent(&oc2);
        cd_detected cd;
        p.handleEvent(&cd);
        play pl;
        p.handleEvent(&pl);
        pause pa1;
        p.handleEvent(&pa1);
        // go back to Playing
        end_pause ep;
        p.handleEvent(&ep);
        pause pa2;
        p.handleEvent(&pa2);
        stop st1;
        p.handleEvent(&st1);
        // event leading to the same state
        stop st2;
        p.handleEvent(&st2);
        open_close oc3;
        p.handleEvent(&oc3);
        open_close oc4;
        p.handleEvent(&oc4);
    }
#ifdef WIN32
    ::QueryPerformanceCounter(&li2);
#else
    gettimeofday(&tv2,NULL);
#endif
#ifdef WIN32
    std::cout << "rhapsody took in s:" << (double)(li2.QuadPart-li.QuadPart)/res.QuadPart <<"\n" <<std::endl;
#else
    std::cout << "rhapsody took in us:" << mtime(tv1,tv2) <<"\n" <<std::endl;
#endif

    //p.handleEvent(new open_close);
        return 0;
}


/********************************************************************
        Rhapsody : 7.5
        Login :
        Component : DefaultComponent
        Configuration : DefaultConfig
        Model Element : Default
//! Generated Date : Wed, 2, Dec 2009
        File Path : DefaultComponent\DefaultConfig\Default.cpp
*********************************************************************/

//## auto_generated
#include "Default.h"
//## auto_generated
#include "Player.h"
//## package Default

//## event cd_detected()
cd_detected::cd_detected() {
    setId(cd_detected_Default_id);
}

bool cd_detected::isTypeOf(short id) const {
    return (cd_detected_Default_id==id);
}

//## event stop()
stop::stop() {
    setId(stop_Default_id);
}

bool stop::isTypeOf(short id) const {
    return (stop_Default_id==id);
}

//## event play()
play::play() {
    setId(play_Default_id);
}

bool play::isTypeOf(short id) const {
    return (play_Default_id==id);
}

//## event pause()
pause::pause() {
    setId(pause_Default_id);
}

bool pause::isTypeOf(short id) const {
    return (pause_Default_id==id);
}

//## event end_pause()
end_pause::end_pause() {
    setId(end_pause_Default_id);
}

bool end_pause::isTypeOf(short id) const {
    return (end_pause_Default_id==id);
}

//## event open_close()
open_close::open_close() {
    setId(open_close_Default_id);
}

bool open_close::isTypeOf(short id) const {
    return (open_close_Default_id==id);
}

/*********************************************************************
        File Path : DefaultComponent\DefaultConfig\Default.cpp
*********************************************************************/


/*********************************************************************
        Rhapsody : 7.5
        Login :
        Component : DefaultComponent
        Configuration : DefaultConfig
        Model Element : Default
//! Generated Date : Wed, 2, Dec 2009
        File Path : DefaultComponent\DefaultConfig\Default.h
*********************************************************************/

#ifndef Default_H
#define Default_H

//## auto_generated
#include <oxf\oxf.h>
//## auto_generated
#include <oxf\event.h>
//## auto_generated
class Player;

//#[ ignore
#define cd_detected_Default_id 18601

#define stop_Default_id 18602

#define play_Default_id 18603

#define pause_Default_id 18604

#define end_pause_Default_id 18605

#define open_close_Default_id 18606
//#]

//## package Default

//## event cd_detected()
class cd_detected : public OMEvent {
    //// Constructors and destructors ////
    
public :

    //## auto_generated
    cd_detected();
    
    //// Framework operations ////
    
    //## statechart_method
    bool isTypeOf(short id) const;
};

//## event stop()
class stop : public OMEvent {
    //// Constructors and destructors ////
    
public :

    //## auto_generated
    stop();
    
    //// Framework operations ////
    
    //## statechart_method
    bool isTypeOf(short id) const;
};

//## event play()
class play : public OMEvent {
    //// Constructors and destructors ////
    
public :

    //## auto_generated
    play();
    
    //// Framework operations ////
    
    //## statechart_method
    bool isTypeOf(short id) const;
};

//## event pause()
class pause : public OMEvent {
    //// Constructors and destructors ////
    
public :

    //## auto_generated
    pause();
    
    //// Framework operations ////
    
    //## statechart_method
    bool isTypeOf(short id) const;
};

//## event end_pause()
class end_pause : public OMEvent {
    //// Constructors and destructors ////
    
public :

    //## auto_generated
    end_pause();
    
    //// Framework operations ////
    
    //## statechart_method
    bool isTypeOf(short id) const;
};

//## event open_close()
class open_close : public OMEvent {
    //// Constructors and destructors ////
    
public :

    //## auto_generated
    open_close();
    
    //// Framework operations ////
    
    //## statechart_method
    bool isTypeOf(short id) const;
};

#endif
/*********************************************************************
        File Path : DefaultComponent\DefaultConfig\Default.h
*********************************************************************/


/********************************************************************
        Rhapsody : 7.5
        Login :
        Component : DefaultComponent
        Configuration : DefaultConfig
        Model Element : Player
//! Generated Date : Tue, 1, Dec 2009
        File Path : DefaultComponent\DefaultConfig\Player.cpp
*********************************************************************/

//## auto_generated
#include <oxf\omthread.h>
//## auto_generated
#include "Player.h"
//## package Default

//## class Player
Player::Player(IOxfActive* theActiveContext) {
    setActiveContext(theActiveContext, false);
    initStatechart();
}

Player::~Player() {
}

bool Player::startBehavior() {
    bool done = false;
    done = OMReactive::startBehavior();
    return done;
}

void Player::initStatechart() {
    rootState_subState = OMNonState;
    rootState_active = OMNonState;
}

void Player::rootState_entDef() {
    {
        rootState_subState = Empty;
        rootState_active = Empty;
    }
}

IOxfReactive::TakeEventStatus Player::rootState_processEvent() {
    IOxfReactive::TakeEventStatus res = eventNotConsumed;
    switch (rootState_active) {
        case Empty:
        {
            if(IS_EVENT_TYPE_OF(open_close_Default_id))
                {
                    //#[ transition 12
                    //open_drawer;
                    //#]
                    rootState_subState = Open;
                    rootState_active = Open;
                    res = eventConsumed;
                }
            else if(IS_EVENT_TYPE_OF(cd_detected_Default_id))
                {
                    //#[ transition 1
                    //store_cd_info;
                    //#]
                    rootState_subState = Stopped;
                    rootState_active = Stopped;
                    res = eventConsumed;
                }
            
        }
        break;
        case Open:
        {
            if(IS_EVENT_TYPE_OF(open_close_Default_id))
                {
                    //#[ transition 11
                    //close_drawer;
                    //#]
                    rootState_subState = Empty;
                    rootState_active = Empty;
                    res = eventConsumed;
                }
            
        }
        break;
        case Stopped:
        {
            if(IS_EVENT_TYPE_OF(open_close_Default_id))
                {
                    //#[ transition 9
                    //open_drawer;
                    //#]
                    rootState_subState = Open;
                    rootState_active = Open;
                    res = eventConsumed;
                }
            else if(IS_EVENT_TYPE_OF(stop_Default_id))
                {
                    //#[ transition 2
                    //stopped_again;
                    //#]
                    rootState_subState = Stopped;
                    rootState_active = Stopped;
                    res = eventConsumed;
                }
            else if(IS_EVENT_TYPE_OF(play_Default_id))
                {
                    //#[ transition 3
                    //start_playback;
                    //#]
                    rootState_subState = Playing;
                    rootState_active = Playing;
                    res = eventConsumed;
                }
            
        }
        break;
        case Paused:
        {
            if(IS_EVENT_TYPE_OF(open_close_Default_id))
                {
                    //#[ transition 8
                    //stop_and_open;
                    //#]
                    rootState_subState = Open;
                    rootState_active = Open;
                    res = eventConsumed;
                }
            else if(IS_EVENT_TYPE_OF(stop_Default_id))
                {
                    //#[ transition 7
                    //stop_playback;
                    //#]
                    rootState_subState = Stopped;
                    rootState_active = Stopped;
                    res = eventConsumed;
                }
            else if(IS_EVENT_TYPE_OF(end_pause_Default_id))
                {
                    //#[ transition 6
                    //resume_playback;
                    //#]
                    rootState_subState = Playing;
                    rootState_active = Playing;
                    res = eventConsumed;
                }
            
        }
        break;
        case Playing:
        {
            if(IS_EVENT_TYPE_OF(open_close_Default_id))
                {
                    //#[ transition 10
                    //stop_and_open;
                    //#]
                    rootState_subState = Open;
                    rootState_active = Open;
                    res = eventConsumed;
                }
            else if(IS_EVENT_TYPE_OF(stop_Default_id))
                {
                    //#[ transition 4
                    //stop_playback;
                    //#]
                    rootState_subState = Stopped;
                    rootState_active = Stopped;
                    res = eventConsumed;
                }
            else if(IS_EVENT_TYPE_OF(pause_Default_id))
                {
                    //#[ transition 5
                    //pause_playback;
                    //#]
                    rootState_subState = Paused;
                    rootState_active = Paused;
                    res = eventConsumed;
                }
            
        }
        break;
        default:
            break;
    }
    return res;
}

/*********************************************************************
        File Path : DefaultComponent\DefaultConfig\Player.cpp
*********************************************************************/


/*********************************************************************
        Rhapsody : 7.5
        Login :
        Component : DefaultComponent
        Configuration : DefaultConfig
        Model Element : Player
//! Generated Date : Tue, 1, Dec 2009
        File Path : DefaultComponent\DefaultConfig\Player.h
*********************************************************************/

#ifndef Player_H
#define Player_H

//## auto_generated
#include <oxf\oxf.h>
//## auto_generated
#include "Default.h"
//## auto_generated
#include <oxf\omreactive.h>
//## auto_generated
#include <oxf\state.h>
//## auto_generated
#include <oxf\event.h>
//## package Default

//## class Player
class Player : public OMReactive {
    //// Constructors and destructors ////
    
public :

    //## auto_generated
    Player(IOxfActive* theActiveContext = 0);
    
    //## auto_generated
    ~Player();
    
    //// Additional operations ////
    
    //## auto_generated
    virtual bool startBehavior();

protected :

    //## auto_generated
    void initStatechart();
    
    //// Framework operations ////

public :

    // rootState:
    //## statechart_method
    inline bool rootState_IN() const;
    
    //## statechart_method
    virtual void rootState_entDef();
    
    //## statechart_method
    virtual IOxfReactive::TakeEventStatus rootState_processEvent();
    
    // Stopped:
    //## statechart_method
    inline bool Stopped_IN() const;
    
    // Playing:
    //## statechart_method
    inline bool Playing_IN() const;
    
    // Paused:
    //## statechart_method
    inline bool Paused_IN() const;
    
    // Open:
    //## statechart_method
    inline bool Open_IN() const;
    
    // Empty:
    //## statechart_method
    inline bool Empty_IN() const;
    
    //// Framework ////

protected :

//#[ ignore
    enum Player_Enum {
        OMNonState = 0,
        Stopped = 1,
        Playing = 2,
        Paused = 3,
        Open = 4,
        Empty = 5
    };
    
    int rootState_subState;
    
    int rootState_active;
//#]
};

inline bool Player::rootState_IN() const {
    return true;
}

inline bool Player::Stopped_IN() const {
    return rootState_subState == Stopped;
}

inline bool Player::Playing_IN() const {
    return rootState_subState == Playing;
}

inline bool Player::Paused_IN() const {
    return rootState_subState == Paused;
}

inline bool Player::Open_IN() const {
    return rootState_subState == Open;
}

inline bool Player::Empty_IN() const {
    return rootState_subState == Empty;
}

#endif
/*********************************************************************
        File Path : DefaultComponent\DefaultConfig\Player.h
*********************************************************************/

CD-Player example

Each test was executed several times. The times are accumulated and divided by
the amount of executed test runs to build an median value.

==============================================================================

100 loops of 11 transitions (10 runs)

sc took in s:0.00039614
sc took in s:0.000461721
sc took in s:0.000461721
sc took in s:0.000553632
sc took in s:0.000485257
sc took in s:0.000415276
sc took in s:0.000773003
sc took in s:0.000579124
sc took in s:0.000709448
sc took in s:0.00036513

--> ~ 0.000520045 s

msm took in s:1.04762e-005
msm took in s:8.93968e-006
msm took in s:8.24127e-006
msm took in s:8.31111e-006
msm took in s:9.91746e-006
msm took in s:2.31873e-005
msm took in s:2.22095e-005
msm took in s:8.38095e-006
msm took in s:1.66222e-005
msm took in s:2.08825e-005

--> ~ 1.37E-05 s

rhapsody took in s:4.5746e-005
rhapsody took in s:5.28e-005
rhapsody took in s:8.73714e-005
rhapsody took in s:8.73016e-005
rhapsody took in s:5.27302e-005
rhapsody took in s:6.19492e-005
rhapsody took in s:5.27302e-005
rhapsody took in s:6.92825e-005
rhapsody took in s:4.59556e-005
rhapsody took in s:5.27302e-005

--> ~ 6.09E-05 s

Result for 100 * 11 transitions:

SC 0.5200 ms
Rhapsody 0.0609 ms
MSM 0.0137 ms

==============================================================================

1000000 loops of 11 transitions (3 runs)

sc took in s:1.93943
sc took in s:1.93943
sc took in s:1.99496

--> ~ 1.95794 s

msm took in s:0.0741568
msm took in s:0.0988071
msm took in s:0.0772959

--> ~ 0.08341 s

rhapsody took in s:0.453220
rhapsody took in s:0.541500
rhapsody took in s:0.474185

--> ~ 0.48964 s

Result for 1000000 * 11 transitions:

SC 1.95794 s
Rhapsody 0.48964 s
MSM 0.08341 s

==============================================================================

10000000 loops of 11 transitions (3 runs)

sc took in s:19.5358
sc took in s:19.6668
sc took in s:19.7063

--> ~ 19.6363 s

msm took in s:1.059320
msm took in s:0.792874
msm took in s:0.870431

--> ~ 0.90754 s

rhapsody took in s:4.95632
rhapsody took in s:5.02131
rhapsody took in s:4.87564

--> ~ 4.95109 s

Result for 10000000 * 11 transitions:

SC 19.63630 s
Rhapsody 4.95109 s
MSM 0.90754 s


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk