Closed dirkwhoffmann closed 3 months ago
Hopefully fixed in v5.0b5
I've added a locking mechanism to the audio unit. My suspicion is that the audio unit callbacks are still invoked after the audio unit has been shut down.
I finally figured out how to reproduce the shutdown bug, it is still present in v5.0.beta6
how to reproduce
runahead
to a value greater than 0delete
it only crashes with runahead enabled
(i.e. > 0), when runahead is disabled
(i.e. ==0) shutdown procedure behaves normal 😎.
UPDATE:
It is sufficient for the crash that runahead > 0
was active, i.e., even if it was disabled (set to 0) before the shutdown, it will still lead to a crash.
Confirmed! Thanks for making the bug reproducible!
I'm already on his trail... stay tuned...
Here is what's happening: Class Keyboard
has an overloaded =
operator:
Keyboard& operator= (const Keyboard& other) {
CLONE_ARRAY(kbMatrixRow)
CLONE_ARRAY(kbMatrixCol)
CLONE_ARRAY(kbMatrixRowCnt)
CLONE_ARRAY(kbMatrixColCnt)
CLONE(shiftLock)
CLONE(pending)
return *this;
}
Variable pending
is a RingBuffer
which has the following members:
template <class T, isize capacity> struct RingBuffer
{
// Element storage
T *elements = new T[capacity]();
// Read and write pointers
isize r, w;
...
The bug is that RingBuffer
does not overload the =
operator itself. The default implementation copies all members over which means that elements
points to the same memory area after the assignment. When run-ahead is active, we end up with two emulator instances sharing the same elements
array. The app crashes with a malloc double-free error when destructing the second instance during shutdown.
The fix is to provide a custom =
operator like this:
RingBuffer& operator= (const RingBuffer& other) {
memcpy(elements, other.elements, capacity);
r = other.r;
w = other.w;
return *this;
}
very beautiful !
Fixed in v5.0b7
To my shame, I haven't managed to implement a safe shutdown procedure for years. It still crashes from time to time on exit (e.g., today):