EmbeddedRelated.com
Forums
The 2026 Embedded Online Conference

Common name for a "Task Loop"

Started by Tim Wescott June 24, 2016
On Wed, 29 Jun 2016 02:29:54 +0300, Dimiter_Popoff wrote:

> On 29.6.2016 г. 01:39, Tim Wescott wrote: >> On Wed, 29 Jun 2016 01:00:02 +0300, Dimiter_Popoff wrote: >> >>> On 28.6.2016 г. 17:40, Tim Wescott wrote: >>>> On Tue, 28 Jun 2016 06:20:43 +0300, Dimiter_Popoff wrote: >>>> >>>>> On 28.6.2016 г. 05:54, Simon Clubley wrote: >>>>>> >>>>> > ..... >>>>>> I still think that's a polling loop because you are reaching out to >>>>>> the sensor and asking it for it's current value. >>>>> >>>>> "Polling" in programming means "as opposed to interrupt handling". >>>>> How is this opposed to "interrupt handling". >>>>> >>>>> What you suggest to be polling is simply a loop of calls to >>>>> subroutines. >>>>> A "call loop" is what describes it - although it does not matter a >>>>> lot what word is used as long as it is not an already widely >>>>> accepted one like "polling" which you want to redefine. Nothing >>>>> wrong with that of course - as long as you don't have to communicate >>>>> with other people using your redefined term. >>>>> >>>>> So much talk about so little :-). Although Tim's topic idea worked, >>>>> produced quite a discussion. >>>> >>>> Well, I was hoping for a name of a design pattern, and I'm still not >>>> happy about my choices. So from that perspective it's a wash. >>>> >>>> "Super loop" seems closest, but it seems to more capture the notion >>>> of _always_ executing A, B, C, rather than executing only those bits >>>> that are ready. >>>> >>>> >>> I encounter the "super loop" term for the first time here, but why >>> not. >>> Although I see nothing "super" about it, to me this is still an >>> endless loop of calls to subroutines either of which may opt to do >>> something or to just return if it has nothing to do. >>> But like I said earlier I have almost never used this approach, it >>> smells of oversimplifying - thus costing more than a decent scheduler >>> does. Implement loops, state machines etc. as you like within tasks >>> but having a true - preemptive allowing cooperative operation- >>> scheduler costs very little in both machine resources and effort to >>> put together so I see no point in trying to avoid it. >> >> The "scheduler" overhead is about 10x lower with this style of >> switching. If you've got a bunch of similar-run-length tasks this >> method works dandy. > > Well it is perhaps 10x in terms of processing time but that makes it > within 1% of the time instead of within 0.1%. I mentioned earlier I once > (some 10years ago) I did a thing using the "loop" approach and in > hindsight I can say it cost me more time (I had plenty of CPU resources > to spare so other considerations just do not apply).
The only times when the "super loop" architecture that I showed is really appropriate is when the problem is small or when the processor is being pushed too hard. Otherwise -- yes, use an RTOS. -- Tim Wescott Control systems, embedded software and circuit design I'm looking for work! See my website if you're interested http://www.wescottdesign.com
Dimiter_Popoff wrote:
> On 28.6.2016 г. 17:40, Tim Wescott wrote: >> On Tue, 28 Jun 2016 06:20:43 +0300, Dimiter_Popoff wrote: >> >>> On 28.6.2016 г. 05:54, Simon Clubley wrote: >>>> >>> > ..... >>>> I still think that's a polling loop because you are reaching out to the >>>> sensor and asking it for it's current value. >>> >>> "Polling" in programming means "as opposed to interrupt handling". >>> How is this opposed to "interrupt handling". >>> >>> What you suggest to be polling is simply a loop of calls to subroutines. >>> A "call loop" is what describes it - although it does not matter a lot >>> what word is used as long as it is not an already widely accepted one >>> like "polling" which you want to redefine. Nothing wrong with that of >>> course - as long as you don't have to communicate with other people >>> using your redefined term. >>> >>> So much talk about so little :-). Although Tim's topic idea worked, >>> produced quite a discussion. >> >> Well, I was hoping for a name of a design pattern, and I'm still not >> happy about my choices. So from that perspective it's a wash. >> >> "Super loop" seems closest, but it seems to more capture the notion of >> _always_ executing A, B, C, rather than executing only those bits that >> are ready. >> > > I encounter the "super loop" term for the first time here, but why not. > Although I see nothing "super" about it, to me this is still an endless > loop of calls to subroutines either of which may opt to do something > or to just return if it has nothing to do. > But like I said earlier I have almost never used this approach, it > smells of oversimplifying - thus costing more than a decent scheduler > does. Implement loops, state machines etc. as you like within tasks > but having a true - preemptive allowing cooperative operation- scheduler > costs very little in both machine resources and effort to put together > so I see no point in trying to avoid it. >
Other than for ISRs, you really don't *need* preemption. I once built a proto-RTOS on top of DOS whilst waiting for the hardware, back in the stone age. This allowed getting most of the work done. It was initially configured as cooperative multitasking. I put a task switch in the timer ISR and got that working. It made nothing particularly better nor worse. But of course, I had the thing working as a fully cooperative multitasker first. But the experience of doing that made me rather queasy about the very concept of preemption. Randomly yoinking the current task based on timer events seems a curious thing when you've A/B compared them.
> Dimiter > > ------------------------------------------------------ > Dimiter Popoff, TGI http://www.tgi-sci.com > ------------------------------------------------------ > http://www.flickr.com/photos/didi_tgi/ >
-- Les Cargill
On 6/29/2016 9:36 AM, Les Cargill wrote:
> Dimiter_Popoff wrote: >> On 28.6.2016 г. 17:40, Tim Wescott wrote: >>> On Tue, 28 Jun 2016 06:20:43 +0300, Dimiter_Popoff wrote: >>> >>>> On 28.6.2016 г. 05:54, Simon Clubley wrote: >>>>> >>>> > ..... >>>>> I still think that's a polling loop because you are reaching out to the >>>>> sensor and asking it for it's current value. >>>> >>>> "Polling" in programming means "as opposed to interrupt handling". >>>> How is this opposed to "interrupt handling". >>>> >>>> What you suggest to be polling is simply a loop of calls to subroutines. >>>> A "call loop" is what describes it - although it does not matter a lot >>>> what word is used as long as it is not an already widely accepted one >>>> like "polling" which you want to redefine. Nothing wrong with that of >>>> course - as long as you don't have to communicate with other people >>>> using your redefined term. >>>> >>>> So much talk about so little :-). Although Tim's topic idea worked, >>>> produced quite a discussion. >>> >>> Well, I was hoping for a name of a design pattern, and I'm still not >>> happy about my choices. So from that perspective it's a wash. >>> >>> "Super loop" seems closest, but it seems to more capture the notion of >>> _always_ executing A, B, C, rather than executing only those bits that >>> are ready. >>> >> >> I encounter the "super loop" term for the first time here, but why not. >> Although I see nothing "super" about it, to me this is still an endless >> loop of calls to subroutines either of which may opt to do something >> or to just return if it has nothing to do. >> But like I said earlier I have almost never used this approach, it >> smells of oversimplifying - thus costing more than a decent scheduler >> does. Implement loops, state machines etc. as you like within tasks >> but having a true - preemptive allowing cooperative operation- scheduler >> costs very little in both machine resources and effort to put together >> so I see no point in trying to avoid it. >> > > Other than for ISRs, you really don't *need* preemption. I once built > a proto-RTOS on top of DOS whilst waiting for the hardware, back > in the stone age. > > This allowed getting most of the work done. > > It was initially configured as cooperative multitasking. I put a task switch in > the timer ISR and got that working. It made nothing > particularly better nor worse. But of course, I had the thing working > as a fully cooperative multitasker first. > > But the experience of doing that made me rather queasy about the very concept > of preemption. Randomly yoinking the current task based on > timer events seems a curious thing when you've A/B compared them.
Preemption isn't just "a timer interrupt" but, rather, anything that causes "something more important" to be invoked (scheduled) instead of the currently executing task. I.e., an ISR is a preemptive task. An ISR can signal an event (or other OS-involved mechanism) that, in turn, causes something that might be blocking, currently, to begin execution -- without waiting for the currently executing task to decide when an appropriate time to yield the processor might be. [Should the executing task *poll* the event flag mentioned above to decide when some other task is probably ready and waiting for THAT event? Should each task know the needs of other concurrent tasks??] The advantage of preemption is that tasks *don't* have to be considerate of the needs of other tasks -- unlike the MTX I illustrated, here, which must be *obsessed* with the needs of other tasks! A fully preemptive MTOS typically also exploits EVERY possibility of rerunning the scheduler, not just at jiffy's. E.g., if a task adjusts its priority downward, a reschedule() should occur (because some other higher priority task may be waiting to run and prohibited from doing so solely by the previously higher priority of the currently executing task. Note that preemption can also extend *into* the kernel -- whereby the kernel itself can be preempted (by other aspects of itself). Developing under a preemptive MTOS requires a different discipline; as does developing under a cooperative MTOS. For projects of substance, it is usually too difficult to obtain maximum performance from a system if you are charged with manually yielding control -- the system's complexity makes it impractical for you to keep track of everything that *might* be happening ("where *might* task32 be in its execution, now?") [Again, that's why the MTX I've illustrated keeps the context switch costs so lightweight; so you aren't concerned about yielding *often* and gratuitously]
Don Y wrote:
> On 6/29/2016 9:36 AM, Les Cargill wrote: >> Dimiter_Popoff wrote: >>> On 28.6.2016 г. 17:40, Tim Wescott wrote: >>>> On Tue, 28 Jun 2016 06:20:43 +0300, Dimiter_Popoff wrote: >>>> >>>>> On 28.6.2016 г. 05:54, Simon Clubley wrote: >>>>>> >>>>> > ..... >>>>>> I still think that's a polling loop because you are reaching out >>>>>> to the >>>>>> sensor and asking it for it's current value. >>>>> >>>>> "Polling" in programming means "as opposed to interrupt handling". >>>>> How is this opposed to "interrupt handling". >>>>> >>>>> What you suggest to be polling is simply a loop of calls to >>>>> subroutines. >>>>> A "call loop" is what describes it - although it does not matter a lot >>>>> what word is used as long as it is not an already widely accepted one >>>>> like "polling" which you want to redefine. Nothing wrong with that of >>>>> course - as long as you don't have to communicate with other people >>>>> using your redefined term. >>>>> >>>>> So much talk about so little :-). Although Tim's topic idea worked, >>>>> produced quite a discussion. >>>> >>>> Well, I was hoping for a name of a design pattern, and I'm still not >>>> happy about my choices. So from that perspective it's a wash. >>>> >>>> "Super loop" seems closest, but it seems to more capture the notion of >>>> _always_ executing A, B, C, rather than executing only those bits that >>>> are ready. >>>> >>> >>> I encounter the "super loop" term for the first time here, but why not. >>> Although I see nothing "super" about it, to me this is still an endless >>> loop of calls to subroutines either of which may opt to do something >>> or to just return if it has nothing to do. >>> But like I said earlier I have almost never used this approach, it >>> smells of oversimplifying - thus costing more than a decent scheduler >>> does. Implement loops, state machines etc. as you like within tasks >>> but having a true - preemptive allowing cooperative operation- scheduler >>> costs very little in both machine resources and effort to put together >>> so I see no point in trying to avoid it. >>> >> >> Other than for ISRs, you really don't *need* preemption. I once built >> a proto-RTOS on top of DOS whilst waiting for the hardware, back >> in the stone age. >> >> This allowed getting most of the work done. >> >> It was initially configured as cooperative multitasking. I put a task >> switch in >> the timer ISR and got that working. It made nothing >> particularly better nor worse. But of course, I had the thing working >> as a fully cooperative multitasker first. >> >> But the experience of doing that made me rather queasy about the very >> concept >> of preemption. Randomly yoinking the current task based on >> timer events seems a curious thing when you've A/B compared them. > > Preemption isn't just "a timer interrupt" but, rather, anything that > causes "something more important" to be invoked (scheduled) instead > of the currently executing task. >
The signal difference between a preemptive and cooperative multitasker is the addition of a ( potential ) context switch in a timer ISR. Please note that you can have fully functional timers in a cooperative multitasker.
> I.e., an ISR is a preemptive task. >
I'd say it's neither. Strictly speaking, an ISR simply modifies state. An ISR may hit signals that may cause eligibility to change in other tasks, but you have to have a completely separate API for semaphores, queues and the like from what user space tasks use.
> An ISR can signal an event (or other OS-involved mechanism) that, in > turn, causes something that might be blocking, currently, to begin > execution -- without waiting for the currently executing task to > decide when an appropriate time to yield the processor might be. > > [Should the executing task *poll* the event flag mentioned above to > decide when some other task is probably ready and waiting for THAT event? > Should each task know the needs of other concurrent tasks??] > > The advantage of preemption is that tasks *don't* have to be considerate > of the needs of other tasks -- unlike the MTX I illustrated, here, which > must be *obsessed* with the needs of other tasks! >
And I claim that this is no advantage at all. It neither enables nor prohibits designing and implementing systems with correct operation. It does reinforce a certain... narcissism. That's all.
> A fully preemptive MTOS typically also exploits EVERY possibility of > rerunning the scheduler, not just at jiffy's. E.g., if a task adjusts > its priority downward, a reschedule() should occur (because some other > higher priority task may be waiting to run and prohibited from doing > so solely by the previously higher priority of the currently > executing task. >
But the same is also true of a cooperative multitasker. Using VxWorks as a metaphor ( it can be configured either way ), you can/(will?) run the ready queue at any context switch.
> Note that preemption can also extend *into* the kernel -- whereby the > kernel itself can be preempted (by other aspects of itself). > > Developing under a preemptive MTOS requires a different discipline; > as does developing under a cooperative MTOS. For projects of substance, > it is usually too difficult to obtain maximum performance from a system > if you are charged with manually yielding control -- the system's > complexity makes it impractical for you to keep track of everything that > *might* be happening ("where *might* task32 be in its execution, now?") >
Gah! It's nothing like remotely difficult. I dunno - maybe it's unusual but I've put many, many things in the field that used that approach. Designing against race conditions is a separate matter.
> [Again, that's why the MTX I've illustrated keeps the context switch > costs so lightweight; so you aren't concerned about yielding *often* > and gratuitously]
-- Les Cargill
On 6/29/2016 10:53 AM, Les Cargill wrote:
>>> Other than for ISRs, you really don't *need* preemption. I once built >>> a proto-RTOS on top of DOS whilst waiting for the hardware, back >>> in the stone age. >>> >>> This allowed getting most of the work done. >>> >>> It was initially configured as cooperative multitasking. I put a task >>> switch in >>> the timer ISR and got that working. It made nothing >>> particularly better nor worse. But of course, I had the thing working >>> as a fully cooperative multitasker first. >>> >>> But the experience of doing that made me rather queasy about the very >>> concept >>> of preemption. Randomly yoinking the current task based on >>> timer events seems a curious thing when you've A/B compared them. >> >> Preemption isn't just "a timer interrupt" but, rather, anything that >> causes "something more important" to be invoked (scheduled) instead >> of the currently executing task. > > The signal difference between a preemptive and cooperative multitasker is the > addition of a ( potential ) context switch in a timer ISR.
No. You don't even NEED a timer in a multitasking system! All you need is a source of asynchronous "events". E.g., receipt of a character on a UART, an ethernet packet on a NIC, etc.
> Please note that you can have fully functional timers in a cooperative > multitasker.
As was apparent in the MTX example I posted, previously. But, the granularity of that time is under the arbitrary control of the other tasks in the system, regardless of the needs or desires of the task *utilizing* it (i.e., a task unconcerned with "time" can introduce skews in the other tasks's notion of time AT IT'S PEROGATIVE.
>> I.e., an ISR is a preemptive task. > > I'd say it's neither. Strictly speaking, an ISR simply modifies state. > An ISR may hit signals that may cause eligibility to change in other tasks, but > you have to have a completely separate API for semaphores, > queues and the like from what user space tasks use.
Semaphores are only required when that form of synchronization is required. task1() blink indicator #4 task2() play musical melody task3() display current time None of this requires a semaphore. Yet, can exploit a preemptive environment to ensure no task starves another.
>> An ISR can signal an event (or other OS-involved mechanism) that, in >> turn, causes something that might be blocking, currently, to begin >> execution -- without waiting for the currently executing task to >> decide when an appropriate time to yield the processor might be. >> >> [Should the executing task *poll* the event flag mentioned above to >> decide when some other task is probably ready and waiting for THAT event? >> Should each task know the needs of other concurrent tasks??] >> >> The advantage of preemption is that tasks *don't* have to be considerate >> of the needs of other tasks -- unlike the MTX I illustrated, here, which >> must be *obsessed* with the needs of other tasks! > > And I claim that this is no advantage at all. It neither enables nor prohibits > designing and implementing systems with correct operation.
Have you designed anything of significant complexity relying on a purely cooperative implementation? How did you prove the performance and correctness of the system? Did you consider every possible interaction of "yields" and verify that all tasks still met their performance goals? Or, did you rely on a Monte Carlo simulation? Note, you said "no advantage at all". I can *prove* that a given thread of code WILL execute and how long it will take to execute in every possible scenario -- without having to walk through all other tasks wondering when and where they will choose to yield() and how those IN CONCERT (not just a single task) will affect the remaining tasks in the system
> It does reinforce a certain... narcissism. That's all.
It does exactly what multitasking is designed to do: allow you to decompose a problem into independant, concurrent tasks. Instead of a spaghetti bowl of tasks writhing to compete cooperatively.
>> A fully preemptive MTOS typically also exploits EVERY possibility of >> rerunning the scheduler, not just at jiffy's. E.g., if a task adjusts >> its priority downward, a reschedule() should occur (because some other >> higher priority task may be waiting to run and prohibited from doing >> so solely by the previously higher priority of the currently >> executing task. > > But the same is also true of a cooperative multitasker. Using VxWorks > as a metaphor ( it can be configured either way ), you can/(will?) > run the ready queue at any context switch.
How does an event raised in an ISR *voluntarily* cause the currently executing task to reschedule -- NOW, not when it decides it's convenient for it?
>> Note that preemption can also extend *into* the kernel -- whereby the >> kernel itself can be preempted (by other aspects of itself). >> >> Developing under a preemptive MTOS requires a different discipline; >> as does developing under a cooperative MTOS. For projects of substance, >> it is usually too difficult to obtain maximum performance from a system >> if you are charged with manually yielding control -- the system's >> complexity makes it impractical for you to keep track of everything that >> *might* be happening ("where *might* task32 be in its execution, now?") > > Gah! It's nothing like remotely difficult. I dunno - maybe it's unusual > but I've put many, many things in the field that used that approach.
I suspect you've not designed many "complex" systems, then. "Many systems" doesn't speak to their complexity. How do you handle updating a video display (synchronizing the display update with the display refresh to eliminate visual artifacts)? *While* receiving network packets. And, digitizing incoming audio for delivery over the network. And, reproducing audio that's coming in over that network connection. And, sampling the voltage and current to a 3-phase load that you're managing. And, decoding RFID fobs that "happen" to present themselves. And, wondering what will be ADDED to the mix before you're finished? Do you spend every other instruction yielding? Checking the current time to decide *if* you should probably yield? Asking those other tasks if they would *like* you to yield? etc. Or, do you throw everything into ISR's for fear that they might not, otherwise, get done? Or, overprovision the hardware so you have resources that you can waste? Preemption gives you the ISR framework OUTSIDE of the (limited) ISR context. The example above -- a distributed, interactive load monitoring/management system (my project for the next month) is just a trivial one...
> Designing against race conditions is a separate matter. > >> [Again, that's why the MTX I've illustrated keeps the context switch >> costs so lightweight; so you aren't concerned about yielding *often* >> and gratuitously]
Don Y wrote:
> On 6/29/2016 10:53 AM, Les Cargill wrote: >>>> Other than for ISRs, you really don't *need* preemption. I once built >>>> a proto-RTOS on top of DOS whilst waiting for the hardware, back >>>> in the stone age. >>>> >>>> This allowed getting most of the work done. >>>> >>>> It was initially configured as cooperative multitasking. I put a task >>>> switch in >>>> the timer ISR and got that working. It made nothing >>>> particularly better nor worse. But of course, I had the thing working >>>> as a fully cooperative multitasker first. >>>> >>>> But the experience of doing that made me rather queasy about the very >>>> concept >>>> of preemption. Randomly yoinking the current task based on >>>> timer events seems a curious thing when you've A/B compared them. >>> >>> Preemption isn't just "a timer interrupt" but, rather, anything that >>> causes "something more important" to be invoked (scheduled) instead >>> of the currently executing task. >> >> The signal difference between a preemptive and cooperative multitasker >> is the >> addition of a ( potential ) context switch in a timer ISR. > > No. You don't even NEED a timer in a multitasking system!
That's not what I'd said.
> All you need is a source of asynchronous "events". E.g., > receipt of a character on a UART, an ethernet packet on a NIC, > etc. >
Yes, and? <disengages> -- Les Cargill
On 6/29/2016 11:41 AM, Les Cargill wrote:
> Don Y wrote: >> On 6/29/2016 10:53 AM, Les Cargill wrote: >>>>> Other than for ISRs, you really don't *need* preemption. I once built >>>>> a proto-RTOS on top of DOS whilst waiting for the hardware, back >>>>> in the stone age. >>>>> >>>>> This allowed getting most of the work done. >>>>> >>>>> It was initially configured as cooperative multitasking. I put a task >>>>> switch in >>>>> the timer ISR and got that working. It made nothing >>>>> particularly better nor worse. But of course, I had the thing working >>>>> as a fully cooperative multitasker first. >>>>> >>>>> But the experience of doing that made me rather queasy about the very >>>>> concept >>>>> of preemption. Randomly yoinking the current task based on >>>>> timer events seems a curious thing when you've A/B compared them. >>>> >>>> Preemption isn't just "a timer interrupt" but, rather, anything that >>>> causes "something more important" to be invoked (scheduled) instead >>>> of the currently executing task. >>> >>> The signal difference between a preemptive and cooperative multitasker >>> is the >>> addition of a ( potential ) context switch in a timer ISR. >> >> No. You don't even NEED a timer in a multitasking system! > > That's not what I'd said.
Right. You add a "(potential) context switch in a (system that doesn't HAVE a) timer"!
>> All you need is a source of asynchronous "events". E.g., >> receipt of a character on a UART, an ethernet packet on a NIC, >> etc. > > Yes, and? > > <disengages>
I guess you just must have considerably more capacity to manage complexity in your braincase than most other folks! The rest of us *mortal* NEED mechanisms to manage complexity.
On 29.6.2016 &#1075;. 19:36, Les Cargill wrote:
> Dimiter_Popoff wrote: >> On 28.6.2016 &#1075;. 17:40, Tim Wescott wrote: >>> On Tue, 28 Jun 2016 06:20:43 +0300, Dimiter_Popoff wrote: >>> >>>> On 28.6.2016 &#1075;. 05:54, Simon Clubley wrote: >>>>> >>>> > ..... >>>>> I still think that's a polling loop because you are reaching out to >>>>> the >>>>> sensor and asking it for it's current value. >>>> >>>> "Polling" in programming means "as opposed to interrupt handling". >>>> How is this opposed to "interrupt handling". >>>> >>>> What you suggest to be polling is simply a loop of calls to >>>> subroutines. >>>> A "call loop" is what describes it - although it does not matter a lot >>>> what word is used as long as it is not an already widely accepted one >>>> like "polling" which you want to redefine. Nothing wrong with that of >>>> course - as long as you don't have to communicate with other people >>>> using your redefined term. >>>> >>>> So much talk about so little :-). Although Tim's topic idea worked, >>>> produced quite a discussion. >>> >>> Well, I was hoping for a name of a design pattern, and I'm still not >>> happy about my choices. So from that perspective it's a wash. >>> >>> "Super loop" seems closest, but it seems to more capture the notion of >>> _always_ executing A, B, C, rather than executing only those bits that >>> are ready. >>> >> >> I encounter the "super loop" term for the first time here, but why not. >> Although I see nothing "super" about it, to me this is still an endless >> loop of calls to subroutines either of which may opt to do something >> or to just return if it has nothing to do. >> But like I said earlier I have almost never used this approach, it >> smells of oversimplifying - thus costing more than a decent scheduler >> does. Implement loops, state machines etc. as you like within tasks >> but having a true - preemptive allowing cooperative operation- scheduler >> costs very little in both machine resources and effort to put together >> so I see no point in trying to avoid it. >> > > Other than for ISRs, you really don't *need* preemption.
> ... I tend to agree with that - excepting the case of a larger system with a user running this and that on it (in fact you still don't "need" it there but it can be very useful, like saving you a reboot sometimes by allowing you to kill the hogger etc.). Like I said in another post here yesterday: > Of course, if you've got a bunch of fast tasks and even one slow one > (reciting the Gettysburg address to a human, for instance) then a > preemptive scheduler gets very attractive. > I see the preemptive feature mostly as a backup plan, basically if the reciter is say reciting through an UART at 9600 bps the output driver would be exiting the task cooperatively long before being preempted (say after filling an output FIFO of some 256 bytes or something). But if the system is dynamic - e.g. a fullblown OS with a user in front of it doing this and that - then preemption may well have to kick in. Dimiter
On Tue, 28 Jun 2016 23:22:51 -0700, Don Y
<blockedofcourse@foo.invalid> wrote:

>On 6/28/2016 10:04 PM, upsidedown@downunder.com wrote: >> On Tue, 28 Jun 2016 15:43:03 -0700, Don Y >> <blockedofcourse@foo.invalid> wrote: >> >>> Imagine having a few HUNDRED bytes of RAM, *total* in your system. >>> How much of this do you "divert" to implementing a formal scheduler? >>> How much do you devote to preserving the state(s) of independant tasks? >> >> With a stack oriented HLL like Pascal r C, the variables are already >> in the (private) stacks, only the CPU registers needs to be saved, >> thus only a few extra bytes needs to be allocated in each local stack >> in addition to the application variables. > >Ah, but that's the problem! You now need stacks for each task >(each large enough to satisfy the task's worst case stack penetration >*plus* any stack required by ISRs -- as they can occur during any >task's "time slot"). > >You can save JUST the SP for each task and use that to "recover" >the stack and CPU registers (saved on the stack during the context >switch). > >But, you will alwyas be tempted to yield() (or equivalent) at some >arbitrary depth on the stack. I.e., you can have a dozen stack >frames nested FOR THIS TASK and decide to yield() -- now, all of those >stack frames must be preserved as part of the task's state (because >you picked a "less than ideal place" to yield the processor to the >"next" task)
In preemptive systems you usually talk about a task becoming not ready e.g. waiting for some event. Some systems execute a Software Interrupt type instruction stacking the registers as a real interrupt would do. The stack pointer is saved into the kernel data structure. There are no nested saved register sets, there is just _one_ saved register set at the top of the stack. When the task finally becomes ready to run, the scheduler reloads the task stack pointer and then performs a Return From Interrupt, which pops the registers and continues with the instruction following the Software Interrupt instruction. So there are at most one saved register set in the task specific stack and it is located at the top of the stack.
On 6/29/2016 11:02 PM, upsidedown@downunder.com wrote:

>> Ah, but that's the problem! You now need stacks for each task >> (each large enough to satisfy the task's worst case stack penetration >> *plus* any stack required by ISRs -- as they can occur during any >> task's "time slot"). >> >> You can save JUST the SP for each task and use that to "recover" >> the stack and CPU registers (saved on the stack during the context >> switch). >> >> But, you will alwyas be tempted to yield() (or equivalent) at some >> arbitrary depth on the stack. I.e., you can have a dozen stack >> frames nested FOR THIS TASK and decide to yield() -- now, all of those >> stack frames must be preserved as part of the task's state (because >> you picked a "less than ideal place" to yield the processor to the >> "next" task) > > In preemptive systems you usually talk about a task becoming not ready > e.g. waiting for some event. Some systems execute a Software Interrupt > type instruction stacking the registers as a real interrupt would do. > The stack pointer is saved into the kernel data structure. > > There are no nested saved register sets, there is just _one_ saved > register set at the top of the stack. > > When the task finally becomes ready to run, the scheduler reloads the > task stack pointer and then performs a Return From Interrupt, which > pops the registers and continues with the instruction following the > Software Interrupt instruction. > > So there are at most one saved register set in the task specific stack > and it is located at the top of the stack.
And the other tasks have NO stacks? And no "processor state" stored? Note the "stack frames" that I mentioned, above, refer to the nesting of *function calls* (or subroutine invocations) within each specific task. I.e., you have no way of knowing how "deep" each task's stack happens to be when the task is preempted or voluntarily yields control of the processor. At that point, ALL of the task's state must be preserved: its CPU registers and its "live" stack. Repeat for all tasks in the system. If, instead, you ensure all context switches occur when there is NOTHING on the "task-specific stack", then all tasks can SHARE a stack (because you've ensured that the stack will be empty BEFORE you allow a reschedule() event). As such, the memory required for the stack is the maximum stack penetration of ANY task -- plus the maximum usage for the foreground (ISR) system. Contrast this with the maximum stack penetration for *each* task, plus the maximum ISR penetration SUMMED for all tasks in the system. (Some processors let you specify a separate stack for ISR's. In which case, you can eliminate the cost of the ISR stack PER TASK)
The 2026 Embedded Online Conference