Crash on Exit

I’m experiencing an intermittent crash upon exiting Rack. The crash report on my Mac looks like this:

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libc++abi.dylib               	0x00007fff6bd7f165 __dynamic_cast + 187
1   com.vcvrack.rack              	0x0000000102d343b9 rack::app::RackWidget::clear() + 265 (RackWidget.cpp:172)
2   com.vcvrack.rack              	0x0000000102d34599 rack::app::RackWidget::~RackWidget() + 25 (RackWidget.cpp:99)
3   com.vcvrack.rack              	0x0000000102d649e1 rack::widget::Widget::clearChildren() + 113
4   com.vcvrack.rack              	0x0000000102d648e3 rack::widget::Widget::~Widget() + 35 (Widget.cpp:14)
5   com.vcvrack.rack              	0x0000000102d6568e rack::widget::ZoomWidget::~ZoomWidget() + 14 (ZoomWidget.hpp:10)
6   com.vcvrack.rack              	0x0000000102d649e1 rack::widget::Widget::clearChildren() + 113
7   com.vcvrack.rack              	0x0000000102d648e3 rack::widget::Widget::~Widget() + 35 (Widget.cpp:14)
8   com.vcvrack.rack              	0x0000000102d64abe rack::widget::Widget::~Widget() + 14 (Widget.cpp:10)
9   com.vcvrack.rack              	0x0000000102d649e1 rack::widget::Widget::clearChildren() + 113
10  com.vcvrack.rack              	0x0000000102d648e3 rack::widget::Widget::~Widget() + 35 (Widget.cpp:14)
11  com.vcvrack.rack              	0x0000000102d33ece rack::app::RackScrollWidget::~RackScrollWidget() + 14 (RackScrollWidget.hpp:12)
12  com.vcvrack.rack              	0x0000000102d649e1 rack::widget::Widget::clearChildren() + 113
13  com.vcvrack.rack              	0x0000000102d648e3 rack::widget::Widget::~Widget() + 35 (Widget.cpp:14)
14  com.vcvrack.rack              	0x0000000102d37c7d rack::app::Scene::~Scene() + 45 (Scene.cpp:45)
15  com.vcvrack.rack              	0x0000000102cdc469 rack::App::~App() + 25 (app.cpp:29)
16  com.vcvrack.rack              	0x0000000102cdc5aa rack::appDestroy() + 26 (app.cpp:58)
17  com.vcvrack.rack              	0x0000000102d00a11 main + 1617 (main.cpp:215)
18  com.vcvrack.rack              	0x0000000102c98034 start + 52

No mention of any of my code, but I’m guessing this is caused by one of the two new modules I’m developing:

ComputerscareBlank.cpp source code

ComputerscareBlankExpander.cpp source code

I’ve got a few guesses:

  1. Implement destructors for my classes. I admit I have absolutely no idea when I’m supposed to be doing this and when not. When I look at other plugins, it’s totally mysterious to me when and why destructors are implemented.
  2. Free the expander message buffer as suggested in Module.hpp. In this case do I free the buffer in the mother module, the expander, or both?

If you intend to receive messages from an expander, allocate both message buffers with identical blocks of memory (arrays, structs, etc). Remember to free the buffer in the Module destructor.

Thanks in advance for any help with this and for helping to reduce my C++ ignorance.

Definitely free the expander buffer in the same module that created it.

And definitely do not try to free it in the other.

1 Like

These modules actually communicate in both directions.

Mother:

struct ComputerscareBlank : ComputerscareMenuParamModule {
    // other module variables...
    float leftMessages[2][11] = {};

Expander:

struct ComputerscareBlankExpander : Module {
    // other module variables...
    float rightMessages[2][11] = {};

So, I’m assuming each module ought free the buffer it created… like this?

Mother:

~ComputerscareBlank() {
    free(leftMessages);
}

Expander:

~ComputerscareBlankExpander() {
    free(rightMessages);
}

Thank you for the reply!

The rules for deletion aren’t that complicated. If you allocate it, you need to delete it. The only thing that makes it confusing is that Rack itself will delete any UI widgets you add, like buttons, sliders, I/O jacks.

If you suspect your expander buffers are the culprit, just remove the code the frees them. If they crash doesn’t go away, then it’s not that.

If you run address sanitizer on your plugin it will most likely tell you what’s up. It’s not too difficult to run on platforms besides windows.

3 Likes

This seems totally reasonable, but looking at the source code for Fundamental SEQ3 (just as a representative example that we’re all familiar with):

struct SEQ3 : Module {
	//params, lights, inputs, etc...

	bool running = true;
	dsp::SchmittTrigger clockTrigger;
	dsp::SchmittTrigger runningTrigger;
	dsp::SchmittTrigger resetTrigger;
	dsp::SchmittTrigger gateTriggers[8];
	/** Phase of internal LFO */
	float phase = 0.f;
	int index = 0;
	bool gates[8] = {};

This module does not implement its own destructor method, and I’m assuming that means that none of these member variables are deleted. Is this a problem?

Thank you very much for the reply and for the suggestions to resolve my problem, I appreciate it!

No, no problem. You need to delete/free objects if allocated by „new“ or a similar operator.

2 Likes

you are declaring the objects on the stack of your module they will be automatically deleted when your module will be gone

1 Like

ah, good Q. short answer - probably not. There is a fancy name for this, and I don’t remember it. But the deal here is:

  • Module will be destroyed by VCV when your plugin is removed, and that wil cause SEQ3 to be destroyed.
  • member variables of simple types (like bool) never need to be destroyed, They didn’t cause a malloc/new when they were created.
  • Objects of a user defined type, like SchmittTrigger will also be destroyed automatically by the destructor of SEQ3. You should assume that if SchmittTrigger did any allocations, that it will free them itself in its own destructor.

So, when do you need do so something?

`class foo {
public:
    foo() : m_bar(new bar()) {}
    ~foo() { delete m_bar; }
private:
    bar* m_bar;
}`

That’s why a lot of people don’t like to put “naked” pointers in their classes - you need to remember to delete them, and you will forget sometimes. The cool kids would do it like this:

`class foo {
public:
    foo()  { m_bar = std::make_shared<bar>(); }
private:
    std::shared_ptr<bar> m_bar;
} `

This way the shared pointer will “remember for you” to clean up. Actually, the cooler kids would use std::unique_ptr in this case, but I’m not that cool.

Bottom line - if you malloc something you better free it. It it’s just an object (non-pointer) in your class it will get destroyed as part of object destruction. The language guarantees it.

Oh, and here is the fancy phrase I couldn’t remember: Resource acquisition is initialization - Wikipedia

1 Like

sorry - I could not get the three tick formatting to work, above. makes the fake code difficult to read. actually, the forum software also changed the code, above, at least on chrome. It should say

foo = std::make_shared<bar>()

But it seems to drop the word bar. [update] well, I put in triple ticks and single ticks. Looks ugly, but better than before.

1 Like

I’m confused by the fact that you have a parent module declaring a pair of left messages AND an expander declaring a pair of right messages.

Normally only one of the modules would declare the pair of messages. And then the parent and expander swap that pair of messages.

I can’t see the rest of your code to know what you are doing, but it looks odd.

And other commenters are correct, you shouldn’t be using free at all on these, because you’ve not allocated them using malloc

edited to expand on this a bit

Arbitrary memory referred to using pointers can be allocated from the heap using malloc and then released again using free

Arbitrary classes referred to using pointers can be created using new and should be destroyed by using delete *

variables and classes referred to directly typically handled automatically. All the member variables that you have pointed out above will be released when the containing object is destroyed. That includes your examples from SEQ3 and your own float arrays.

*Typically widgets in VCVRack are created by you using new, but when you attach them to the scene tree using methods like addChild VCVRack takes on the responsibility for deleting them

2 Likes

Thank you for this, the examples you provided are very helpful!

Thanks for the reply! Yours and the other replies in this thread have helped very much to de-mystify this for me. Unfortunately I haven’t been able to reproduce my bug in HUNDREDS of attempts so… the struggle continues.

1 Like