ESP8266Scheduler icon indicating copy to clipboard operation
ESP8266Scheduler copied to clipboard

Example using delay or yield in class instantiated in task

Open thijse opened this issue 5 years ago • 7 comments

It would be great if you can add an example on how to use a non blocking delay in a other class than the Task itself.

I am now doing it like the example below (based on the Simple.ino example), but it requires changing the scheduler to make delay & yield public

class PrintManager  {
  protected:
    Task* _task;
  public:
    PrintManager(Task* task) {
      _task = task;
    }
    
    void print() {
        Serial.println("Print Loop Start");
        _task->delay(4000);
        Serial.println("Print Loop End");
        _task->delay(4000);
    }
};


class PrintTask : public Task {
public:
    PrintTask() : Task(), printManager((Task *) this){}; 

protected:
    PrintManager printManager;
    void loop()  {      
        printManager.print();
    }
} print_task;

Thanks!

thijse avatar May 22 '20 08:05 thijse

Hi,

This is not something I have ever thought of doing. Off the top of my head, I would probably pass the delay function through as a param, rather than the task, would this work for you?

nrwiersma avatar May 22 '20 15:05 nrwiersma

Hi, I am currently using with a modified version of NTPclient to do a non-blocking wait for an update from a NTP server, but I expect there are many more libraries that have internal delays that I may want to make non-blocking in the future

What would passing the function look like? It should still set delay_time and delay_ms of the task in order to have shouldRun run correctly, right?

ps. By the way, thanks for the library!

thijse avatar May 23 '20 03:05 thijse

Hi,

It would look something like this:

typedef void (*delayFunc)(unsigned long ms);

class PrintManager  {
  protected:
    delayFunc _delay;
  public:
    PrintManager(delayFunc delay) {
      _delay = delay;
    }
    
    void print() {
        Serial.println("Print Loop Start");
        _delay(4000);
        Serial.println("Print Loop End");
        _delay(4000);
    }
};


class PrintTask : public Task {
public:
    PrintTask() : Task(), printManager(this->delay){}; 

protected:
    PrintManager printManager;
    void loop()  {      
        printManager.print();
    }
} print_task;

I havnt tried the code, but it is around there somewhere.

nrwiersma avatar May 23 '20 04:05 nrwiersma

Thanks, I will try it out!

thijse avatar May 23 '20 17:05 thijse

I tried another approach which would not require passing a function or object:

I added the static function

void SchedulerClass::delay(unsigned long ms) {
	 current->delay(ms);
 }

to Scheduler. My thinking was that I could then call it the Non-Task object (e.g. PrintManager) as Scheduler.Delay(4000) or SchedulerClass::Delay(4000) Which would then call the Delay function in the Task that is currently running

However, this gives a niece big crash as soon as current->delay calls the underlying cont_yield(&context); in Task class.

Do you have any idea why? I am afraid that I do not understand the mechanics deeply enough to see why this fails.

thijse avatar Jun 07 '20 20:06 thijse

It entirely depends on how this is done. It would be very difficult to say without being able to map its exact flow.

nrwiersma avatar Jun 08 '20 06:06 nrwiersma

I'd also like to call delay and yield without passing those functions all the way down. I just tried what @thijse proposes and it seems to work just fine.

However, this gives a niece big crash as soon as current->delay calls the underlying cont_yield(&context); in Task class.

did you also happen to define yield as a class method?, if you did, make sure to also change yield(); to ::yield(); inside void SchedulerClass::begin() {}. Otherwise that yield refers to the class method, and then it crashes. See Qualified name lookup

dbuezas avatar Feb 01 '21 20:02 dbuezas