/* HTHREADS.C (C) Copyright "Fish" (David B. Trout), 2013 */ /* Hercules locking and threading */ /* */ /* Released under "The Q Public License Version 1" */ /* (http://www.hercules-390.org/herclic.html) as modifications to */ /* Hercules. */ /*-------------------------------------------------------------------*/ /* Based on original pttrace.c (C) Copyright Greg Smith, 2003-2013 */ /* Collaboratively enhanced and extended in May-June 2013 */ /* by Mark L. Gaubatz and "Fish" (David B. Trout) */ /*-------------------------------------------------------------------*/ #include "hstdinc.h" #define _HTHREAD_C_ #define _HUTIL_DLL_ #include "hercules.h" /*-------------------------------------------------------------------*/ /* 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,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) /*-------------------------------------------------------------------*/ /* Default stack size for create_thread */ /*-------------------------------------------------------------------*/ #define HTHREAD_STACK_SIZE ONE_MEGABYTE /*-------------------------------------------------------------------*/ /* Issue locking call error message */ /*-------------------------------------------------------------------*/ static void loglock( ILOCK* ilk, const int rc, const char* calltype, const char* err_loc ) { const char* err_desc; switch (rc) { case EAGAIN: err_desc = "max recursion"; break; case EPERM: err_desc = "not owned"; break; case EINVAL: err_desc = "invalid argument"; break; case EDEADLK: err_desc = "deadlock"; break; case ENOTRECOVERABLE: err_desc = "not recoverable"; break; case EOWNERDEAD: err_desc = "owner dead"; break; case EBUSY: err_desc = "busy"; break; /* (should not occur) */ case ETIMEDOUT: err_desc = "timeout"; break; /* (should not occur) */ default: err_desc = "(unknown)"; break; } // "'%s(%s)' failed: rc=%d: %s; tid="TIDPAT", loc=%s" WRMSG( HHC90013, "E", calltype, ilk->name, rc, err_desc, hthread_self(), TRIMLOC( err_loc )); if (ilk->tid) { // "lock %s was obtained by thread "TIDPAT" at %s" WRMSG( HHC90014, "I", ilk->name, ilk->tid, TRIMLOC( ilk->location )); } } /*-------------------------------------------------------------------*/ /* Hthreads internal variables */ /*-------------------------------------------------------------------*/ 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 */ static int herc_policy; /* Herc scheduling policy */ static int herc_low_pri; /* Herc policy minimum priority */ static int herc_pri_amt; /* Herc policy priority range */ static int herc_pri_rvrsd; /* More negative is higher prio */ static int host_low_pri; /* Host policy minimim priority */ static int host_pri_amt; /* Host policy priority range */ static int host_pri_rvrsd; /* More negative is higher prio */ /*-------------------------------------------------------------------*/ /* 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, herc_high_pri, host_high_pri; /* 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; /* Initialize thread scheduling variables */ herc_policy = HTHREAD_SCHED_DEF; herc_low_pri = HTHREAD_MIN_PRI; herc_high_pri = HTHREAD_MAX_PRI; host_low_pri = hthread_sched_get_priority_min( herc_policy ); host_high_pri = hthread_sched_get_priority_max( herc_policy ); herc_pri_rvrsd = (herc_high_pri < herc_low_pri); host_pri_rvrsd = (host_high_pri < host_low_pri); herc_pri_amt = herc_pri_rvrsd ? (herc_low_pri - herc_high_pri) : (herc_high_pri - herc_low_pri); host_pri_amt = host_pri_rvrsd ? (host_low_pri - host_high_pri) : (host_high_pri - host_low_pri); /* One-time initialization completed */ 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* name, const char* location ) { int rc; MATTR attr; ILOCK* ilk = hthreads_get_ILOCK( plk, name ); /* Initialize the requested 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( &ilk->locklock, &attr ); if (rc) goto fatal; rc = hthread_mutex_init( &ilk->lock, &attr ); if (rc) goto fatal; rc = hthread_mutexattr_destroy( &attr ); if (rc) goto fatal; plk->ilk = ilk; /* (LOCK is now initialized) */ PTTRACE( "lock init", plk, 0, location, PTT_MAGIC ); return 0; fatal: 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* name, const char* location ) { int rc; RWATTR attr1; /* for primary lock */ MATTR attr2; /* for internal locklock */ ILOCK* ilk = hthreads_get_ILOCK( plk, name ); /* Initialize the requested lock */ rc = hthread_rwlockattr_init( &attr1 ); if (rc) goto fatal; rc = hthread_mutexattr_init( &attr2 ); if (rc) goto fatal; rc = hthread_rwlockattr_setpshared( &attr1, HTHREAD_RWLOCK_DEFAULT ); if (rc) goto fatal; rc = hthread_mutexattr_settype( &attr2, HTHREAD_MUTEX_DEFAULT ); if (rc) goto fatal; rc = hthread_rwlock_init( &ilk->rwlock, &attr1 ); if (rc) goto fatal; rc = hthread_mutex_init( &ilk->locklock, &attr2 ); if (rc) goto fatal; rc = hthread_rwlockattr_destroy( &attr1 ); if (rc) goto fatal; rc = hthread_mutexattr_destroy( &attr2 ); if (rc) goto fatal; plk->ilk = ilk; /* (RWLOCK is now initialized) */ PTTRACE( "rwlock init", plk, &attr1, location, PTT_MAGIC ); return 0; fatal: perror( "Fatal error in hthread_initialize_rwlock function" ); exit(1); } /*-------------------------------------------------------------------*/ /* Obtain a lock */ /*-------------------------------------------------------------------*/ DLL_EXPORT int hthread_obtain_lock( LOCK* plk, const char* location ) { int rc; 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) { waitdur = host_tod(); rc = hthread_mutex_lock( &ilk->lock ); gettimeofday( &tv, NULL ); waitdur = host_tod() - waitdur; } else { gettimeofday( &tv, NULL ); waitdur = 0; } PTTRACE2( "lock after", plk, (void*) waitdur, location, rc, &tv ); if (rc) loglock( ilk, rc, "obtain_lock", location ); if (!rc || EOWNERDEAD == rc) { 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; } /*-------------------------------------------------------------------*/ /* Release a lock */ /*-------------------------------------------------------------------*/ DLL_EXPORT int hthread_release_lock( LOCK* plk, const char* location ) { int rc; ILOCK* ilk; ilk = (ILOCK*) plk->ilk; rc = hthread_mutex_unlock( &ilk->lock ); PTTRACE( "unlock", plk, NULL, location, rc ); if (rc) 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* location ) { int rc; ILOCK* ilk; ilk = (ILOCK*) plk->ilk; rc = hthread_rwlock_unlock( &ilk->rwlock ); PTTRACE( "rwunlock", plk, NULL, location, rc ); if (rc) 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* location ) { int rc; 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* location ) { int rc; 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* location ) { int 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( ilk, rc, "try_obtain_lock", location ); if (!rc || EOWNERDEAD == rc) { 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; } /*-------------------------------------------------------------------*/ /* Test if lock is held */ /*-------------------------------------------------------------------*/ DLL_EXPORT int hthread_test_lock( LOCK* plk, const char* location ) { int rc; ILOCK* ilk; UNREFERENCED( location ); ilk = (ILOCK*) plk->ilk; rc = hthread_mutex_trylock( &ilk->lock ); if (rc) return rc; 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* location ) { int rc; 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) { waitdur = host_tod(); rc = hthread_rwlock_rdlock( &ilk->rwlock ); waitdur = host_tod() - waitdur; } else waitdur = 0; PTTRACE( "rdlock after", plk, (void*) waitdur, location, rc ); if (rc) 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* location ) { int rc; 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) { waitdur = host_tod(); rc = hthread_rwlock_wrlock( &ilk->rwlock ); gettimeofday( &tv, NULL ); waitdur = host_tod() - waitdur; } else { gettimeofday( &tv, NULL ); waitdur = 0; } PTTRACE2( "wrlock after", plk, (void*) waitdur, location, rc, &tv ); if (rc) loglock( ilk, rc, "obtain_wrlock", location ); if (!rc || EOWNERDEAD == rc) { 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; } /*-------------------------------------------------------------------*/ /* Try to obtain read-only access to a R/W lock */ /*-------------------------------------------------------------------*/ DLL_EXPORT int hthread_try_obtain_rdlock( RWLOCK* plk, const char* location ) { int 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( 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* location ) { int 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( ilk, rc, "try_obtain_wrlock", location ); if (!rc) { 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; } /*-------------------------------------------------------------------*/ /* Test if read-only access to a R/W lock is held */ /*-------------------------------------------------------------------*/ DLL_EXPORT int hthread_test_rdlock( RWLOCK* plk, const char* location ) { int rc; ILOCK* ilk; UNREFERENCED( location ); ilk = (ILOCK*) plk->ilk; rc = hthread_rwlock_tryrdlock( &ilk->rwlock ); if (rc) return rc; 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* location ) { int rc; ILOCK* ilk; UNREFERENCED( location ); ilk = (ILOCK*) plk->ilk; rc = hthread_rwlock_trywrlock( &ilk->rwlock ); if (rc) return rc; hthread_rwlock_unlock( &ilk->rwlock ); return 0; } /*-------------------------------------------------------------------*/ /* Initialize a condition variable */ /*-------------------------------------------------------------------*/ DLL_EXPORT int hthread_initialize_condition( COND* plc, const char* location ) { int rc; PTTRACE( "cond init", NULL, plc, location, PTT_MAGIC ); rc = hthread_cond_init( plc ); return rc; } /*-------------------------------------------------------------------*/ /* Destroy a condition variable */ /*-------------------------------------------------------------------*/ DLL_EXPORT int hthread_destroy_condition( COND* plc, const char* location ) { int rc; UNREFERENCED( location ); rc = hthread_cond_destroy( plc ); return rc; } /*-------------------------------------------------------------------*/ /* Signal a condition variable (releases only one thread) */ /*-------------------------------------------------------------------*/ DLL_EXPORT int hthread_signal_condition( COND* plc, const char* location ) { int rc; rc = hthread_cond_signal( plc ); 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* location ) { int rc; rc = hthread_cond_broadcast( plc ); 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* location ) { int 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( 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* location ) { int 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( ilk, rc, "timed_wait_condition", location ); return rc; } /*-------------------------------------------------------------------*/ /* Internal helper function to initialize a thread attribute */ /*-------------------------------------------------------------------*/ static INLINE int hthread_init_thread_attr( ATTR* pat, int state, const char* location ) { int rc; UNREFERENCED( location ); rc = hthread_attr_init( pat ); if (!rc) rc = hthread_attr_setstacksize( pat, HTHREAD_STACK_SIZE ); if (!rc) rc = hthread_attr_setdetachstate( pat, state ); return rc; } /*-------------------------------------------------------------------*/ /* Initialize a thread attribute to "joinable" */ /*-------------------------------------------------------------------*/ DLL_EXPORT int hthread_initialize_join_attr( ATTR* pat, const char* location ) { int rc; 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* location ) { int rc; 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; if (sysblk.shutdown) return; LockLocksList(); for (ple = locklist.Flink; ple != &locklist; ple = ple->Flink) { ilk = CONTAINING_RECORD( ple, ILOCK, locklink ); if (hthread_equal(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* location ) { int rc; 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* location ) { int rc; PTTRACE( "join before", (void*) tid, prc ? *prc : NULL, location, PTT_MAGIC ); rc = hthread_join( tid, prc ); 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* location ) { int rc; PTTRACE( "dtch before", (void*) tid, NULL, location, PTT_MAGIC ); rc = hthread_detach( tid ); 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* location ) { int rc; PTTRACE( "kill", (void*) tid, (void*)(long)sig, location, PTT_MAGIC ); rc = hthread_kill( tid, sig ); return rc; } /*-------------------------------------------------------------------*/ /* Return calling thread's ID */ /*-------------------------------------------------------------------*/ DLL_EXPORT TID hthread_thread_id( const char* location ) { TID tid; UNREFERENCED( location ); tid = hthread_self(); return tid; } /*-------------------------------------------------------------------*/ /* Exit immediately from a thread */ /*-------------------------------------------------------------------*/ DLL_EXPORT void hthread_exit_thread( void* rc, const char* location ) { 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* location ) { int rc; UNREFERENCED( location ); rc = hthread_equal( tid1, tid2 ); return rc; } #if defined(_MSVC_) /*-------------------------------------------------------------------*/ /* Return Windows thread HANDLE */ /*-------------------------------------------------------------------*/ DLL_EXPORT HANDLE hthread_win_thread_handle( TID tid ) { return hthread_get_handle( tid ); } #endif /*-------------------------------------------------------------------*/ /* Convert Hercules thread scheduling priority to host value */ /*-------------------------------------------------------------------*/ static int herc2host( int herc_pri ) { double pct; int host_pri; if (0 || (!herc_pri_rvrsd && (herc_pri < herc_low_pri || (herc_pri - herc_low_pri) > herc_pri_amt)) || ( herc_pri_rvrsd && (herc_pri > herc_low_pri || (herc_low_pri - herc_pri) > herc_pri_amt)) ) return INT_MAX; pct = herc_pri_rvrsd ? ((double)(herc_low_pri - herc_pri) / (double)herc_pri_amt) : ((double)(herc_pri - herc_low_pri) / (double)herc_pri_amt); host_pri = host_pri_rvrsd ? host_low_pri - (int)((pct * (double)host_pri_amt) + 0.5) : host_low_pri + (int)((pct * (double)host_pri_amt) + 0.5); return host_pri; } /*-------------------------------------------------------------------*/ /* Convert Host thread scheduling priority to Hercules value */ /*-------------------------------------------------------------------*/ static int host2herc( int host_pri ) { double pct; int herc_pri; if (0 || (!host_pri_rvrsd && (host_pri < host_low_pri || (host_pri - host_low_pri) > host_pri_amt)) || ( host_pri_rvrsd && (host_pri > host_low_pri || (host_low_pri - host_pri) > host_pri_amt)) ) return INT_MAX; pct = host_pri_rvrsd ? ((double)(host_low_pri - host_pri) / (double)host_pri_amt) : ((double)(host_pri - host_low_pri) / (double)host_pri_amt); herc_pri = herc_pri_rvrsd ? herc_low_pri - (int)((pct * (double)herc_pri_amt) + 0.5) : herc_low_pri + (int)((pct * (double)herc_pri_amt) + 0.5); return herc_pri; } /*-------------------------------------------------------------------*/ /* Change a thread's dispatching priority (host function) */ /*-------------------------------------------------------------------*/ static int hthread_setschedprio( TID tid, int prio, const char* location ) { int rc; struct sched_param param; memset( ¶m, 0, sizeof( param )); if (equal_threads(tid,0)) tid = thread_id(); param.sched_priority = prio; rc = hthread_setschedparam( tid, herc_policy, ¶m ); if (rc != 0) // "'%s' failed at loc=%s: rc=%d: %s" WRMSG( HHC90020, "W", "set_thread_priority", TRIMLOC( location ), rc, strerror( rc )); return rc; } /*-------------------------------------------------------------------*/ /* Change a thread's dispatching priority (HTHREADS function) */ /*-------------------------------------------------------------------*/ DLL_EXPORT int hthread_set_thread_prio( TID tid, int prio, const char* location ) { int host_prio, rc; host_prio = herc2host( prio ); SETMODE(ROOT); rc = hthread_setschedprio( tid, host_prio, location ); SETMODE(USER); return rc; } /*-------------------------------------------------------------------*/ /* Retrieve a thread's dispatching priority (host function) */ /*-------------------------------------------------------------------*/ static int hthread_getschedprio( TID tid, const char* location ) { int rc, prio, dummy; struct sched_param param; memset( ¶m, 0, sizeof( param )); if (equal_threads(tid,0)) tid = thread_id(); rc = hthread_getschedparam( tid, &dummy, ¶m ); if (rc == 0) prio = param.sched_priority; else { prio = INT_MAX; // "'%s' failed at loc=%s: rc=%d: %s" WRMSG( HHC90020, "W", "get_thread_priority", TRIMLOC( location ), rc, strerror( rc )); } return prio; } /*-------------------------------------------------------------------*/ /* Retrieve a thread's dispatching priority (HTHREADS function) */ /*-------------------------------------------------------------------*/ DLL_EXPORT int hthread_get_thread_prio( TID tid, const char* location ) { int host_prio, herc_prio; SETMODE(ROOT); host_prio = hthread_getschedprio( tid, location ); SETMODE(USER); if (INT_MAX == host_prio) return INT_MAX; herc_prio = host2herc( host_prio ); return herc_prio; } /*-------------------------------------------------------------------*/ /* 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], TIDPAT"%c", (U64*)&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; }