I have a reproducible crash when I quit my app. It doesn’t happen every time, but maybe one in ten runs. It seems to be related to using multiple threads - I’ve stripped almost everything out and made an example app that reproduces the crash - code below. I’ve reproduced the crash on two different machines, both Windows 7 64-bit, building SDL from the SDL2-2.0.3 source .zip linked on the site (the crash happens with the redistributable too, but there doesn’t seem to be any source for symbols so I’m building it myself to get a proper callstack). I’m building a Win32 console app in Debug.
The crashes can happen in various places, but it’s almost always in SDL’s dlmalloc code. Sometimes it comes from SDL_Quit, sometimes SDL_RunThread, and sometimes in other event handling code. Sometimes it’s freeing a bad pointer, but other times it’s while trying to get a free block. It’s fairly random.
The code is pretty straight-forward - it starts a few threads that simply wait on a semaphore, which the main thread signals after the esape key is pressed. Can anyone see something I’m doing wrong? Or might this be an issue on SDL’s side? This is my first time using SDL so I wouldn’t be surprised if I’m doing something I shouldn’t.
Code:
SDL_sem* killSignal;
SDL_sem* deadSignal;
SDL_atomic_t numAliveThreads;
int testcrashThread(void* data)
{
SDL_AtomicAdd(&numAliveThreads, 1);
// Just wait to be killed
SDL_SemWait(killSignal);
// Last alive thread sends the dead signal
int prevNumAliveThreads = SDL_AtomicAdd(&numAliveThreads, -1);
if(prevNumAliveThreads == 1)
{
SDL_SemPost(deadSignal);
}
return 0;
}
int main(int argc, char **argv)
{
//SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE);
// Set up SDL
SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_EVENTS);
SDL_Window* sdlWindow = SDL_CreateWindow("testcrash", 200, 200, 100, 100, SDL_WINDOW_SHOWN);
SDL_Renderer* sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
// Create sync prims
killSignal = SDL_CreateSemaphore(0);
deadSignal = SDL_CreateSemaphore(0);
SDL_AtomicSet(&numAliveThreads, 0);
// Kick off threads
unsigned numThreads = SDL_GetCPUCount();
for (unsigned t = 0; t < numThreads; t++)
{
SDL_Thread* thread = SDL_CreateThread(testcrashThread, NULL, NULL);
SDL_DetachThread(thread);
}
// Wait for escape to be hit
while(true)
{
SDL_Event e;
if(SDL_PollEvent(&e) && e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE)
{
break;
}
}
// Kill threads
for(unsigned t = 0; t < numThreads; t++)
{
SDL_SemPost(killSignal);
}
// Wait for them to die
SDL_SemWait(deadSignal);
// Clean up & shut down
SDL_DestroySemaphore(killSignal);
SDL_DestroySemaphore(deadSignal);
SDL_DestroyRenderer(sdlRenderer);
SDL_DestroyWindow(sdlWindow);
SDL_Quit();
return 0;
}
And an example callstack:
Code:
SDL2.dll!SDL_free_REAL(void * mem) Line 4379 C
SDL2.dll!SDL_RunThread(void * data) Line 294 C
SDL2.dll!RunThread(void * data) Line 85 C
SDL2.dll!RunThreadViaBeginThreadEx(void * data) Line 100 C
msvcr120d.dll!_callthreadstartex() Line 376 C
msvcr120d.dll!_threadstartex(void * ptd) Line 359 C
kernel32.dll!@BaseThreadInitThunk at 12
() Unknown
ntdll.dll!__RtlUserThreadStart() Unknown
ntdll.dll!__RtlUserThreadStart at 8
() Unknown