| Summary: | Bug when freeing a timer (during mutex lock) | ||
|---|---|---|---|
| Product: | SDL | Reporter: | Asfand Yar Qazi <ayqazi> |
| Component: | timer | Assignee: | Sam Lantinga <slouken> |
| Status: | RESOLVED FIXED | QA Contact: | Sam Lantinga <slouken> |
| Severity: | blocker | ||
| Priority: | P2 | CC: | ayqazi, terence1819 |
| Version: | 1.2.9 | ||
| Hardware: | x86 | ||
| OS: | Linux | ||
I'd like to get this fixed for SDL 1.2.10 release, if possible. I believe this is now fixed in Subversion. What was happening was a freed mutex was being waited on, after the timer subsystem had already been shut down. |
Hi boys'n'girls (ok, so basically boys...) I think there may be a bug when exclusive-locking a mutex when freeing a timer caused at the end of my application (I hope my terminology is correct - not too good at concurrency terminology, which is bad, because concurrency is teh futur.) I'm using the RUDL game library (SDL based game library for Ruby) and when my application window terminates and goes off the screen, process doesn't actually end - (i.e. I don't go back to my shell prompt). I have to manually terminate the process by pressing 'ctrl-c' (i.e. it hasn't crashed.) This is a fully recreatable bug, it happens ALMOST every time (sometimes the process actually terminates successfully, which is wierd.) Now, I've gdb'ed it while its in this non-terminated state, and here's some info (actually I used ddd but I've reproduced it using plain terminal mode gdb.) The actual script I run is irrelevant - as long as the script makes a SDL timer, this happens. By the way, this bug also happens exactly the same on SDL-1.2.8 as well as SDL-1.2.9. Amazingly, it DIDN'T happen on a 2.4 kernel with GLIBC 2.2.5 (i.e. before NPTL). Now I'm on the latest stable Gentoo. Here's a dump of a GDB session where I've noticed some strange stuff going on (note stuff inbetween '<' and '>' is my editing): $ gdb ../common/root/bin/ruby GNU gdb 6.4 Copyright 2005 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/tls/libthread_db.so.1". (gdb) set args test/linktest.rb (gdb) run Starting program: <snip>/ruby test/linktest.rb [Thread debugging using libthread_db enabled] [New Thread -1210422624 (LWP 12988)] (Init_RUDL()) (Starting video subsystem) (Starting audio subsystem) [New Thread -1217238096 (LWP 12991)] (Starting timer subsystem) [New Thread -1235706960 (LWP 12993)] (Starting TTF) (Reached RUDL_at_exit) [Thread -1217238096 (zombie) exited] (Stopping audio subsystem) (Stopping video subsystem) (Stopping TTF) (Quitting the rest of it) [Thread -1235706960 (zombie) exited] <I PRESS CONTROL-C HERE> Program received signal SIGINT, Interrupt. [Switching to Thread -1210422624 (LWP 12988)] 0xffffe410 in __kernel_vsyscall () (gdb) backtrace #0 0xffffe410 in __kernel_vsyscall () #1 0xb7f2117e in __lll_mutex_lock_wait () from /lib/tls/libpthread.so.0 #2 0xb7f1dfc7 in _L_mutex_lock_150 () from /lib/tls/libpthread.so.0 #3 0xb7f5bfd4 in ?? () from /lib/ld-linux.so.2 #4 0x08152fb0 in ?? () #5 0x00000001 in ?? () #6 0xbff575b0 in ?? () #7 0xb7f51542 in fixup () from /lib/ld-linux.so.2 #8 0xb7d39df6 in SDL_mutexP (mutex=0xfffffffc) at SDL_sysmutex.c:133 #9 0xb7d3a77e in SDL_RemoveTimer (id=0x826bdd8) at SDL_timer.c:221 #10 0xb7d5873d in freeEventTimer (freeMe=0xfffffffc) at rudl_timer.c:46 #11 0x08070eaa in rb_gc_call_finalizer_at_exit () at gc.c:1855 #12 0x08052e65 in ruby_finalize_1 () at eval.c:1488 #13 0x08066b73 in ruby_cleanup (ex=0) at eval.c:1523 #14 0x08066c31 in ruby_stop (ex=-4) at eval.c:1554 #15 0x08066c7f in ruby_run () at eval.c:1575 #16 0x080524e8 in main (argc=-4, argv=0xfffffffc, envp=0xbff578a0) at main.c:46 (gdb) frame 10 #10 0xb7d5873d in freeEventTimer (freeMe=0xfffffffc) at rudl_timer.c:46 46 SDL_RemoveTimer(freeMe); (gdb) l 41 } 42 43 ///////////////////////////////// EVENTTIMER 44 void freeEventTimer(SDL_TimerID freeMe) 45 { 46 SDL_RemoveTimer(freeMe); 47 } 48 49 Uint32 timerCallback(Uint32 interval, void *param) 50 { (gdb) frame 9 #9 0xb7d3a77e in SDL_RemoveTimer (id=0x826bdd8) at SDL_timer.c:221 221 SDL_mutexP(SDL_timer_mutex); (gdb) l 216 { 217 SDL_TimerID t, prev = NULL; 218 SDL_bool removed; 219 220 removed = SDL_FALSE; 221 SDL_mutexP(SDL_timer_mutex); 222 /* Look for id in the linked list of timers */ 223 for (t = SDL_timers; t; prev=t, t = t->next ) { 224 if ( t == id ) { 225 if(prev) { (gdb) l 210 205 if ( ! SDL_timer_threaded ) { 206 SDL_SetError("Multiple timers require threaded events!"); 207 return NULL; 208 } 209 SDL_mutexP(SDL_timer_mutex); 210 t = SDL_AddTimerInternal(interval, callback, param); 211 SDL_mutexV(SDL_timer_mutex); 212 return t; 213 } 214 (gdb) 215 SDL_bool SDL_RemoveTimer(SDL_TimerID id) 216 { 217 SDL_TimerID t, prev = NULL; 218 SDL_bool removed; 219 220 removed = SDL_FALSE; 221 SDL_mutexP(SDL_timer_mutex); 222 /* Look for id in the linked list of timers */ 223 for (t = SDL_timers; t; prev=t, t = t->next ) { 224 if ( t == id ) { (gdb) 225 if(prev) { 226 prev->next = t->next; 227 } else { 228 SDL_timers = t->next; 229 } 230 free(t); 231 --SDL_timer_running; 232 removed = SDL_TRUE; 233 list_changed = SDL_TRUE; 234 break; (gdb) 235 } 236 } 237 #ifdef DEBUG_TIMERS 238 printf("SDL_RemoveTimer(%08x) = %d num_timers = %d thread = %d\n", (Uint32)id, removed, SDL_timer_running, SDL_ThreadID()); 239 #endif 240 SDL_mutexV(SDL_timer_mutex); 241 return removed; 242 } 243 244 /* Old style callback functions are wrapped through this */ (gdb) frame 8 #8 0xb7d39df6 in SDL_mutexP (mutex=0xfffffffc) at SDL_sysmutex.c:133 133 if ( pthread_mutex_lock(&mutex->id) < 0 ) { (gdb) l 128 SDL_SetError("pthread_mutex_lock() failed"); 129 retval = -1; 130 } 131 } 132 #else 133 if ( pthread_mutex_lock(&mutex->id) < 0 ) { 134 SDL_SetError("pthread_mutex_lock() failed"); 135 retval = -1; 136 } 137 #endif (gdb) frame 9 #9 0xb7d3a77e in SDL_RemoveTimer (id=0x826bdd8) at SDL_timer.c:221 221 SDL_mutexP(SDL_timer_mutex); (gdb) l 216 { 217 SDL_TimerID t, prev = NULL; 218 SDL_bool removed; 219 220 removed = SDL_FALSE; 221 SDL_mutexP(SDL_timer_mutex); 222 /* Look for id in the linked list of timers */ 223 for (t = SDL_timers; t; prev=t, t = t->next ) { 224 if ( t == id ) { 225 if(prev) { (gdb) print SDL_timer_mutex $1 = (SDL_mutex *) 0x826a9d8 (gdb) frame 8 #8 0xb7d39df6 in SDL_mutexP (mutex=0xfffffffc) at SDL_sysmutex.c:133 133 if ( pthread_mutex_lock(&mutex->id) < 0 ) { (gdb) l 128 SDL_SetError("pthread_mutex_lock() failed"); 129 retval = -1; 130 } 131 } 132 #else 133 if ( pthread_mutex_lock(&mutex->id) < 0 ) { 134 SDL_SetError("pthread_mutex_lock() failed"); 135 retval = -1; 136 } 137 #endif (gdb) print mutex $2 = (SDL_mutex *) 0xfffffffc (gdb) quit The program is running. Exit anyway? (y or n) y Sorry if that's a bit long, I didn't want to leave anything out. Ideas?