I’m playing with tasks and have a question about
I have a task with the following body (the full implementation is on GitHub):
Wait : loop
-- Wait for pings. When they come, reset Last_Ping.
accept Ping do
Log.Debug ("Received ping.");
Last_Ping := Clock;
-- If there is no ping within the expected interval, do something.
delay until (Last_Ping + Expected_Ping_Interval);
-- Do something.
-- Is there any way to keep running the task loop here?
end loop Wait;
From how I understand the
select, it waits for the specified delay - if there is a
Ping before the end of the delay, the loop iteration is finished and the next iteration is started, again waiting for the specified delay or accepting a
Now, when the delay is over and there was no entry in
Ping, it does not proceed with the next loop iteration but is finished, right? Why is that? And is there a way to write this loop so the task continues running?
(Also happily take pointers to good resources for learning about task behavior in more depth. )
It should continue to run, and your example here will do that.
There are two ways to get your described behaviour.
One is an exception is raised, but not caught (so always add an exception handler to your tasks, or add a last chance handler).
The other is a deadlock.
Your full implementation calls a callback,
Reboot_Watched_Application.all, pointing to
Reboot, which calls
Start_Processing, which calls
This means you have a task trying to call itself.
It may or may not be caught by the runtime, so it either just deadlocks, or it raises an exception (probably Tasking_Error or Program_Error)
It looks like it deadlocks. But it seems to do that also if I split it into two tasks, one timer to keep track of the pings and delay, and one that calls the reboot.
Btw, what would be a good resource to read about concurrency? Maybe “Concurrent and Real-Time Programming in Ada” by Burns & Welling? I feel like my mental model of how things work is not only very fuzzy but also mostly wrong.
That’s a good option. My personal favourite for the whole language (not just tasking), is " Programming in Ada" by John Barnes. The current version is upddated with a preview of Ada 2022 features.
Btw, deadlocks should be “visible” in the stack traces of your tasks.
if on Linux, try
pstack <pid> or run
thread apply all backtrace when attached to your program in
delay until (Last_Ping + Expected_Ping_Interval); is an absolute delay, not a relative delay. It uses
Last_Ping as a time reference.
I can see 2 problems in your example :
Last_Ping is not set before entering the loop.
- Once the timeout has occurred,
Last_Ping is not updated so the
delay until statement is non blocking making the loop entering the
or branch permanently (when there is no
Ping) potentially hanging your application by consuming all CPU cycles.
Thanks everyone for helping out! This is gold.
I’m now off for a vacation, but will dive deeper into tasking afterwards.