Cleanup function executes twice
pryon_

I have an Asteroids clone written in C that I'm running on Linux (gcc) and Windows (MSVC).

It has a main and shutdown function as follows:

#SelectExpand
1int main(int argc, char **argv) 2{ 3 (void)argc; 4 (void)argv; 5 6 init_game(); 7 game_loop(); 8 shutdown(); 9 10 return 0; 11}

#SelectExpand
1void shutdown(void) 2{ 3 if (timer) 4 al_destroy_timer(timer); 5 if (dbuf) 6 al_destroy_bitmap(dbuf); 7 if (display) 8 al_destroy_display(display); 9 if (event_queue) 10 al_destroy_event_queue(event_queue); 11 if (theShip) { 12 free(theShip->bc); 13 free(theShip); 14 } 15 if (allEntities) 16 free(allEntities); 17 18 destroy_font(); 19 destroy_all_blasts(); 20 destroy_all_asteroids(); 21}

All the global game variables like timer, dbuf, display, etc. get initialized in the init_game() function. Quite straightforward so far. The problem arises when I exit the game and the shutdown function gets executed.

When it executes for the first time, everything goes fine, however, when the execution gets to return 0; in main, another thread steps in and starts to execute the shutdown() function from the beginning again, causing errors like this on Linux if al_destroy_timer() is the first one to get executed:

*** Error in `./blasteroids': double free or corruption (!prev): 0x0978d7a8 ***
Aborted (core dumped)

... or segfaults if I comment out allegro's own cleanup functions and just do the free() calls on my own variables. (On Windows/MSVC, there are no error messages popping up about this anywhere.)

If I run the game in gdb and type thread apply all backtrace when entering shutdown(), I see these badboys:

Thread 3 (Thread 0xb3123b40 (LWP 20966)):
#0  0xb7fdbbe0 in __kernel_vsyscall ()
#1  0xb7d333e6 in nanosleep () at ../sysdeps/unix/syscall-template.S:81
#2  0xb7f46c67 in al_rest () from /usr/lib/i386-linux-gnu/liballegro.so.5.2
#3  0xb7f0aada in ?? () from /usr/lib/i386-linux-gnu/liballegro.so.5.2
#4  0xb7f46dfe in ?? () from /usr/lib/i386-linux-gnu/liballegro.so.5.2
#5  0xb7c691aa in start_thread (arg=0xb3123b40) at pthread_create.c:333
#6  0xb7d69fde in clone () at ../sysdeps/unix/sysv/linux/i386/clone.S:122

Thread 2 (Thread 0xb794db40 (LWP 20962)):
#0  0xb7fdbbe0 in __kernel_vsyscall ()
#1  0xb7d622d1 in select () at ../sysdeps/unix/syscall-template.S:81
#2  0xb7f4b718 in _al_xwin_background_thread ()
   from /usr/lib/i386-linux-gnu/liballegro.so.5.2
#3  0xb7f46dfe in ?? () from /usr/lib/i386-linux-gnu/liballegro.so.5.2
#4  0xb7c691aa in start_thread (arg=0xb794db40) at pthread_create.c:333
#5  0xb7d69fde in clone () at ../sysdeps/unix/sysv/linux/i386/clone.S:122

Thread 1 (Thread 0xb794e900 (LWP 20961)):
#0  shutdown () at Blasteroids/main.c:83
#1  0x08049900 in main (argc=1, argv=0xbffff0b4) at Blasteroids/main.c:195

Do note that I don't create any threads in my code, neither by using allegro's thread module, nor with any platform-specific solution like pthreads.

When the function runs for the first time, one of these "invisible" allegro threads exits right after the timer is destroyed, but the other remains. When the function is done executing, the second one exits as well, getting back to my original, only thread just to execute the function again.

What causes this? Is there an elegant way to fix this? I could obviously use a flag and set it to true if shutdown() has already run once, but I'd have to enclose the function in an if statement, which is ugly and requires explanation in a comment.

Thanks for your help.

Elias

shutdown is the name of a standard libc function like printf, various places may call it. Try renaming yours.

pryon_

Well, that was it. Didn't know about that function up until now.
Thank you for the quick answer!

Thread #616751. Printed from Allegro.cc