On 6/29/16 10:53 AM, Les Cargill wrote:> > > 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. >The difference between a preemptive and a cooperative system is in a cooperative system, a context switch to a different task only occurs a defined points in each task (typically a blocking call or an explicit yield). A preemptive system has the possibility of an interrupt event causing a context switch at almost any point in the execution of a task (there may be small regions, critical sections, which guard against such possibilities). It isn't just timer ISRs that can cause the context switch.
Common name for a "Task Loop"
Started by ●June 24, 2016
Reply by ●July 3, 20162016-07-03
Reply by ●July 3, 20162016-07-03
Hi Dimiter, On 7/2/2016 8:23 PM, Dimiter_Popoff wrote:> On 03.7.2016 г. 00:36, Don Y wrote: >> On 7/2/2016 11:44 AM, upsidedown@downunder.com wrote: >>> With 64 bytes/task, you could fit 3 tasks into 200 bytes and six into >>> 400 bytes of RAM. >> >> With 3 tasks, you probably couldn't implement a municipal traffic light >> controller! (don't forget the globals and ISR's) > > Don, even splitting things in two tasks can be quite helpful. Say one > handling the I/O and the other the rest.Yes, but you ideally want to come up with a "natural decomposition"; how do things *want* to split themselves up instead of how *can* I split them up. I like doing lots of little things and getting them 100.0% correct instead of trying to walk and chew gum -- and risking a latent bug. E.g., the barcode decoder in the example I previously posted runs in 6 layers: - an ISR grabs the contents of a freerunning two byte counter-timer using 3 byte-wide reads (H-L-H / L-H-L); it also reads another counter-timer (the one that generated the IRQ from the rising/falling edge of the "video" signal) to determine "how long ago" the actual edge occured (this allows interrupt latency to be removed from the 3 byte read!); then, programs the counter-timer to look for the "opposite" edge (black-to-white vs. white-to-black) - the first "background" task simply watches the FIFO maintained by the ISR and extracts data as it becomes available; it notices special encodings in the FIFO that indicate the ISR overran the FIFO (so, data has been lost *at* that point -- but the data preceding it may still represent a valid code); as it extracts each 4-byte entry, it converts the 3-byte timer read into a 2-byte read (by noticing if the low byte "wrapped" while the read was happening); then, uses the "latency time" to adjust the 2-byte value to account for any delay that might have been present when the IRQ was actually *serviced*; it also passes flags that indicate if the 2-byte timer wrapped more than once (cuz the ISR can be idle for long periods of time between uses; 0x65535 and 0x0002 may have far more than 3 counts between them!). So, this task's primary purpose is to keep the ISR FIFO empty cuz each entry there consumes 4 bytes - the second background task looks at these 2-byte counts (and any interspersed flag encodings) and computes time *differences* between them. It knows that the 2 byte counter might not be counting [0..65535] -- or even something like [0..MAX_COUNT] -- rather, it can be counting ([0..MAX_LSB],[0..MAX_MSB]) so has to play games with modular arithmetic but can do the math /in situ/ without consuming any additional memory (recall the first background task will be trying to fill *this* buffer as well!) - the third grabs groups of 8 (?) "bars" (a "space" is essentially a bar) and tries to see if the widths fit the ratios of the barcode that is being decoded -- assuming the label has been scanned at a relatively constant speed over the course of those bars/spaces; it reduces the bars/spaces to a "character" in the format supported by the label. If the widths defy the rules for the legal characters, it discards the oldest bar/space and tries again - the fourth looks at groups of characters to decide if they fit the form of a label -- correct start character, number of characters, check digit, stop character, etc. If not, then the oldest character must be rejected (which means the oldest bar/space must be rejected in the *third* task that feeds this. It also applies more "global" tests in an attempt to discard dubious data (goal is to achieve a very high "first pass read rate" but an even HIGHER *accuracy* level!). So, it looks at things like how "fast" the label passed the point scanner for each of the characters and argues as to whether it is possible for that set of scanning speeds to be encountered "in such rapid succession". E.g., if the first character appears to have been scanned at a rate of 10 inches per second (based on the time-widths of the bars and spaces that it is composed of) but the second character (one tenth of an inch from the start of the first!) indicates a scanning speed of 80 ips, we know something just isn't right and can discard the oldest character, etc. - Finally, the fifth level takes "whole labels" and tries to concatenate them into "super labels" by recognizing certain start-stop character combinations AND the time (physical distance between the end of one and the start of the next) to see if they conform to a grammar that allows them to be combined into a single, continuous label (with the EMBEDDED start-stop characters elided) On top of all this, you're also looking at the data in the context of it being representative of a label scanned *backwards* -- so, the oldest bar/space may, in fact, be the LAST one in the label, part of the LAST character 9stop character), etc. You can write this as one giant piece of spaghetti code. And, spend a boatload of time trying to get it to work RELIABLY (note that you have to allow a label to be scanned at ANY time, not just when the user tells you "I'm going to scan a label, now!"). So, testing is an abysmal way of assuring yourself that the code REALLY works! Prove it on paper; verify it in the lab!> Having a multitask scheduler does not mean you don't use loops. You do > of course - practically always. You just don't have to care about what > the scheduler gives you in every subroutine you write - just call the > "task exit for rescheduling" call when your code has to wait. If you > don't have the scheduler you will have to care about this every step of > the way, make sure you know where your current stack pointer is > (subroutine calls; then a subroutine which exits cannot be just > called from any nesting level etc. etc.).When you can *afford* a more "flush" environment, I agree that it makes life a *lot* easier; you don't have to think about how many CPU cycles you've burned since you last yield()-ed ("Am I starving some task -- that I don't want to be aware of! -- that really NEEDS the CPU more than me?"). You don't have to refactor the design as you add tasks/complexity, etc. You just concentrate on the individual "jobs" that need to be done. But, what I'm attempting to show with my example is that you don't have to make it an all-or-nothing decision; you can still avail yourself of a multitasking environment (including other "services" that a real OS would provide) even without all the resources that you would otherwise ASSUME were needed! Spaghetti code is never justified, IMO. Even tiny PICs can benefit from more structured execution environments.> If we really have to count the bytes of RAM - let us say we turn the > clock 25 years back - we can statically assign as much stack as needed > to each task. We may find out that splitting in 3 tasks can at > times _save_ memory above a certain complexity... > > I thought we'd be done with counting for days every byte by now,There are still a lot of designs that pinch pennies. The LORAN plotter I mentioned ran in 12K of CODE and 256 bytes of RAM. Back when 2716's were $50/each. We actually went through the code to pull out a few hundred bytes by mapping the most frequent subroutine CALL's (a 3 byte operation) to ReSTart instructions (one byte SWI's, of a sort). Silly to put an extra $50 into the device if it wasn't needed! Nowadays, there's a lot of cruft added to many products that eats up a lot of resources before you write your first byte of code. E.g., communication stacks (ethernet, bluetooth, USB, etc.) are now common whereas ages ago it was a hundred bytes to handle a UART!> last "small" MCU I used was a coldfire in tqfp100 with 16k RAM and > a sort of 68000 running at 80 MHz.... I just copied and slightly > adapted the initial (68340 from 1994) DPS scheduler and trap handler, > cost me virtually nothing. > > http://tgi-sci.com/dsv/swgboard.gif > http://tgi-sci.com/dsv/swg800.gifSheesh! Couldn't you have used a RULER to line up the mounting screws?? <grin>> http://tgi-sci.com/misc/PICT6999.avi > http://tgi-sci.com/tgi/auxhv.htm > No picture of the last, this MCU replaced a huge computer board > in an 8 channel Ortec alpha counting system so they could use > two netmca-s to simultaneously select any two vacuum chhambers > to count, measure the vacuum and plenty of other internals, the > analog part was intact). > > And such an MCU costs well below $10 at Mouser or the likes for > a single piece....Yes, but there are complete products that *sell* for less than that! E.g., I can buy a BT stereo headphone for $5 -- and a FEW people in the distribution chain are making money on that! :-/ If you are looking to create a "consumable" (disposable) product, then you still *do* need to pinch pennies! (My speech synthesizer has to fit *inside* something like that ALONGSIDE its existing functionality and at a comparable price point) OTOH, there are far too many fun/interesting things that are just not practical to undertake when hobbled with a resource-starved environment. And, once you get beyond a certain cost/complexity, the *added* costs of moving to even richer execution environments is insignificant! E.g., I recently doubled my clock speed (500M to 1G) and quadrupled the number of cores in the design. And, its still laughably "cheap"!
Reply by ●July 3, 20162016-07-03
Richard Damon wrote:> On 6/29/16 10:53 AM, Les Cargill wrote: >> >> >> 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. >> > > > The difference between a preemptive and a cooperative system is in a > cooperative system, a context switch to a different task only occurs a > defined points in each task (typically a blocking call or an explicit > yield). A preemptive system has the possibility of an interrupt event > causing a context switch at almost any point in the execution of a task > (there may be small regions, critical sections, which guard against such > possibilities). It isn't just timer ISRs that can cause the context switch.Couldn't agree more - by "signal difference" I meant something akin to "primary difference". Not the only difference. -- Les Cargill
Reply by ●July 3, 20162016-07-03
On 7/3/16 1:07 AM, Les Cargill wrote:> Richard Damon wrote: >> On 6/29/16 10:53 AM, Les Cargill wrote: >>> >>> >>> 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. >>> >> >> >> The difference between a preemptive and a cooperative system is in a >> cooperative system, a context switch to a different task only occurs a >> defined points in each task (typically a blocking call or an explicit >> yield). A preemptive system has the possibility of an interrupt event >> causing a context switch at almost any point in the execution of a task >> (there may be small regions, critical sections, which guard against such >> possibilities). It isn't just timer ISRs that can cause the context >> switch. > > Couldn't agree more - by "signal difference" I meant something akin to > "primary difference". Not the only difference. >I would describe the primary difference as 'Preemption', that a task can be switched at arbitrary points, not defined points. This is typically done with task context switch from ISRs. While the timer is a common event to cause a switch, it is by far not the most important. It is possible to make a (low performance) preemptive system using only timer context switches, but most systems with significant real time requirements need switches on other interrupts.
Reply by ●July 3, 20162016-07-03
Richard Damon wrote:> On 7/3/16 1:07 AM, Les Cargill wrote: >> Richard Damon wrote: >>> On 6/29/16 10:53 AM, Les Cargill wrote: >>>> >>>> >>>> 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. >>>> >>> >>> >>> The difference between a preemptive and a cooperative system is in a >>> cooperative system, a context switch to a different task only occurs a >>> defined points in each task (typically a blocking call or an explicit >>> yield). A preemptive system has the possibility of an interrupt event >>> causing a context switch at almost any point in the execution of a task >>> (there may be small regions, critical sections, which guard against such >>> possibilities). It isn't just timer ISRs that can cause the context >>> switch. >> >> Couldn't agree more - by "signal difference" I meant something akin to >> "primary difference". Not the only difference. >> > > I would describe the primary difference as 'Preemption', that a task can > be switched at arbitrary points, not defined points. This is typically > done with task context switch from ISRs.Yes.> While the timer is a common > event to cause a switch, it is by far not the most important. It is > possible to make a (low performance) preemptive system using only timer > context switches, but most systems with significant real time > requirements need switches on other interrupts.Even in a cooperative multitasker, an ISR may hit a semaphore or other object to make a thread ready. Because it's an ISR, it's quite painful to make this perform an actual context switch - this generally just marks a task or tasks as ready, the ISR finishes and the kernel then at some point does a context switch. A raw ISR has no "furniture" to do any context switching on. Some O/S offerings offer a "kernel thread" so that the kernel can maintain a context in which to do things. An ISR may return to the kernel thread, which can then do what you say. But even then, I'd cal that potentially a cooperative multitasker. I'm pretty sure VxWorks works in exactly this way - when configured as "cooperative", you still have a kernel thread that enforces these disciplines. And note well that this may account for any bias I show. Linux is, I believe, quite different but I've never had cause to fully understand it at that level. Linux depends on having A Lot Of CPU and there are a thousand heresies on what "high performance" means there. Language gets in the way, but an ISR will essentially request a context switch, not execute one. Er, at least in what I have seen espoused as best practices. Maybe some kernel does exactly what you say, but I'd have to do some measuring with a kernel like that before I was comfortable with it. Given that, the principal difference between preemptive and cooperative continues to be the timer interrupt being allowed to preempt between tasks. There's a lot of tradeoff between latency, jitter and (very roughly) determinism. I don't care for designs where events cause lots of other events. I prefer designs that keep counters for missed transitions and degrade gracefully, rather than treating the CPU as a zero sum resource. I also find nothing noble about trying to squeeze the last full measure of ... effort from a CPU. Finally, please understand that I've had to explain to people that assigning task priority by "well, that's more important" doesn't work, and that raising task priority doesn't make your task any faster :) This has pushed me to claim that "If you have to set the vector of task priorities a certain way for your system to work, it's broken." -- Les Cargill
Reply by ●July 3, 20162016-07-03
On 06/24/16 18:37, Tim Wescott wrote:> So, this is the third time in a month or so that I've needed to tell > someone "use a task loop" -- but I'm not sure if I can say "just Google > it". > > So: When I say "task loop" I mean that I'm _not_ using an RTOS, but > rather that I'm doing some small thing in a small processor, and > somewhere in my code there's a loop that goes: > > for (;;) > { > if (task_1_ready) > { > task_1_update(); > } > else if (task_2_ready) > { > task_2_update(); > } > else if (task_3_ready) > // et cetera > } > > The "task_n_ready" variables are set offstage (in an ISR, or by one of > the task_n_update functions) and reset within the tasks. > > So -- is there a common Google-able term for this? >What you describe is a round robin scheduler, with each task running to completion once it has something to do. It's a common technique where you don't want or need the complexity of a real time OS. For a slightly more formal solution, you can encapsulate that idea into a variable length table of function pointers, where tasks can be set sleeping, ready, running etc. Include a count of clock ticks that say how often the task runs to provide priority scheduling. Implement each task as a state machine, with simple message passing between tasks, always dependent on the next message to run and you have a compact and efficient real time OS applicable to any processor. The advantage is that it can all be written in C, with no fussy processor specific context switching code. You can do all that in not much more than a page of C :-)... Regards, Chris
Reply by ●July 3, 20162016-07-03
On Sun, 3 Jul 2016 12:39:30 -0500, Les Cargill <lcargill99@comcast.com> wrote:> >Finally, please understand that I've had to explain to people that >assigning task priority by "well, that's more important" doesn't work, >and that raising task priority doesn't make your task any faster :)I have long used the principle: Do not ask which task priority should be increased, but ask, which task priority can be _lowered_., works nicely.
Reply by ●July 3, 20162016-07-03
On 7/3/16 10:39 AM, Les Cargill wrote:> Richard Damon wrote: >> On 7/3/16 1:07 AM, Les Cargill wrote: >>> Richard Damon wrote: >>>> On 6/29/16 10:53 AM, Les Cargill wrote: >>>>> >>>>> >>>>> 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. >>>>> >>>> >>>> >>>> The difference between a preemptive and a cooperative system is in a >>>> cooperative system, a context switch to a different task only occurs a >>>> defined points in each task (typically a blocking call or an explicit >>>> yield). A preemptive system has the possibility of an interrupt event >>>> causing a context switch at almost any point in the execution of a task >>>> (there may be small regions, critical sections, which guard against >>>> such >>>> possibilities). It isn't just timer ISRs that can cause the context >>>> switch. >>> >>> Couldn't agree more - by "signal difference" I meant something akin to >>> "primary difference". Not the only difference. >>> >> >> I would describe the primary difference as 'Preemption', that a task can >> be switched at arbitrary points, not defined points. This is typically >> done with task context switch from ISRs. > > Yes. > >> While the timer is a common >> event to cause a switch, it is by far not the most important. It is >> possible to make a (low performance) preemptive system using only timer >> context switches, but most systems with significant real time >> requirements need switches on other interrupts. > > Even in a cooperative multitasker, an ISR may hit a semaphore or other > object to make a thread ready. Because it's an ISR, it's quite > painful to make this perform an actual context switch - this generally > just marks a task or tasks as ready, the ISR finishes and the kernel > then at some point does a context switch.In a cooperative system, an ISR can not cause a context switch, because then it isn't a cooperative system. The hallmark of a cooperative system is that context switches only occur at defined points.> > A raw ISR has no "furniture" to do any context switching on. >Actually, in my experience, most preemptive systems the context switch IS normally done in an interrupt context, and a task initiated context MAnyswitch either uses a software interrupt or the code emulates an interrupt. The ISR effectively changes which tasks stack is used to return to.> Some O/S offerings offer a "kernel thread" so that the kernel can > maintain a context in which to do things. An ISR may return to the > kernel thread, which can then do what you say. But even then, I'd > cal that potentially a cooperative multitasker. I'm pretty sure > VxWorks works in exactly this way - when configured as "cooperative", > you still have a kernel thread that enforces these disciplines.Many O/Ses will have a kernel thread for certain operations that don't need a full independent task. Every Preemptive Real Time system I know has the ability for an ISR to cause an immediate context switch. The key difference between a cooperative and a preemptive system is that in a cooperative system, user tasks don't need to worry as much about data sharing as other user tasks can only get in at defined points to change/access the data, on the other hand, every task needs to think about the needs of other tasks and makes sure that every task offers opportunity for other tasks to run often enough. In preemptive systems, you do need to worry about data sharing, as another task (of higher priority) might get in at any time, but tasks only need to worry about interfering with lower priority tasks (and you should establish priorities so this isn't normally an issue).> > And note well that this may account for any bias I show. Linux is, > I believe, quite different but I've never had cause to fully understand > it at that level. Linux depends on having A Lot Of CPU and there are a > thousand heresies on what "high performance" means there. > > Language gets in the way, but an ISR will essentially request a context > switch, not execute one. Er, at least in what I have seen espoused as > best practices. Maybe some kernel does exactly what you say, but I'd > have to do some measuring with a kernel like that before I was > comfortable with it.What I am familiar with for small Real Time OSes, the task context is saved as if the task was interrupted by an interrupt, and for an ISR to perform a context switch, it changes what stack to use to return.> > Given that, the principal difference between preemptive and cooperative > continues to be the timer interrupt being allowed to preempt between > tasks. >Absolutely WRONG. If the timer tasks can cause a context switch at the task level then you are preemptive, but the timer is no different than any other interrupt in this respect.> There's a lot of tradeoff between latency, jitter and (very roughly) > determinism. > > I don't care for designs where events cause lots of other events. > I prefer designs that keep counters for missed transitions and > degrade gracefully, rather than treating the CPU as a zero sum > resource. I also find nothing noble about trying to squeeze > the last full measure of ... effort from a CPU.If you can assume infinite CPU, then yes, you can be very sloppy with allocations. In most systems I deal with, while you do still design to fail gracefully, or as graceful as possible, 'missed' events tend to be a sign that your system is failing and not something to just idly try to gloss over. Sometimes it is a sign you need to attempt to shut down to a safe mode, or you report an error and back off on operation.> > Finally, please understand that I've had to explain to people that > assigning task priority by "well, that's more important" doesn't work, > and that raising task priority doesn't make your task any faster :) > > This has pushed me to claim that "If you have to set the vector of task > priorities a certain way for your system to work, it's broken." > >Yes, higher priority won't make a task execute any faster, it just says less tasks can get in your way. If a task is slow because it gets blocked by other tasks, raising its priority can help (but you need to watch out that raising the priority can cause it to interfere with other tasks).
Reply by ●July 4, 20162016-07-04
Richard Damon wrote:> On 7/3/16 10:39 AM, Les Cargill wrote: >> Richard Damon wrote: >>> On 7/3/16 1:07 AM, Les Cargill wrote: >>>> Richard Damon wrote: >>>>> On 6/29/16 10:53 AM, Les Cargill wrote: >>>>>> >>>>>> >>>>>> 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. >>>>>> >>>>> >>>>> >>>>> The difference between a preemptive and a cooperative system is in a >>>>> cooperative system, a context switch to a different task only occurs a >>>>> defined points in each task (typically a blocking call or an explicit >>>>> yield). A preemptive system has the possibility of an interrupt event >>>>> causing a context switch at almost any point in the execution of a >>>>> task >>>>> (there may be small regions, critical sections, which guard against >>>>> such >>>>> possibilities). It isn't just timer ISRs that can cause the context >>>>> switch. >>>> >>>> Couldn't agree more - by "signal difference" I meant something akin to >>>> "primary difference". Not the only difference. >>>> >>> >>> I would describe the primary difference as 'Preemption', that a task can >>> be switched at arbitrary points, not defined points. This is typically >>> done with task context switch from ISRs. >> >> Yes. >> >>> While the timer is a common >>> event to cause a switch, it is by far not the most important. It is >>> possible to make a (low performance) preemptive system using only timer >>> context switches, but most systems with significant real time >>> requirements need switches on other interrupts. >> >> Even in a cooperative multitasker, an ISR may hit a semaphore or other >> object to make a thread ready. Because it's an ISR, it's quite >> painful to make this perform an actual context switch - this generally >> just marks a task or tasks as ready, the ISR finishes and the kernel >> then at some point does a context switch. > > In a cooperative system, an ISR can not cause a context switch, because > then it isn't a cooperative system. The hallmark of a cooperative system > is that context switches only occur at defined points."Make ready" != "context switch".>> >> A raw ISR has no "furniture" to do any context switching on. >> > Actually, in my experience, most preemptive systems the context switch > IS normally done in an interrupt context,... which is not the same thing as an ISR... An actual context switch is a context switch - similar to things available in <ucontext.h> in Linux.> and a task initiated context > MAnyswitch either uses a software interrupt or the code emulates an > interrupt. The ISR effectively changes which tasks stack is used to > return to. >Those happen too.>> Some O/S offerings offer a "kernel thread" so that the kernel can >> maintain a context in which to do things. An ISR may return to the >> kernel thread, which can then do what you say. But even then, I'd >> cal that potentially a cooperative multitasker. I'm pretty sure >> VxWorks works in exactly this way - when configured as "cooperative", >> you still have a kernel thread that enforces these disciplines. > > Many O/Ses will have a kernel thread for certain operations that don't > need a full independent task. Every Preemptive Real Time system I know > has the ability for an ISR to cause an immediate context switch. >I've avoided this so far - but there may be serious mechanical problems with having an actual interrupt cause a context switch - outside of when a "software" interrupt might do such a thing. Interrupt handlers may have a different preamble or postamble ( or both ) than a blocking context switch or return from a blocking context switch. There may be a different stack signature. In addition, it may be necessary to allocate a stack just for a certain ISR. Of course there are hundreds of O/S out there on a great number of processors, and they're all quite different. I'll leave it at that. If you'd identify which O/S you're referring to, I'll look into it.> The key difference between a cooperative and a preemptive system is that > in a cooperative system, user tasks don't need to worry as much about > data sharing as other user tasks can only get in at defined points to > change/access the data, on the other hand, every task needs to think > about the needs of other tasks and makes sure that every task offers > opportunity for other tasks to run often enough.Both are good disciplines to have anyway :)> In preemptive systems, > you do need to worry about data sharing, as another task (of higher > priority) might get in at any time, but tasks only need to worry about > interfering with lower priority tasks (and you should establish > priorities so this isn't normally an issue). > >> >> And note well that this may account for any bias I show. Linux is, >> I believe, quite different but I've never had cause to fully understand >> it at that level. Linux depends on having A Lot Of CPU and there are a >> thousand heresies on what "high performance" means there. >> >> Language gets in the way, but an ISR will essentially request a context >> switch, not execute one. Er, at least in what I have seen espoused as >> best practices. Maybe some kernel does exactly what you say, but I'd >> have to do some measuring with a kernel like that before I was >> comfortable with it. > > What I am familiar with for small Real Time OSes, the task context is > saved as if the task was interrupted by an interrupt, and for an ISR to > perform a context switch, it changes what stack to use to return. >I have to admit - I haven't seen that variant except for one case - a custom job. And it took tracking whether the current context was interrupt or not, and it branched in rather ugly ways.>> >> Given that, the principal difference between preemptive and cooperative >> continues to be the timer interrupt being allowed to preempt between >> tasks. >> > Absolutely WRONG. If the timer tasks can cause a context switch at the > task level then you are preemptive, but the timer is no different than > any other interrupt in this respect. >Ah, you missed my point. Anyway...>> There's a lot of tradeoff between latency, jitter and (very roughly) >> determinism. >> >> I don't care for designs where events cause lots of other events. >> I prefer designs that keep counters for missed transitions and >> degrade gracefully, rather than treating the CPU as a zero sum >> resource. I also find nothing noble about trying to squeeze >> the last full measure of ... effort from a CPU. > > If you can assume infinite CPU, then yes, you can be very sloppy with > allocations. In most systems I deal with, while you do still design to > fail gracefully, or as graceful as possible, 'missed' events tend to be > a sign that your system is failing and not something to just idly try to > gloss over. Sometimes it is a sign you need to attempt to shut down to a > safe mode, or you report an error and back off on operation.If you can't assume *sufficient* CPU then you have other problems. Again, I'm constitutionally predisposed away from trying to squeeze every last cycle out of a processor in this day and age. YMMV.>> >> Finally, please understand that I've had to explain to people that >> assigning task priority by "well, that's more important" doesn't work, >> and that raising task priority doesn't make your task any faster :) >> >> This has pushed me to claim that "If you have to set the vector of task >> priorities a certain way for your system to work, it's broken." >> >> > > Yes, higher priority won't make a task execute any faster, it just says > less tasks can get in your way. If a task is slow because it gets > blocked by other tasks, raising its priority can help (but you need to > watch out that raising the priority can cause it to interfere with other > tasks).But in that case, it may be worth thinking hard about the design choices. -- Les Cargill
Reply by ●July 4, 20162016-07-04
On 7/3/16 9:57 PM, Les Cargill wrote:> Richard Damon wrote: >> On 7/3/16 10:39 AM, Les Cargill wrote: >>> Richard Damon wrote: >>>> On 7/3/16 1:07 AM, Les Cargill wrote: >>>>> Richard Damon wrote: >>>>>> On 6/29/16 10:53 AM, Les Cargill wrote: >>>>>>> >>>>>>> >>>>>>> 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. >>>>>>> >>>>>> >>>>>> >>>>>> The difference between a preemptive and a cooperative system is in a >>>>>> cooperative system, a context switch to a different task only >>>>>> occurs a >>>>>> defined points in each task (typically a blocking call or an explicit >>>>>> yield). A preemptive system has the possibility of an interrupt event >>>>>> causing a context switch at almost any point in the execution of a >>>>>> task >>>>>> (there may be small regions, critical sections, which guard against >>>>>> such >>>>>> possibilities). It isn't just timer ISRs that can cause the context >>>>>> switch. >>>>> >>>>> Couldn't agree more - by "signal difference" I meant something akin to >>>>> "primary difference". Not the only difference. >>>>> >>>> >>>> I would describe the primary difference as 'Preemption', that a task >>>> can >>>> be switched at arbitrary points, not defined points. This is typically >>>> done with task context switch from ISRs. >>> >>> Yes. >>> >>>> While the timer is a common >>>> event to cause a switch, it is by far not the most important. It is >>>> possible to make a (low performance) preemptive system using only timer >>>> context switches, but most systems with significant real time >>>> requirements need switches on other interrupts. >>> >>> Even in a cooperative multitasker, an ISR may hit a semaphore or other >>> object to make a thread ready. Because it's an ISR, it's quite >>> painful to make this perform an actual context switch - this generally >>> just marks a task or tasks as ready, the ISR finishes and the kernel >>> then at some point does a context switch. >> >> In a cooperative system, an ISR can not cause a context switch, because >> then it isn't a cooperative system. The hallmark of a cooperative system >> is that context switches only occur at defined points. > > "Make ready" != "context switch". > >>> >>> A raw ISR has no "furniture" to do any context switching on. >>> >> Actually, in my experience, most preemptive systems the context switch >> IS normally done in an interrupt context, > > ... which is not the same thing as an ISR... An actual context switch is > a context switch - similar to things available in <ucontext.h> in > Linux. >In every machine I have used, unless you are righting in very low level assembly code, the entry into an ISR begins with a 'context switch', where the context of the currently executing program is saved, and an interrupt context is started. It has to, as the ISR must not change the context of the running program. (Context is the environment that something is executing in). Often part of this context switch is built into the interrupt hardware, and some is in the preamble of the ISR. An OS may define some additional information that is part of its context for a user program, (your <ucontext.h>).>> and a task initiated context >> MAnyswitch either uses a software interrupt or the code emulates an >> interrupt. The ISR effectively changes which tasks stack is used to >> return to. >> > > Those happen too. > >>> Some O/S offerings offer a "kernel thread" so that the kernel can >>> maintain a context in which to do things. An ISR may return to the >>> kernel thread, which can then do what you say. But even then, I'd >>> cal that potentially a cooperative multitasker. I'm pretty sure >>> VxWorks works in exactly this way - when configured as "cooperative", >>> you still have a kernel thread that enforces these disciplines. >> >> Many O/Ses will have a kernel thread for certain operations that don't >> need a full independent task. Every Preemptive Real Time system I know >> has the ability for an ISR to cause an immediate context switch. >> > > I've avoided this so far - but there may be serious mechanical problems > with having an actual interrupt cause a context switch - outside of when > a "software" interrupt might do such a thing. > > Interrupt handlers may have a different preamble or postamble ( or both > ) than a blocking context switch or return from a blocking context > switch. There may be a different stack signature. In addition, it may > be necessary to allocate a stack just for a certain ISR. > > Of course there are hundreds of O/S out there on a great number of > processors, and they're all quite different. > > I'll leave it at that. If you'd identify which O/S you're referring to, > I'll look into it. >One OS I use a lot is FreeRTOS, a multi-platform real-time micro-kernel. This is the sort of OS I would expect in an embedded system. (In my mind, a system using an OS as big as linux is on the fringe of what is 'embedded').>> The key difference between a cooperative and a preemptive system is that >> in a cooperative system, user tasks don't need to worry as much about >> data sharing as other user tasks can only get in at defined points to >> change/access the data, on the other hand, every task needs to think >> about the needs of other tasks and makes sure that every task offers >> opportunity for other tasks to run often enough. > > Both are good disciplines to have anyway :) > >> In preemptive systems, >> you do need to worry about data sharing, as another task (of higher >> priority) might get in at any time, but tasks only need to worry about >> interfering with lower priority tasks (and you should establish >> priorities so this isn't normally an issue). >> >>> >>> And note well that this may account for any bias I show. Linux is, >>> I believe, quite different but I've never had cause to fully understand >>> it at that level. Linux depends on having A Lot Of CPU and there are a >>> thousand heresies on what "high performance" means there. >>> >>> Language gets in the way, but an ISR will essentially request a context >>> switch, not execute one. Er, at least in what I have seen espoused as >>> best practices. Maybe some kernel does exactly what you say, but I'd >>> have to do some measuring with a kernel like that before I was >>> comfortable with it. >> >> What I am familiar with for small Real Time OSes, the task context is >> saved as if the task was interrupted by an interrupt, and for an ISR to >> perform a context switch, it changes what stack to use to return. >> > > I have to admit - I haven't seen that variant except for one case - a > custom job. And it took tracking whether the current context was > interrupt or not, and it branched in rather ugly ways.Sounds like you are working on 'big' embedded systems, and either it doesn't support anything like real-time ability or more likely this operation is hidden from the user behind system calls the ISR does. (Do you even write ISRs?)> >>> >>> Given that, the principal difference between preemptive and cooperative >>> continues to be the timer interrupt being allowed to preempt between >>> tasks. >>> >> Absolutely WRONG. If the timer tasks can cause a context switch at the >> task level then you are preemptive, but the timer is no different than >> any other interrupt in this respect. >> > > Ah, you missed my point. Anyway...You miss my point.> >>> There's a lot of tradeoff between latency, jitter and (very roughly) >>> determinism. >>> >>> I don't care for designs where events cause lots of other events. >>> I prefer designs that keep counters for missed transitions and >>> degrade gracefully, rather than treating the CPU as a zero sum >>> resource. I also find nothing noble about trying to squeeze >>> the last full measure of ... effort from a CPU. >> >> If you can assume infinite CPU, then yes, you can be very sloppy with >> allocations. In most systems I deal with, while you do still design to >> fail gracefully, or as graceful as possible, 'missed' events tend to be >> a sign that your system is failing and not something to just idly try to >> gloss over. Sometimes it is a sign you need to attempt to shut down to a >> safe mode, or you report an error and back off on operation. > > If you can't assume *sufficient* CPU then you have other problems. > Again, I'm constitutionally predisposed away from trying to squeeze > every last cycle out of a processor in this day and age. YMMV. >Yes, you need sufficient CPU, but CPU time IS a zero-sum game, if you spend CPU time on one operation, it isn't available for another (as it is a limited resource). If you have an operation that needs to respond within 100usecs, you only have 100usecs of CPU available to spend to get there. You need to either manage what you do during that time period so that you meet that dead-line, or you need a much faster processor than you would otherwise need (which consume excess power which can be a bigger problem).>>> >>> Finally, please understand that I've had to explain to people that >>> assigning task priority by "well, that's more important" doesn't work, >>> and that raising task priority doesn't make your task any faster :) >>> >>> This has pushed me to claim that "If you have to set the vector of task >>> priorities a certain way for your system to work, it's broken." >>> >>> >> >> Yes, higher priority won't make a task execute any faster, it just says >> less tasks can get in your way. If a task is slow because it gets >> blocked by other tasks, raising its priority can help (but you need to >> watch out that raising the priority can cause it to interfere with other >> tasks). > > But in that case, it may be worth thinking hard about the design > choices. >The setting of priorities is not something to do lightly, but is a significant part of design.







