DEVTHREADPARMS* CreateDeviceThread(unsigned short wDevNum) { DEVTHREADPARMS* pThreadParms; // ptr to returned device_thread parameters DWORD dwThreadID; // (work) pThreadParms = malloc(sizeof(DEVTHREADPARMS)); // (allocate structure) if (!pThreadParms) { WRMSG ( HHC04111, "E", 0, wDevNum, "malloc(DEVTHREADPARMS)", errno, strerror(errno) ); return NULL; // (error) } pThreadParms->hShutdownEvent = MyCreateEvent(NULL,TRUE,FALSE,NULL); if (!pThreadParms->hShutdownEvent) { WRMSG ( HHC04111, "E", 0, wDevNum, "CreateEvent(hShutdownEvent)", errno, strerror(errno) ); free(pThreadParms); return NULL; // (error) } pThreadParms->hRequestQueuedEvent = MyCreateEvent(NULL,TRUE,FALSE,NULL); if (!pThreadParms->hRequestQueuedEvent) { WRMSG ( HHC04111, "E", 0, wDevNum, "CreateEvent(hRequestQueuedEvent)", errno, strerror(errno) ); MyCloseHandle(pThreadParms->hShutdownEvent); free(pThreadParms); return NULL; // (error) } MyInitializeCriticalSection(&pThreadParms->IORequestListLock); InitializeListLink(&pThreadParms->ThreadListLinkingListEntry); InitializeListHead(&pThreadParms->IORequestListHeadListEntry); pThreadParms->bThreadIsDead = FALSE; pThreadParms->dwThreadID = 0; if (fthread_create(&dwThreadID,NULL,DeviceThread,pThreadParms,"DeviceThread") != 0) { WRMSG ( HHC04111, "E", 0, wDevNum, "fthread_create(DeviceThread)", errno, strerror(errno) ); MyCloseHandle(pThreadParms->hShutdownEvent); MyCloseHandle(pThreadParms->hRequestQueuedEvent); MyDeleteCriticalSection(&pThreadParms->IORequestListLock); free(pThreadParms); return NULL; // (error) } // Add the newly created device_thread to the end of our list of managed threads. InsertListTail(&ThreadListHeadListEntry,&pThreadParms->ThreadListLinkingListEntry); if (++ios_devtnbr > ios_devthwm) ios_devthwm = ios_devtnbr; LockThreadParms(pThreadParms); // (lock thread parms before using) return pThreadParms; // (success) }
DLL_EXPORT void FishHangInit( const char* pszFileCreated, const int nLineCreated ) { FISH_THREAD* pFISH_THREAD; FIXFILENAME(pszFileCreated); if ( !( fh_report_stream = fopen( "FishHangReport.txt", "w" ) ) ) { perror( "FishHang report o/p file open failure" ); abort(); } InitializeListHead(&ThreadsListHead); InitializeListHead(&LocksListHead); InitializeListHead(&EventsListHead); InitializeCriticalSection(&ListsLock); if (!(pFISH_THREAD = CreateFISH_THREAD(pszFileCreated,nLineCreated))) { logmsg("** FishHangInit: CreateFISH_THREAD failed\n"); exit(-1); } pFISH_THREAD->dwThreadID = GetCurrentThreadId(); InsertListTail(&ThreadsListHead,&pFISH_THREAD->ThreadListLink); }
DEVTHREADPARMS* SelectDeviceThread() { DEVTHREADPARMS* pThreadParms; // ptr to selected device thread LIST_ENTRY* pListEntry; // (work) pListEntry = ThreadListHeadListEntry.Flink; while (pListEntry != &ThreadListHeadListEntry) { pThreadParms = CONTAINING_RECORD(pListEntry,DEVTHREADPARMS,ThreadListLinkingListEntry); LockThreadParms(pThreadParms); // (freeze moving target) if (pThreadParms->bThreadIsDead) { UnlockThreadParms(pThreadParms); RemoveThisThreadFromOurList(pThreadParms); pListEntry = ThreadListHeadListEntry.Flink; continue; } if (!IsEventSet(pThreadParms->hRequestQueuedEvent)) { RemoveListEntry(pListEntry); InsertListTail(&ThreadListHeadListEntry,pListEntry); return pThreadParms; } UnlockThreadParms(pThreadParms); // (can't use this one) pListEntry = pListEntry->Flink; } return NULL; // (all of them are busy) }
DLL_EXPORT BOOL FishHang_TryEnterCriticalSection ( const char* pszFileWaiting, // source file that attempted it const int nLineWaiting, // line number of source file LPCRITICAL_SECTION lpCriticalSection // address of critical section object ) { FISH_THREAD* pFISH_THREAD; FISH_LOCK* pFISH_LOCK; BOOL bSuccess; FIXFILENAME(pszFileWaiting); LockFishHang(); if (!GetThreadAndLockPtrs(&pFISH_THREAD,&pFISH_LOCK,lpCriticalSection)) FishHangAbort(pszFileWaiting,nLineWaiting); pFISH_THREAD->bWaitingForLock = FALSE; pFISH_THREAD->bTryingForLock = TRUE; pFISH_THREAD->pWhatWaiting = pFISH_LOCK; pFISH_THREAD->pszFileWaiting = pszFileWaiting; pFISH_THREAD->nLineWaiting = nLineWaiting; GetSystemTime(&pFISH_THREAD->timeWaiting); if (PrintDeadlock(pFISH_THREAD,pszFileWaiting,nLineWaiting)) FishHangAbort(pszFileWaiting,nLineWaiting); UnlockFishHang(); bSuccess = TryEnterCriticalSection(lpCriticalSection); LockFishHang(); pFISH_THREAD->bTryingForLock = FALSE; if (bSuccess) { pFISH_THREAD->bWaitingForLock = FALSE; if (pFISH_LOCK->pOwningThread != pFISH_THREAD) { pFISH_LOCK->pOwningThread = pFISH_THREAD; pFISH_LOCK->nLockedDepth = 1; InsertListTail(&pFISH_THREAD->ThreadLockListHead,&pFISH_LOCK->ThreadLockListLink); } else { pFISH_LOCK->nLockedDepth++; } } UnlockFishHang(); return bSuccess; }
DLL_EXPORT HANDLE FishHang_CreateThread ( const char* pszFileCreated, // source file that created it const int nLineCreated, // line number of source file LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to security attributes DWORD dwStackSize, // initial thread stack size LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread function LPVOID lpParameter, // argument for new thread DWORD dwCreationFlags, // creation flags LPDWORD lpThreadId // pointer to receive thread ID ) { FISH_THREAD* pFISH_THREAD; HANDLE hThread; FIXFILENAME(pszFileCreated); if (!(pFISH_THREAD = CreateFISH_THREAD(pszFileCreated,nLineCreated))) return NULL; #ifdef _MSVC_ hThread = (HANDLE) _beginthreadex #else // (Cygwin) hThread = CreateThread #endif ( lpThreadAttributes, // pointer to security attributes dwStackSize, // initial thread stack size lpStartAddress, // pointer to thread function lpParameter, // argument for new thread dwCreationFlags, // creation flags lpThreadId // pointer to receive thread ID ); if (hThread) { pFISH_THREAD->dwThreadID = *lpThreadId; LockFishHang(); InsertListTail(&ThreadsListHead,&pFISH_THREAD->ThreadListLink); UnlockFishHang(); } else { free(pFISH_THREAD); } return hThread; }
/*-------------------------------------------------------------------*/ 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; }
DLL_EXPORT void FishHang_InitializeCriticalSection ( const char* pszFileCreated, // source file that created it const int nLineCreated, // line number of source file LPCRITICAL_SECTION lpCriticalSection // address of critical section object ) { FISH_LOCK* pFISH_LOCK; FIXFILENAME(pszFileCreated); LockFishHang(); pFISH_LOCK = FindFISH_LOCK(lpCriticalSection); if (pFISH_LOCK) { logmsg( "** ERROR ** FISH_LOCK %8.8X already initialized!\n", (int)pFISH_LOCK); logmsg("%s(%d)\n",pszFileCreated,nLineCreated); logmsg("%s\n",PrintFISH_LOCK(pFISH_LOCK)); FishHangAbort(pszFileCreated,nLineCreated); } if (!(pFISH_LOCK = CreateFISH_LOCK(pszFileCreated,nLineCreated))) { logmsg("** FishHang_InitializeCriticalSection: CreateFISH_LOCK failed!\n"); FishHangAbort(pszFileCreated,nLineCreated); } InitializeCriticalSection(lpCriticalSection); pFISH_LOCK->pLock = lpCriticalSection; InsertListTail(&LocksListHead,&pFISH_LOCK->LockListLink); UnlockFishHang(); }
DLL_EXPORT HANDLE FishHang_CreateEvent ( const char* pszFileCreated, // source file that created it const int nLineCreated, // line number of source file LPSECURITY_ATTRIBUTES lpEventAttributes, // pointer to security attributes BOOL bManualReset, // flag for manual-reset event BOOL bInitialState, // flag for initial state LPCTSTR lpName // pointer to event-object name ) { FISH_EVENT* pFISH_EVENT; HANDLE hEvent; FIXFILENAME(pszFileCreated); if (!(pFISH_EVENT = CreateFISH_EVENT(pszFileCreated,nLineCreated))) { logmsg("** FishHang_CreateEvent: CreateFISH_EVENT failed\n"); exit(-1); } if (!(hEvent = CreateEvent(lpEventAttributes,bManualReset,bInitialState,lpName))) { free(pFISH_EVENT); } else { pFISH_EVENT->hEvent = hEvent; LockFishHang(); InsertListTail(&EventsListHead,&pFISH_EVENT->EventsListLink); UnlockFishHang(); } return hEvent; }
int ScheduleIORequest(void* pDevBlk, unsigned short wDevNum, int* pnDevPrio) { ///////////////////////////////////////////////////////////////// // PROGRAMMING NOTE: The various errors that can occur in this // function should probably be reported by returning cc1 with // 'channel control check' set (or something similar), but until // I can work out the details, I'm going to be lazy and just // return cc2 (subchannel busy) for now to allow the system to // retry the i/o request if it so desires. Hopefully the problem // was only intermittent and will work the second time around. ///////////////////////////////////////////////////////////////// DEVIOREQUEST* pIORequest; // ptr to i/o request DEVTHREADPARMS* pThreadParms; // ptr to device_thread parameters // Create an i/o request queue entry for this i/o request pIORequest = (DEVIOREQUEST*) malloc(sizeof(DEVIOREQUEST)); if (!pIORequest) { WRMSG ( HHC04111, "E", 0, wDevNum, "malloc(DEVIOREQUEST)", errno, strerror(errno) ); return 2; } InitializeListLink(&pIORequest->IORequestListLinkingListEntry); pIORequest->pDevBlk = pDevBlk; pIORequest->wDevNum = wDevNum; pIORequest->pnDevPrio = pnDevPrio; // Schedule a device_thread to process this i/o request LockScheduler(); // (lock scheduler vars) if (ios_devtmax < 0) { // Create new "one time only" thread each time... // (Note: the device thread's parms will automatically // be locked upon return if creation was successful.) RemoveDeadThreadsFromList(); // (prevent runaway list) if (!(pThreadParms = CreateDeviceThread(wDevNum))) { UnlockScheduler(); // (unlock scheduler vars) free(pIORequest); // (discard i/o request) return 2; // (return device busy) } } else { // Select a non-busy device thread to handle this i/o request... // (Note: the device thread's parms will automatically // be locked upon return if selection was successful.) if (!(pThreadParms = SelectDeviceThread())) { // All threads are currently busy or no threads exist yet. // Since the possibility of a deadlock[1] can easily occur if we schedule // an i/o request to a currently busy device thread[2], we have no choice // but to create another device thread. // [1] Not an actual programmatic deadlock of course, but nonetheless // a deadlock from the guest operating system's point of view. // [2] A curently busy device thread could easily have its channel program // suspended and thus would never see the queued request until such time // its suspended channel program was resumed and allowed to complete, and // if the request we queued (that would end up waiting to be processed // by the currently suspended device thread) just so happens to be one // that the operating system needs to have completed before it can resume // the currently suspended channel program, then the operating system will // obviously hang. (It would end up waiting for an i/o to complete that // would never complete until the currently suspended channel program was // resumed, which would never be resumed until the queued i/o completes, // which would never even be processed until the currently suspended // channel program is resumed ..... etc.) if (ios_devtmax && ios_devtnbr >= ios_devtmax) // max threads already created? { WRMSG ( HHC04110, "W", ios_devtmax, ( ios_devtnbr - ios_devtmax ) + 1 ); ios_devtunavail++; // (count occurrences) } // Create a new device thread for this i/o request... // (Note: the device thread's parms will automatically // be locked upon return if creation was successful.) if (!(pThreadParms = CreateDeviceThread(wDevNum))) { UnlockScheduler(); // (unlock scheduler vars) free(pIORequest); // (discard i/o request) return 2; // (return device busy) } } } // (Note: the thread parms lock should still be held at this point) // Queue the i/o request to the selected device_thread's i/o request queue... InsertListTail(&pThreadParms->IORequestListHeadListEntry,&pIORequest->IORequestListLinkingListEntry); // Tell device_thread it has work (must do this while its request list is still // locked to prevent it from prematurely exiting in case it's just about to die) MySetEvent(pThreadParms->hRequestQueuedEvent); // Now unlock its request queue so it can process the request we just gave it UnlockThreadParms(pThreadParms); // (let it proceed) // We're done, so unlock the scheduler vars so another cpu thread // in the configuration can schedule and i/o request and then exit // back the the startio function... UnlockScheduler(); // (unlock vars and exit; we're done) return 0; // (success) }
/*-------------------------------------------------------------------*/ int bind_device_ex (DEVBLK* dev, char* spec, ONCONNECT fn, void* arg ) { bind_struct* bs; int was_list_empty; if (!init_done) init_sockdev(); if (sysblk.shutdown) return 0; logdebug("bind_device (%4.4X, %s)\n", dev->devnum, spec); /* Error if device already bound */ if (dev->bs) { logmsg (_("HHCSD001E Device %4.4X already bound to socket %s\n"), dev->devnum, dev->bs->spec); return 0; /* (failure) */ } /* Create a new bind_struct entry */ bs = malloc(sizeof(bind_struct)); if (!bs) { logmsg (_("HHCSD002E bind_device malloc() failed for device %4.4X\n"), dev->devnum); return 0; /* (failure) */ } memset(bs,0,sizeof(bind_struct)); bs->fn = fn; bs->arg = arg; if (!(bs->spec = strdup(spec))) { logmsg (_("HHCSD003E bind_device strdup() failed for device %4.4X\n"), dev->devnum); free (bs); return 0; /* (failure) */ } /* Create a listening socket */ if (bs->spec[0] == '/') bs->sd = unix_socket (bs->spec); else bs->sd = inet_socket (bs->spec); if (bs->sd == -1) { /* (error message already issued) */ free( bs->spec ); free( bs ); return 0; /* (failure) */ } /* Chain device and bind_struct to each other */ dev->bs = bs; bs->dev = dev; /* Add the new entry to our list of bound devices and create the socket thread that will listen for connections (if it doesn't already exist) */ obtain_lock( &bind_lock ); was_list_empty = IsListEmpty( &bind_head ); InsertListTail( &bind_head, &bs->bind_link ); if ( was_list_empty ) { if ( create_thread( &sysblk.socktid, JOINABLE, socket_thread, NULL, "socket_thread" ) ) { logmsg( _( "HHCSD023E Cannot create socketdevice thread: errno=%d: %s\n" ), errno, strerror( errno ) ); RemoveListEntry( &bs->bind_link ); close_socket(bs->sd); free( bs->spec ); free( bs ); release_lock( &bind_lock ); return 0; /* (failure) */ } } SIGNAL_SOCKDEV_THREAD(); release_lock( &bind_lock ); logmsg (_("HHCSD004I Device %4.4X bound to socket %s\n"), dev->devnum, dev->bs->spec); return 1; /* (success) */ }
/*-------------------------------------------------------------------*/ int bind_device_ex (DEVBLK* dev, char* spec, ONCONNECT fn, void* arg ) { bind_struct* bs; int was_list_empty; int rc; if (!init_done) init_sockdev(); if (sysblk.shutdown) return 0; logdebug("bind_device (%4.4X, %s)\n", dev->devnum, spec); /* Error if device already bound */ if (dev->bs) { WRMSG (HHC01041, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->bs->spec); return 0; /* (failure) */ } /* Create a new bind_struct entry */ bs = malloc(sizeof(bind_struct)); if (!bs) { char buf[40]; MSGBUF( buf, "malloc(%d)", (int)sizeof(bind_struct)); WRMSG (HHC01000, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, buf, strerror(errno)); return 0; /* (failure) */ } memset(bs, 0, sizeof(bind_struct)); bs->fn = fn; bs->arg = arg; if (!(bs->spec = strdup(spec))) { WRMSG (HHC01000, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "strdup()", strerror(errno) ); free (bs); return 0; /* (failure) */ } /* Create a listening socket */ if (bs->spec[0] == '/') bs->sd = unix_socket (bs->spec); else bs->sd = inet_socket (bs->spec); if (bs->sd == -1) { /* (error message already issued) */ free( bs->spec ); free( bs ); return 0; /* (failure) */ } /* Chain device and bind_struct to each other */ dev->bs = bs; bs->dev = dev; /* Add the new entry to our list of bound devices and create the socket thread that will listen for connections (if it doesn't already exist) */ obtain_lock( &bind_lock ); was_list_empty = IsListEmpty( &bind_head ); InsertListTail( &bind_head, &bs->bind_link ); if ( was_list_empty ) { rc = create_thread( &sysblk.socktid, JOINABLE, socket_thread, NULL, "socket_thread" ); if (rc) { WRMSG(HHC00102, "E", strerror( rc ) ); RemoveListEntry( &bs->bind_link ); close_socket(bs->sd); free( bs->spec ); free( bs ); release_lock( &bind_lock ); return 0; /* (failure) */ } } SIGNAL_SOCKDEV_THREAD(); release_lock( &bind_lock ); WRMSG (HHC01042, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->bs->spec); return 1; /* (success) */ }