Help on Handling delays within Cooperative scheduling.

Go To Last Post
12 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi everyone.

Last time I really made a use of Kartman's Tutorial on Cooperative multitasking and it helped me a lot. However I still need to know more about how we manage to handle natural delays that occur within that some particular Tasks.
For simplicity Let reflect back to Kartman's great Tutorial already here at avrfreak
https://www.avrfreaks.net/index.p...

We have seen that each task is allocated a given constant time slice for ex 10 ms. Although Task_Timer[Task] allows a task to use a certain desired time,
But since this is Cooperative multitask no task is allowed to contains delays within it (for ex: _dalay_ms(500) ) As this will violate the use of cooperation between tasks right?
My question was "How do we cope when delays in the middle of tasks when delays are even a must depending on your project?"

In tutorial, task 6 was something like

    void task6(void) 
     { 
        PORTB ^= (1<<4); 
        task_timers[6] = 25;      //every 250ms 
        reset_task(6); 
      }

let's assume we wanted it to be in steady:

    void task6(void) 
     { 
        PORTB |= (1<<4); 
        _delays_ms(500);
        PORTB &=~ (1<<4); 
        task_timers[6] = 25;      //every 250ms 
        reset_task(6); 
      }

Calling delay f(x) will surely block the rest of tasks which will cause
some violations of the cooperation across the system hence undesirable.

AFAIU we really have to use some form of Finite State machine to be able to handle delays within cooperative scheduling. And if i remember well from what i have been reading this will involve some form of spinning in empty loop for that specific amount of time virtually doing nothing ':roll:'
This is getting me confused, How are finite state machine used with RTOS in this manner?
If you think this is silly question please don't Laugh in facts i have used Finite State machine in some few projects but i haven't never though using one in such instances
For ex in a simple traffic Lights project i did a while ago i started by simply some physical real world facts defining which states makes transition happen ex if there are no Vehicles waiting on the other side of the road OR the time is out! then we can Allow the Green Light to the other coming-by vehicles etc.. defining these transitions leads to some forms of boolean expressions of each State and you end up building an actual physical digital circuit involving some Flip flops etc... However i have also been aware that we can achieve the same job use a simple C Switch statement except that one is more like hardware whereas the other one is purely software. But again all this come to my original questions how are FSM really used in RTOS to Eliminate Blocking delays?

I would appreciate it if someone could give me just a snippet in terms of how we would configure FSM to work with Task 6 so that its delays () function will not be messing up the rest of system.

/* I hope i didn't post in the wrong forum again?? it's been a while ago! */

Many Thanks.
Regards.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

AFAIU we really have to use some form of Finite State machine to be able to handle delays within cooperative scheduling.

That's certainly the way I would have handled it in the past. If you have a job that takes 1s to complete but the task is only allowed to operate for 10ms each time then it obviously needs the task split into 100 steps and each time the task is called the next 10ms are performed. That "work" could simply be delaying. But if you are going to delay you might as well not waste an entire 10ms "time slice" but during the delay period simply return when you get called. One way to do this would be to use a hardware timer. Note it's value at the start of the operation and each time your task function gets called if it has not reached an end value then you just yield back to the OS.

I'm not familiar with Kartman's implementation so don't know how this fits in but you are right you could never use _delay_ms() in this kind of system.

In fact in many OS there is the concept of system timers. A task can often set a timer for say 1s from now then simply go idle. The OS will "call back" when the timer expires. (as most OS have a 10ms tick or whatever it is trivial for them to be incrementing 50 or 100 soft timers on each "tick" and any that reach the final count then schedule that waiting task to be called. Whether this is instantaneous (a callback immediately out of the timer ISR) or "jittery" (just ensure the task gets to run "soon" after the event expiry) will depend on how the system is implemented.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for your Feedback Cliff.

So are suggesting the Cooperative probably isn't your best bet especially if your projects involves some form of irregular delays everywhere? well i am really a newbie into these OS stuff but will a preemptive multitasking solve the issue?
But again! i have heard that building one is an absolute nightmare :cry:

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh no you can certainly do this in co-operative OS, it's just it takes more work on the programmers behalf as they/you have to consciously decide how you split a long task/delay into bite size chunks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Whaoou.... Here I come face to face with myself!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for all your Feedback Cliff.
I am pretty sure i am going to get this done sometimes

appreciated your ideas & thoughts.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kase57 wrote:
... a simple C Switch statement ...
Superloop vs event-driven framework by Miro Samek - considers the 'switch' way for FSMs versus a certain framework way.
Kase57 wrote:
... how are FSM really used in RTOS to Eliminate Blocking delays?
RTOS without blocking? by Miro Samek.
The concept of inversion of control
(Is it the application controlling the environment or the environment controlling the application?)
The Communicating Sequential Processes (CSP) and Rate Monotonic Analysis (RMA) methods are one way of working this problem; Miro's state machine method is another way.
Note that Miro's product, QP, can run in an RTOS (or OS) computing environment.

Cooperative vs. Preemptive - cooperative is the preferred way if reasonable. Preemptive can open a can of worms w.r.t. deadlock, livelock, etc.; be careful with shared resources (memory, I/O).

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Pre-emptive is only really needed if you have a CPU hog, like some terrifyingly complex calculation that's cumbersome to split up in bite size chunks. Especially when you use some standard library trig functions that have non-deterministic timing (Ganssle has an article about that). Another advantage is that timing is more stable as you add stuff. Of course, guys like Jean Labrosse of uC/OS fame will tell you an RTOS is almost always needed :)

Recently I stumbled across the QP framework, very interesting read on HSMs and stuff. Been experimenting with pure event-driven stuff, and working on and off on a even simpler version of that QP-nano (QP-pico?). I don't think it's needed to implement all UML statechart semantics to get something very useful. And from what I saw, it seems QP does not implement all UML semantics either. I have projects of mine that I would like to convert to an almost purely event driven HSM architecture. One thing I especially like of HSMs is that you code by difference.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

According to task6, you want the bit on for 500ms and off for 250ms, rinse and repeat. Within the framework we have a number of choices.

1.

void task6(void)
{
static char state = 0;

  switch(state)
    {
    case 0:
        // set bit on
        state  = 1;
        break;
    case 1:
        state = 2;
        break;
    case 2:
        //port bit off
        state =0;
        break;
      }
 task_timers[6] =25;
 reset_task(6);
}

Second method is a variation on 1 where you have two states and change the task timer value.

If you want precise timing, then the third method would involve a state machine in the timer tick.

For even more precise timing, a timer with compare would be used.

If all you're doing is flashing a light, then the first two methods are adequate.

How would you do it in a rtos? Basically as you've written your code but delay_ms would be a system call to suspend the task for the given time.

Last Edited: Mon. Jul 2, 2012 - 02:03 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you have library with timers that support callbacks when the timer expires:


main()
{
.
.
.
turn_led_on();
.
.
.
}

void turn_led_on(void)
{
 !! LED ON
 timer_set(250,turn_led_off);
}

void turn_led_off(void)
{
 !! LED OFF
 timer_set(500,turn_led_on);
}

Basically it's just a hidden state machine of course.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks everyone who participated in answering my concerns about non blocking delays!
at last i can get every things sorted.

Pretty much I managed to Eliminate the blocking delays with the help of Finite States Machines in same ways as Kartman except that in steady of using a switch i have wrapped all the States in the dedicated 2 structures to use a small code and avoid mixing up shared variables across. The 1 structure contains all the states Enumerated, and the 2nd structure Calls the 1st one with even some state time variables which govern what amount of time each state should last.
here is a snippet of a bit of what a code looks like.


   typed enum
    { 
      SUB_TSK1_A;
      SUB_TSK1_B;
      SUB_TSK1_C;
    }  TASK1_PRIMARY;

   typedef struct
    {
      TASK1_PRIMARY next_state;
      int32_t                  state_time;
    } TASK1_SECONDARY;
  
    static const PROGMEM TASK1_SECONDARY state_table [] = 
     {
         { .next_state = SUB_TSK1_B, .state_time = 5}  
       //5 can be anytime & SUB_TSK1_B is obviously the next.
         { .next_state = SUB_TSK1_C, .state_time = 4}
         { .next_state = SUB_TSK1_A, .state_time = 7}
     };  
  // having this table in hand then calling states & their time it's straightforward
   . 
   .
   .
   .
   .
  main()
    {
      . 
      .
      .
      .   
        Set_Task(5) ; //Task 5 as example;
      .  
      .
      .
    }


  void Task(5)
   { 
     static TSK1_PRIMARY_current_state = TSK1_C; 
   // Just the one that comes before the first task.
     static int32_t   time_left = 2; //again 2 as example;

    if(--time_left==0)
         {
            current_state = pgm_read_word(&(state_table[current_state].next_state));
            time_left = get_time(); 
            Set_state(current_state); //Do something with that state.
         }
        Task_timers[5] = 25;
        Reset_Task(5);
     }

.

// get_time() might be needed if we want to keep in track of the states and time! This is the case where the Task you broke into pieces of chunks (Sub_Tasks) had irregular and non periodic delays which also depends on other if statements, in this case you might / OR not delay. in such case you might want to use get_time to sort this out. Otherwise if all delays are periodic and regular. then you might as well leave "time_left" to something like

time_left = pgm_read_word(&(state_table[current_state].state_time));" 

Many Thanks to Kartman who introduces all this Cooperative Multitasking Scheduling in his tutorial.
and Many thanks to Cliff who kept telling me that we break a typical task into pieces of chunks then we treat each as a typical state of FSM Accordingly But Well.. I didn't know what he meant ':lol:' hihihihi
Many Thanks to everyone who took part into answering aswell.

Cheers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I basically wrote the exact same thing years ago, except no provision for states per task, just one function pointer.

The only difference from your approach was that I did not keep a down counting tick per task, instead every time the task ran, the current system time was stored, and the scheduler just computed the time difference between the current time and stored previous time, if the time span was equal or greater than the period time of the task, that task was run again.

Advantage is if there is ever a slip of a tick or so by a task, it does not accumulate.