From dbff865b9932f2c8aec0432d22397caf910d522b Mon Sep 17 00:00:00 2001 From: "Fish (David B. Trout)" Date: Sat, 15 Jun 2013 08:27:31 -0700 Subject: [PATCH] HTHREADS/PTTRACE rework Part 2(b): Report abandoned locks Still to be done: Part 3(a): fthreads R/W locks support Part 3(b): Robust Mutexes support --- cmdtab.c | 1 + cmdtab.h | 7 + hsccmd.c | 19 ++ hthreads.c | 804 ++++++++++++++++++++++++++++++++++++++++------------- hthreads.h | 80 +++--- msgenu.h | 11 +- pttrace.c | 19 +- pttrace.h | 4 +- w32util.c | 29 +- 9 files changed, 713 insertions(+), 261 deletions(-) diff --git a/cmdtab.c b/cmdtab.c index f72fbfe2..aa290d85 100644 --- a/cmdtab.c +++ b/cmdtab.c @@ -119,6 +119,7 @@ static CMDTAB cmdtab[] = { /* (COMMAND table) */ CALL_EXTCMD ( cachestats_cmd ) /* (lives in cache.c) */ CALL_EXTCMD ( shared_cmd ) /* (lives in shared.c) */ CALL_EXTCMD ( ptt_cmd ) /* (lives in pttrace.c) */ + CALL_EXTCMD ( locks_cmd ) /* (lives in hthreads.c) */ /*-------------------------------------------------------------------*/ /* $zapcmd - internal debug - may cause havoc - use with caution */ diff --git a/cmdtab.h b/cmdtab.h index e409055f..e55fb086 100644 --- a/cmdtab.h +++ b/cmdtab.h @@ -666,6 +666,11 @@ "identical to the 'loadcore' command except that it loads a text deck\n" \ "file with \"TXT\" and \"END\" 80 byte records (i.e. an object deck).\n" +#define locks_cmd_desc "Display internal locks list" +#define locks_cmd_help \ + \ + "Format: \"locks [HELD|tid|ALL] [SORT [TIME|TOD]|[OWNER|TID]|NAME|LOC]\"\n" + #define log_cmd_desc "Direct logger output" #define log_cmd_help \ \ @@ -1594,6 +1599,8 @@ COMMAND( "exit", quit_cmd, SYSALLNDIAG8, COMMAND( "sizeof", sizeof_cmd, SYSCMDNOPERNPROG, sizeof_cmd_desc, NULL ) +COMMAND( "locks", EXTCMD( locks_cmd ), SYSPROGDEVEL, locks_cmd_desc, locks_cmd_help ) + /*-------------------------------------------------------------------*/ /* Commands optional by build option */ /*-------------------------------------------------------------------*/ diff --git a/hsccmd.c b/hsccmd.c index bb08ee4c..d902b13a 100644 --- a/hsccmd.c +++ b/hsccmd.c @@ -105,6 +105,15 @@ static void* test_thread( void* parg) return NULL; } +/* $test command helper thread */ +static void* test_locks_thread( void* parg) +{ + // test thread exit with lock still held + static LOCK testlock; + initialize_lock( &testlock ); + obtain_lock( &testlock ); + return NULL; +} #define NUM_THREADS 10 #define MAX_WAIT_SECS 6 @@ -119,8 +128,18 @@ int test_cmd(int argc, char *argv[],char *cmdline) UNREFERENCED(cmdline); if (argc > 1) + { if ( CMD(argv[1],crash,5) ) cause_crash(); // (see hscutl.c) + else if (CMD( argv[1], locks, 5 )) + { + // test thread exit with lock still held + static TID tid; + VERIFY( create_thread( &tid, DETACHED, + test_locks_thread, 0, "test_locks_thread" ) == 0); + return 0; + } + } /*-------------------------------------------*/ /* test 'nanosleep' */ diff --git a/hthreads.c b/hthreads.c index a15aeef5..b2c4d6ea 100644 --- a/hthreads.c +++ b/hthreads.c @@ -7,8 +7,8 @@ /*-------------------------------------------------------------------*/ /* Based on original pttrace.c (C) Copyright Greg Smith, 2003-2013 */ -/* Checking rc and calling loglock based on collaboration between */ -/* "Fish" (David B. Trout) and Mark L. Gaubatz, May 2013 */ +/* Collaboratively enhanced and extended in May-June 2013 */ +/* by Mark L. Gaubatz and "Fish" (David B. Trout) */ /*-------------------------------------------------------------------*/ #include "hstdinc.h" @@ -19,12 +19,38 @@ #include "hercules.h" /*-------------------------------------------------------------------*/ -/* Internal PTT trace macro with hard coded class and loc argument */ +/* Hercules Internal ILOCK structure */ +/*-------------------------------------------------------------------*/ +struct ILOCK /* Hercules internal ILOCK structure */ +{ + LIST_ENTRY locklink; /* links locks together in a chain */ + void* addr; /* Lock address */ + const char* name; /* Lock name */ + const char* location; /* Location where it was obtained */ + TIMEVAL time; /* Time of day when it was obtained */ + TID tid; /* Thread-Id of who obtained it */ + HLOCK locklock; /* Internal ILOCK structure lock */ + union { + HLOCK lock; /* The actual locking model mutex */ + HRWLOCK rwlock; /* The actual locking model rwlock */ + }; +}; +typedef struct ILOCK ILOCK; /* Shorter name for the same thing */ + +/*-------------------------------------------------------------------*/ +/* Internal PTT trace helper macros */ /*-------------------------------------------------------------------*/ #define PTTRACE(_type,_data1,_data2,_loc,_result) \ do { \ if (pttclass & PTT_CL_THR) \ - ptt_pthread_trace(PTT_CL_THR,_type,_data1,_data2,_loc,_result); \ + ptt_pthread_trace(PTT_CL_THR, \ + _type,_data1,_data2,_loc,_result,NULL); \ + } while(0) +#define PTTRACE2(_type,_data1,_data2,_loc,_result,_tv) \ + do { \ + if (pttclass & PTT_CL_THR) \ + ptt_pthread_trace(PTT_CL_THR, \ + _type,_data1,_data2,_loc,_result,_tv); \ } while(0) /*-------------------------------------------------------------------*/ @@ -35,7 +61,8 @@ /*-------------------------------------------------------------------*/ /* Issue locking call error message */ /*-------------------------------------------------------------------*/ -static void loglock( LOCK* plk, const int rc, const char* calltype, const char* err_loc ) +static void loglock( ILOCK* ilk, const int rc, const char* calltype, + const char* err_loc ) { const char* err_desc; @@ -52,150 +79,248 @@ static void loglock( LOCK* plk, const int rc, const char* calltype, const char* default: err_desc = "(unknown)"; break; } - // "Pttrace: '%dur' failed: rc=%d (%dur), tid="TIDPAT", loc=%dur" - WRMSG( HHC90013, "E", calltype, rc, err_desc, hthread_self(), TRIMLOC( err_loc )); + // "'%s(%s)' failed: rc=%d: %s; tid="TIDPAT", loc=%s" + WRMSG( HHC90013, "E", calltype, ilk->name, rc, err_desc, + hthread_self(), TRIMLOC( err_loc )); - // "Pttrace: lock was obtained by thread "TIDPAT" at %dur" - WRMSG( HHC90014, "I", plk->tid, TRIMLOC( plk->loc )); + if (ilk->tid) + { + // "lock %s was obtained by thread "TIDPAT" at %s" + WRMSG( HHC90014, "I", ilk->name, ilk->tid, TRIMLOC( ilk->location )); + } +} + +/*-------------------------------------------------------------------*/ +/* Internal locks list and associated lock for updating it */ +/*-------------------------------------------------------------------*/ +static LIST_ENTRY locklist; /* Internal Locks list anchor */ +static HLOCK listlock; /* Lock for accessing Locks list */ +static int lockcount; /* Number of locks in our list */ + +/*-------------------------------------------------------------------*/ +/* Internal macros to control access to our internal locks list */ +/*-------------------------------------------------------------------*/ +#define LockLocksList() hthread_mutex_lock( &listlock ) +#define UnlockLocksList() hthread_mutex_unlock( &listlock ) + +/*-------------------------------------------------------------------*/ +/* Initialize internal locks list */ +/*-------------------------------------------------------------------*/ +static void hthreads_internal_init() +{ + static BYTE bDidInit = FALSE; + + if (!bDidInit) + { + MATTR attr; + int rc; + + /* Initialize our internal lock */ + + rc = hthread_mutexattr_init( &attr ); + if (rc) + goto fatal; + + rc = hthread_mutexattr_settype( &attr, HTHREAD_MUTEX_DEFAULT ); + if (rc) + goto fatal; + + rc = hthread_mutex_init( &listlock, &attr ); + if (rc) + goto fatal; + + rc = hthread_mutexattr_destroy( &attr ); + if (rc) + goto fatal; + + /* Initialize our locks list anchor */ + + InitializeListHead( &locklist ); + lockcount = 0; + bDidInit = TRUE; + return; + +fatal: + perror( "Fatal error in hthreads_internal_init function" ); + exit(1); + } + return; +} + +/*-------------------------------------------------------------------*/ +/* Find or allocate and initialize an internal ILOCK structure. */ +/*-------------------------------------------------------------------*/ +static ILOCK* hthreads_get_ILOCK( void* addr, const char* name ) +{ + ILOCK* ilk; /* Pointer to ILOCK structure */ + LIST_ENTRY* ple; /* Ptr to LIST_ENTRY structure */ + + hthreads_internal_init(); + + /* Search list to see if this lock has already been allocated */ + + LockLocksList(); + + for (ple = locklist.Flink; ple != &locklist; ple = ple->Flink) + { + ilk = CONTAINING_RECORD( ple, ILOCK, locklink ); + if (ilk->addr == addr) + break; + } + + /* If needed, alloacte a new ILOCK structure for this lock */ + + if (&locklist == ple) + { + if (!(ilk = calloc_aligned( sizeof( ILOCK ), 64 ))) + { + perror( "Fatal error in hthreads_get_ILOCK function" ); + exit(1); + } + ilk->addr = addr; + InsertListTail( &locklist, &ilk->locklink ); + lockcount++; + } + + ilk->name = name; + ilk->location = "null:0"; + ilk->tid = 0; + ilk->time.tv_sec = 0; + ilk->time.tv_usec = 0; + + UnlockLocksList(); + + return ilk; } /*-------------------------------------------------------------------*/ /* Initialize a lock */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_initialize_lock( LOCK* plk, const char* loc ) +DLL_EXPORT int hthread_initialize_lock( LOCK* plk, const char* name, + const char* location ) { - int rc; - MATTR attr; + int rc; + MATTR attr; + ILOCK* ilk = hthreads_get_ILOCK( plk, name ); - PTTRACE( "lock init", plk, &attr, loc, PTT_MAGIC ); + /* Initialize the requested lock */ rc = hthread_mutexattr_init( &attr ); if (rc) - goto init_error; + goto fatal; rc = hthread_mutexattr_settype( &attr, HTHREAD_MUTEX_DEFAULT ); if (rc) - goto init_error; + goto fatal; - rc = hthread_mutex_init( &plk->locklock, &attr ); + rc = hthread_mutex_init( &ilk->locklock, &attr ); if (rc) - goto init_error; + goto fatal; - rc = hthread_mutex_init( &plk->lock, &attr ); + rc = hthread_mutex_init( &ilk->lock, &attr ); if (rc) - goto init_error; + goto fatal; rc = hthread_mutexattr_destroy( &attr ); if (rc) - goto init_error; - - rc = hthread_mutex_lock( &plk->locklock ); - if (rc) - goto init_error; - - plk->loc = "null:0"; - plk->tid = 0; - - rc = hthread_mutex_unlock( &plk->locklock ); - if (rc) - goto init_error; + goto fatal; + plk->ilk = ilk; /* (LOCK is now initialized) */ + PTTRACE( "lock init", plk, 0, location, PTT_MAGIC ); return 0; -init_error: +fatal: - perror( "Fatal error initializing Mutex Locking Model" ); + perror( "Fatal error in hthread_initialize_lock function" ); exit(1); } /*-------------------------------------------------------------------*/ /* Initialize a R/W lock */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_initialize_rwlock( RWLOCK* plk, const char* loc ) +DLL_EXPORT int hthread_initialize_rwlock( RWLOCK* plk, const char* name, + const char* location ) { int rc; RWATTR attr1; /* for primary lock */ MATTR attr2; /* for internal locklock */ + ILOCK* ilk = hthreads_get_ILOCK( plk, name ); - PTTRACE( "rwlock init", plk, &attr1, loc, PTT_MAGIC ); + /* Initialize the requested lock */ rc = hthread_rwlockattr_init( &attr1 ); if (rc) - goto init_error; + goto fatal; rc = hthread_mutexattr_init( &attr2 ); if (rc) - goto init_error; - - rc = hthread_mutexattr_settype( &attr2, HTHREAD_MUTEX_DEFAULT ); - if (rc) - goto init_error; + goto fatal; rc = hthread_rwlockattr_setpshared( &attr1, HTHREAD_RWLOCK_DEFAULT ); if (rc) - goto init_error; + goto fatal; - rc = hthread_mutex_init( &plk->locklock, &attr2 ); + rc = hthread_mutexattr_settype( &attr2, HTHREAD_MUTEX_DEFAULT ); if (rc) - goto init_error; + goto fatal; - rc = hthread_rwlock_init( &plk->lock, &attr1 ); + rc = hthread_rwlock_init( &ilk->rwlock, &attr1 ); if (rc) - goto init_error; + goto fatal; + + rc = hthread_mutex_init( &ilk->locklock, &attr2 ); + if (rc) + goto fatal; rc = hthread_rwlockattr_destroy( &attr1 ); if (rc) - goto init_error; + goto fatal; rc = hthread_mutexattr_destroy( &attr2 ); if (rc) - goto init_error; - - rc = hthread_mutex_lock( &plk->locklock ); - if (rc) - goto init_error; - - plk->loc = "null:0"; - plk->tid = 0; - - rc = hthread_mutex_unlock( &plk->locklock ); - if (rc) - goto init_error; + goto fatal; + plk->ilk = ilk; /* (RWLOCK is now initialized) */ + PTTRACE( "rwlock init", plk, &attr1, location, PTT_MAGIC ); return 0; -init_error: +fatal: - perror( "Fatal error initializing Robust Mutex Locking Model" ); + perror( "Fatal error in hthread_initialize_rwlock function" ); exit(1); } /*-------------------------------------------------------------------*/ /* Obtain a lock */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_obtain_lock( LOCK* plk, const char* loc ) +DLL_EXPORT int hthread_obtain_lock( LOCK* plk, const char* location ) { int rc; - U64 dur; - PTTRACE( "lock before", plk, NULL, loc, PTT_MAGIC ); - rc = hthread_mutex_trylock( &plk->lock ); + U64 waitdur; + ILOCK* ilk; + TIMEVAL tv; + ilk = (ILOCK*) plk->ilk; + PTTRACE( "lock before", plk, NULL, location, PTT_MAGIC ); + rc = hthread_mutex_trylock( &ilk->lock ); if (EBUSY == rc) { - dur = host_tod(); - rc = hthread_mutex_lock( &plk->lock ); - dur = host_tod() - dur; + waitdur = host_tod(); + rc = hthread_mutex_lock( &ilk->lock ); + waitdur = host_tod() - waitdur; } else - dur = 0; - PTTRACE( "lock after", plk, (void*) dur, loc, rc ); + waitdur = 0; + gettimeofday( &tv, NULL ); + PTTRACE2( "lock after", plk, (void*) waitdur, location, rc, &tv ); if (rc) - loglock( plk, rc, "obtain_lock", loc ); + loglock( ilk, rc, "obtain_lock", location ); if (!rc || EOWNERDEAD == rc) { - hthread_mutex_lock( &plk->locklock ); - plk->loc = loc; - plk->tid = hthread_self(); - hthread_mutex_unlock( &plk->locklock ); + hthread_mutex_lock( &ilk->locklock ); + ilk->location = location; + ilk->tid = hthread_self(); + memcpy( &ilk->time, &tv, sizeof( TIMEVAL )); + hthread_mutex_unlock( &ilk->locklock ); } return rc; } @@ -203,68 +328,101 @@ DLL_EXPORT int hthread_obtain_lock( LOCK* plk, const char* loc ) /*-------------------------------------------------------------------*/ /* Release a lock */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_release_lock( LOCK* plk, const char* loc ) +DLL_EXPORT int hthread_release_lock( LOCK* plk, const char* location ) { int rc; - rc = hthread_mutex_unlock( &plk->lock ); - PTTRACE( "unlock", plk, NULL, loc, rc ); + ILOCK* ilk; + ilk = (ILOCK*) plk->ilk; + rc = hthread_mutex_unlock( &ilk->lock ); + PTTRACE( "unlock", plk, NULL, location, rc ); if (rc) - loglock( plk, rc, "release_lock", loc ); + loglock( ilk, rc, "release_lock", location ); + hthread_mutex_lock( &ilk->locklock ); + ilk->location = "null:0"; + ilk->tid = 0; + hthread_mutex_unlock( &ilk->locklock ); return rc; } /*-------------------------------------------------------------------*/ /* Release a R/W lock */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_release_rwlock( RWLOCK* plk, const char* loc ) +DLL_EXPORT int hthread_release_rwlock( RWLOCK* plk, const char* location ) { int rc; - rc = hthread_rwlock_unlock( &plk->lock ); - PTTRACE( "rwunlock", plk, NULL, loc, rc ); + ILOCK* ilk; + ilk = (ILOCK*) plk->ilk; + rc = hthread_rwlock_unlock( &ilk->rwlock ); + PTTRACE( "rwunlock", plk, NULL, location, rc ); if (rc) - loglock( (LOCK*) plk, rc, "release_rwlock", loc ); + loglock( ilk, rc, "release_rwlock", location ); + hthread_mutex_lock( &ilk->locklock ); + ilk->location = "null:0"; + ilk->tid = 0; + hthread_mutex_unlock( &ilk->locklock ); return rc; } /*-------------------------------------------------------------------*/ /* Destroy a lock */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_destroy_lock( LOCK* plk, const char* loc ) +DLL_EXPORT int hthread_destroy_lock( LOCK* plk, const char* location ) { int rc; - UNREFERENCED( loc ); - rc = hthread_mutex_destroy( &plk->lock ); + ILOCK* ilk; + UNREFERENCED( location ); + ilk = (ILOCK*) plk->ilk; + rc = hthread_mutex_destroy( &ilk->lock ); + LockLocksList(); + RemoveListEntry( &ilk->locklink ); + lockcount--; + UnlockLocksList(); + free_aligned( ilk ); + plk->ilk = NULL; return rc; } /*-------------------------------------------------------------------*/ /* Destroy a R/W lock */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_destroy_rwlock( RWLOCK* plk, const char* loc ) +DLL_EXPORT int hthread_destroy_rwlock( RWLOCK* plk, const char* location ) { int rc; - UNREFERENCED( loc ); - rc = hthread_rwlock_destroy( &plk->lock ); + ILOCK* ilk; + UNREFERENCED( location ); + ilk = (ILOCK*) plk->ilk; + rc = hthread_rwlock_destroy( &ilk->rwlock ); + LockLocksList(); + RemoveListEntry( &ilk->locklink ); + lockcount--; + UnlockLocksList(); + free_aligned( ilk ); + plk->ilk = NULL; return rc; } /*-------------------------------------------------------------------*/ /* Try to obtain a lock */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_try_obtain_lock( LOCK* plk, const char* loc ) +DLL_EXPORT int hthread_try_obtain_lock( LOCK* plk, const char* location ) { int rc; - PTTRACE( "try before", plk, NULL, loc, PTT_MAGIC ); - rc = hthread_mutex_trylock( &plk->lock ); - PTTRACE( "try after", plk, NULL, loc, rc ); + ILOCK* ilk; + TIMEVAL tv; + ilk = (ILOCK*) plk->ilk; + PTTRACE( "try before", plk, NULL, location, PTT_MAGIC ); + rc = hthread_mutex_trylock( &ilk->lock ); + gettimeofday( &tv, NULL ); + PTTRACE2( "try after", plk, NULL, location, rc, &tv ); if (rc && EBUSY != rc) - loglock( plk, rc, "try_obtain_lock", loc ); + loglock( ilk, rc, "try_obtain_lock", location ); if (!rc || EOWNERDEAD == rc) { - hthread_mutex_lock( &plk->locklock ); - plk->loc = loc; - plk->tid = hthread_self(); - hthread_mutex_unlock( &plk->locklock ); + hthread_mutex_lock( &ilk->locklock ); + ilk->location = location; + ilk->tid = hthread_self(); + memcpy( &ilk->time, &tv, sizeof( TIMEVAL )); + hthread_mutex_unlock( &ilk->locklock ); } return rc; } @@ -272,66 +430,75 @@ DLL_EXPORT int hthread_try_obtain_lock( LOCK* plk, const char* loc ) /*-------------------------------------------------------------------*/ /* Test if lock is held */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_test_lock( LOCK* plk, const char* loc ) +DLL_EXPORT int hthread_test_lock( LOCK* plk, const char* location ) { int rc; - UNREFERENCED( loc ); - rc = hthread_mutex_trylock( &plk->lock ); + ILOCK* ilk; + UNREFERENCED( location ); + ilk = (ILOCK*) plk->ilk; + rc = hthread_mutex_trylock( &ilk->lock ); if (rc) return rc; - hthread_mutex_unlock( &plk->lock ); + hthread_mutex_unlock( &ilk->lock ); return 0; } /*-------------------------------------------------------------------*/ /* Obtain read-only access to R/W lock */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_obtain_rdlock( RWLOCK* plk, const char* loc ) +DLL_EXPORT int hthread_obtain_rdlock( RWLOCK* plk, const char* location ) { int rc; - U64 dur; - PTTRACE( "rdlock before", plk, NULL, loc, PTT_MAGIC ); - rc = hthread_rwlock_tryrdlock( &plk->lock ); + U64 waitdur; + ILOCK* ilk; + ilk = (ILOCK*) plk->ilk; + PTTRACE( "rdlock before", plk, NULL, location, PTT_MAGIC ); + rc = hthread_rwlock_tryrdlock( &ilk->rwlock ); if (EBUSY == rc) { - dur = host_tod(); - rc = hthread_rwlock_rdlock( &plk->lock ); - dur = host_tod() - dur; + waitdur = host_tod(); + rc = hthread_rwlock_rdlock( &ilk->rwlock ); + waitdur = host_tod() - waitdur; } else - dur = 0; - PTTRACE( "rdlock after", plk, (void*) dur, loc, rc ); + waitdur = 0; + PTTRACE( "rdlock after", plk, (void*) waitdur, location, rc ); if (rc) - loglock( (LOCK*) plk, rc, "obtain_rdloc", loc ); + loglock( ilk, rc, "obtain_rdloc", location ); return rc; } /*-------------------------------------------------------------------*/ /* Obtain exclusive write access to a R/W lock */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_obtain_wrlock( RWLOCK* plk, const char* loc ) +DLL_EXPORT int hthread_obtain_wrlock( RWLOCK* plk, const char* location ) { int rc; - U64 dur; - PTTRACE( "wrlock before", plk, NULL, loc, PTT_MAGIC ); - rc = hthread_rwlock_trywrlock( &plk->lock ); + U64 waitdur; + ILOCK* ilk; + TIMEVAL tv; + ilk = (ILOCK*) plk->ilk; + PTTRACE( "wrlock before", plk, NULL, location, PTT_MAGIC ); + rc = hthread_rwlock_trywrlock( &ilk->rwlock ); if (EBUSY == rc) { - dur = host_tod(); - rc = hthread_rwlock_wrlock( &plk->lock ); - dur = host_tod() - dur; + waitdur = host_tod(); + rc = hthread_rwlock_wrlock( &ilk->rwlock ); + waitdur = host_tod() - waitdur; } else - dur = 0; - PTTRACE( "wrlock after", plk, (void*) dur, loc, rc ); + waitdur = 0; + gettimeofday( &tv, NULL ); + PTTRACE2( "wrlock after", plk, (void*) waitdur, location, rc, &tv ); if (rc) - loglock( (LOCK*) plk, rc, "obtain_wrlock", loc ); + loglock( ilk, rc, "obtain_wrlock", location ); if (!rc || EOWNERDEAD == rc) { - hthread_mutex_lock( &plk->locklock ); - plk->loc = loc; - plk->tid = hthread_self(); - hthread_mutex_unlock( &plk->locklock ); + hthread_mutex_lock( &ilk->locklock ); + ilk->location = location; + ilk->tid = hthread_self(); + memcpy( &ilk->time, &tv, sizeof( TIMEVAL )); + hthread_mutex_unlock( &ilk->locklock ); } return rc; } @@ -339,34 +506,41 @@ DLL_EXPORT int hthread_obtain_wrlock( RWLOCK* plk, const char* loc ) /*-------------------------------------------------------------------*/ /* Try to obtain read-only access to a R/W lock */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_try_obtain_rdlock( RWLOCK* plk, const char* loc ) +DLL_EXPORT int hthread_try_obtain_rdlock( RWLOCK* plk, const char* location ) { int rc; - PTTRACE( "tryrd before", plk, NULL, loc, PTT_MAGIC ); - rc = hthread_rwlock_tryrdlock( &plk->lock ); - PTTRACE( "tryrd after", plk, NULL, loc, rc ); + ILOCK* ilk; + ilk = (ILOCK*) plk->ilk; + PTTRACE( "tryrd before", plk, NULL, location, PTT_MAGIC ); + rc = hthread_rwlock_tryrdlock( &ilk->rwlock ); + PTTRACE( "tryrd after", plk, NULL, location, rc ); if (rc && EBUSY != rc) - loglock( (LOCK*) plk, rc, "try_obtain_rdlock", loc ); + loglock( ilk, rc, "try_obtain_rdlock", location ); return rc; } /*-------------------------------------------------------------------*/ /* Try to obtain exclusive write access to a R/W lock */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_try_obtain_wrlock( RWLOCK* plk, const char* loc ) +DLL_EXPORT int hthread_try_obtain_wrlock( RWLOCK* plk, const char* location ) { int rc; - PTTRACE( "trywr before", plk, NULL, loc, PTT_MAGIC ); - rc = hthread_rwlock_trywrlock( &plk->lock ); - PTTRACE( "trywr after", plk, NULL, loc, rc ); + ILOCK* ilk; + TIMEVAL tv; + ilk = (ILOCK*) plk->ilk; + PTTRACE( "trywr before", plk, NULL, location, PTT_MAGIC ); + rc = hthread_rwlock_trywrlock( &ilk->rwlock ); + gettimeofday( &tv, NULL ); + PTTRACE2( "trywr after", plk, NULL, location, rc, &tv ); if (rc && EBUSY != rc) - loglock( (LOCK*) plk, rc, "try_obtain_wrlock", loc ); + loglock( ilk, rc, "try_obtain_wrlock", location ); if (!rc || EOWNERDEAD == rc) { - hthread_mutex_lock( &plk->locklock ); - plk->loc = loc; - plk->tid = hthread_self(); - hthread_mutex_unlock( &plk->locklock ); + hthread_mutex_lock( &ilk->locklock ); + ilk->location = location; + ilk->tid = hthread_self(); + memcpy( &ilk->time, &tv, sizeof( TIMEVAL )); + hthread_mutex_unlock( &ilk->locklock ); } return rc; } @@ -374,38 +548,42 @@ DLL_EXPORT int hthread_try_obtain_wrlock( RWLOCK* plk, const char* loc ) /*-------------------------------------------------------------------*/ /* Test if read-only access to a R/W lock is held */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_test_rdlock( RWLOCK* plk, const char* loc ) +DLL_EXPORT int hthread_test_rdlock( RWLOCK* plk, const char* location ) { int rc; - UNREFERENCED( loc ); - rc = hthread_rwlock_tryrdlock( &plk->lock ); + ILOCK* ilk; + UNREFERENCED( location ); + ilk = (ILOCK*) plk->ilk; + rc = hthread_rwlock_tryrdlock( &ilk->rwlock ); if (rc) return rc; - hthread_rwlock_unlock( &plk->lock ); + hthread_rwlock_unlock( &ilk->rwlock ); return 0; } /*-------------------------------------------------------------------*/ /* Test if exclusive write access to a R/W lock is held */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_test_wrlock( RWLOCK* plk, const char* loc ) +DLL_EXPORT int hthread_test_wrlock( RWLOCK* plk, const char* location ) { int rc; - UNREFERENCED( loc ); - rc = hthread_rwlock_trywrlock( &plk->lock ); + ILOCK* ilk; + UNREFERENCED( location ); + ilk = (ILOCK*) plk->ilk; + rc = hthread_rwlock_trywrlock( &ilk->rwlock ); if (rc) return rc; - hthread_rwlock_unlock( &plk->lock ); + hthread_rwlock_unlock( &ilk->rwlock ); return 0; } /*-------------------------------------------------------------------*/ /* Initialize a condition variable */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_initialize_condition( COND* plc, const char* loc ) +DLL_EXPORT int hthread_initialize_condition( COND* plc, const char* location ) { int rc; - PTTRACE( "cond init", NULL, plc, loc, PTT_MAGIC ); + PTTRACE( "cond init", NULL, plc, location, PTT_MAGIC ); rc = hthread_cond_init( plc ); return rc; } @@ -413,10 +591,10 @@ DLL_EXPORT int hthread_initialize_condition( COND* plc, const char* loc ) /*-------------------------------------------------------------------*/ /* Destroy a condition variable */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_destroy_condition( COND* plc, const char* loc ) +DLL_EXPORT int hthread_destroy_condition( COND* plc, const char* location ) { int rc; - UNREFERENCED( loc ); + UNREFERENCED( location ); rc = hthread_cond_destroy( plc ); return rc; } @@ -424,60 +602,67 @@ DLL_EXPORT int hthread_destroy_condition( COND* plc, const char* loc ) /*-------------------------------------------------------------------*/ /* Signal a condition variable (releases only one thread) */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_signal_condition( COND* plc, const char* loc ) +DLL_EXPORT int hthread_signal_condition( COND* plc, const char* location ) { int rc; rc = hthread_cond_signal( plc ); - PTTRACE( "signal", NULL, plc, loc, rc ); + PTTRACE( "signal", NULL, plc, location, rc ); return rc; } /*-------------------------------------------------------------------*/ /* Broadcast a condition variable (releases all waiting threads) */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_broadcast_condition( COND* plc, const char* loc ) +DLL_EXPORT int hthread_broadcast_condition( COND* plc, const char* location ) { int rc; rc = hthread_cond_broadcast( plc ); - PTTRACE( "broadcast", NULL, plc, loc, rc ); + PTTRACE( "broadcast", NULL, plc, location, rc ); return rc; } /*-------------------------------------------------------------------*/ /* Wait for condition variable to be signaled */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_wait_condition( COND* plc, LOCK* plk, const char* loc ) +DLL_EXPORT int hthread_wait_condition( COND* plc, LOCK* plk, const char* location ) { int rc; - PTTRACE( "wait before", plk, plc, loc, PTT_MAGIC ); - rc = hthread_cond_wait( plc, &plk->lock ); - PTTRACE( "wait after", plk, plc, loc, rc ); + ILOCK* ilk; + ilk = (ILOCK*) plk->ilk; + PTTRACE( "wait before", plk, plc, location, PTT_MAGIC ); + rc = hthread_cond_wait( plc, &ilk->lock ); + PTTRACE( "wait after", plk, plc, location, rc ); if (rc) - loglock( plk, rc, "wait_condition", loc ); + loglock( ilk, rc, "wait_condition", location ); return rc; } /*-------------------------------------------------------------------*/ /* Timed wait for a condition variable to be signaled */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_timed_wait_condition( COND* plc, LOCK* plk, const struct timespec* tm, const char* loc ) +DLL_EXPORT int hthread_timed_wait_condition( COND* plc, LOCK* plk, + const struct timespec* tm, + const char* location ) { int rc; - PTTRACE( "tw before", plk, plc, loc, PTT_MAGIC ); - rc = hthread_cond_timedwait( plc, &plk->lock, tm ); - PTTRACE( "tw after", plk, plc, loc, rc ); + ILOCK* ilk; + ilk = (ILOCK*) plk->ilk; + PTTRACE( "tw before", plk, plc, location, PTT_MAGIC ); + rc = hthread_cond_timedwait( plc, &ilk->lock, tm ); + PTTRACE( "tw after", plk, plc, location, rc ); if (rc && ETIMEDOUT != rc) - loglock( plk, rc, "timed_wait_condition", loc ); + loglock( ilk, rc, "timed_wait_condition", location ); return rc; } /*-------------------------------------------------------------------*/ -/* (internal helper function) */ +/* Internal helper function to initialize a thread attribute */ /*-------------------------------------------------------------------*/ -static INLINE int hthread_init_thread_attr( ATTR* pat, int state, const char* loc ) +static INLINE int hthread_init_thread_attr( ATTR* pat, int state, + const char* location ) { int rc; - UNREFERENCED( loc ); + UNREFERENCED( location ); rc = hthread_attr_init( pat ); if (!rc) rc = hthread_attr_setstacksize( pat, HTHREAD_STACK_SIZE ); @@ -489,80 +674,134 @@ static INLINE int hthread_init_thread_attr( ATTR* pat, int state, const char* lo /*-------------------------------------------------------------------*/ /* Initialize a thread attribute to "joinable" */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_initialize_join_attr( ATTR* pat, const char* loc ) +DLL_EXPORT int hthread_initialize_join_attr( ATTR* pat, const char* location ) { int rc; - UNREFERENCED( loc ); - rc = hthread_init_thread_attr( pat, HTHREAD_CREATE_JOINABLE, loc ); + rc = hthread_init_thread_attr( pat, HTHREAD_CREATE_JOINABLE, location ); return rc; } /*-------------------------------------------------------------------*/ /* Initialize a thread attribute to "detached" (non-joinable) */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_initialize_detach_attr( ATTR* pat, const char* loc ) +DLL_EXPORT int hthread_initialize_detach_attr( ATTR* pat, const char* location ) { int rc; - UNREFERENCED( loc ); - rc = hthread_init_thread_attr( pat, HTHREAD_CREATE_DETACHED, loc ); + rc = hthread_init_thread_attr( pat, HTHREAD_CREATE_DETACHED, location ); + return rc; +} + +/*-------------------------------------------------------------------*/ +/* Internal function to list abandoned locks when thread exits */ +/*-------------------------------------------------------------------*/ +static void hthread_list_abandoned_locks( TID tid, const char* exit_loc ) +{ + ILOCK* ilk; + LIST_ENTRY* ple; + + LockLocksList(); + for (ple = locklist.Flink; ple != &locklist; ple = ple->Flink) + { + ilk = CONTAINING_RECORD( ple, ILOCK, locklink ); + if (ilk->tid == tid) + { + char tod[27]; /* "YYYY-MM-DD HH:MM:SS.uuuuuu" */ + + FormatTIMEVAL( &ilk->time, tod, sizeof( tod )); + + if (exit_loc) + { + // "Thread "TIDPAT" has abandoned at %s lock %s obtained on %s at %s" + WRMSG( HHC90016, "E", tid, TRIMLOC( exit_loc ), + ilk->name, &tod[11], TRIMLOC( ilk->location )); + } + else + { + // "Thread "TIDPAT" has abandoned lock %s obtained on %s at %s" + WRMSG( HHC90015, "E", tid, + ilk->name, &tod[11], TRIMLOC( ilk->location )); + } + } + } + UnlockLocksList(); +} + +/*-------------------------------------------------------------------*/ +/* Internal thread function to intercept thread exit via return */ +/*-------------------------------------------------------------------*/ +static void* hthread_func( void* arg2 ) +{ + THREAD_FUNC* pfn = (THREAD_FUNC*) *((void**)arg2+0); + void* arg = (void*) *((void**)arg2+1); + TID tid = hthread_self(); + void* rc; + free( arg2 ); + rc = pfn( arg ); + hthread_list_abandoned_locks( tid, NULL ); return rc; } /*-------------------------------------------------------------------*/ /* Create a new thread */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_create_thread( TID* ptid, ATTR* pat, THREAD_FUNC* pfn, void* arg, const char* name, const char* loc ) +DLL_EXPORT int hthread_create_thread( TID* ptid, ATTR* pat, + THREAD_FUNC* pfn, void* arg, + const char* name, const char* location ) { int rc; - UNREFERENCED( name ); - rc = hthread_create( ptid, pat, pfn, arg, name ); - PTTRACE( "create", (void*)*ptid, NULL, loc, rc ); - pttthread = 1; /* Set a mark on the wall */ + void** arg2; + UNREFERENCED( name ); /* unref'ed on non-Windows */ + pttthread = 1; /* Set a mark on the wall */ + arg2 = malloc( 2 * sizeof( void* )); + *(arg2+0) = (void*) pfn; + *(arg2+1) = (void*) arg; + rc = hthread_create( ptid, pat, hthread_func, arg2, name ); + PTTRACE( "create", (void*)*ptid, NULL, location, rc ); return rc; } /*-------------------------------------------------------------------*/ /* Wait for a thread to terminate */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_join_thread( TID tid, void** prc, const char* loc ) +DLL_EXPORT int hthread_join_thread( TID tid, void** prc, const char* location ) { int rc; - PTTRACE( "join before", (void*) tid, prc ? *prc : NULL, loc, PTT_MAGIC ); + PTTRACE( "join before", (void*) tid, prc ? *prc : NULL, location, PTT_MAGIC ); rc = hthread_join( tid, prc ); - PTTRACE( "join after", (void*) tid, prc ? *prc : NULL, loc, rc ); + PTTRACE( "join after", (void*) tid, prc ? *prc : NULL, location, rc ); return rc; } /*-------------------------------------------------------------------*/ /* Detach from a thread (release resources or change to "detached") */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_detach_thread( TID tid, const char* loc ) +DLL_EXPORT int hthread_detach_thread( TID tid, const char* location ) { int rc; - PTTRACE( "dtch before", (void*) tid, NULL, loc, PTT_MAGIC ); + PTTRACE( "dtch before", (void*) tid, NULL, location, PTT_MAGIC ); rc = hthread_detach( tid ); - PTTRACE( "dtch after", (void*) tid, NULL, loc, rc ); + PTTRACE( "dtch after", (void*) tid, NULL, location, rc ); return rc; } /*-------------------------------------------------------------------*/ /* Send a signal to a thread */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_signal_thread( TID tid, int sig, const char* loc ) +DLL_EXPORT int hthread_signal_thread( TID tid, int sig, const char* location ) { int rc; - PTTRACE( "kill", (void*) tid, (void*)(long)sig, loc, PTT_MAGIC ); + PTTRACE( "kill", (void*) tid, (void*)(long)sig, location, PTT_MAGIC ); rc = hthread_kill( tid, sig ); return rc; } /*-------------------------------------------------------------------*/ -/* Return calling thread'dur ID */ +/* Return calling thread's ID */ /*-------------------------------------------------------------------*/ -DLL_EXPORT TID hthread_thread_id( const char* loc ) +DLL_EXPORT TID hthread_thread_id( const char* location ) { TID tid; - UNREFERENCED( loc ); + UNREFERENCED( location ); tid = hthread_self(); return tid; } @@ -570,19 +809,21 @@ DLL_EXPORT TID hthread_thread_id( const char* loc ) /*-------------------------------------------------------------------*/ /* Exit immediately from a thread */ /*-------------------------------------------------------------------*/ -DLL_EXPORT void hthread_exit_thread( void* rc, const char* loc ) +DLL_EXPORT void hthread_exit_thread( void* rc, const char* location ) { - UNREFERENCED( loc ); + TID tid; + tid = hthread_self(); + hthread_list_abandoned_locks( tid, location ); hthread_exit( rc ); } /*-------------------------------------------------------------------*/ /* Compare two thread IDs */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_equal_threads( TID tid1, TID tid2, const char* loc ) +DLL_EXPORT int hthread_equal_threads( TID tid1, TID tid2, const char* location ) { int rc; - UNREFERENCED( loc ); + UNREFERENCED( location ); rc = hthread_equal( tid1, tid2 ); return rc; } @@ -590,16 +831,183 @@ DLL_EXPORT int hthread_equal_threads( TID tid1, TID tid2, const char* loc ) /*-------------------------------------------------------------------*/ /* Return Windows thread HANDLE */ /*-------------------------------------------------------------------*/ -DLL_EXPORT int hthread_win_thread_handle( TID tid, const char* loc ) +DLL_EXPORT int hthread_win_thread_handle( TID tid, const char* location ) { #if defined( _MSVC_ ) int rc; - UNREFERENCED( loc ); + UNREFERENCED( location ); rc = (int) (uintptr_t) hthread_get_handle( tid ); return rc; #else // !defined( _MSVC_ ) - UNREFERENCED( loc ); + UNREFERENCED( location ); UNREFERENCED( tid ); return 0; #endif } + +/*-------------------------------------------------------------------*/ +/* locks_cmd helper function: save offline copy of all locks in list */ +/*-------------------------------------------------------------------*/ +static int hthreads_copy_locks_list( ILOCK** ppILOCK ) +{ + ILOCK* ilk; /* Pointer to ILOCK structure */ + ILOCK* ilka; /* Pointer to ILOCK array */ + LIST_ENTRY* ple; /* Ptr to LIST_ENTRY structure */ + int i; + + LockLocksList(); + + if (!(*ppILOCK = ilka = (ILOCK*) malloc( lockcount * sizeof( ILOCK )))) + { + UnlockLocksList(); + return 0; + } + + for (i=0, ple = locklist.Flink; ple != &locklist; ple = ple->Flink, i++) + { + ilk = CONTAINING_RECORD( ple, ILOCK, locklink ); + memcpy( &ilka[i], ilk, sizeof( ILOCK )); + } + + i = lockcount; + UnlockLocksList(); + return i; +} + +/*-------------------------------------------------------------------*/ +/* locks_cmd sort functions */ +/*-------------------------------------------------------------------*/ +static int sortby_tim( const ILOCK* p1, const ILOCK* p2 ) +{ + return (p1->time.tv_sec != p2->time.tv_sec) ? + ((long) p1->time.tv_sec - (long) p2->time.tv_sec) : + ((long) p1->time.tv_usec - (long) p2->time.tv_usec) ; +} +static int sortby_tid( const ILOCK* p1, const ILOCK* p2 ) +{ + return equal_threads( p1->tid, p2->tid ) ? 0 : + ((intptr_t) p1->tid - (intptr_t) p2->tid); +} +static int sortby_nam( const ILOCK* p1, const ILOCK* p2 ) +{ + return strcasecmp( p1->name, p2->name ); +} +static int sortby_loc( const ILOCK* p1, const ILOCK* p2 ) +{ + return strcasecmp( p1->location, p2->location ); +} + +/*-------------------------------------------------------------------*/ +/* locks_cmd - list internal locks */ +/*-------------------------------------------------------------------*/ +DLL_EXPORT int locks_cmd( int argc, char* argv[], char* cmdline ) +{ + ILOCK* ilk; /* Pointer to ILOCK array */ + char tod[27]; /* "YYYY-MM-DD HH:MM:SS.uuuuuu" */ + TID tid = 0; /* Requested thread id */ + int seq = 1; /* Requested sort order */ + int count, i, rc = 0; + char c; + + UNREFERENCED( cmdline ); + + /* Format: "locks [ALL|tid|HELD] [SORT NAME|OWNER|TIME|LOC]" */ + /* Note: TID is alias for OWNER, TOD is alias for TIME. */ + + if (argc <= 1) + tid = 0; + else if (strcasecmp( argv[1], "ALL" ) == 0) + tid = 0; + else if (strcasecmp( argv[1], "HELD" ) == 0) + tid = -1; + else if (sscanf( argv[1], "%x%c", (U32*) &tid, &c ) != 1) + rc = -1; + + if (!rc) + { + if (argc == 4) + { + if (strcasecmp( argv[2], "SORT" ) != 0) + rc = -1; + else + { + if (strcasecmp( argv[3], "TIME" ) == 0) seq = 1; + else if (strcasecmp( argv[3], "TOD" ) == 0) seq = 1; + else if (strcasecmp( argv[3], "OWNER" ) == 0) seq = 2; + else if (strcasecmp( argv[3], "TID" ) == 0) seq = 2; + else if (strcasecmp( argv[3], "NAME" ) == 0) seq = 3; + else if (strcasecmp( argv[3], "LOC" ) == 0) seq = 4; + else + rc = -1; + } + } + else if (argc != 1 && argc != 2) + rc = -1; + + /* If no errors, perform the requested function */ + + if (!rc) + { + /* Retrieve a copy of the locks list */ + + count = hthreads_copy_locks_list( &ilk ); + + /* Sort them into the requested sequence */ + + if (count) + { + switch (seq) + { + case 1: qsort( ilk, count, sizeof( ILOCK ), (CMPFUNC*) sortby_tim ); break; + case 2: qsort( ilk, count, sizeof( ILOCK ), (CMPFUNC*) sortby_tid ); break; + case 3: qsort( ilk, count, sizeof( ILOCK ), (CMPFUNC*) sortby_nam ); break; + case 4: qsort( ilk, count, sizeof( ILOCK ), (CMPFUNC*) sortby_loc ); break; + default: + BREAK_INTO_DEBUGGER(); + } + + /* Display the requested locks */ + + for (c=0, i=0; i < count; i++ ) + { + if (0 + || !tid + || (equal_threads( tid, -1 ) && !equal_threads( ilk[i].tid, 0 )) + || equal_threads( tid, ilk[i].tid ) + ) + { + c=1; + FormatTIMEVAL( &ilk[i].time, tod, sizeof( tod )); + // "Lock=%s, tid="TIDPAT", tod=%s, loc=%s" + WRMSG( HHC90017, "I", ilk[i].name, ilk[i].tid, &tod[11], TRIMLOC( ilk[i].location )); + } + } + + free( ilk ); + } + else + c = 1; + + /* Print results */ + + if (!c) + { + // "No locks found for thread "TIDPAT"." + WRMSG( HHC90019, "W", tid ); + } + else if (!tid) + { + // "Total locks defined: %d" + WRMSG( HHC90018, "I", count ); + } + } + } + + if (rc) + { + // "Missing or invalid argument(s)" + WRMSG( HHC17000, "E" ); + } + + return rc; +} diff --git a/hthreads.h b/hthreads.h index 86c6f52b..d74452ed 100644 --- a/hthreads.h +++ b/hthreads.h @@ -212,75 +212,71 @@ typedef pthread_rwlock_t HRWLOCK; #endif /* !defined( OPTION_FTHREADS ) */ /*-------------------------------------------------------------------*/ -/* hthreads lock structures */ +/* Hercules lock structures */ /*-------------------------------------------------------------------*/ struct LOCK { - TID tid; /* Thread-Id of who obtained lock */ - const char* loc; /* Location where it was obtained */ - HLOCK locklock; /* Internal LOCK structure lock */ - HLOCK lock; /* The actual locking model lock */ + void* ilk; /* ptr to internal ILOCK structure */ }; typedef struct LOCK LOCK; struct RWLOCK { - TID tid; /* Thread-Id of who obtained lock */ - const char* loc; /* Location where it was obtained */ - HLOCK locklock; /* Internal LOCK structure lock */ - HRWLOCK lock; /* The actual locking model rwlock */ + void* ilk; /* ptr to internal ILOCK structure */ }; typedef struct RWLOCK RWLOCK; /*-------------------------------------------------------------------*/ /* hthreads exported functions */ /*-------------------------------------------------------------------*/ -HT_DLL_IMPORT int hthread_initialize_lock ( LOCK* plk, const char* loc ); -HT_DLL_IMPORT int hthread_obtain_lock ( LOCK* plk, const char* loc ); -HT_DLL_IMPORT int hthread_try_obtain_lock ( LOCK* plk, const char* loc ); -HT_DLL_IMPORT int hthread_test_lock ( LOCK* plk, const char* loc ); -HT_DLL_IMPORT int hthread_release_lock ( LOCK* plk, const char* loc ); -HT_DLL_IMPORT int hthread_destroy_lock ( LOCK* plk, const char* loc ); +HT_DLL_IMPORT int locks_cmd( int argc, char* argv[], char* cmdline ); -HT_DLL_IMPORT int hthread_initialize_rwlock ( RWLOCK* plk, const char* loc ); -HT_DLL_IMPORT int hthread_destroy_rwlock ( RWLOCK* plk, const char* loc ); -HT_DLL_IMPORT int hthread_obtain_rdlock ( RWLOCK* plk, const char* loc ); -HT_DLL_IMPORT int hthread_obtain_wrlock ( RWLOCK* plk, const char* loc ); -HT_DLL_IMPORT int hthread_release_rwlock ( RWLOCK* plk, const char* loc ); -HT_DLL_IMPORT int hthread_try_obtain_rdlock ( RWLOCK* plk, const char* loc ); -HT_DLL_IMPORT int hthread_try_obtain_wrlock ( RWLOCK* plk, const char* loc ); -HT_DLL_IMPORT int hthread_test_rdlock ( RWLOCK* plk, const char* loc ); -HT_DLL_IMPORT int hthread_test_wrlock ( RWLOCK* plk, const char* loc ); +HT_DLL_IMPORT int hthread_initialize_lock ( LOCK* plk, const char* name, const char* location ); +HT_DLL_IMPORT int hthread_obtain_lock ( LOCK* plk, const char* location ); +HT_DLL_IMPORT int hthread_try_obtain_lock ( LOCK* plk, const char* location ); +HT_DLL_IMPORT int hthread_test_lock ( LOCK* plk, const char* location ); +HT_DLL_IMPORT int hthread_release_lock ( LOCK* plk, const char* location ); +HT_DLL_IMPORT int hthread_destroy_lock ( LOCK* plk, const char* location ); -HT_DLL_IMPORT int hthread_initialize_condition ( COND* plc, const char* loc ); -HT_DLL_IMPORT int hthread_destroy_condition ( COND* plc, const char* loc ); -HT_DLL_IMPORT int hthread_signal_condition ( COND* plc, const char* loc ); -HT_DLL_IMPORT int hthread_broadcast_condition ( COND* plc, const char* loc ); -HT_DLL_IMPORT int hthread_wait_condition ( COND* plc, LOCK* plk, const char* loc ); -HT_DLL_IMPORT int hthread_timed_wait_condition ( COND* plc, LOCK* plk, const struct timespec* tm, const char* loc ); +HT_DLL_IMPORT int hthread_initialize_rwlock ( RWLOCK* plk, const char* name, const char* location ); +HT_DLL_IMPORT int hthread_destroy_rwlock ( RWLOCK* plk, const char* location ); +HT_DLL_IMPORT int hthread_obtain_rdlock ( RWLOCK* plk, const char* location ); +HT_DLL_IMPORT int hthread_obtain_wrlock ( RWLOCK* plk, const char* location ); +HT_DLL_IMPORT int hthread_release_rwlock ( RWLOCK* plk, const char* location ); +HT_DLL_IMPORT int hthread_try_obtain_rdlock ( RWLOCK* plk, const char* location ); +HT_DLL_IMPORT int hthread_try_obtain_wrlock ( RWLOCK* plk, const char* location ); +HT_DLL_IMPORT int hthread_test_rdlock ( RWLOCK* plk, const char* location ); +HT_DLL_IMPORT int hthread_test_wrlock ( RWLOCK* plk, const char* location ); -HT_DLL_IMPORT int hthread_initialize_join_attr ( ATTR* pat, const char* loc ); -HT_DLL_IMPORT int hthread_initialize_detach_attr ( ATTR* pat, const char* loc ); -HT_DLL_IMPORT int hthread_create_thread ( TID* ptid, ATTR* pat, THREAD_FUNC* pfn, void* arg, const char* name, const char* loc ); -HT_DLL_IMPORT int hthread_join_thread ( TID tid, void** prc, const char* loc ); -HT_DLL_IMPORT int hthread_detach_thread ( TID tid, const char* loc ); -HT_DLL_IMPORT int hthread_signal_thread ( TID tid, int sig, const char* loc ); -HT_DLL_IMPORT TID hthread_thread_id ( const char* loc ); -HT_DLL_IMPORT void hthread_exit_thread ( void* rc, const char* loc ); -HT_DLL_IMPORT int hthread_equal_threads ( TID tid1, TID tid2, const char* loc ); -HT_DLL_IMPORT int hthread_win_thread_handle ( TID tid, const char* loc ); +HT_DLL_IMPORT int hthread_initialize_condition ( COND* plc, const char* location ); +HT_DLL_IMPORT int hthread_destroy_condition ( COND* plc, const char* location ); +HT_DLL_IMPORT int hthread_signal_condition ( COND* plc, const char* location ); +HT_DLL_IMPORT int hthread_broadcast_condition ( COND* plc, const char* location ); +HT_DLL_IMPORT int hthread_wait_condition ( COND* plc, LOCK* plk, const char* location ); +HT_DLL_IMPORT int hthread_timed_wait_condition ( COND* plc, LOCK* plk, const struct timespec* tm, const char* location ); + +HT_DLL_IMPORT int hthread_initialize_join_attr ( ATTR* pat, const char* location ); +HT_DLL_IMPORT int hthread_initialize_detach_attr ( ATTR* pat, const char* location ); +HT_DLL_IMPORT int hthread_create_thread ( TID* ptid, ATTR* pat, THREAD_FUNC* pfn, void* arg, const char* name, const char* location ); +HT_DLL_IMPORT int hthread_join_thread ( TID tid, void** prc, const char* location ); +HT_DLL_IMPORT int hthread_detach_thread ( TID tid, const char* location ); +HT_DLL_IMPORT int hthread_signal_thread ( TID tid, int sig, const char* location ); +HT_DLL_IMPORT TID hthread_thread_id ( const char* location ); +HT_DLL_IMPORT void hthread_exit_thread ( void* rc, const char* location ); +HT_DLL_IMPORT int hthread_equal_threads ( TID tid1, TID tid2, const char* location ); +HT_DLL_IMPORT int hthread_win_thread_handle ( TID tid, const char* location ); /*-------------------------------------------------------------------*/ /* Hercules threading/locking macros */ /*-------------------------------------------------------------------*/ -#define initialize_lock( plk ) hthread_initialize_lock( plk, PTT_LOC ) +#define initialize_lock( plk ) hthread_initialize_lock( plk, #plk, PTT_LOC ) #define obtain_lock( plk ) hthread_obtain_lock( plk, PTT_LOC ) #define try_obtain_lock( plk ) hthread_try_obtain_lock( plk, PTT_LOC ) #define test_lock( plk ) hthread_test_lock( plk, PTT_LOC ) #define release_lock(plk) hthread_release_lock( plk, PTT_LOC ) #define destroy_lock(plk) hthread_destroy_lock( plk, PTT_LOC ) -#define initialize_rwlock( plk ) hthread_initialize_rwlock( plk, PTT_LOC ) +#define initialize_rwlock( plk ) hthread_initialize_rwlock( plk, #plk, PTT_LOC ) #define destroy_rwlock( plk ) hthread_destroy_rwlock( plk, PTT_LOC ) #define obtain_rdlock( plk ) hthread_obtain_rdlock( plk, PTT_LOC ) #define obtain_wrlock( plk ) hthread_obtain_wrlock( plk, PTT_LOC ) diff --git a/msgenu.h b/msgenu.h index dc6b7970..8820bfec 100644 --- a/msgenu.h +++ b/msgenu.h @@ -1999,12 +1999,17 @@ do { \ #define HHC90000 "DBG: %s" #define HHC90001 " *** Assertion Failed! *** %s(%d); function: %s()" -/* pttrace.c */ +/* hthreads.c, pttrace.c */ #define HHC90010 "Pttrace: trace is busy" #define HHC90011 "Pttrace: invalid argument %s" #define HHC90012 "Pttrace: %s%s%s%s%s%s%s%s%s%s%s %s %s to %d %d" -#define HHC90013 "'%s' failed: rc=%d (%s), tid="TIDPAT", loc=%s" -#define HHC90014 "lock was obtained by thread "TIDPAT" at %s" +#define HHC90013 "'%s(%s)' failed: rc=%d: %s; tid="TIDPAT", loc=%s" +#define HHC90014 "lock %s was obtained by thread "TIDPAT" at %s" +#define HHC90015 "Thread "TIDPAT" abandoned lock %s obtained on %s at %s" +#define HHC90016 "Thread "TIDPAT" abandoned at %s lock %s obtained on %s at %s" +#define HHC90017 "Lock=%s, tid="TIDPAT", tod=%s, loc=%s" +#define HHC90018 "Total locks defined: %d" +#define HHC90019 "No locks found for thread "TIDPAT"." /* from crypto/dyncrypt.c when compiled with debug on */ #define HHC90100 "%s" diff --git a/pttrace.c b/pttrace.c index 947ef33b..a334592a 100644 --- a/pttrace.c +++ b/pttrace.c @@ -381,7 +381,7 @@ DLL_EXPORT void ptt_trace_init( int n, int init ) /*-------------------------------------------------------------------*/ DLL_EXPORT void ptt_pthread_trace (int trclass, const char *msg, const void *data1, const void *data2, - const char *loc, int rc) + const char *loc, int rc, TIMEVAL* pTV) { int i, n; @@ -413,7 +413,12 @@ int i, n; /* Fill in the trace table entry */ if (pttnotod == 0) - gettimeofday(&pttrace[i].tv,NULL); + { + if (pTV) + memcpy( &pttrace[i].tv, pTV, sizeof( TIMEVAL )); + else + gettimeofday( &pttrace[i].tv, NULL ); + } pttrace[i].tid = hthread_self(); pttrace[i].trclass = trclass; pttrace[i].msg = msg; @@ -430,8 +435,7 @@ DLL_EXPORT int ptt_pthread_print () { int i, n, count = 0; char retcode[32]; // (retcode is 'int'; if x64, 19 digits or more!) -char tbuf[256]; -time_t tt; +char tod[27]; // "YYYY-MM-DD HH:MM:SS.uuuuuu" if (pttrace && pttracen) { @@ -447,7 +451,7 @@ time_t tt; { if (pttrace[i].tid) { - tt = pttrace[i].tv.tv_sec; strlcpy(tbuf, ctime(&tt),sizeof(tbuf)); tbuf[19] = '\0'; + FormatTIMEVAL( &pttrace[i].tv, tod, sizeof( tod )); if (pttrace[i].rc == PTT_MAGIC && (pttrace[i].trclass & PTT_CL_THR)) retcode[0] = '\0'; @@ -459,7 +463,7 @@ time_t tt; logmsg ( "%-18s " // File name - "%s.%6.6ld " // Time of day (HH:MM:SS.usecs) + "%s " // Time of day (HH:MM:SS.usecs) I32_FMTX" " // Thread id (low 32 bits) "%-12s " // Trace message (string; 12 chars) PTR_FMTx" " // Data value 1 @@ -467,8 +471,7 @@ time_t tt; "%s\n" // Return code (or empty string) ,pttrace[i].loc // File name - ,tbuf + 11 // Time of day (HH:MM:SS) - ,pttrace[i].tv.tv_usec // Time of day (usecs) + ,&tod[11] // Time of day (HH:MM:SS.usecs) ,(U32)(uintptr_t)(pttrace[i].tid) // Thread id (low 32 bits) ,pttrace[i].msg // Trace message (string; 12 chars) ,(uintptr_t)pttrace[i].data1 // Data value 1 diff --git a/pttrace.h b/pttrace.h index 98bf64f8..ea091fcc 100644 --- a/pttrace.h +++ b/pttrace.h @@ -70,7 +70,7 @@ do { \ ptt_pthread_trace( (_class), (_msg),(void*)(uintptr_t)(_data1), \ (void*)(uintptr_t)(_data2), \ PTT_LOC, \ - (int)(_rc)); \ + (int)(_rc),NULL); \ } while(0) /*-------------------------------------------------------------------*/ @@ -85,7 +85,7 @@ do { \ /*-------------------------------------------------------------------*/ PTT_DLL_IMPORT void ptt_trace_init ( int n, int init ); PTT_DLL_IMPORT int ptt_cmd ( int argc, char* argv[], char* cmdline ); -PTT_DLL_IMPORT void ptt_pthread_trace ( int, const char*, const void*, const void*, const char*, int ); +PTT_DLL_IMPORT void ptt_pthread_trace ( int, const char*, const void*, const void*, const char*, int, TIMEVAL* ); PTT_DLL_IMPORT int ptt_pthread_print (); PTT_DLL_IMPORT U32 pttclass; PTT_DLL_IMPORT int pttthread; diff --git a/w32util.c b/w32util.c index a054613a..afe3be41 100644 --- a/w32util.c +++ b/w32util.c @@ -443,8 +443,17 @@ static ULARGE_INTEGER FileTimeTo1970Nanoseconds( const FILETIME* pFT ) ////////////////////////////////////////////////////////////////////////////////////////// -// (PUBLIC) Nanosecond resolution TOD clock (clock_gettime) - +// (PUBLIC) Nanosecond resolution (not quite but almost!) TOD clock (clock_gettime) +// +// ** CRITICAL PROGRAMMING NOTE! ** +// +// Because the new hthreads design calls gettimeofday to save the time when a lock is +// initialized or obtained, etc, the below function nor any of the functions it calls +// may call logmsg either directly or indirectly (such as using the 'TRACE' macro) or +// else an infinite loop will occur since our logger design uses locks! hthreads will +// call gettimeofday which issues a message which calls logger which uses a lock and +// hthreads calls gettimeofday again, etc. +// DLL_EXPORT int clock_gettime ( clockid_t clk_id, struct timespec *tp ) { ULARGE_INTEGER uliWork; // (current HPC tick count and work) @@ -494,7 +503,6 @@ DLL_EXPORT int clock_gettime ( clockid_t clk_id, struct timespec *tp ) if (!uliHPCTicksPerSec.QuadPart) // (we only need to do this once) { VERIFY( QueryPerformanceFrequency( (LARGE_INTEGER*)&uliHPCTicksPerSec )); - TRACE("w32util: uliHPCTicksPerSec = 0x%16.16llX (%llu)\n", uliHPCTicksPerSec.QuadPart, uliHPCTicksPerSec.QuadPart ); // Verify the length of time between host TOD clock resyncs isn't // so very long that the number of High Performance Counter ticks @@ -503,11 +511,9 @@ DLL_EXPORT int clock_gettime ( clockid_t clk_id, struct timespec *tp ) while (uliHPCTicksPerSec.QuadPart > (_UI64_MAX / (uiResyncSecs + 1))) uiResyncSecs--; - TRACE("w32util: uiResyncSecs = %lu\n", uiResyncSecs ); uliMaxElapsedHPCTicks.QuadPart = uliHPCTicksPerSec.QuadPart * uiResyncSecs; - TRACE("w32util: uliMaxElapsedHPCTicks = 0x%16.16llX (%llu)\n", uliMaxElapsedHPCTicks.QuadPart, uliMaxElapsedHPCTicks.QuadPart ); // Calculate the maximum supported clock resolution such that we don't // resync with the host TOD clock more than once every resync interval. @@ -517,7 +523,6 @@ DLL_EXPORT int clock_gettime ( clockid_t clk_id, struct timespec *tp ) { u64ClockResolution /= 10; // (decrease TOD clock resolution) } - TRACE("w32util: u64ClockResolution = %llu (%11.9f)\n", u64ClockResolution, 1.0 / (double)u64ClockResolution ); // (check for error condition...) @@ -529,7 +534,6 @@ DLL_EXPORT int clock_gettime ( clockid_t clk_id, struct timespec *tp ) } u64ClockNanoScale = (MAX_GTOD_RESOLUTION / u64ClockResolution); - TRACE("w32util: u64ClockNanoScale = %llu\n", u64ClockNanoScale ); } uliStartingNanoTime = FileTimeTo1970Nanoseconds( &ftStartingSystemTime ); @@ -623,7 +627,16 @@ DLL_EXPORT int clock_gettime ( clockid_t clk_id, struct timespec *tp ) ////////////////////////////////////////////////////////////////////////////////////////// // (PUBLIC) Microsecond resolution GTOD (getimeofday)... - +// +// ** CRITICAL PROGRAMMING NOTE! ** +// +// Because the new hthreads design calls gettimeofday to save the time when a lock is +// initialized or obtained, etc, the below function nor any of the functions it calls +// may call logmsg either directly or indirectly (such as using the 'TRACE' macro) or +// else an infinite loop will occur since our logger design uses locks! hthreads will +// call gettimeofday which issues a message which calls logger which uses a lock and +// hthreads calls gettimeofday again, etc. +// DLL_EXPORT int gettimeofday ( struct timeval* pTV, void* pTZ ) { static struct timeval tvPrevRetVal = {0};