arkhipenko / TaskScheduler

Cooperative multitasking for Arduino, ESPx, STM32, nRF and other microcontrollers
http://playground.arduino.cc/Code/TaskScheduler
BSD 3-Clause "New" or "Revised" License
1.24k stars 226 forks source link

"Asynchronous while" - Call execute() inside a task callback #156

Closed YannikW closed 1 year ago

YannikW commented 1 year ago

Hi,

first of all thanks for that amazing library, it's really usefull when you need some kind of parallel and asynchronous execution.

Starting point

I'm working an SPS / PLC type software where I have one main task, which handles my main doing in a serial way, and some other tasks to check sensor readings and so on.

In my main task I often call something and then need to wait for an event (serial command or digital input state) to continue.

How I'm doing it now


// Task is tMain, first Callback is tCallback01

void tCallback01() {
  // Do something that takes some time to finish after triggering

  // Wait for the doing to finish
  // The finishing is tracked with a statusreqest sr
  sr.setWaiting();
  tMain.setCallback(&tCallback02);  // Set the new callback
  tMain.waitFor(&sr);
}

void tCallback02() {
  // Doing that was triggered in tCallback01 is finished
  // We entered here because sr has completed

  // Do the next step

  // Wait for the doing to finish
  // The finishing is tracked with a statusreqest sr
  sr.setWaiting();
  tMain.setCallback(&tCallback03);  // Set the new callback
  tMain.waitFor(&sr);
}

This works fine and with two functions like above it's no problem, however when adding more and more steps in the main task you will get many function which often have only one or two lines of code. Personal I like to have one callback function per task, I thinks thats easier to understand. Also I would like to reduce the overhead defining all that functions.

Proposal

My proposal / question would be to call the execute() function from inside my task-callback while waiting in the statusrequest to finish.

void tMainCallback() {
  // Do something that takes some time to finish after triggering

  // Wait for the doing to finish
  // The finishing is tracked with a statusreqest sr
  sr.setWaiting();

  // Now we just execute other functions while waiting
  while (!sr.completed()) ts.execute();

  // When we get here the statusrequest has completed

  // Do the next step

  // ...
}

I did not try if it simply works, because I'm afraid getting unpredictable behaviour when calling execute() inside a running and not finished task.

I have looked into the libraries code, however I do not fully understand everything thats happening there, so I'm asking it here. Is this proposal feasible or is it bad practise (or simply not working at all)?

Thanks!

Best regards Yannik

arkhipenko commented 1 year ago

Hi Yannik,

First of all - ts.execute() should not be called anywhere else outside of the main loop() method. In fact I wanted to even hide it inside TS implementation to spare developers from temptation to use it somewhere else.

With respect to tasks using same callback for different task: https://github.com/arkhipenko/TaskScheduler/blob/master/examples/Scheduler_example08_LTS/Scheduler_example08_LTS.ino

With respect to synchronizing tasks waiting for something https://github.com/arkhipenko/TaskScheduler/tree/master/examples/Scheduler_example04_StatusRequest https://github.com/arkhipenko/TaskScheduler/tree/master/examples/Scheduler_example05_StatusRequest

And putting it all together with different task priorities: https://github.com/arkhipenko/TaskScheduler/tree/master/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object

If you need further help with specific code, we will need to discuss some sort of consulting arrangement.

Good luck!