This is all great, and kudos as ever to
@shawn for being a responsive library maintainer who works with "his" users to provide an incredibly high level of support for all abilities.
For users at the level of
@joepasquariello, who are prepared to dive deeply into the Teensyduino ecosystem, override
yield()
, and deal with the consequences, things are probably fine. Clearly if you're creating/using a co-operative OS you know to keep to the "one task, one piece of hardware" rule, so you're OK. I guess you could get into issues if you use QNEthernet with SPI-based Ethernet hardware plus, say, a SPI display, because then you'd have to
know SPI uses
yield()
(which the naïve user doesn't because it's not documented). And then your task has to run
both the display and Ethernet, which is messy.
If you're a library writer wanting to take advantage of facilities built into
cores
, then limitations
need to be documented
much better than they currently are.
EventResponder
is nearly 6 years old, yet still documented as "an experimental API". The documentation for
attach()
states "Calls from yield() allow use of Arduino libraries, String, Serial, etc." - as I've discovered, this is simply
not true in the general case: you
can't safely use serial, Wire, SPI, SD, ADC, USB ...
As an experimental API
EventResponder
should of course be open to carefully considered and ideally non-breaking changes. I've tried that, once, and basically it's a brick wall. In a way I hope that QNEthernet never gets rolled into Teensyduino, because the released version will almost certainly be out of date compared to current users' needs.
I'm still not sure what would be a minimal, useful, non-breaking set of changes to vanilla Teensyduino (putting slightly aside super-users like
@joepasquariello, while still trying to make sure their existing codebase is unlikely to break - I would never advocate that!). Some of the following, maybe?:
- embrace @jmarsh's idea of a separately named
default_yield()
, with yield()
weak and aliased to it, but ovverideable
default_yield()
has an optional parameter, showing which library code called it and is thus currently unsafe to re-enter: if the parameter is set, calls...
- ...
default_yield_from(where)
has an required parameter, showing which library code called it and is thus currently unsafe to re-enter
default_yield_from(where)
is effectively the existing yield()
code, plus maintenance of the flag(s) of which libraries have called it; existing yield()
prevents re-entering its body, with the exception of the EventResponder
response code
- all library calls to
yield()
modified to use the default_yield_from(where)
directly
- add a function to check whether you're executing from a
yield()
context, and which libraries' flag(s) are set
EventResponder
modified so that instantiations can flag libraries they use, and trigger processing will not execute pending events if the default_yield_from(where)
shows there will be a clash
I don't expect any of this to be done quickly, even if it's agreed to be a Good Thing, so I'm going to adopt
@jmarsh's suggestion of a derived-ish class, and re-invent the wheel. In the spirit of not breaking my existing API, having to explicitly call the
AudioEventResponder::runEvents()
function will not be the default, though it'd probably be the preferred route to robust code.
And I will document what I do.