Basic (?) C++ destructor + interrupt question

h4yn0nnym0u5e

Well-known member
Hi folks

This is directly-related to Teensy only in that I've been banging my head against this wall for a few days with getting some Audio library objects to die quietly and not take the whole system with them! I've tried Googling but not really found much that's relevant. Do bear in mind my C++ understanding is pretty superficial at the moment: I'm pretty experienced at embedded C, but I'm closer to bare metal there.

So, background, just in case. I'm working on dynamic audio objects, where they can be created and destroyed in a running system with new and delete, or by making them automatic variables in a function. Obviously all useful object classes are (a) derived from the AudioStream class, and maybe others, and (b) provide an update() function that is called under interrupt every 2.9ms, if the object's "active" flag is set. I believe I've got the infrastructure working OK, so on object destruction connections get unlinked, pending audio blocks are handed back to the system etc., which is mostly taken care of in the ~AudioStream() code as it's essentially common to all objects. Some classes use audio blocks which the base AudioStream isn't aware of, and those need their own destructor code to return them to the pool, to avoid "block leaks". The unlinking process is fairly hairy, and interrupts are thus internally disabled while that's going on, using __disable_irq() and __enable_irq(), similar to other places in the code.

My understanding of the C++ destructor process is that destructors get called in turn in "reverse order", so last-created-first-destroyed, derived class destructor before base, etc.. Only after this process has finished is the object's memory freed up and possibly corrupted, and / or other actions taken by the C++ run-time system. As destructors operate on the object being destroyed, I'd presume it still exists while the destructor "housekeeping" is executing.

What I have found is that if the whole destruction process is not protected by wrapping the delete in Audio[No]Interrupts() calls, or by setting the active flag to false in the derived class's destructor (doing this in ~AudioStream is too late, apparently), then an audio interrupt occurring prior to the __disable_irq() in the ~AudioStream() destructor can cause the object's update() call never to return. I don't understand this, because although the destruction process has started, essentially at this point it's surely just another piece of foreground code, and as very little of my destructor code has run, none of which will have put the data structure in an unsafe state, I would expect simply to get one last-gasp execution of the update() code before the destruction resumes and completes. Actually what happens is update() is called, and never returns. CrashReport yields no information.

While it's not vital, it's a bit of a pain to mandate every Audio class provides a destructor with active = false in it; wrapping every delete in Audio[No]Interrupts() calls is tedious for the programmer, and in any case isn't possible for objects instantiated as automatic variables in a function.

Any ideas? I'd be more than happy with a solution that works at the AudioStream base class level.

Cheers

Jonathan
 
I was trying to understand better the audio system as well, and was looking at the audiostream class in the Teensy 4 core (at the repo master branch)

Is the dynamic lib at another folder? because there is no destructor defined for the AudioStream base class.
 
I was trying to understand better the audio system as well, and was looking at the audiostream class in the Teensy 4 core (at the repo master branch)

Is the dynamic lib at another folder? because there is no destructor defined for the AudioStream base class.
So far the Dynamic Audio Objects are only available in a branch of a fork I made of the Audio library, along with some associated modifications to the Teensy 3 and 4 cores. You need both to have freely creatable and destructible AudioStream and AudioConnection objects. The thread on this can be found at https://forum.pjrc.com/threads/66840-Roadmap-quot-Dynamic-Updates-quot-any-effort-going-on - every so often I find a bug or make an improvement, and if I'm vaguely happy with it I'll post there. Paul has made noises about rolling it into the mainstream Teensyduino library, but it looks as if more urgent matters have delayed that for the time being. If you give it a go, please do report back.

As it happens, I did solve the problem this thread is about, but had long since forgotten I'd asked! The inputQueue array for an AudioStream-derived object is in that derived object, not the base AudioStream class, so although the base class has a pointer to it, by the time its destructor runs the pointer is invalid. Most of the time the invalid memory hasn't been re-used, but if it has, and the base class tries to e.g. check the allocated blocks are freed up, Bad Things occur. So I had to do a bit of a re-write there...
 
Ups, I misread the date, I keep forgetting we are not in 2021 anymore :D

I remember reading a thread about your modifications, and while is supper interesting to be able to connect/disconnect nodes at runtime, for now I will stick with the current static implementation, at it seems smaller and fits my use case. I just need a circular buffer where to put audio data directly, and is continuously read and written.

Oh and btw, are there any tests in the teensy library? or even an interest to making some? I see some room to improve the current implementation (without adding any new features) but it would be better to have tests to make sure I am not breaking anything.
 
Where does the time go, indeed :) Though we're not yet in November (or indeed even in April, which my UK brain keeps reading as the OP date!).

True, the dynamic library isn't for everyone, though I don't think it actually takes much more code space than the static one, and it should work exactly the same if you want it to.

AFAIK there's no official test framework for the Teensy libraries. I did have a quick play running tests of some audio objects in non-real time, but haven't really pursued it. The trouble with "improvements" is everyone seems to want something different: bit depth, floating point, higher sample rates, massive object counts ... the list goes on!
 
Paul has made noises about rolling it into the mainstream Teensyduino library, but it looks as if more urgent matters have delayed that for the time being. If you give it a go, please do report back.

Would you believe I was actually planning to do this a few days ago? But then I got distracted by a ton of PJRC business stuff all week.

Today I'm looking at this and several other audio library contributions. Hope to merge most in the coming days, and merge the USB host MSC stuff, then start 1.57 beta testing with that (and everything else from the last few months) as 1.57-beta1.


Oh and btw, are there any tests in the teensy library?

Currently no software tests exist. At one point I considered setting up a server for custom github action which would cause various examples to run on pairs of boards, one synthesizing audio and the other analyzing it. But like so many other ideas, just getting Teensy 4.0 & 4.1 released, then PJRC surviving the pandemic & chip shortages, and many libraries in need of updates (like Wire slave mode) has caused so many ideas to be suspended for years.


The trouble with "improvements" is everyone seems to want something different: bit depth, floating point, higher sample rates, massive object counts ... the list goes on!

Yes indeed, so much this!
 
About the testing environment, I would start with unit tests mocking up the rest. For example, simple testing that connecting/disconnecting AudioStreams doesn't leave any dangling pointers. I am not sure how valuable that would be though, as some stuff like disabling irqs at the right time needs at least basic emulation of the target hardware.

About the improvements, I didn't meant to add any new features, just simplifying the current implementation, but I am kinda scare to mess up something that isn't broken just for sake of improving readability.
 
Would you believe I was actually planning to do this a few days ago? But then I got distracted by a ton of PJRC business stuff all week.

Today I'm looking at this and several other audio library contributions. Hope to merge most in the coming days, and merge the USB host MSC stuff, then start 1.57 beta testing with that (and everything else from the last few months) as 1.57-beta1.
I would indeed believe that ... and that you then got "distracted" :) something about having a business to run so people can eat? No worries at this end, right is better than rushed any day...

I've just merged in (I think) the latest updates on both cores and Audio, and have been doing a bit of testing today. Haven't found any obvious issues, but I can't test the Teensy 4.x PWM at the moment, due to a lack of capacitors to make a proper output network.
 
Back
Top