Closed AlirezaSalehy closed 3 years ago
You cannot change the schedule of a task that was started at a fixed rate, this is because the library is used within RTOS environments and it's difficult to make that thread safe without locking. However, there are two other solutions:
Keep rescheduling a single shot task (aka Javascript's setTimeout), for example:
void myTask() {
// do some work
taskManager.scheduleOnce(myIntervalInMillis, myTask);
}
// then within setup, loop or main initiate by calling it for the first time
myTask();
The other way is to use an event, where you can both control the next execution time, and also trigger it at any time. See https://www.thecoderscorner.com/products/arduino-libraries/taskmanager-io/using-polled-events-taskmanager/
Thanks very much for instant reply. Nice alternative way. I tried this way though before reading your comment. I used a while loop and a class property that can be changed from somewhere outside task and taksmanager.yeildForMicros function as bellow:
class HeartBeatExecuatble : public Executable {
public:
Link* link;
HeartBeatExecuatble(Link* link) {
this->link = link;
}
void exec() override {
while (1) {
this->link->sendHeartBeat();
taskManager.yieldForMicros(this->link->heartbeatTickMS * 1000);
}
}
~HeartBeatExecuatble() override {};
};
Do you think it's a correct approach or should be replaced by your recommended ways? I mean is there chance of stack overflow due to increasing number of function stack frames filling up stack? Because i think if i use yeildForMicros in all of my tasks then none of them will clean up their stack frames. Am i wrong? because i think that's how it should work.
Thanks in advance!
I would use the way I showed, yielding has a side effect of building up the call stack.
So this means that there is an ever increase in building up call stack? Then if we keep using while loop with yield within, for all tasks then stack will eventually overflow sooner of later?
Thanks so i try to implement the way you demonstrated.
I would use the way I showed, yielding has a side effect of building up the call stack.
Dear Dave can you please respond to my above question?
Thanks!
Every time you call yield, you essentially nest the runloop within your code, it's designed for small delays in microseconds while tasks happen. Even in the worst case, let's say to had 20 tasks, and each yielded, you'd be 20 deep which on most boards would be fine.
An example usage of yieldForMicros would be waiting for some hardware state to settle.
For example this is how it would look in code if you had three tasks and 2 of them called yield:
taskManager.runLoop()
task1.execute
task1 calls yieldForMicros
evaluate run loop (task1 cannot be scheduled as it is running)
task2.execute
task2 calls yieldForMicros
evaluate runloop (task1 and task2 cannot be scheduled as they are running)
task3.execute is called
task3 finishes
yieldForMiros of task2 exits.
task2 finishes
yieldForMicros of task1 exits.
task1 finishes
next runLoop
In future please can you ask questions in the forum (https://www.thecoderscorner.com/jforum/recentTopics/list.page). There is however no guarantee of an answer there either. I try to answer on a best efforts basis. It keeps the noise down here. Please close this issue once read and understood
In future please can you ask questions in the forum (https://www.thecoderscorner.com/jforum/recentTopics/list.page). There is however no guarantee of an answer there either. I try to answer on a best efforts basis. It keeps the noise down here. Please close this issue once read and understood
Alright thanks, for future issues i'll post there.
I got your example, But what if we have all tasks doing there services as the model bellow:
task_n:
while (1) {
do_service_n();
taskmanager.yieldForMicros(<SOME_MICROS>);
}
Then call stack keeps growing up until it overflows?
No, within yieldForMicros the only possibility of increasing the stack is if another yieldForMicros is located within another task. While within a yield for micros the calling task is frozen and will not be called recursively.
runLoop
execute taskN
yieldForMicros
while(duration not reached)
execute additional tasks (but do not reenter taskN)
end while
finish yield
finish taskN
Just for anyone else looking at this issue, I don't recommend using task manager the way presented here with busy loops. Yield for micros is not intended for use in busy loops, I don't recommend you use task manager if that's the way you're composing the application, it is not a good fit.
Reason for that is that it is not a scheduler in the way you're thinking it is, it uses unfair semantics and with many busy loops you'll risk tasks not being run. It can execute on a schedule, immediately, on an interrupt or event taking place. But a task scheduler it is not.
Thanks Dave.
I wish you the best.
I hope you didn't think my last comment to be rude, but I'd rather be upfront that we've never tested that scenario and have doubts about it working properly. Rather than bake it into your project and find problems later.
The intended purpose of yieldForMicros(..) is for waits longer than a few microseconds to make code look more synchronous and readable. Here's an example:
void onTakeReadingFromHardware() {
myHardware.initialise(); // takes 500uS to start..
taskManager.yieldForMicros(500); // allows other tasks to run
myHardware.doSomething(); // now we do something with the hardware
}
At the end of the day, you could test it in a lot of scenarios and see if it does work for your cases. It may well work but we just don't know as we've never considered using it that way. If you did, I'd focus on:
I hope you didn't think my last comment to be rude, but I'd rather be upfront that we've never tested that scenario and have doubts about it working properly. Rather than bake it into your project and find problems later.
Yes i didn't think so. You answered in a good manner, and i appreciate. I wish i could donate to support but i'm not able to involve in international payments due to beast dictator which literally ramming his evil dreams down our throat while holding us beaten and tied by ruining our dream DEMOCRACY.
It may well work but we just don't know as we've never considered using it that way. If you did, I'd focus on:
- Do all the busy loops continue to function over a time period of hours.
- What happens to other tasks written the traditional way, do they schedule.
- How deep is the call stack (you may need a hardware debugger to determine this).
Yes i can do this. I'll email you the results as soon as i am free to do the experiment.
Hi there! First thanks for this great library.
I think that there is no possibility to reschedule a task in latest version of library right? So i am asking whether there is a solution for this or would you please add it in next version?
This is specially useful when a task has a default service rate and later in execution we adjust service rate, base on run-time conditions.
Kind regards. A. Salehy