I continue to chase down a bug that manifests itself as an invalid array element value that seems most likely to be the result of a buffer overflow or out of bounds access. I’m in the process of writing a runtime audit function that will crawl all of my structures and array elements and detect apparent invalid data and log that. Since my arrays have elements that are themselves array element indices, such could easily be happening.
But, I have a general question that perhaps someone can answer, or point me towards the answer if it has already been answered. The question has some sub-questions.
What are the most frequently sources of memory corruption in Rack modules?
- Out of bounds array access?
- Module process() re-entrancy? What happens if process() is called before the previous call has completed processing?
- Other threads? Is a widget process()/step() function called on the same thread or on a different thread from the module process()? Can these two calls interfere with each other?
- Are there any caveats about changing parameters and user variables during execution, that could lead to one part of the module not knowing what the other part is doing or if the changes are complete and ready?
In my own experience writing to an array out-of-bounds.
As far as I know this is not possible in Rack.
Yes, widgets and modules live on different threads, you have to be careful about what to change from a widget in a module. Ideally you should only do atomic operations like setting a flag or something or you have to do some synchronization if necessary.
Does anyone use a mutex or similar in a module process() and a widget process()?
There has been a discussion about that topic not long ago: you shouldn’t use mutex or similar stuff on the audio thread, say in
Is the module widget a special case? I.E., I have a lot of run time processing occurring during run time in the module widget, including panel rendering and module structures member updates, etc. Updates occur in the module widget draw() function. Actually I have a TransparentWidget member of the module widget and this is where a lot of updates and rendering occur.
I could be mistaken, but my understanding of the Widget class, is that
draw() should be used purely for rendering, and any code updating
module should be written in
step() or in event handlers.
I have a mixture of data updates and rendering in both draw() and step(). How often are draw() and step() called? It would take quite a bit of rewrite to limit rendering to draw() and structure changes to step(). What is the reasoning for segregation in this manner? Would you expect any problems having such a mixture?
They are called both each frame. Only
Module::process is called each sample.
I’m definitely still learning. My module does no DSP audio processing but just uses the DSP timing via the process() function. Since no audio, really not much needs to be done at every step(), which is probably why I put almost everything in the draw() function. In process() I set flags to indicate variable changes and display dirty status and then I just update or render when something changes.
I do have a lot of variables changing during runtime, but I use the clock/timer functionality to only update the variables at certain times, such as at measure begin or on the beat etc. So, although there is quite a bit of data, there is no need to update it more than a few times per second. I thought something updates with the frame rate. I thought that was step() and it was used for low frequency UI updates. I guess I was wrong.
Ok, my mistake. Maybe the difference is related to the thread where the method is called ?
Actually it is not entirely true what I said. As far as I remember
Widget::step is called every frame,
Widget::draw is called every frame if the widget is currently visible in Rack. @Vortico may correct me if I’m wrong.
There was some details in this topic on why Widget should be completely stateless. With the forthcoming v2.0 rack will be able to run in headless mode therefore the widget will not be processing at all.
If you need to run code every
n sample to save up performances in your
process(), you can use the
dsp::clockDivider class as a helper
This is a valid requirement and there is
dsp::ClockDivider for use in
Module::process if something has not to be done for every sample, like updating LED status.
Keep in mind that in Rack v2 there will be a headless mode which runs modules without an active widget.
Edit: As @23volts said
Looks like I need to read up on v2 changes. Can you sum up simply what it means “Widget should be completely stateless”? Does this apply only to the module widget or to any widget? Will modules have to be specifically rewritten to run headless or with a GUI? I’ll take a look at dsp::clockDivider class. I’m just manually counting clock ticks from the DSP in process() and dividing.
What I mean by that is that any parameters or variable that are necessary for your module to function properly.
Stateless is a bit of an overstatement, as a button object may of course have a
on off state , but this state should be linked to the
parameter from the module. In short, the module holds the data/process them, and the moduleWidget, when needs to draw, refers to the
module data and not the other way around.
And yes for the
dsp::clockDivider it’s nothing fancy really.
I’ll have to think about that some more. I just wrote out a bare bones flow chart of my Meander module program logic and it took 4 pages. I’m still a beginner. Thanks for the info.
I think the idea is that the modules need to be able to run without the GUI widgets around. Which is how you should be writing your code already anyway. The GUI widgets should just be a “view” into the DSP module’s state and not have any particular custom state of their own.
I appreciate your offer and I will take you up on it if I cannot redesign Meander to be more array safe. I’m taking @23volts suggestion and experimenting with converting all of my C arrays to C++ std::array usage. I have approximately 1100 array expressions in the main Meander cpp file, so this is a challenge but obviously a big opportunity to make Meander array safe without address sanitizer since I am on Windows. The speed penalty will be worth it.
Is the source code on github? if you are getting an out of bounds error with that many array’s it would be a needle in a field of haystacks.