I finally figured out the reason of the above effect.
Assume Ev1 and Ev2 are both deferred in the current state. Ev1 and Ev2
(in this order) are currently in state_machine::eventQueue_, and
state_machine::process_queued_events() is invoked.
If between Ev1 and Ev2 there's an event, which causes transition from
the current state, then Ev1 will be processed (deferred), but Ev2 will
have no chance to get processed, and thus it will remain in
state_machine::eventQueue_.
Now, when the current state is getting destroyed due to the
transition, release_events() is invoked, and the following line puts
Ev1 (which is deferred) *after* Ev2 (which is still is the
eventQueue_):
eventQueue_.splice( eventQueue_.end(), pFound->second );
The mechanism is clear now, but the question is whether it's a bug or
a feature :).
I attach a simple reproducing program (MSVC10, boost 1.44).
Thanks.
#include <iostream>
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace boost::statechart;
namespace mpl = boost::mpl;
struct ev1to2 : event<ev1to2>
{};
struct ev2to3 : event<ev2to3>
{};
struct ev3to4_1 : event
{};
struct ev3to4_2 : event
{};
struct s1;
struct s2;
struct s3;
struct s4_1;
struct s4_2;
struct fsm : asynchronous_state_machine
{
fsm(my_context ctx) : my_base(ctx)
{}
};
struct s1 : simple_state
{
typedef mpl::list<
transition,
deferral<ev2to3>,
deferral,
deferral
reactions;
s1()
{
std::cout << "s1" << std::endl;
}
};
struct s2 : simple_state
{
typedef mpl::list<
transition,
deferral,
deferral
reactions;
s2()
{
std::cout << "s2" << std::endl;
}
};
struct s3 : simple_state
{
typedef mpl::list<
transition,
transition
reactions;
s3()
{
std::cout << "s3" << std::endl;
}
};
struct s4_1 : simple_state
{
s4_1()
{
std::cout << "s4_1" << std::endl;
}
};
struct s4_2 : simple_state
{
s4_2()
{
std::cout << "s4_2" << std::endl;
}
};
void enqueue(fifo_scheduler<> &sched, fifo_scheduler<>::processor_handle handle)
{
std::cout << "e3to4_1" << std::endl;
sched.queue_event(handle, new ev3to4_1);
std::cout << "e2to3" << std::endl;
sched.queue_event(handle, new ev2to3);
std::cout << "e3to4_2" << std::endl;
sched.queue_event(handle, new ev3to4_2);
std::cout << "e1to2" << std::endl;
sched.queue_event(handle, new ev1to2);
}
int main()
{
fifo_scheduler<> fifoSched(true);
boost::thread t1(boost::bind(&fifo_scheduler<>::operator(), &fifoSched, 0));
fifo_scheduler<>::processor_handle sessionProcessor =
fifoSched.create_processor<fsm>();
fifoSched.initiate_processor(sessionProcessor);
boost::thread t2(enqueue, boost::ref(fifoSched), sessionProcessor);
t1.join();
t2.join();
}