Boost logo

Boost Users :

Subject: [Boost-users] [Boost Statechart] Execute Function
From: Georg Gast (schorsch_76_at_[hidden])
Date: 2009-09-29 15:20:03


Hi,

i try to build a easy statechart to learn and understand how to use
boost::statechart. All i used until today was "homemade" statemachines.

At some states i need a "Execute" function. In the FAQ i read, that
therefore i should use a a thread and send there an event to the
statemachine and process this event. So i wrote a timerclass which uses
a thread to process an event via signals2.

<code>
-----------
timeout.h

#ifndef __timeout_h__
#define __timeout_h__

#include <boost/thread.hpp>
#include <boost/signals2.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

class Timeout
{
public:
        typedef boost::signals2::signal<void ()> signal_t;

        Timeout()
        : mp_thread(0)
        {
                lock lk(m_monitor);
        }

        ~Timeout()
        {
                lock lk(m_monitor);
                Terminate();
        }

        boost::signals2::connection connect(const signal_t::slot_type &subscriber)
        {
                lock lk(m_monitor);
                return m_sig.connect(subscriber);
        }

        void Start(boost::posix_time::time_duration delta_time, bool repeat =
false)
        {
                lock lk(m_monitor);

                if (mp_thread)
                {
                        Terminate();
                }

                m_repeat = repeat;
                m_delta_time = delta_time;
                mp_thread = new boost::thread( boost::bind( &Timeout::Execute, this));
        }

        void Stop()
        {
                lock lk(m_monitor);

                if (mp_thread)
                {
                        Terminate();
                }
        }
private:
        typedef boost::mutex::scoped_lock lock;
        void Execute()
        {
                try
                {
                        do
                        {
                                boost::this_thread::sleep(m_delta_time);

                                {
                                        lock lk(m_monitor);
                                        m_sig();
                                }
                        } while (m_repeat);
                }
                catch(boost::thread_exception& /* e */)
                {
                        // we got interrupted
                }
        }

        void Terminate()
        {
                if (mp_thread)
                {
                        mp_thread->interrupt();
                        delete mp_thread;
                        mp_thread = 0;
                }
        }

        signal_t m_sig;
        boost::thread* mp_thread;
        boost::mutex m_monitor;
        
        boost::posix_time::time_duration m_delta_time;
        bool m_repeat;
};

-----------
main.cpp

// SimpleBoostSCProblem
// main.cpp

#include "Timeout.h"

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

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

        // Our objects
        struct Obj1 {};

        // --------------------------------------------------
        // Events
        // --------------------------------------------------
        struct EvWork : sc::event< EvWork > {};

        struct EvExit : sc::event< EvExit > {};

        // --------------------------------------------------
        // Forward deklaration of states
        // --------------------------------------------------
        struct Active;
        struct DoStuff1;
        struct DoStuff2;
        struct Exit;

        // --------------------------------------------------
        // Statemachine
        // --------------------------------------------------
        struct TestMachine : sc::state_machine< TestMachine, Active >
        {
                TestMachine()
                {
                }
                ~TestMachine()
                {
                }

        };

        // --------------------------------------------------
        // States
        // --------------------------------------------------
        // TestMachine / Active
        struct Active : sc::state<Active, TestMachine, mpl::list< DoStuff1 > >
        {
                typedef mpl::list<
                        sc::transition< EvExit , Exit >
> reactions;

                Active(my_context ctx) : my_base(ctx)
                {
                        m_worker_connection =
                                m_worker.connect(boost::bind(&Active::ProcessEvent<EvWork>, this));
                }
                ~Active()
                {
                        m_worker.Stop();
                        m_worker_connection.disconnect();
                }

                template<typename ev>
                void ProcessEvent()
                {
                        outermost_context().process_event(ev());
                }

                Timeout m_worker;
        private:
                boost::signals2::connection m_worker_connection;
        };

        // TestMachine / Active / DoStuff1
        struct DoStuff1 : sc::state<DoStuff1, Active >
        {
                typedef mpl::list<
                        sc::transition< EvWork, DoStuff2 >
> reactions;

                DoStuff1(my_context ctx) : my_base(ctx)
                {
                        // start work timeout : Repeat every 10 ms
                        context< Active >().m_worker.Start(boost::posix_time::milliseconds(100));
                }
                ~DoStuff1()
                {
                }
        };

        // TestMachine / Active / DoStuff2
        struct DoStuff2 : sc::state<DoStuff2, Active >
        {
                typedef mpl::list<
                        sc::custom_reaction< EvWork >
> reactions;

                DoStuff2(my_context ctx) : my_base(ctx)
                {
                        // start work timeout : Repeat every 10 ms
                        context< Active
>().m_worker.Start(boost::posix_time::milliseconds(1),true);
                }
                ~DoStuff2()
                {
                }

                sc::result react( const EvWork & )
                {
                        // do stuff
                        // ..

                        // Nothing more to do with this event
                        return discard_event();
                }
        };

        // TestMachine / Exit
        struct Exit : sc::state<Exit, TestMachine >
        {
                Exit(my_context ctx) : my_base(ctx)
                {
                }
                ~Exit()
                {
                }
        };

};

int main(int argc, char* argv[])
{
        MyTest::TestMachine machine;
        machine.initiate();

        boost::this_thread::sleep(boost::posix_time::seconds(10));
        machine.process_event(MyTest::EvExit());

        return 0;
}

</code>

It the statemachine exits, i get constantly crashes in the
process_event<EvWork>() function, because the statemachine itself got
destructed and outermost_context() is no more vaild, but the state which
holds my timeout object (and the thread) did not yet got destructed and
therefore the thread stopped.

I dont have an idea to solve that problem. There is an assertion:
"get_pointer( pOutermostUnstableState_ )==0" in state_machine.hpp.

It does only happen if there was a transition from one state (DoStuff1)
to DoState2.

Thanks in advance!

Georg Gast

P.S.:
My system: Windows XP / Visualstudio 2008 / Boost 1.40.0


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net