-
Notifications
You must be signed in to change notification settings - Fork 82
updateHook() called too often #70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
updateHook() called too often #70
Conversation
This patch addresses issue orocos-toolchain#70 by a minor modification of TaskCore and the ExecutionEngine. The big scheme throughout this patch are these 3 rules: 1. one uses task.setPeriod( s ) to set the period adn task.getPeriod() to read the period 2. event port callbacks no longer schedule an updateHook(). Also completed operation calls no longer schedule an updateHook. 3. one uses task.trigger() to schedule the execution of updateHook() or processFunctions() in non-periodic tasks. This can be called from anywhere, so from event port callbacks or external components. 4. one uses task.update() to directly execute an updateHook() in case of a SlaveActivity. Side effects: A. For periodic components, event port callbacks will be called directly in the thread of the component, no longer waiting for a period to expire. This effectively decouples the reception of data on ports from updateHook(), while still being fully thread-safe. B. If rule 1. is followed, a task.getPeriod() will return 's', while a task.engine()->getActivity()->getPeriod() will return '0.0'. This could be fixed though... C. For non-periodic components, the execution of an event port callback will no longer schedule an updateHook(), unless task.trigger() is called in that callback D. For non-periodic components, script functions and updateHook() are only called as many times as trigger() is called, with the semantics that trigger() calls do not accumulate, but schedule at most one execution of updateHook/processFunctions. This patch only required a single modification in the unit tests: a corba server had to schedule an updateHook() (effect C.) from an event port callback. If your code does not follow convention '1.' above, the code will behave as before for periodic components/activities (side effect A+B not present). For non-periodic components, side effect C and D is always present. This patch is a work in progress, and some minor follow-up patches may follow. Technical solution: - We no longer let the Activity schedule a run of the ExecutionEngine, but use the EE's condition variable to schedule periodic/non-periodic task execution. Because of this, we can distinguish between a 'user' task.trigger() and a 'thread/internal' ( = getActivity()->trigger()) trigger. Signed-off-by: Peter Soetens <[email protected]>
What I am missing is the event port stuff. Event ports are meant to trigger, aren't they. I have to admit that I still need clarifications on the callback mechanism / semantics. I did check the RTT API documentation but very little is said there. |
I agree we need another patch that updates the docs. The new convention would be that if you want updateHook to be executed, you need to do this->trigger() at the end of your event port callback. This gives the user the power to decide if updateHook must be called or not. See also d03c66b which should probably be relooked at in the light of this proposal. |
I also would propose that an event port without a user callback should trigger the execution of the component (
? I remember that
I agree. But your patch does not fully solve the problem reported in #61. The core question is whether user callbacks should be executed even if the component is not running or not even configured (I tend to yes). This change should also be documented and is not mentioned in the commit message of 4d45250 yet. |
The discussion back in 2012 (http://www.orocos.org/forum/rtt/rtt-dev/changing-behaviour-updatehook) was also about removing the |
I personally believe that the port callbacks should not be executed if the component is not running. The operations should, however (as they are commonly used for configuration) Also +1 to @meyerj analysis: trigger should be the default callback. One can override it if he so chooses, but we definitely should retain current behaviour by default. And I would definitely love us to use the opportunity to rediscuss the trigger() in Task::start() (as @meyerj suggests). I really regularly have people asking why the task is called once just after start ... |
Yes... we're getting there. I forgot about the case where we trigger by default, if no callback is given. @doudou do you agree ? I also recall that you can provide a bind function which has less arguments. The extra args are dropped then. This can probably be fixed (and documented) in the implementation of addEventPort() I also suggested an update on d03c66b which probably solves the 'callback when running policy' easily. |
Yeah, I was also surprised by this when I first started using RTT. |
We need a broader consensus about this... since it does most likely change some components' behavior out there ? |
Given how both sneaky such a change would be, the only way we could do it would be by having it optional at first and let people test it on their system. The only thing I can personally say is that I doubt that any Rock component needs that particular behaviour. |
If no callback is attached, the EventPort triggers updateHook(), if a callback is attached, this replaces that trigger with the new callback instead. Signed-off-by: Peter Soetens <[email protected]>
The difference between the periodicity of a component/its ExecutionEngine and the activity running the ExecutionEngine has some unexpected consequences. The API allows to set the period of a component in different ways, which would have different results with this patch:
For now I think the cleanest solution would be to revert the separate |
…t TaskContext::dataOnPortHook() This allow the user to decide in an overwritten dataOnPortHook() whether the user callbacks should be enqueued, indepedent of whether the component is running or not. Note that the callbacks are not executed until the component starts unless #70 is merged. Signed-off-by: Johannes Meyer <[email protected]>
…t TaskContext::dataOnPortHook() This allows the user to decide in an overwritten dataOnPortHook() implementation whether the user callbacks should be enqueued or not, indepedent of whether the component is running or not. Note that the callbacks are not executed until the component is started unless #70 is merged. See d03c66b#commitcomment-8247917. Signed-off-by: Johannes Meyer <[email protected]>
This patch is supposed to fix orocos-toolchain#70 in a similar, but improved way as the original proposal. The main introduction is the addition of RunnableInterface::work( WorkReason ) and ActivityInterface::timeout() . This allows us to inform the runnable interface of the reason why it is being executed: because of a TimeOut, a Trigger, or IO ready. Depending on the reason, the RunnableInterface subclass can choose to do something different. The ExecutionEngine uses this to distinguish between message bookkeeping (Triggers) and real work to do in updateHook ( TimeOut and IOReady ). In addition, when a TaskContext is Running, an EventPort will call updateHook() upon newdata or, if a callback is attached, that callback and not updateHook. This works for periodic and non-periodic *Activity implementations. Unfortunately, the TaskContext::trigger() will forward to ActivityInterface::timeout() and the ExecutionEngine will use ActivityInterface::trigger() internally to do the message/operation bookkeeping. This is confusing in the implementation side, but keeps the API fully backwards portable. The implementation implements this mechanism for each Activity kind in RTT. The side effects are minimal to none. We do call always loop() now from Thread, and this patch does not yet detect Thread overruns (to be implemented in Activity). Signed-off-by: Peter Soetens <[email protected]>
…t TaskContext::dataOnPortHook() This allows the user to decide in an overwritten dataOnPortHook() implementation whether the user callbacks should be enqueued or not, indepedent of whether the component is running or not. Note that the callbacks are not executed until the component is started unless #70 is merged. See d03c66b#commitcomment-8247917. Signed-off-by: Johannes Meyer <[email protected]>
This patch is supposed to fix orocos-toolchain#70 in a similar, but improved way as the original proposal. The main introduction is the addition of RunnableInterface::work( WorkReason ) and ActivityInterface::timeout() . This allows us to inform the runnable interface of the reason why it is being executed: because of a TimeOut, a Trigger, or IO ready. Depending on the reason, the RunnableInterface subclass can choose to do something different. The ExecutionEngine uses this to distinguish between message bookkeeping (Triggers) and real work to do in updateHook ( TimeOut and IOReady ). In addition, when a TaskContext is Running, an EventPort will call updateHook() upon newdata or, if a callback is attached, that callback and not updateHook. This works for periodic and non-periodic *Activity implementations. Unfortunately, the TaskContext::trigger() will forward to ActivityInterface::timeout() and the ExecutionEngine will use ActivityInterface::trigger() internally to do the message/operation bookkeeping. This is confusing in the implementation side, but keeps the API fully backwards portable. The implementation implements this mechanism for each Activity kind in RTT. The side effects are minimal to none. We do call always loop() now from Thread, and this patch does not yet detect Thread overruns (to be implemented in Activity). Signed-off-by: Peter Soetens <[email protected]>
As discussed in length in this forum thread: http://www.orocos.org/forum/rtt/rtt-dev/changing-behaviour-updatehook :
We have observed lately two major issues:
I'm creating a simple patch that addresses both issues, in a backwards compatible way.