diff options
Diffstat (limited to 'src/server/locks.c')
-rw-r--r-- | src/server/locks.c | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/src/server/locks.c b/src/server/locks.c new file mode 100644 index 0000000..71a1845 --- /dev/null +++ b/src/server/locks.c @@ -0,0 +1,306 @@ +/* + * locks.c + * + * Created on: 16.07.2013 + * Author: sr + */ + +#include "locks.h" +#include "helper.h" +#include "../shared/timing.h" + +#ifdef _DEBUG +#define MAXLOCKS (SERVER_MAX_CLIENTS * 2 + SERVER_MAX_ALTS + 200 + SERVER_MAX_IMAGES) +#define MAXTHREADS (SERVER_MAX_CLIENTS + 100) +#define LOCKLEN 60 +typedef struct +{ + void *lock; + ticks locktime; + char locked; + pthread_t thread; + int lockId; + char name[LOCKLEN]; + char where[LOCKLEN]; +} debug_lock_t; + +typedef struct +{ + pthread_t tid; + ticks time; + char name[LOCKLEN]; + char where[LOCKLEN]; + +} debug_thread_t; + +int debugThreadCount = 0; + +static debug_lock_t locks[MAXLOCKS]; +static debug_thread_t threads[MAXTHREADS]; +static int init_done = 0; +static pthread_spinlock_t initdestory; +static int lockId = 0; +static pthread_t watchdog = 0; +static dnbd3_signal_t* watchdogSignal = NULL; + +static void *debug_thread_watchdog(void *something); + +int debug_spin_init(const char *name, const char *file, int line, pthread_spinlock_t *lock, int shared) +{ + if ( !init_done ) { + memset( locks, 0, MAXLOCKS * sizeof(debug_lock_t) ); + memset( threads, 0, MAXTHREADS * sizeof(debug_thread_t) ); + pthread_spin_init( &initdestory, PTHREAD_PROCESS_PRIVATE ); + init_done = 1; + } + int first = -1; + pthread_spin_lock( &initdestory ); + for (int i = 0; i < MAXLOCKS; ++i) { + if ( locks[i].lock == lock ) { + logadd( LOG_ERROR, "Lock %p (%s) already initialized (%s:%d)\n", (void*)lock, name, file, line ); + exit( 4 ); + } + if ( first == -1 && locks[i].lock == NULL ) first = i; + } + if ( first == -1 ) { + logadd( LOG_ERROR, "No more free debug locks (%s:%d)\n", file, line ); + pthread_spin_unlock( &initdestory ); + debug_dump_lock_stats(); + exit( 4 ); + } + locks[first].lock = (void*)lock; + locks[first].locked = 0; + snprintf( locks[first].name, LOCKLEN, "%s", name ); + snprintf( locks[first].where, LOCKLEN, "I %s:%d", file, line ); + pthread_spin_unlock( &initdestory ); + return pthread_spin_init( lock, shared ); +} + +int debug_spin_lock(const char *name, const char *file, int line, pthread_spinlock_t *lock) +{ + debug_lock_t *l = NULL; + pthread_spin_lock( &initdestory ); + for (int i = 0; i < MAXLOCKS; ++i) { + if ( locks[i].lock == lock ) { + l = &locks[i]; + break; + } + } + pthread_spin_unlock( &initdestory ); + if ( l == NULL ) { + logadd( LOG_ERROR, "Tried to lock uninitialized lock %p (%s) at %s:%d\n", (void*)lock, name, file, line ); + debug_dump_lock_stats(); + exit( 4 ); + } + debug_thread_t *t = NULL; + pthread_spin_lock( &initdestory ); + for (int i = 0; i < MAXTHREADS; ++i) { + if ( threads[i].tid != 0 ) continue; + threads[i].tid = pthread_self(); + timing_get( &threads[i].time ); + snprintf( threads[i].name, LOCKLEN, "%s", name ); + snprintf( threads[i].where, LOCKLEN, "%s:%d", file, line ); + t = &threads[i]; + break; + } + pthread_spin_unlock( &initdestory ); + if ( t == NULL ) { + logadd( LOG_ERROR, "Lock sanity check: Too many waiting threads for lock %p (%s) at %s:%d\n", (void*)lock, name, file, line ); + exit( 4 ); + } + const int retval = pthread_spin_lock( lock ); + pthread_spin_lock( &initdestory ); + t->tid = 0; + pthread_spin_unlock( &initdestory ); + if ( l->locked ) { + logadd( LOG_ERROR, "Lock sanity check: lock %p (%s) already locked at %s:%d\n", (void*)lock, name, file, line ); + exit( 4 ); + } + l->locked = 1; + timing_get( &l->locktime ); + l->thread = pthread_self(); + snprintf( l->where, LOCKLEN, "L %s:%d", file, line ); + pthread_spin_lock( &initdestory ); + l->lockId = ++lockId; + pthread_spin_unlock( &initdestory ); + return retval; +} + +int debug_spin_trylock(const char *name, const char *file, int line, pthread_spinlock_t *lock) +{ + debug_lock_t *l = NULL; + pthread_spin_lock( &initdestory ); + for (int i = 0; i < MAXLOCKS; ++i) { + if ( locks[i].lock == lock ) { + l = &locks[i]; + break; + } + } + pthread_spin_unlock( &initdestory ); + if ( l == NULL ) { + logadd( LOG_ERROR, "Tried to lock uninitialized lock %p (%s) at %s:%d\n", (void*)lock, name, file, line ); + debug_dump_lock_stats(); + exit( 4 ); + } + debug_thread_t *t = NULL; + pthread_spin_lock( &initdestory ); + for (int i = 0; i < MAXTHREADS; ++i) { + if ( threads[i].tid != 0 ) continue; + threads[i].tid = pthread_self(); + timing_get( &threads[i].time ); + snprintf( threads[i].name, LOCKLEN, "%s", name ); + snprintf( threads[i].where, LOCKLEN, "%s:%d", file, line ); + t = &threads[i]; + break; + } + pthread_spin_unlock( &initdestory ); + if ( t == NULL ) { + logadd( LOG_ERROR, "Lock sanity check: Too many waiting threads for %p (%s) at %s:%d\n", (void*)lock, name, file, line ); + exit( 4 ); + } + const int retval = pthread_spin_trylock( lock ); + pthread_spin_lock( &initdestory ); + t->tid = 0; + pthread_spin_unlock( &initdestory ); + if ( retval == 0 ) { + if ( l->locked ) { + logadd( LOG_ERROR, "Lock sanity check: lock %p (%s) already locked at %s:%d\n", (void*)lock, name, file, line ); + exit( 4 ); + } + l->locked = 1; + timing_get( &l->locktime ); + l->thread = pthread_self(); + snprintf( l->where, LOCKLEN, "L %s:%d", file, line ); + pthread_spin_lock( &initdestory ); + l->lockId = ++lockId; + pthread_spin_unlock( &initdestory ); + } + return retval; +} + +int debug_spin_unlock(const char *name, const char *file, int line, pthread_spinlock_t *lock) +{ + debug_lock_t *l = NULL; + pthread_spin_lock( &initdestory ); + for (int i = 0; i < MAXLOCKS; ++i) { + if ( locks[i].lock == lock ) { + l = &locks[i]; + break; + } + } + pthread_spin_unlock( &initdestory ); + if ( l == NULL ) { + logadd( LOG_ERROR, "Tried to unlock uninitialized lock %p (%s) at %s:%d\n", (void*)lock, name, file, line ); + exit( 4 ); + } + if ( !l->locked ) { + logadd( LOG_ERROR, "Unlock sanity check: lock %p (%s) not locked at %s:%d\n", (void*)lock, name, file, line ); + exit( 4 ); + } + l->locked = 0; + l->thread = 0; + snprintf( l->where, LOCKLEN, "U %s:%d", file, line ); + int retval = pthread_spin_unlock( lock ); + return retval; +} + +int debug_spin_destroy(const char *name, const char *file, int line, pthread_spinlock_t *lock) +{ + pthread_spin_lock( &initdestory ); + for (int i = 0; i < MAXLOCKS; ++i) { + if ( locks[i].lock == lock ) { + if ( locks[i].locked ) { + logadd( LOG_ERROR, "Tried to destroy lock %p (%s) at %s:%d when it is still locked\n", (void*)lock, name, file, line ); + exit( 4 ); + } + locks[i].lock = NULL; + snprintf( locks[i].where, LOCKLEN, "D %s:%d", file, line ); + pthread_spin_unlock( &initdestory ); + return pthread_spin_destroy( lock ); + } + } + logadd( LOG_ERROR, "Tried to destroy non-existent lock %p (%s) at %s:%d\n", (void*)lock, name, file, line ); + exit( 4 ); +} + +void debug_dump_lock_stats() +{ + declare_now; + pthread_spin_lock( &initdestory ); + printf( "\n **** LOCKS ****\n\n" ); + for (int i = 0; i < MAXLOCKS; ++i) { + if ( locks[i].lock == NULL ) continue; + if ( locks[i].locked ) { + printf( "* *** %s ***\n" + "* Where: %s\n" + "* When: %d secs ago\n" + "* Locked: %d\n" + "* Serial: %d\n" + "* Thread: %d\n", locks[i].name, locks[i].where, (int)timing_diff( &locks[i].locktime, &now ), (int)locks[i].locked, locks[i].lockId, + (int)locks[i].thread ); + } else { + printf( "* *** %s ***\n" + "* Where: %s\n" + "* Locked: %d\n", locks[i].name, locks[i].where, (int)locks[i].locked ); + } + } + printf( "\n **** WAITING THREADS ****\n\n" ); + for (int i = 0; i < MAXTHREADS; ++i) { + if ( threads[i].tid == 0 ) continue; + printf( "* *** Thread %d ***\n" + "* Lock: %s\n" + "* Where: %s\n" + "* How long: %d secs\n", (int)threads[i].tid, threads[i].name, threads[i].where, (int)timing_diff( &threads[i].time, &now ) ); + } + pthread_spin_unlock( &initdestory ); +} + +static void *debug_thread_watchdog(void *something UNUSED) +{ + setThreadName( "debug-watchdog" ); + while ( !_shutdown ) { + if ( init_done ) { + declare_now; + pthread_spin_lock( &initdestory ); + for (int i = 0; i < MAXTHREADS; ++i) { + if ( threads[i].tid == 0 ) continue; + const uint32_t diff = timing_diff( &threads[i].time, &now ); + if ( diff > 6 && diff < 100000 ) { + printf( "\n\n +++++++++ DEADLOCK ++++++++++++\n\n" ); + pthread_spin_unlock( &initdestory ); + debug_dump_lock_stats(); + exit( 99 ); + } + } + pthread_spin_unlock( &initdestory ); + } + if ( watchdogSignal == NULL || signal_wait( watchdogSignal, 5000 ) == SIGNAL_ERROR ) sleep( 5 ); + } + return NULL ; +} + +#endif + +void debug_locks_start_watchdog() +{ +#ifdef _DEBUG + watchdogSignal = signal_new(); + if ( 0 != thread_create( &watchdog, NULL, &debug_thread_watchdog, (void *)NULL ) ) { + logadd( LOG_ERROR, "Could not start debug-lock watchdog." ); + return; + } +#endif +} + +void debug_locks_stop_watchdog() +{ +#ifdef _DEBUG + _shutdown = true; + printf( "Killing debug watchdog...\n" ); + pthread_spin_lock( &initdestory ); + signal_call( watchdogSignal ); + pthread_spin_unlock( &initdestory ); + thread_join( watchdog, NULL ); + signal_close( watchdogSignal ); +#endif +} |