//Copyright (c) 1998 - 1999 Microsoft Corporation /******************************************************************************* * * threads.cpp * * implementation of WINCFG thread classes * * copyright notice: Copyright 1994, Citrix Systems Inc. * * $Author: butchd $ Butch Davis * * $Log: N:\NT\PRIVATE\UTILS\CITRIX\WINUTILS\WINCFG\VCS\THREADS.CPP $ * * Rev 1.18 19 Sep 1996 15:58:52 butchd * update * * Rev 1.17 12 Sep 1996 16:16:44 butchd * update * *******************************************************************************/ /* * include files */ #include "stdafx.h" #include "wincfg.h" #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif extern CWincfgApp *pApp; //////////////////////////////////////////////////////////////////////////////// // CThread class construction / destruction, implementation /******************************************************************************* * * CThread - CThread constructor * * ENTRY: * EXIT: * ******************************************************************************/ CThread::CThread() { m_hThread = NULL; m_dwThreadID = 0; } // end CThread::CThread /******************************************************************************* * * ~CThread - CThread destructor * * ENTRY: * EXIT: * ******************************************************************************/ CThread::~CThread() { } // end CThread::~CThread /******************************************************************************* * * operator new - CThread operator override * * ENTRY: * EXIT: * ******************************************************************************/ void * CThread::operator new(size_t nSize) { return( ::malloc(nSize) ); } // end CThread::operator new /******************************************************************************* * * operator delete - CThread operator override * * ENTRY: * EXIT: * ******************************************************************************/ void CThread::operator delete(void *p) { ::free(p); } // end CThread::operator delete //////////////////////////////////////////////////////////////////////////////// // CThread operations: primary thread /******************************************************************************* * * CreateThread - CThread implementation function * * Class wrapper for the Win32 CreateThread API. * * ENTRY: * EXIT: * ******************************************************************************/ HANDLE CThread::CreateThread( DWORD cbStack, DWORD fdwCreate ) { /* * Simple wrapper for Win32 CreateThread API. */ return( m_hThread = ::CreateThread( NULL, cbStack, ThreadEntryPoint, (LPVOID)this, fdwCreate, &m_dwThreadID ) ); } // end CThread::CreateThread //////////////////////////////////////////////////////////////////////////////// // CThread operations: secondary thread /******************************************************************************* * * ThreadEntryPoint - CThread implementation function * (SECONDARY THREAD) * * ENTRY: * EXIT: * ******************************************************************************/ DWORD __stdcall CThread::ThreadEntryPoint(LPVOID lpParam) { CThread *pThread; DWORD dwResult; /* * (lpParam is actually the 'this' pointer) */ pThread = (CThread*)lpParam; VERIFY(pThread != NULL); /* * Run the thread. */ dwResult = pThread->RunThread(); /* * Return the result. */ return(dwResult); } // end CThread::ThreadEntryPoint //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // CATDlgInputThread class construction / destruction, implementation /******************************************************************************* * * CATDlgInputThread - CATDlgInputThread constructor * * ENTRY: * EXIT: * ******************************************************************************/ CATDlgInputThread::CATDlgInputThread() { /* * Initialize member variables. */ m_bExit = FALSE; m_ErrorStatus = ERROR_SUCCESS; m_hConsumed = m_OverlapSignal.hEvent = m_OverlapRead.hEvent = NULL; } // end CATDlgInputThread::CATDlgInputThread /******************************************************************************* * * ~CATDlgInputThread - CATDlgInputThread destructor * * ENTRY: * EXIT: * ******************************************************************************/ CATDlgInputThread::~CATDlgInputThread() { /* * Close the semaphore and events when the CATDlgInputThread * object is destroyed. */ if ( m_hConsumed ) CloseHandle(m_hConsumed); if ( m_OverlapRead.hEvent ) CloseHandle(m_OverlapRead.hEvent); if ( m_OverlapSignal.hEvent ) CloseHandle(m_OverlapSignal.hEvent); } // end CATDlgInputThread::~CATDlgInputThread /******************************************************************************* * * RunThread - CATDlgInputThread secondary thread main function loop * (SECONDARY THREAD) * * ENTRY: * EXIT: * (DWORD) exit status for the secondary thread. * ******************************************************************************/ DWORD CATDlgInputThread::RunThread() { HANDLE hWait[2]; DWORD Status; int iStat; /* * Initialize for overlapped status and read input. */ if ( !(m_hConsumed = CreateSemaphore( NULL, 0, MAX_STATUS_SEMAPHORE_COUNT, NULL )) || !(m_OverlapRead.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL )) || !(m_OverlapSignal.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL )) || !SetCommMask( m_hDevice, EV_CTS | EV_DSR | EV_ERR | EV_RING | EV_RLSD | EV_BREAK ) ) { NotifyAbort(IDP_ERROR_CANT_INITIALIZE_INPUT_THREAD); return(1); } /* * Query initial comm status to initialize dialog with (return if error). */ if ( (iStat = CommStatusAndNotify()) != -1 ) return(iStat); /* * Post Read for input data. */ if ( (iStat = PostInputRead()) != -1 ) return(iStat); /* * Post Read for status. */ if ( (iStat = PostStatusRead()) != -1 ) return(iStat); /* * Loop till exit requested. */ for ( ; ; ) { /* * Wait for either input data or an comm status event. */ hWait[0] = m_OverlapRead.hEvent; hWait[1] = m_OverlapSignal.hEvent; Status = WaitForMultipleObjects(2, hWait, FALSE, INFINITE); /* * Check for exit. */ if ( m_bExit ) return(0); if ( Status == WAIT_OBJECT_0 ) { /* * Read event: * Get result of overlapped read. */ if ( !GetOverlappedResult( m_hDevice, &m_OverlapRead, &m_BufferBytes, TRUE ) ) { NotifyAbort(IDP_ERROR_GET_OVERLAPPED_RESULT_READ); return(1); } /* * Notify dialog. */ if ( (iStat = CommInputNotify()) != -1 ) return(iStat); /* * Post Read for input data. */ if ( (iStat = PostInputRead()) != -1 ) return(iStat); } else if ( Status == WAIT_OBJECT_0+1 ) { /* * Comm status event: * Query comm status and notify dialog. */ if ( (iStat = CommStatusAndNotify()) != -1 ) return(iStat); /* * Post Read for status. */ if ( (iStat = PostStatusRead()) != -1 ) return(iStat); } else { /* * Unknown event: Abort. */ NotifyAbort(IDP_ERROR_WAIT_FOR_MULTIPLE_OBJECTS); return(1); } } } // end CATDlgInputThread::RunThread //////////////////////////////////////////////////////////////////////////////// // CATDlgInputThread operations: primary thread /******************************************************************************* * * SignalConsumed - CATDlgInputThread member function: public operation * * Release the m_hConsumed semaphore to allow secondary thread to continue * running. * * ENTRY: * EXIT: * ******************************************************************************/ void CATDlgInputThread::SignalConsumed() { ReleaseSemaphore( m_hConsumed, 1, NULL ); } // end CATDlgInputThread::SignalConsumed /******************************************************************************* * * ExitThread - CATDlgInputThread member function: public operation * * Tell the secondary thread to exit and cleanup after. * * ENTRY: * EXIT: * ******************************************************************************/ void CATDlgInputThread::ExitThread() { DWORD dwReturnCode; int i; CWaitCursor wait; /* * If the thread was not created properly, just delete object and return. */ if ( !m_hThread ) { delete this; return; } /* * Set the m_bExit flag to TRUE, wake up the run thread's WaitCommEvent() by * resetting device's Comm mask, and bump the consumed semaphore to assure exit. */ m_bExit = TRUE; SetCommMask(m_hDevice, 0); SignalConsumed(); /* * Purge the recieve buffer and any pending read. */ PurgeComm(m_hDevice, PURGE_RXABORT | PURGE_RXCLEAR); /* * Wait a while for the thread to exit. */ for ( i = 0, GetExitCodeThread( m_hThread, &dwReturnCode ); (i < MAX_SLEEP_COUNT) && (dwReturnCode == STILL_ACTIVE); i++ ) { Sleep(100); GetExitCodeThread( m_hThread, &dwReturnCode ); } /* * If the thread has still not exited, terminate it. */ if ( dwReturnCode == STILL_ACTIVE ) { TerminateThread( m_hThread, 1 ); #ifdef _DEBUG TRACE( TEXT("WINCFG: Forced Terminate of Async Test Input thread after %u 100msec exit waits.\n"), MAX_SLEEP_COUNT ); #endif } /* * Close the thread handle and delete this CATDlgInputThread object */ VERIFY( CloseHandle(m_hThread) ); delete this; } // end CATDlgInputThread::ExitThread //////////////////////////////////////////////////////////////////////////////// // CATDlgInputThread operations: secondary thread /******************************************************************************* * * NotifyAbort - CATDlgInputThread member function: private operation * (SECONDARY THREAD) * * Notify the dialog of thread abort and reason. * * ENTRY: * idError (input) * Resource id for error message. * EXIT: * ******************************************************************************/ void CATDlgInputThread::NotifyAbort( UINT idError ) { ::PostMessage(m_hDlg, WM_ASYNCTESTABORT, idError, GetLastError()); } // end CATDlgInputThread::NotifyAbort /******************************************************************************* * * CommInputNotify - CATDlgInputThread member function: private operation * (SECONDARY THREAD) * * Notify the dialog of comm input. * * ENTRY: * EXIT: * -1 no error and continue thread * 0 if ExitThread was requested by parent * ******************************************************************************/ int CATDlgInputThread::CommInputNotify() { /* * Tell the dialog that we've got some new input. */ ::PostMessage(m_hDlg, WM_ASYNCTESTINPUTREADY, 0, 0); WaitForSingleObject(m_hConsumed, INFINITE); /* * Check for thread exit request. */ if ( m_bExit ) return(0); else return(-1); } // end CATDlgInputThread::CommInputNotify /******************************************************************************* * * CommStatusAndNotify - CATDlgInputThread member function: private operation * (SECONDARY THREAD) * * Read the comm port status and notify dialog. * * ENTRY: * EXIT: * -1 no error and continue thread * 0 if ExitThread was requested by parent * 1 error condition * ******************************************************************************/ int CATDlgInputThread::CommStatusAndNotify() { PFLOWCONTROLCONFIG pFlow; DWORD ModemStatus, Error; if ( !GetCommModemStatus(m_hDevice, &ModemStatus) ) { /* * We can't query the comm information; tell the primary thread * that we've aborted, and return error (will exit thread). */ NotifyAbort(IDP_ERROR_GET_COMM_MODEM_STATUS); return(1); } /* * Update modem status */ m_Status.AsyncSignal = ModemStatus; /* * Or in status of DTR and RTS */ pFlow = &m_PdConfig.Params.Async.FlowControl; if ( pFlow->fEnableDTR ) m_Status.AsyncSignal |= MS_DTR_ON; if ( pFlow->fEnableRTS ) m_Status.AsyncSignal |= MS_RTS_ON; /* * OR in new event mask */ m_Status.AsyncSignalMask |= m_EventMask; /* * Update async error counters */ if ( m_EventMask & EV_ERR ) { (VOID) ClearCommError( m_hDevice, &Error, NULL ); if ( Error & CE_OVERRUN ) m_Status.Output.AsyncOverrunError++; if ( Error & CE_FRAME ) m_Status.Input.AsyncFramingError++; if ( Error & CE_RXOVER ) m_Status.Input.AsyncOverflowError++; if ( Error & CE_RXPARITY ) m_Status.Input.AsyncParityError++; } /* * Tell the dialog that we've got some new status information. */ ::PostMessage(m_hDlg, WM_ASYNCTESTSTATUSREADY, 0, 0); WaitForSingleObject(m_hConsumed, INFINITE); /* * Check for thread exit request. */ if ( m_bExit ) return(0); else return(-1); } // end CATDlgInputThread::CommStatusAndNotify /******************************************************************************* * * PostInputRead - CATDlgInputThread member function: private operation * (SECONDARY THREAD) * * Post a ReadFile operation for the device, processing as long as data * is present. * * ENTRY: * EXIT: * -1 if read operation posted sucessfully * 0 if ExitThread was requested by parent * 1 if error condition * ******************************************************************************/ int CATDlgInputThread::PostInputRead() { int iStat; /* * Post read for input data, processing immediataly if not 'pending'. */ while ( ReadFile( m_hDevice, m_Buffer, MAX_COMMAND_LEN, &m_BufferBytes, &m_OverlapRead ) ) { if ( (iStat = CommInputNotify()) != -1 ) return(iStat); } /* * Make sure read is pending (not some other error). */ if ( GetLastError() != ERROR_IO_PENDING ) { NotifyAbort(IDP_ERROR_READ_FILE); return(1); } /* * Return 'posted sucessfully' status. */ return(-1); } // end CATDlgInputThread::PostInputRead /******************************************************************************* * * PostStatusRead - CATDlgInputThread member function: private operation * (SECONDARY THREAD) * * Post a WaitCommStatus operation for the device. * * ENTRY: * EXIT: * -1 if status operation posted sucessfully * 1 if error condition * ******************************************************************************/ int CATDlgInputThread::PostStatusRead() { /* * Post read for comm status. */ if ( !WaitCommEvent(m_hDevice, &m_EventMask, &m_OverlapSignal) ) { /* * Make sure comm status read is pending (not some other error). */ if ( GetLastError() != ERROR_IO_PENDING ) { NotifyAbort(IDP_ERROR_WAIT_COMM_EVENT); return(1); } } /* * Return 'posted sucessfully' status. */ return(-1); } // end CATDlgInputThread::PostStatusRead ////////////////////////////////////////////////////////////////////////////////