So one of my testers recently uncovered an odd crash:
engine(32687,0x102a9e000) malloc: *** error for object 0x101809a00: pointer being freed was not allocated
With help from lldb, I was able to get a stack trace, implicating Allegro's AQueue routines:
frame #4: 0x00000001000e29c6 liballegro_audio.5.1.dylib`_aqueue_deallocate_voice(voice=0x00000001005359f0) + 70 at aqueue.m:212
209 static void _aqueue_deallocate_voice(ALLEGRO_VOICE *voice)
210 {
211 al_free(voice->extra);
-> 212 al_free(silence);
213 voice->extra = NULL;
214 }
215
frame #5: 0x00000001000e1a38 liballegro_audio.5.1.dylib`al_destroy_voice(voice=0x00000001005359f0) + 40 at kcm_voice.c:114
111 ASSERT(al_get_voice_playing(voice) == false);
112
113 /* We do NOT lock the voice mutex when calling this method. */
-> 114 voice->driver->deallocate_voice(voice);
115 al_destroy_mutex(voice->mutex);
116 al_destroy_cond(voice->cond);
117
The entire stack trace:
* thread #6: tid = 0x33298d, 0x00007fff96371866 libsystem_kernel.dylib`__pthread_kill + 10, stop reason = signal SIGABRT
* frame #0: 0x00007fff96371866 libsystem_kernel.dylib`__pthread_kill + 10
frame #1: 0x00007fff92a1c35c libsystem_pthread.dylib`pthread_kill + 92
frame #2: 0x00007fff96f58b1a libsystem_c.dylib`abort + 125
frame #3: 0x00007fff9161707f libsystem_malloc.dylib`free + 411
frame #4: 0x00000001000e29c6 liballegro_audio.5.1.dylib`_aqueue_deallocate_voice(voice=0x00000001005359f0) + 70 at aqueue.m:212
frame #5: 0x00000001000e1a38 liballegro_audio.5.1.dylib`al_destroy_voice(voice=0x00000001005359f0) + 40 at kcm_voice.c:114
frame #6: 0x0000000100164bab liballegro.5.1.dylib`_al_run_destructors(dtors=0x0000000100535980) + 155 at dtor.c:117
frame #7: 0x00000001000d8675 liballegro_audio.5.1.dylib`_al_kcm_shutdown_destructors + 21 at kcm_dtor.c:46
frame #8: 0x00000001000d74e8 liballegro_audio.5.1.dylib`al_uninstall_audio + 24 at audio.c:401
frame #9: 0x0000000100166108 liballegro.5.1.dylib`_al_run_exit_funcs + 136 at exitfunc.c:92
frame #10: 0x000000010016cac5 liballegro.5.1.dylib`al_uninstall_system + 21 at system.c:298
frame #11: 0x00007fff96f59794 libsystem_c.dylib`__cxa_finalize + 164
frame #12: 0x00007fff96f59a4c libsystem_c.dylib`exit + 22
frame #13: 0x00000001001a77be liballegro.5.1.dylib`+[AllegroAppDelegate app_main:] [inlined] call_user_main + 30 at osx_app_delegate.m:214
frame #14: 0x00000001001a77a4 liballegro.5.1.dylib`+[AllegroAppDelegate app_main:](self=<unavailable>, _cmd=<unavailable>, arg=<unavailable>) + 4 at osx_app_delegate.m:225
frame #15: 0x00007fff917d0d8b Foundation`__NSThread__main__ + 1318
frame #16: 0x00007fff92a1b899 libsystem_pthread.dylib`_pthread_body + 138
frame #17: 0x00007fff92a1b72a libsystem_pthread.dylib`_pthread_start + 137
frame #18: 0x00007fff92a1ffc9 libsystem_pthread.dylib`thread_start + 13
Note there is no application code in the callstack, only system calls and Allegro. This happens on shutdown.
It's worth noting that I already call al_uninstall_audio() myself. Allegro seems to be calling it again, which I suspect is the cause of the crash.
Did you try to remove that al_uninstall_audio call and see if it's really the culprit ?
Actually the plot thickens on this one. Notice how there's no user code on the stack at all? At the point the crash happens, there are still a few more steps in the shutdown process (going by my own console output), and yet here you can see that main() has already returned. I'm completely baffled.
All the atexit() functions are called after main() has returned.
That's the thing though--I have console output for my initialization and shutdown procedures, and at the point this crash happens, main shouldn't have returned yet. That's why I'm confused.
Why do you think it has returned already then?
Look at the callstack above.
Wow. Looks like the aqueue driver doesn't support multiple voices at all. I'll investigate if that's an API limitation, and if not, I'll implement it tomorrow.
Is that what's causing the crash, then? Still weird that it returned from main prematurely like that...
What's the point of more than one voice? I never understood that...
One case where it's useful is if you want to output, say, 8- and 16-bit audio in the same app (maybe your game has some sort of retro effect or something), you can't actually create an 8-bit mixer so you need to make the voice 8-bit. In this case you would need 2 voices.
A more practical use case is that Allegro won't let you attach a mixer to another mixer unless the formats match, whereas voices have automatic conversion. For this reason the Mixer objects in my JS-based minisphere game engine encapsulate both a mixer and a voice. Since the JS code could create a mixer of any format, attaching it to the default mixer is likely to fail. It's easier just to create multiple voices at that point and let the OS handle mixing them all together.
Alright, should be implemented now.
Is that what's causing the crash, then?
If there were two voices, then that 'silence' variable would be destroyed twice. I'm not sure about the early return business, but I think that's probably normal.
A more practical use case is that Allegro won't let you attach a mixer to another mixer unless the formats match
Probably wouldn't be hard to lift that restriction. The problem with voices is that there is no guarantee you will get more than one, so technically a game relying on it is faulty.
The fact remains that you still can't create an 8-bit mixer, so my example use case would be impossible then.