Thoughts about Handling of Complexity

Status
Not open for further replies.
Yield only calls EventResponder.

Not exactly...
Code:
void yield(void)
{
    static uint8_t running=0;
    if (!yield_active_check_flags) return;    // nothing to do
    if (running) return; // TODO: does this need to be atomic?
    running = 1;


    // USB Serial - Add hack to minimize impact...
    if (yield_active_check_flags & YIELD_CHECK_USB_SERIAL) {
        if (Serial.available()) serialEvent();
        if (_serialEvent_default) yield_active_check_flags &= ~YIELD_CHECK_USB_SERIAL;
    }

#if defined(USB_DUAL_SERIAL) || defined(USB_TRIPLE_SERIAL)
    if (yield_active_check_flags & YIELD_CHECK_USB_SERIALUSB1) {
        if (SerialUSB1.available()) serialEventUSB1();
        if (_serialEventUSB1_default) yield_active_check_flags &= ~YIELD_CHECK_USB_SERIALUSB1;
    }
#endif
#ifdef USB_TRIPLE_SERIAL
    if (yield_active_check_flags & YIELD_CHECK_USB_SERIALUSB2) {
        if (SerialUSB2.available()) serialEventUSB2();
        if (_serialEventUSB2_default) yield_active_check_flags &= ~YIELD_CHECK_USB_SERIALUSB2;
    }
#endif

    // Current workaround until integrate with EventResponder.
    if (yield_active_check_flags & YIELD_CHECK_HARDWARE_SERIAL) HardwareSerial::processSerialEventsList();

    running = 0;
    if (yield_active_check_flags & YIELD_CHECK_EVENT_RESPONDER) EventResponder::runFromYield();

It calls everything what is "active".
Active however is for Hardware serial for example every port where "begin()" was called. So it's enough that the port is active. Does not matter if there was an event or not.
For USB, this means. As soon as you call Serial.begin, yield_active_check_flags it never zero and the shortcut with the early return does not work anymore. So, most programs are affected.

Edit:
Oops, its worse:
Code:
#ifdef USB_TRIPLE_SERIAL
uint8_t yield_active_check_flags = YIELD_CHECK_USB_SERIAL | YIELD_CHECK_USB_SERIALUSB1 | YIELD_CHECK_USB_SERIALUSB2; // default to check USB.
extern const uint8_t _serialEventUSB2_default;    
extern const uint8_t _serialEventUSB1_default;    

#elif defined(USB_DUAL_SERIAL)
uint8_t yield_active_check_flags = YIELD_CHECK_USB_SERIAL | YIELD_CHECK_USB_SERIALUSB1; // default to check USB.
extern const uint8_t _serialEventUSB1_default;    

[B]#else
uint8_t yield_active_check_flags = YIELD_CHECK_USB_SERIAL; // default to check USB.
#endif[/B]

It does not matter wether USB is active. YOu can use "Audio" as USB type - it will still run that code.

It seems zhe check for zero is superfluent, as YIELD_CHECK_USB_SERIAL is always set(?) Or gets the flag deleted somewhere? Might be.
Edit: Ok, probably after the first check.
 
Last edited:
..and its buggy. If you don't read the Serial data it gets called again and again and again.. From my understanding an event should be called ONCE - at least the name "event" suggests that.
That flag does not work. Try this:
Code:
extern uint8_t yield_active_check_flags;

void serialEvent()
{
  Serial.print("Event!");
}

void setup() {
 Serial.begin(9600); 
}

void loop() {
  Serial.println(yield_active_check_flags, HEX);
  delay(500);
}
Watch the running "1"... And then send a character.

After that.. add " while(Serial.available()) Serial.read();" to the "event". Watch the still running "1".

-> Its a "PollResponder" with massive overhead. Even if you DONT use it.
 
Last edited:
I see the discussion has gotten into details of EventResponder. In my case yield() is overridden to (only) do a cooperative task switch, so my question for Paul was whether and where the core could contain calls to yield() to allow for that purpose. I don't intend to try to mix the use of EventResponder and RTOS.
 
I wouldn't call that detail. Is affects 100% (ALL) programs and makes them unnecessary slower, and is helpful for maybe 1 of 10000 (nobody uses it)
In my case yield() is overridden to (only) do a cooperative task switch,
As said above, overriding yield() is not enough to get rid of it.
so my question for Paul was whether and where the core could contain calls to yield()
is does.
 
Code:
Suche "yield();" (20 Treffer in 15 Dateien von 155 gesucht)
  C:\Arduino\hardware\teensy\avr\cores\teensy4\delay.c (1 Treffer)
    Zeile 62:         yield();
  C:\Arduino\hardware\teensy\avr\cores\teensy4\HardwareSerial.cpp (3 Treffer)
    Zeile 235:     while (transmitting_) yield();  // wait for buffered data to send
    Zeile 530:     while (transmitting_) yield(); // wait
    Zeile 570:             yield(); // wait
  C:\Arduino\hardware\teensy\avr\cores\teensy4\main.cpp (1 Treffer)
    Zeile 54:         yield();
  C:\Arduino\hardware\teensy\avr\cores\teensy4\Stream.cpp (2 Treffer)
    Zeile 36:     yield();
    Zeile 49:     yield();
  C:\Arduino\hardware\teensy\avr\cores\teensy4\usb_flightsim.cpp (1 Treffer)
    Zeile 766:         yield();
  C:\Arduino\hardware\teensy\avr\cores\teensy4\usb_joystick.c (1 Treffer)
    Zeile 96:                 yield();
  C:\Arduino\hardware\teensy\avr\cores\teensy4\usb_keyboard.c (1 Treffer)
    Zeile 545:         yield();
  C:\Arduino\hardware\teensy\avr\cores\teensy4\usb_midi.c (1 Treffer)
    Zeile 163:         yield();
  C:\Arduino\hardware\teensy\avr\cores\teensy4\usb_mouse.c (1 Treffer)
    Zeile 160:                 yield();
  C:\Arduino\hardware\teensy\avr\cores\teensy4\usb_mtp.c (2 Treffer)
    Zeile 138:         yield();
    Zeile 176:         yield();
  C:\Arduino\hardware\teensy\avr\cores\teensy4\usb_rawhid.c (2 Treffer)
    Zeile 110:         yield();
    Zeile 135:         yield();
  C:\Arduino\hardware\teensy\avr\cores\teensy4\usb_seremu.c (1 Treffer)
    Zeile 277:             yield();
  C:\Arduino\hardware\teensy\avr\cores\teensy4\usb_serial.c (1 Treffer)
    Zeile 362:             yield();
  C:\Arduino\hardware\teensy\avr\cores\teensy4\usb_serial2.c (1 Treffer)
    Zeile 344:             yield();
  C:\Arduino\hardware\teensy\avr\cores\teensy4\usb_serial3.c (1 Treffer)
    Zeile 363:             yield();
 
Okay, as usual no answer from Paul, and i guess Kurt (who did that) does not see the problem :)
The issue is, currently the yield_active_check_flags is used as something like "enabled".
That's not correct. At least, it wasn't intended to be used like this, when i proposed this variable.
Instead, it should mean "Do we have an event here?". Then, everything would work *much* better, and with way less overhead - at least the part in yield(). And the zero check at the beginning would suddenly make sense (now it is totally useless - its always != 0)
Also, resetting the individual bits isn't needed , and could be replaced by yield_active_check_flags=0. -> even less overhead.



So.. have fun... I know it will never be fixed and stay as it is for some years more (please prove me wrong).
 
Last edited:
so my question for Paul was whether and where the core could contain calls to yield() to allow for that purpose.

The core library does already have yield() calls. But more should probably be added. For example, we probably need yield() when Serial.available() will return zero.


I don't intend to try to mix the use of EventResponder and RTOS.

I tried in msg #16 to explain the idea that future hypothetical RTOS would replace the non-RTOS EventResponder with a thin compatibility wrapper, similar to how we now use SdFat with a thin SD.h wrapper for compatibility with existing programs. The idea is programs written today with EventResponder would automatically make use of RTOS mailbox / queue / notification... in that hypothetical future.

Which is why I asked the specific question, what features of EventResponder are believed to be incompatible with someday replacing EventResponder with a thin compatibility wrapper that just uses RTOS mailbox or queue or notification.


I also tried to explain in msg #16 the reality of today's situation, where I'm catching up to a massive backlog of work that stalled while PJRC ran short-staffed due to Oregon's shelter-in-place order and other pandemic issues, which happened just as we had released Teensy 4.1 and a massive amount of software development was planned. Major developments are happening. Code security, MTP improvements, preliminary Arduino CLI & 2.0 packages have happened over the last several months. But there is still a huge backlog. I tried to explain in msg #16 that at this moment I'm *only* looking for specific feedback (my question above) and even minor work on EventResponder is not expected soon. Yet still, this thread is now filled with message complaining about low-level implementation details with a rather impatient tone, as if the words I wrote on msg #16 where invisible!

I still do want to help well reasoned opinion about specific EventResponder which may not be feasible to someday implement with a thin compatibility wrapper for RTOS APIs like mailboxes & queues. I would also appreciate a list of additional places yield() should be called. But I am NOT working on yield() and EventResponder at this time. Now is not the time to redo the active status stuff or significantly change the code. I will NOT merge such changes at this time. Too many far more important projects are pending, and I've learned the hard way many times how "quick" changes tend to become a major time sink. At this moment, I'm really only interested to hear about my original question for the sake of long-term planning.
 
Just for ref : Arduino-Events

2017 and three pages ... some other links on the forum with more recent notes/examples are out there ... cooperative multitasking ...
-> this will be important in some fashion when PJRC gets the DEV cycles to make a new Dual Core Teensy - and the world settles, and the chips can go into production. g00gle announced a non-shipping NXP 1176 item probably because they aren't in general production/release yet.

Would be fun to see a version of TeensyThreads set to be purely co-op multitasking. No timing swapping and interrupt overhead and spawned threads act like little loop()'s that run until they get bored or stuck. Though that wouldn't help on dual core without accounting for that.

As far as ESP32 and RTOS - it is a major PIA. ESP32's at 240 MHz are really SLOW as Frank noted.
> T_3.6 at 180 MHz counts 2 million loops/second
-->: Loops/sec=2044011
> An ESP32 Pico D4 at 240 MHz under Arduino can get a whopping 700 thousand loops/second - okay maybe not 400K in this sketch
-->: Loops/sec=392321
> An ESP32 Pico D4 at 240 MHz under Arduino with freeRTOS can get a whopping 1 million loops/second when the second core adds in a couple hundred thousand
-->: 678435 ... << A cnt...349549 ... << B cnt... == 1027984 ... L cnt >> 23 ... ALL cnt... == 1028007

BUT: When ESP32 Boot or 'helpful checking' doesn't casually stop execution (with a perpetual repeat restart) with a wonderfully painful DUMP of stuff I can't get deciphered doing 'Arduino'
>> It gets worse with RTOS threads enabled when some obscure WDT stops and repeat restarts the MCU trying to add their PSRAM with the above dual core loop test

> So early project code was edit of the resetReason code to STOP the perpetual restart if the prior start failed - then the SerMon spew at least stopped to be read ...

All the PJRC type resetHandler code for ESP is handled in their world of code starting slower and doing who knows what - scans all PSRAM to be good before allowing it to start or begin setup()
 
Would be fun to see a version of TeensyThreads set to be purely co-op multitasking. No timing swapping and interrupt overhead and spawned threads act like little loop()'s that run until they get bored or stuck. Though that wouldn't help on dual core without accounting for that.

years ago, I played with cooperative multitasking
https://github.com/WMXZ-EU/wmxzCore/tree/master/examples/testScheduler

Idea was to have audio processing running a lower priorities than acquisition isr.
It worked and only requirement was that sum of processing tasks where in average faster than acquisition (obviously).
 
years ago, I played with cooperative multitasking
https://github.com/WMXZ-EU/wmxzCore/tree/master/examples/testScheduler

Idea was to have audio processing running a lower priorities than acquisition isr.
It worked and only requirement was that sum of processing tasks where in average faster than acquisition (obviously).

Cool.

TeensyThreads has the means to save stack and jump tasks. Seems if not done with pre-emption ... yanking cycles away ... in the mid ... because fixed time expired ... dle of a process ... but rather when a task was at a 'natural' pausing point with operation completed, or when awaiting input. I suppose TeensyThreads could run with a 1,000+ ms time slice and tasks could do thread.yield() at their leisure and accomplish the avoidance of unexpected task switching?

Seems this Co-OP is how RTOS works in general? Except when having two symmetric cores helps, but each task has to surrender cycles to the lower PRI background task, or it halts the MCU with a WDT stop. It is still Arduino drivers lacking re-entry, etc. And a TON of overhead when two 240 Mhz cores cycle loop() calls at half the rate of a 180 MHz T_3.6 (where some 25% of the CPU [half of one core] doing something else even then without active radio usage).

This isn't the EventResponder model, that allows for registered Events to move data [Uarts, etc], or other, than can take advantage of otherwise idle time. Maybe EventResponder 'yield()' could be extended with eventYield() to perform TeensyThread type thread.yield() ...

Wanted to add to prior post - the frustrating freeRTOS has current REV number of 10.2 or 10.4 ... so this is after many revisions. I spent some too many 'WWW Search' minutes trying to understand why the ESP32 at hand is having trouble touching PSRAM - even a call to ESP.getPsramSize() causes system HALT - reboot loop :(, and that is after the PSRAM was validated on startup to have a usable 4MB of accessible space [that is known because unpowered PSRAM halts Startup with message that PSRAM didn't pass the test], that works when no RTOS threads are used. Haven't done much with ESP since early ESP8266 ... expected more from ESP32 by now ... not ongoing grief of p#35. ... Teensy ROCKS ... but doesn't Roll with a radio ...
 
Just for ref : Arduino-Events

Would be fun to see a version of TeensyThreads set to be purely co-op multitasking. No timing swapping and interrupt overhead and spawned threads act like little loop()'s that run until they get bored or stuck. Though that wouldn't help on dual core without accounting for that.

Thanks a lot for the link to Events, it is nice to know, what the insiders are speaking of...

In my opinion, you can quite easily set up some cooperative multitasking with TeensyThreads. You only have to use a large time slice (for this task) and use threads.yield() to switch to the next task. Actually I am doing this for my high priority threads. Something like
while(triggerVariable==0) threads.yield();
The idea is, that if triggered, this high priority thread has enough time to complete it's job.

I am wondering, what percentage of my troubles, are caused by lack or hidden or hard to understand documentation. And some sort of assurance, that it was checked, that these components are working together. This is a valuable statement, because proved working libraries are a very good reason to buy Teensy.

What about the second suggestion of a proved Reference Configuration?
 
Hm, now had a look into EventResponder. This seems to be related to improve code efficiency in some cases as it would avoid polling and be faster.
For me this is something new and therefore would need very good examples and very good documentation.

I have learned in the past few years, that inventing something new or improving something makes it necessary to document it really well to make it useful. Often the workload for good documentation is higher, than for the implementation itself. So if you have got limited resources it might be better for everyone to implement something, with already existing and accessible documentation. (Yes, this is boring.)

All in all I don't see the discussion about EventResponder to be helpful for beginners or advanced beginners in the context of the question: How to Handle Complexity more easily?
 
Which is why I asked the specific question, what features of EventResponder are believed to be incompatible with someday replacing EventResponder with a thin compatibility wrapper that just uses RTOS mailbox or queue or notification.
IMHO ... the only "compatibility" problem that might be needed to address how EventResponder works in a hypothetical RTOS environment is to ensure the context it executes in is that of the task that created it (or, possibly, another task selected when the callback is registered ... or changeable on the fly...).

To me, the advantage of EventResponder is its "fire and forget" nature. Its event-to-callback latency depends only on how often yield() is executed: this may actually be the elephant in the room here, but once the user has taken steps to ensure this is the case, there's no additional requirement to write carefully-placed code to check a mailbox/queue/notification every so often. In the current environment my preferred solution is to ensure loop() exits as often as possible: with an RTOS, a high-priority task doing nothing but polling the EventResponder queue at an acceptable period would perform the same function. I'm assuming a pre-emptive RTOS, of course.


I also tried to explain in msg #16 the reality of today's situation, where I'm catching up to a massive backlog of work that stalled while PJRC ran short-staffed due to Oregon's shelter-in-place order and other pandemic issues, which happened just as we had released Teensy 4.1 and a massive amount of software development was planned. Major developments are happening. Code security, MTP improvements, preliminary Arduino CLI & 2.0 packages have happened over the last several months. But there is still a huge backlog. I tried to explain in msg #16 that at this moment I'm *only* looking for specific feedback (my question above) and even minor work on EventResponder is not expected soon. Yet still, this thread is now filled with message complaining about low-level implementation details with a rather impatient tone, as if the words I wrote on msg #16 where invisible!

I still do want to help well reasoned opinion about specific EventResponder which may not be feasible to someday implement with a thin compatibility wrapper for RTOS APIs like mailboxes & queues. I would also appreciate a list of additional places yield() should be called. But I am NOT working on yield() and EventResponder at this time. Now is not the time to redo the active status stuff or significantly change the code. I will NOT merge such changes at this time. Too many far more important projects are pending, and I've learned the hard way many times how "quick" changes tend to become a major time sink. At this moment, I'm really only interested to hear about my original question for the sake of long-term planning.
I fear that at least part of the "rather impatient tone" stems from a combination of "I'm catching up to a massive backlog of work" and "I will NOT merge such changes". A lot of people can see issues and have solutions, and it's frustrating for those people when even "minor work" and "quick changes" languish in the PR queue for years without any (apparent) attempt to triage them into "no, never", "look at later", and "yes, merge now" categories.

You obviously have to run a profitable business in order to provide a living for you and the PJRC staff. Out here in forum-land we're mostly in this for our own amusement, I'd guess, which doesn't necessarily speak well for code quality, so it's easy to see how even "minor" changes could "become a major time sink". I don't have a solution to this. Even if there were an up-to-date "Guide to getting your PR merged", and a cohort of trusted lieutenants who would rather test others' code than write their own, managing that would still be a drain on your time.
 
Thanks a lot for the link to Events, it is nice to know, what the insiders are speaking of...

In my opinion, you can quite easily set up some cooperative multitasking with TeensyThreads. You only have to use a large time slice (for this task) and use threads.yield() to switch to the next task. Actually I am doing this for my high priority threads. Something like
while(triggerVariable==0) threads.yield();
The idea is, that if triggered, this high priority thread has enough time to complete it's job.

Nice, that the p#37 idea of large/non-limiting time slice is a used/tested feature of TeensyThreads for CO-OP tasking. As noted that seems to be what freeRTOS under Arduino does - and if not appeased by enough yielding in 3 or 5 seconds ... the Processor is halted, and that seems to be 300ms for some monitoring on 'interrupt' processing.

TeensyThreads as CO-OP could be enhanced/improved no doubt with an implementation of Paul's p#16 noted: freertos.org/RTOS-task-notifications.html - as well as all the other stuff that goes with that, beyond mutex as TeensyThreads has now.
> and, of course, when a Dual Core Teensy comes online some method to use the NXP's MCU processor 'overlord' features will need to map into that for using that hardware feature to allow efficient CO-OP between the non-symmetric cores.
> Paul and others have gotten the NXP 1170 EVB board - and Paul has made some start, but the chips are not yet shipping, and his time has been diverted by pandemic side effects.

I am wondering, what percentage of my troubles, are caused by lack or hidden or hard to understand documentation. And some sort of assurance, that it was checked, that these components are working together. This is a valuable statement, because proved working libraries are a very good reason to buy Teensy.

What about the second suggestion of a proved Reference Configuration?

Can more detail on these notes/questions help to clarify what is meant/needed?
 
@h4yn0nnym0u5e - indeed the list of things to get to before addressing anything presented here was likely the reason for the 'caveat' of not now but 'future' to see progress.

One good reason might be if the next Teensy with dual asymmetric cores somehow ships in 2022, any implementation will need to take into account the essential use and limitations of NXP's inbuilt 'Message Unit' and semaphore {assuming those are the right parts of the 'System Control' at hand} hardware implementation. So making a perfect solution now that isn't supported by those elements would require appropriate changes for production inter-processor functionality and full utility of both cores.
 
Hm, now had a look into EventResponder. This seems to be related to improve code efficiency in some cases as it would avoid polling and be faster.
For me this is something new and therefore would need very good examples and very good documentation.

I have learned in the past few years, that inventing something new or improving something makes it necessary to document it really well to make it useful. Often the workload for good documentation is higher, than for the implementation itself. So if you have got limited resources it might be better for everyone to implement something, with already existing and accessible documentation. (Yes, this is boring.)
EventResponder was new to me, too. But it's sufficiently simple that I could manage without the documentation, read the code, and get it to do what I wanted. Obviously that's not going to be the case for everyone, for every library: I have two questions languishing in these forums without answers, where I simply can't see what's going on / what I'm "doing wrong", and no-one has (apparently) an answer.

What I should have done, of course, is document EventResponder as I looked into it, and put that in as a PR!

All in all I don't see the discussion about EventResponder to be helpful for beginners or advanced beginners in the context of the question: How to Handle Complexity more easily?
Such is the nature of a forum discussion, I'm afraid...

But to go back to your OP: an RTOS would be great (I'd love it), but the existing libraries simply aren't written to support one, so you The User have a realistic choice of having an RTOS (as noted elsewhere, several have been ported) and doing without many of the libraries, or doing without an RTOS and picking your way through the rich ecosystem of subtly-incompatible libraries. At the moment, I choose the latter, but neither is "easy".

As for a Reference Setup, I believe (I could be wrong) there are simply too many libraries and use cases for such a thing to be possible. I don't think there's even a table or database anywhere which documents (that word again) minor and major incompatibilities, let alone a reference example which includes (nearly) everything in a single working system. If documentation did exist, it would almost certainly be outdated by now, as the one thing even less likely to get done than documentation is maintenance of the documentation...
 
@h4yn0nnym0u5e - indeed the list of things to get to before addressing anything presented here was likely the reason for the 'caveat' of not now but 'future' to see progress.

One good reason might be if the next Teensy with dual asymmetric cores somehow ships in 2022, any implementation will need to take into account the essential use and limitations of NXP's inbuilt 'Message Unit' and semaphore {assuming those are the right parts of the 'System Control' at hand} hardware implementation. So making a perfect solution now that isn't supported by those elements would require appropriate changes for production inter-processor functionality and full utility of both cores.
I guess that depends on Paul's and your definition of a "good" reason. Clearly a chunk of time has to be taken to support forthcoming elements that will be "forced" on the Teensy community, such as Arduino 2.0. But a lot of effort is also expended on things that aren't of interest to me (like code security, to take one example), and it's very hard for me to judge how much damage to PJRC's business there would be if they were not done, and PR and documentation maintenance were given a higher priority.

Likewise iMX-RT1170 support. I don't need it, no-one can be sure they can get supplies this year, so why do any more for now than keep an eye on the situation, acquire demo boards as they become available, and try hard not to roll in incompatible changes? Possible answer - because it's more fun to grab an early release and have a play with it... I'm not against Paul doing that, it's his business and he can set the priorities, but plenty of people are still using Teensy 3.x quite happily (if they can get them), and Teensy 4.x is surely insanely powerful enough for most people for at least the next few months.

All of which is totally OT - sorry cebersp!
 
And some sort of assurance, that it was checked, that these components are working together. This is a valuable statement, because proved working libraries are a very good reason to buy Teensy.

Historically, libraries which actually work together has been one of Teensy's main features, beyond just faster hardware and optimizations on the software side. Over and over on this forum, when people encounter combinations of libraries which don't work when used together, a *lot* of work has gone into solving those problems. It is never ending work which never reaches a perfect goal, but over time many small incremental improvements do add up.

EventResponder was mainly inspired by the USB host work which happened in the ~2 years following Teensy 3.6 release, with the goal of allowing fully interrupt event driven EHCI driver code (compare against other USB host work on Arduino, mostly polling). Its main purpose was to have callbacks that avoid running any Arduino sketch, so we could have almost everything "just work" even with USB host running.

Many things I wanted to do with EventResponder got cut short around mid-2018 to focus on IMXRT, which is why it's stuck in a sort of not-used-much, not-documented limbo.


I am wondering, what percentage of my troubles, are caused by lack or hidden or hard to understand documentation.
....
I have learned in the past few years, that inventing something new or improving something makes it necessary to document it really well to make it useful. Often the workload for good documentation is higher, than for the implementation itself. So if you have got limited resources it might be better for everyone to implement something, with already existing and accessible documentation. (Yes, this is boring.)

Indeed, lack of documentation is probably the biggest problem right now.


A lot of people can see issues and have solutions, and it's frustrating for those people when even "minor work" and "quick changes" languish in the PR queue for years without any (apparent) attempt to triage them into "no, never", "look at later", and "yes, merge now" categories.

Yes, it is very frustrating, and I'm truly sorry for how terribly far behind everything has gotten over these last couple years. With every new Teensy model has come a year or more of software backlog, but the release of Teensy 4.0 in September 2019 and Teensy 4.1 in May 2020 turned out to be pretty much worst case timing.

Your work in particular with the audio connection destructor is near the top of my list of important contributions to merge for 1.57.


But a lot of effort is also expended on things that aren't of interest to me (like code security, to take one example), and it's very hard for me to judge how much damage to PJRC's business there would be if they were not done, and PR and documentation maintenance were given a higher priority.

Code security was by far the most commonly requested missing feature after Teensy 4.0 & 4.1 release. Well, maybe 2nd place. Debug is #1.


All in all I don't see the discussion about EventResponder to be helpful for beginners or advanced beginners in the context of the question: How to Handle Complexity more easily?

Well, the honest but unpleasant reality of today's situation is EventResponder never really left "experiment" status. It was also not really intended for direct use by Arduino sketches. The main envisioned application was within interrupt-based libraries like USBHost_t36 to interact with Arduino sketches without running any user sketch code from interrupt context.
 
The core library does already have yield() calls. But more should probably be added. For example, we probably need yield() when Serial.available() will return zero.

Thanks, Paul. That's all I was hoping for. I will start to put together a list of places where I think yield() would be useful, and I can test on my own. I won't do any formal requests for changes until you say it's time.

@defragster said:

TeensyThreads has the means to save stack and jump tasks. Seems if not done with pre-emption ... but rather when a task was at a 'natural' pausing point with operation completed, or when awaiting input. Seems this Co-OP is how RTOS works in general?

Yes, exactly. An RTOS that is priority-based or time-sliced is preemptive. In a cooperative RTOS (some people would say the two words do not belong together), each task runs until it voluntarily yields(). The great benefit is that issues with non-reentrant code and shared data are avoided. The programmer still needs to exercise care, and make sure that no task runs too long without yielding, but things are greatly simplified. Interrupts work exactly the same in both preemptive and cooperative RTOS. The difference is that in a preemptive RTOS, an ISR that calls an RTOS function, such as posting data to a queue, or calling the RTOS tick function, can result in a task switch at interrupt level. In a cooperative system, task switches occur only at task level. Even if the ISR calls an RTOS function that makes some data available for processing, the ISR returns to the task that was running prior to the interrupt, and the task switch only occurs when that task calls yield().
 
As far as ESP32 and RTOS - it is a major PIA. ESP32's at 240 MHz are really SLOW as Frank noted.
> T_3.6 at 180 MHz counts 2 million loops/second
As long you don't add a simple, empty serialEvent() or a "real event"
However, a "loops/second" is so far away from something I'd call a benchmark... not worth to spend even a second on it.

BUT: When ESP32 Boot or 'helpful checking' doesn't casually stop execution (with a perpetual repeat restart) with a wonderfully painful DUMP of stuff I can't get deciphered doing 'Arduino'
that's not a problem of ESP - it's your problem. It works good for me. On my Platformio a human readable dump gets displayed automatically. It show in which function, in which line somethin crashed and why. Automatically. You just need to enable it. You are not used to that... perhaps spend more time outside the Teesy-universe?
Debugging takes maybe 2% of the time you need to find the same problem on T.

gets worse with RTOS threads enabled when some obscure WDT
LOL. The WDT is - surprise - it's documented. I know, it's confusing.. "docs"--you've never seen this on Teensy. And it prevents an endless hang. (The most usual crash on T4..)

> So early project code was edit of the resetReason code to STOP the perpetual restart if the prior start failed - then the SerMon spew at least stopped to be read ...
It's also not a problem of the esp chip if it does not run on a experiental board by a dev who never read the basic datasheets (the board has basic schematic faults as you told me via mail) and a chiprev that is officially not recommended as has PSRAM problem which are documented in the errata.

. Teensy ROCKS
and crashes silently... you wait... and after some minutes you realize that it stopped working.. . yes that rocks.. If a least a by default running ("obscure") *g*) WDT would restart it.. so that you have a chance to notice... hm. No.
The cool new feature to report hangs does work in a few cases only.

But I don't think it's useful to do ESP or Teensy bashing... lol... i thought the "CPU" or "OS War" was 20 Years ago?! But if you like, we can continue. How fast is T's WLAN? *ggg*
 
Last edited:
The core library does already have yield() calls. But more should probably be added.
To be really useful it has to be called by external, third-party libraries, too. Otherwise it may happen that it does not get called for several seconds.
I'm really only interested to hear about my original question for the sake of long-term planning.
Make sure there is a way to disable it in a way that it does not use resources like the mailboxes if an rtos needs them. and perhaps rename yield() (or find an other way) as yield() is (as far i know) needed by most rtos.
I pretty sure nobody wants both - EventResponder AND an rtos.
Then, - this has nothing to do with rtos or not, please stop using these global interrupt disables.

Edit: (sorry for repetition) please fix yield() first - before changing anything else - so that it returns early if no event happened - regardless of "enabled" or not (the information "enabled" is not even needed). Just make it as fast as possible. It worth to spend time for it.
 
Last edited:
As long nothing disables interrupts, the highest priority one has deterministic timing. Wether it has an rtos or not. The problem is not the OS - it's the Teensy core (and libs)

Well except caching issues (disabling all caching can have a hit on performance) and probably some other smaal thing (hardware eeprom emulation for example). Off course if you run at a higher priority than the highest interrupt level of the rtos you could hang the rtos. I suspect that it runs a high priority watchdog above you're highest allowed interrupt level to avoid that.
 
To be really useful it has to be called by external, third-party libraries, too. Otherwise it may happen that it does not get called for several seconds.

Here is the definition of yield() in Arduino (hooks.c) and the only use I can find, in wiring.c function delay(). I'm sure many (most? almost all?) libraries don't use it. If the Teensy core and some key libraries use yield() this way, that would be great. It can be overridden to do EventResponder stuff, or a cooperative RTOS, or both.

Code:
/* Empty yield() hook. This function is intended to be used by library writers 
 * to build libraries/sketches that support cooperative threads. It's defined as
 * a weak symbol and can be redefined to implement a cooperative scheduler.
 */
static void __empty() { // Empty }
void yield(void) __attribute__ ((weak, alias("__empty")));

void delay(unsigned long ms) {
	uint32_t start = micros();
	while (ms > 0) {
		yield();
		while ( ms > 0 && (micros() - start) >= 1000) {
			ms--;
			start += 1000;
		}
	}
}
 
Status
Not open for further replies.
Back
Top