Subject: Re: [boost] [msm] Message queue and copy constructed events
From: Christophe Henry (christophe.j.henry_at_[hidden])
Date: 2010-06-22 05:47:15
>Lets separate 2 thinks.
>1: no heap operation this is a nice to have feature we can live with
>it. According my understanding the only place where heap is used is in
>the std:queue ? Am I correct ? If yes than if the user has a
>possibility to provide its own queue implementation than he/she can
>make sure about avoiding heap usage.
In the queue AND in the boost::function objects contained in the
queue. So merely changing the container will not be sufficient to
eliminate all heap usage (read further on).
>2: event copying. You saying copy is happening when bind is used to
>anonymize events to be able to store them in a queue.
>According my understanding of event driven systems we can say that as
>log an RTC step is not finished -> means that the most outer
>process_event does not retuned the event which was used in the call of
>the process_event should be valid. This means that events will be
>valid even with queuing for queues which are used during pseudo entry
>and exit because the processing of those events are always finished
>before the more outer process_event is returned.
>The only place where this is not true ->when the event should be
>available longer than the RTC step is when the event is deferred.
>So I think it would be enough for non deferred queues to internally
>before the bind store the event in a ref/cref and bind that and put
>to the queue -> no copy.
This is incorrect. You can have the same behaviour with the event
queue. Consider test/SimpleWithFunctors.cpp:
You have a transition:
Row < Empty , cd_detected , Stopped , store_cd_info , ... >
And in the action, we generate another event:
template <class EVT,class FSM,class SourceState,class TargetState>
void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& )
fsm.process_event(play()); // temporary play will be
queued, then the function returns
// ooops, temporary is gone
/// end of action, transition now ends and will start examining
the queue and will find inside a reference to a destroyed event
Now, if msm was using ref/cref, we would be processing a destroyed
event, which would not end well ;-)
Unfortunately, it is not possible for process_event to know if the
event object is a temporary, therefore the copy. The only way to
change this would be to have the user tell msm about it and take
responsibility for ensuring event validity. We could:
- have an extra parameter to process_event (run-time cost for all users)
- allow some compile-time flag (compile-time cost for all users)
None is very appealing to me as I doubt many will require this,
especially as I fail to see why you could not pimpl the event data,
thus making the event cheap to copy. But everybody would have to pay
for it somehow. And the heap issue would still be unsolved.
This would likely also end at the very bottom of my feature list.
I think a possible solution would be the fact that I intend to offer
customization of the queue container, which is a much more popular
feature. Instead of calling push on it with the boost::function
object, I could call push(event), so that you can provide a container
not of function objects but of anything (like of a pointers to some
base event type or void* if you prefer to cast a little, which I
prefer not knowing of ;-) ).
This allows complete removal of any heap usage AND could also be used
to get rid of event copying (in this case though, you are taking
responsibility of having thought of the event objects' lifetime). The
downside is that providing a new container will require slightly more
>For deferred queues there is 2 ways to go.
>First is to copy the event during bind or have a user implement a
>queue which interacts whit the underlaying zero copy scheduler. and
>only again store a ref/cref in a bind. maybe it is enough to have 2
>callback to the user when an event is deferred and when it is recalled
>and not needed any more.
Fore the deferred queue, I can provide the same mechanism, so that you
have have different containers for both if you wish.
Using boost.parameter will make these extra template arguments more bearable.