/*++ Copyright (c) 1994 Microsoft Corporation All rights reserved. Module Name: spcompat.cxx Abstract: porting the spool library code into printui Author: Lazar Ivanov (LazarI) Jul-05-2000 Revision History: --*/ #include "precomp.hxx" #pragma hdrstop /////////////////////////////////////////////////// // @@file@@ debug.cxx /////////////////////////////////////////////////// #if DBG /******************************************************************** TStatus automated error logging and codepath testing. ********************************************************************/ TStatusBase& TStatusBase:: pNoChk( VOID ) { _pszFileA = NULL; return (TStatusBase&)*this; } TStatusBase& TStatusBase:: pSetInfo( UINT uDbg, UINT uLine, LPCSTR pszFileA, LPCSTR pszModuleA ) { _uDbg = uDbg; _uLine = uLine; _pszFileA = pszFileA; SPLASSERT( pszFileA ); _pszModuleA = pszModuleA; return (TStatusBase&)*this; } DWORD TStatus:: dwGetStatus( VOID ) { // // For now, return error code. Later it will return the actual // error code. // return _dwStatus; } DWORD TStatusBase:: operator=( DWORD dwStatus ) { // // Check if we have an error, and it's not one of the two // accepted "safe" errors. // // If pszFileA is not set, then we can safely ignore the // error as one the client intended. // if( _pszFileA && dwStatus != ERROR_SUCCESS && dwStatus != _dwStatusSafe1 && dwStatus != _dwStatusSafe2 && dwStatus != _dwStatusSafe3 ){ DBGMSG( DBG_WARN, ( "TStatus set to %d\nLine %d, %hs\n", dwStatus, _uLine, _pszFileA )); } return _dwStatus = dwStatus; } /******************************************************************** Same, but for HRESULTs. ********************************************************************/ TStatusHBase& TStatusHBase:: pNoChk( VOID ) { _pszFileA = NULL; return (TStatusH&)*this; } TStatusHBase& TStatusHBase:: pSetInfo( UINT uDbg, UINT uLine, LPCSTR pszFileA, LPCSTR pszModuleA ) { _uDbg = uDbg; _uLine = uLine; _pszFileA = pszFileA; SPLASSERT( pszFileA ); _pszModuleA = pszModuleA; return (TStatusH&)*this; } HRESULT TStatusHBase:: operator=( HRESULT hrStatus ) { // // Check if we have an error, and it's not one of the two // accepted "safe" errors. // // If pszFileA is not set, then we can safely ignore the // error as one the client intended. // if( _pszFileA && FAILED(hrStatus) && hrStatus != _hrStatusSafe1 && hrStatus != _hrStatusSafe2 && hrStatus != _hrStatusSafe3 ){ DBGMSG( DBG_WARN, ( "TStatusH set to %x\nLine %d, %hs\n", hrStatus, _uLine, _pszFileA )); } return _hrStatus = hrStatus; } HRESULT TStatusH:: hrGetStatus( VOID ) { // // For now, return error code. Later it will return the actual // error code. // return _hrStatus; } /******************************************************************** Same, but for BOOLs. ********************************************************************/ TStatusBBase& TStatusBBase:: pNoChk( VOID ) { _pszFileA = NULL; return (TStatusBBase&)*this; } TStatusBBase& TStatusBBase:: pSetInfo( UINT uDbg, UINT uLine, LPCSTR pszFileA, LPCSTR pszModuleA ) { _uDbg = uDbg; _uLine = uLine; _pszFileA = pszFileA; SPLASSERT( pszFileA ); _pszModuleA = pszModuleA; return (TStatusBBase&)*this; } BOOL TStatusB:: bGetStatus( VOID ) { // // For now, return error code. Later it will return the actual // error code. // return _bStatus; } BOOL TStatusBBase:: operator=( BOOL bStatus ) { // // Check if we have an error, and it's not one of the two // accepted "safe" errors. // // If pszFileA is not set, then we can safely ignore the // error as one the client intended. // if( _pszFileA && !bStatus ){ DWORD dwLastError = GetLastError(); if( dwLastError != _dwStatusSafe1 && dwLastError != _dwStatusSafe2 && dwLastError != _dwStatusSafe3 ){ DBGMSG( DBG_WARN, ( "TStatusB set to FALSE, LastError = %d\nLine %d, %hs\n", GetLastError(), _uLine, _pszFileA )); } } return _bStatus = bStatus; } #endif // DBG /////////////////////////////////////////////////// // @@file@@ string.cxx /////////////////////////////////////////////////// // // Class specific NULL state. // TCHAR TString::gszNullState[2] = {0,0}; // // Default construction. // TString:: TString( VOID ) : _pszString( &TString::gszNullState[kValid] ) { } // // Construction using an existing LPCTSTR string. // TString:: TString( IN LPCTSTR psz ) : _pszString( &TString::gszNullState[kValid] ) { bUpdate( psz ); } // // Destruction, ensure we don't free our NULL state. // TString:: ~TString( VOID ) { vFree( _pszString ); } // // Copy constructor. // TString:: TString( const TString &String ) : _pszString( &TString::gszNullState[kValid] ) { bUpdate( String._pszString ); } // // Indicates if a string has any usable data. // BOOL TString:: bEmpty( VOID ) const { return _pszString[0] == 0; } // // Indicates if a string object is valid. // BOOL TString:: bValid( VOID ) const { return _pszString != &TString::gszNullState[kInValid]; } // // Return the length of the string. // UINT TString:: uLen( VOID ) const { return lstrlen( _pszString ); } BOOL TString:: bCat( IN LPCTSTR psz ) /*++ Routine Description: Safe concatenation of the specified string to the string object. If the allocation fails, return FALSE and the original string is not modified. Arguments: psz - Input string, may be NULL. Return Value: TRUE = update successful FALSE = update failed --*/ { BOOL bStatus = FALSE; // // If a valid string was passed. // if( psz ){ LPTSTR pszTmp = _pszString; // // Allocate the new buffer consisting of the size of the orginal // string plus the sizeof of the new string plus the null terminator. // _pszString = (LPTSTR)AllocMem( ( lstrlen( pszTmp ) + lstrlen( psz ) + 1 ) * sizeof ( pszTmp[0] ) ); // // If memory was not available. // if( !_pszString ){ // // Release the original buffer. // vFree( pszTmp ); // // Mark the string object as invalid. // _pszString = &TString::gszNullState[kInValid]; } else { // // Copy the string and concatenate the passed string. // lstrcpy( _pszString, pszTmp ); lstrcat( _pszString, psz ); // // Release the original buffer. // vFree( pszTmp ); // // Indicate success. // bStatus = TRUE; } // // Skip null pointers, not an error. // } else { bStatus = TRUE; } return bStatus; } BOOL TString:: bLimitBuffer( IN UINT nMaxLength ) /*++ Routine Description: Truncates th string if longer than nMaxLength including the terminating character Arguments: nMaxLength - the max length of the buffer Return Value: TRUE if the string is truncated and FALSE otherwise --*/ { BOOL bRet = FALSE; if( lstrlen(_pszString) >= nMaxLength ) { // truncate up to nMaxLength including the terminating character _pszString[nMaxLength-1] = 0; bRet = TRUE; } return bRet; } BOOL TString:: bDeleteChar( IN UINT nPos ) /*++ Routine Description: Deletes the character at the specified pos Arguments: nPos - which char to delete Return Value: TRUE if on success and FALSE otherwise --*/ { BOOL bRet = FALSE; UINT u, uLen = lstrlen(_pszString); ASSERT(nPos < uLen); if( nPos < uLen ) { // the new length uLen--; // shift all the characters for( u=nPos; u 0 && iReturn != iSize ) { break; } // // String did not fit release the current buffer. // if( pszBuff ) { FreeMem( pszBuff ); } // // Double the buffer size after each failure. // iSize *= 2; // // If the size is greater than 100k exit without formatting a string. // if( iSize > kStrMaxFormatSize ) { DBGMSG( DBG_ERROR, ("TString::vsntprintf failed string too long.\n") ); pszBuff = NULL; break; } } return pszBuff; } /////////////////////////////////////////////////// // @@file@@ state.cxx /////////////////////////////////////////////////// /******************************************************************** Ref counting ********************************************************************/ VOID MRefQuick:: vIncRef( VOID ) { ++_cRef; } LONG MRefQuick:: cDecRef( VOID ) { --_cRef; if( !_cRef ){ vRefZeroed(); return 0; } return _cRef; } VOID MRefQuick:: vDecRefDelete( VOID ) { --_cRef; if( !_cRef ){ vRefZeroed(); } } /******************************************************************** MRefCom: Reference counting using interlocked references. This avoids creating a common critical section. vDeleteDecRef is the same as vDecRef, but it logs differently. ********************************************************************/ VOID MRefCom:: vIncRef( VOID ) { InterlockedIncrement( &_cRef ); } LONG MRefCom:: cDecRef( VOID ) { LONG cRefReturn = InterlockedDecrement( &_cRef ); if( !cRefReturn ){ vRefZeroed(); } return cRefReturn; } VOID MRefCom:: vDecRefDelete( VOID ) { if( !InterlockedDecrement( &_cRef )){ vRefZeroed(); } } VOID MRefCom:: vRefZeroed( VOID ) { } /******************************************************************** State ********************************************************************/ #if DBG TState:: TState( VOID ) : _StateVar(0) { } TState:: TState( STATEVAR StateVar ) : _StateVar(StateVar) { } TState:: ~TState( VOID ) { } STATEVAR TState:: operator|=( STATEVAR StateVarOn ) { SPLASSERT( bValidateSet( StateVarOn )); _StateVar |= StateVarOn; return _StateVar; } STATEVAR TState:: operator&=( STATEVAR StateVarMask ) { SPLASSERT( bValidateMask( StateVarMask )); _StateVar &= StateVarMask; return _StateVar; } #endif /////////////////////////////////////////////////// // @@file@@ splutil.cxx /////////////////////////////////////////////////// MEntry* MEntry:: pFindEntry( PDLINK pdlink, LPCTSTR pszName ) { PDLINK pdlinkT; MEntry* pEntry; for( pdlinkT = pdlink->FLink; pdlinkT != pdlink; pdlinkT = pdlinkT->FLink ){ pEntry = MEntry::Entry_pConvert( pdlinkT ); if( pEntry->_strName == pszName ){ return pEntry; } } return NULL; } /////////////////////////////////////////////////// // @@file@@ threadm.cxx /////////////////////////////////////////////////// /******************************************************************** Public interfaces. ********************************************************************/ TThreadM:: TThreadM( UINT uMaxThreads, UINT uIdleLife, CCSLock* pCritSec OPTIONAL ) : _uMaxThreads(uMaxThreads), _uIdleLife(uIdleLife), _uActiveThreads(0), _uRunNowThreads(0), _iIdleThreads(0), _lLocks(1) // the one who calls 'new TThreadM' acquires the initial lock /*++ Routine Description: Construct a Thread Manager object. Arguments: uMaxThreads - Upper limit of threads that will be created. uIdleLife - Maximum life once a thread goes idle (in ms). pCritSec - Use this crit sec for synchronization (if not specified, a private one will be created). Return Value: Notes: _hTrigger is our validity variable. When this value is NULL, instantiation failed. If it's non-NULL, the entire object is valid. --*/ { _hTrigger = CreateEvent( NULL, FALSE, FALSE, NULL ); if( !_hTrigger ){ return; } // // If no critical section, create our own. // if (!pCritSec) { _pCritSec = new CCSLock(); if( !_pCritSec ){ // // _hTrigger is our valid variable. If we fail to create // the critical section, prepare to return failure. // CloseHandle( _hTrigger ); _hTrigger = NULL; return; } _State |= kPrivateCritSec; } else { _pCritSec = pCritSec; } } VOID TThreadM:: vDelete( VOID ) /*++ Routine Description: Indicates that the object is pending deletion. Any object that inherits from vDelete should _not_ call the destructor directly, since there may be pending jobs. Instead, they should call TThreadM::vDelete(). Arguments: Return Value: --*/ { CCSLock::Locker CSL( *_pCritSec ); // // Mark as wanting to be destroyed. // _State |= kDestroyReq; } BOOL TThreadM:: bJobAdded( BOOL bRunNow ) /*++ Routine Description: Notify the thread manager that a new job has been added. This job will be processed fifo. Arguments: bRunNow - Ignore the thread limits and run the job now. Return Value: TRUE - Job successfully added. FALSE - Job could not be added. --*/ { DWORD dwThreadId; HANDLE hThread; BOOL rc = TRUE; CCSLock::Locker CSL( *_pCritSec ); if( _State.bBit( kDestroyReq )){ DBGMSG( DBG_THREADM | DBG_ERROR, ( "ThreadM.bJobAdded: add failed since DESTROY requested.\n" )); SetLastError( ERROR_INVALID_PARAMETER ); rc = FALSE; } else { // // We either: give it to an idle thread, create a new thread, // or leave it in the queue. // if( _iIdleThreads > 0 ){ // // There are some idle threads--trigger the event and it // will be picked up. // --_iIdleThreads; DBGMSG( DBG_THREADM, ( "ThreadM.bJobAdded: Trigger: --iIdle %d, uActive %d\n", _iIdleThreads, _uActiveThreads )); // // If we set the event, then the worker thread that receives // this event should _not_ decrement _iIdleThreads, since // we already did this. // SetEvent( _hTrigger ); } else if( _uActiveThreads < _uMaxThreads || bRunNow ){ // // No idle threads, but we can create a new one since we // haven't reached the limit, or the bRunNow flag is set. // Lock(); hThread = TSafeThread::Create( NULL, 0, xdwThreadProc, this, 0, &dwThreadId ); if( hThread ){ CloseHandle( hThread ); // // We have successfully created a thread; up the // count. // ++_uActiveThreads; // // We have less active threads than the max; create a new one. // DBGMSG( DBG_THREADM, ( "ThreadM.bJobAdded: ct: iIdle %d, ++uActive %d\n", _iIdleThreads, _uActiveThreads )); } else { Unlock(); rc = FALSE; DBGMSG( DBG_THREADM | DBG_WARN, ( "ThreadM.bJobAdded: unable to ct %d\n", GetLastError( ))); } } else { // // No idle threads, and we are already at the max so we // can't create new threads. Dec iIdleThreads anyway // (may go negative, but that's ok). // // iIdleThreads represents the number of threads that // are currently not processing jobs. If the number is // negative, this indicates that even if a thread suddenly // completes a job and would go idle, there is a queued // job that would immediately grab it, so the thread really // didn't go into an idle state. // // The negative number indicates the number of outstanding // jobs that are queued (e.g., -5 indicate 5 jobs queued). // // There is always an increment of iIdleThreads when a // job is compeleted. // --_iIdleThreads; // // No threads idle, and at max threads. // DBGMSG( DBG_THREADM, ( "ThreadM.bJobAdded: wait: --iIdle %d, uActive %d\n", _iIdleThreads, _uActiveThreads )); } // // If we succeeded and bRunNow is set, this indicates that // we were able to start a special thread, so we need to adjust // the maximum number of threads. When a this special thread // job completes, we will decrement it. // if( bRunNow && rc ){ ++_uMaxThreads; ++_uRunNowThreads; } } return rc; } /******************************************************************** Private routines. ********************************************************************/ TThreadM:: ~TThreadM( VOID ) /*++ Routine Description: Destroy the thread manager object. This is private; to request that the thread manager quit, call vDelete(). Arguments: Return Value: --*/ { SPLASSERT( _State.bBit( kDestroyReq )); if( _State.bBit( kPrivateCritSec )){ SPLASSERT( _pCritSec->bOutside( )); delete _pCritSec; } if( _hTrigger ) CloseHandle( _hTrigger ); } DWORD TThreadM:: xdwThreadProc( LPVOID pVoid ) /*++ Routine Description: Worker thread routine that calls the client to process the jobs. Arguments: pVoid - pTMStateVar Return Value: Ignored. --*/ { TThreadM* pThreadM = (TThreadM*)pVoid; DWORD dwExitCode = pThreadM->dwThreadProc(); pThreadM->Unlock(); // release return dwExitCode; } DWORD TThreadM:: dwThreadProc( VOID ) { CCSLock::Locker CSL( *_pCritSec ); DBGMSG( DBG_THREADM, ( "ThreadM.dwThreadProc: ct: iIdle %d, uActive %d\n", _iIdleThreads, _uActiveThreads)); PJOB pJob = pThreadMJobNext(); while( TRUE ){ for( ; pJob; pJob=pThreadMJobNext( )){ // // If bRunNow count is non-zero, this indicates that we just // picked up a RunNow job. As soon as it completes, we // can decrement the count. // BOOL bRunNowCompleted = _uRunNowThreads > 0; { _pCritSec->Unlock(); // // Call back to client to process the job. // DBGMSG( DBG_THREADM, ( "ThreadM.dwThreadProc: %x processing\n", (ULONG_PTR)pJob )); // // Call through virtual function to process the // user's job. // vThreadMJobProcess( pJob ); DBGMSG( DBG_THREADM, ( "ThreadM.dwThreadProc: %x processing done\n", (ULONG_PTR)pJob )); // aquire the CS again _pCritSec->Lock(); } // // If a RunNow job has been completed, then decrement both // counts. uMaxThreads was increased by one when the job was // accepted, so now it must be lowered. // if( bRunNowCompleted ){ --_uMaxThreads; --_uRunNowThreads; } ++_iIdleThreads; DBGMSG( DBG_THREADM, ( "ThreadM.dwThreadProc: ++iIdle %d, uActive %d\n", _iIdleThreads, _uActiveThreads )); } DBGMSG( DBG_THREADM, ( "ThreadM.dwThreadProc: Sleep: iIdle %d, uActive %d\n", _iIdleThreads, _uActiveThreads )); { _pCritSec->Unlock(); // // Done, now relax and go idle for a bit. We don't // care whether we timeout or get triggered; in either // case we check for another job. // WaitForSingleObject( _hTrigger, _uIdleLife ); // aquire the CS again _pCritSec->Lock(); } // // We must check here instead of relying on the return value // of WaitForSingleObject since someone may see iIdleThreads!=0 // and set the trigger, but we timeout before it gets set. // pJob = pThreadMJobNext(); if( pJob ){ DBGMSG( DBG_THREADM, ( "ThreadM.dwThreadProc: Woke and found job: iIdle %d, uActive %d\n", _iIdleThreads, _uActiveThreads )); } else { // // No jobs found; break. Be sure to reset the hTrigger, since // there are no waiting jobs, and the main thread might // have set it in the following case: // // MainThread: WorkerThread: // Sleeping // Awoke, not yet in CS. // GotJob // SetEvent // --iIdleThreads // Enter CS, found job, process it. // // In this case, the event is set, but there is no thread // to pick it up. // ResetEvent( _hTrigger ); break; } } // // Decrement ActiveThreads. This was incremented when the thread // was successfully created, and should be decremented when the thread // is about to exit. // --_uActiveThreads; // // The thread enters an idle state right before it goes to sleep. // // When a job is added, the idle count is decremented by the main // thread, so the worker thread doesn't decrement it (avoids sync // problems). If the worker thread timed out and there were no jobs, // then we need to decrement the matching initial increment here. // --_iIdleThreads; DBGMSG( DBG_THREADM, ( "ThreadM.dwThreadProc: dt: --iIdle %d, --uActive %d\n", _iIdleThreads, _uActiveThreads)); return 0; } /////////////////////////////////////////////////// // @@file@@ exec.cxx /////////////////////////////////////////////////// /*++ Copyright (c) 1994 Microsoft Corporation All rights reserved. Module Name: exec.cxx Abstract: Handles async commands. The client give TExec a worker object (MExecWork). At some later time, the worker is given a thread (called via svExecute). If the Job is already on the list but is waiting, we just return. If it's not on the list but currently executing, we add it to the list when it's done executing. kExecActive -> currently executing (so it's not on the list) kExecActiveReq -> kExecActive is on, and it needs to run again. If kExecExit is added, we call vExecExitComplete() (virtual function) when all jobs have been completed and assume the the client is cleaning up after itself. In fact, the MExecWork object may be destroyed in vExecExitComplete(). Author: Albert Ting (AlbertT) 8-Nov-1994 Revision History: --*/ BOOL TExec:: bJobAdd( MExecWork* pExecWork, STATEVAR StateVar ) /*++ Routine Description: Adds a job request to move to a given state. Arguments: pExecWork - Work structure. StateVar - StateVar that we want to move toward. If kExecExit is set, then we will complete the currently executing job then exit by calling vExecExitComplete(). Note that kExecExit should be added by itself; if it is added with other commands, they will be ignored. Return Value: TRUE = Job added (or job already on wait list) FALSE = failed, GLE. Adding kExecExit is guarenteed to succeed here. --*/ { BOOL bReturn = TRUE; BOOL bCallExecExitComplete = FALSE; { CCSLock::Locker CSL( *_pCritSec ); DBGMSG( DBG_EXEC, ( "Exec.bJobAdd: %x StateVar = %x\n", pExecWork, StateVar )); // // Job bits must not have PRIVATE bits. // SPLASSERT( !( StateVar & kExecPrivate )); // // Don't allow adding a job after we are set to exit. // SPLASSERT( !( pExecWork->_State & kExecExit )); // // If it's active (currently executing in a thread), then it is // not on the wait list and we mark it as ACTIVE_REQ so that // when the job completes, it sees that more jobs have accumulated. // if( pExecWork->_State & kExecActive ){ DBGMSG( DBG_EXEC, ( "\n ACTIVE, ++REQ _State %x _StatePending %x\n", (STATEVAR)pExecWork->_State, (STATEVAR)pExecWork->_StatePending )); // // Can't be an immediate request if executing already. // SPLASSERT( !( StateVar & kExecRunNow )); pExecWork->_StatePending |= StateVar; pExecWork->_State |= kExecActiveReq; bReturn = TRUE; } else { // // Store up the work requested since we aren't currently active. // pExecWork->_State |= StateVar; // // If we are not on the wait list, add it. // if( !pExecWork->Work_bLinked( )){ if( StateVar & kExecExit ){ bCallExecExitComplete = TRUE; } else { DBGMSG( DBG_EXEC, ( "not linked, added\n" )); SPLASSERT( NULL == Work_pFind( pExecWork )); bReturn = bJobAddWorker( pExecWork ); } } else { DBGMSG( DBG_EXEC, ( "linked, NOP\n" )); } } } if( bCallExecExitComplete ){ // // Special case exit: we should exit. Once we call // vExecExitComplete, we can't refer to *this anymore, // since we may have deleted ourself. // pExecWork->vExecExitComplete(); bReturn = TRUE; } return bReturn; } VOID TExec:: vJobDone( MExecWork* pExecWork, STATEVAR StateVar ) /*++ Routine Description: A job has compeleted execution, clean up. Arguments: pExecWork - Unit that just completed executing. StateVar - New state that the object is in (requests that successfully completed should be turned off--done by client). Return Value: --*/ { BOOL bCallExecExitComplete = FALSE; BOOL bCallExecFailedAddJob = FALSE; { CCSLock::Locker CSL( *_pCritSec ); DBGMSG( DBG_EXEC, ( "Exec.vJobDone: %x completed -> %x +(new) %x = %x\n", pExecWork, StateVar, (DWORD)pExecWork->_StatePending, (DWORD)pExecWork->_State | pExecWork->_StatePending )); // // kExecRunNow can not be set when the object is working. // SPLASSERT( !( StateVar & kExecRunNow )); // // We have completed the work request, put in the new state. // Keep the private bits, and add in the new state variable, // plus any additional work that was pending. // // The ExecNow bit is not saved (it's not part of kExecPrivate) // since it's good for one shot only. // pExecWork->_State = ( pExecWork->_State & kExecPrivate ) | ( StateVar & ~kExecPrivate ) | pExecWork->_StatePending; pExecWork->_State &= ~kExecActive; // // If job is done, then quit. // if( pExecWork->_State & kExecExit ){ DBGMSG( DBG_EXEC, ( "Exec.vJobDone: _State %x, calling vExecExitComplete\n", (STATEVAR)pExecWork->_State )); bCallExecExitComplete = TRUE; } else { // // If we have more work to do, add ourselves back // to the queue. // if( pExecWork->_State & kExecActiveReq && !bJobAddWorker( pExecWork )){ bCallExecFailedAddJob = TRUE; } } } if( bCallExecFailedAddJob ){ // // Fail on delayed job add. // pExecWork->vExecFailedAddJob(); } if( bCallExecExitComplete ){ // // Once vExecExitComplete has been called, the current object // pExecWork may be destroyed. // // Don't refer to it again since vExecExitComplete may delete // this as part of cleanup. // pExecWork->vExecExitComplete(); } } STATEVAR TExec:: svClearPendingWork( MExecWork* pExecWork ) /*++ Routine Description: Queries what work is currently pending. Arguments: pExecWork -- Work item. Return Value: --*/ { CCSLock::Locker CSL( *_pCritSec ); // // Return pending work, minus the private and kExecRunNow // bits. // STATEVAR svPendingWork = pExecWork->_StatePending & ~kExecNoOutput; pExecWork->_StatePending = 0; return svPendingWork; } /******************************************************************** Private ********************************************************************/ TExec:: TExec( CCSLock* pCritSec ) : TThreadM( 10, 2000, pCritSec ), _pCritSec( pCritSec ) { } BOOL TExec:: bJobAddWorker( MExecWork* pExecWork ) /*++ Routine Description: Common code to add a job to our linked list. Must be called inside the _pCritSec. It does leave it inside this function, however. Arguments: Return Value: --*/ { SPLASSERT( _pCritSec->bInside( )); BOOL bRunNow = FALSE; // // Check if the client wants the job to run right now. // if( pExecWork->_State & kExecRunNow ){ // // Add the job to the head of the queue. Since we always pull // jobs from the beginning, we'll get to this job first. // // If a non-RunNow job is added to the list before we execute, // we'll still run, since the other job will be added to the // end of the list. // // If another RunNow job is added, we'll spawn separate threads // for each (unless an idle thread is available). // Work_vAdd( pExecWork ); bRunNow = TRUE; } else { Work_vAppend( pExecWork ); } if( !bJobAdded( bRunNow ) ){ DBGMSG( DBG_INFO, ( "Exec.vJobProcess: unable to add job %x: %d\n", pExecWork, GetLastError( ))); Work_vDelink( pExecWork ); return FALSE; } return TRUE; } PJOB TExec:: pThreadMJobNext( VOID ) /*++ Routine Description: Gets the next job from the queue. This function is defined in TThreadM. Arguments: Return Value: --*/ { CCSLock::Locker CSL( *_pCritSec ); MExecWork* pExecWork = Work_pHead(); if( !pExecWork ){ return NULL; } Work_vDelink( pExecWork ); // // Job should never be active here. // SPLASSERT( !(pExecWork->_State & kExecActive) ); // // We will handle all requests now, so clear kExecActiveReq. // Also remove kExecRunNow, since it's one shot only, and mark us // as currently active (kExecActive). // pExecWork->_State &= ~( kExecActiveReq | kExecRunNow ); pExecWork->_State |= kExecActive; return (PJOB)pExecWork; } VOID TExec:: vThreadMJobProcess( PJOB pJob ) /*++ Routine Description: Process a job in the current thread. We call the virtual function with the job object, then clear out the bits that it has completed. (This is a member of TThreadM.) If there is additional pending work (ACTIVE_REQ), then we re-add the job. If there is a failure in the re-add case, we must send an asynchronous fail message. Arguments: pJob - MExecWork instance. Return Value: --*/ { SPLASSERT( _pCritSec->bOutside( )); STATEVAR StateVar; MExecWork* pExecWork = (MExecWork*)pJob; // // Do the work. // StateVar = pExecWork->svExecute( pExecWork->State() & ~kExecNoOutput ); vJobDone( pExecWork, StateVar ); } /////////////////////////////////////////////////// // @@file@@ bitarray.cxx /////////////////////////////////////////////////// /******************************************************************** Bit Array class ********************************************************************/ TBitArray:: TBitArray( IN UINT nBits, IN UINT uGrowSize ) : _nBits( nBits ), _pBits( NULL ), _uGrowSize( uGrowSize ) { DBGMSG( DBG_TRACE, ( "TBitArray::ctor\n" ) ); // // If the initial number of bits is not specified then // create a default bit array size. // if( !_nBits ) { _nBits = kBitsInType; } // // This could fail, thus leaving the bit array // in an invalid state, Note _pBits being true is // the bValid check. // _pBits = new Type [ nBitsToType( _nBits ) ]; if( _pBits ) { // // The grow size should be at least the // number of bits in the type. // if( _uGrowSize < kBitsInType ) { _uGrowSize = kBitsInType; } // // Clear all the bits. // memset( _pBits, 0, nBitsToType( _nBits ) * sizeof( Type ) ); } } TBitArray:: TBitArray( const TBitArray &rhs ) : _nBits( kBitsInType ), _pBits( NULL ) { DBGMSG( DBG_TRACE, ( "TBitArray::copy_ctor\n" ) ); bClone( rhs ); } const TBitArray & TBitArray:: operator =( const TBitArray &rhs ) { DBGMSG( DBG_TRACE, ( "TBitArray::operator =\n" ) ); bClone( rhs ); return *this; } TBitArray:: ~TBitArray( VOID ) { DBGMSG( DBG_TRACE, ( "TBitArray::dtor\n" ) ); delete [] _pBits; } BOOL TBitArray:: bValid( VOID ) const { return _pBits != NULL; } BOOL TBitArray:: bToString( IN TString &strBits ) const { BOOL bStatus = bValid(); if( bStatus ) { TString strString; strBits.bUpdate( NULL ); // // Get the upper bound bit. // UINT uIndex = _nBits - 1; // // Print the array in reverse order to make the bit array // appear as one large binary number. // for( UINT i = 0; i < _nBits; i++, uIndex-- ) { strString.bFormat( TEXT( "%d" ), bRead( uIndex ) ); strBits.bCat( strString ); } bStatus = strBits.bValid(); } return bStatus; } BOOL TBitArray:: bRead( IN UINT Bit ) const { BOOL bStatus = bIsValidBit( Bit ); if( bStatus ) { bStatus = _pBits[BitToIndex( Bit )] & BitToMask( Bit ) ? TRUE : FALSE; } return bStatus; } BOOL TBitArray:: bSet( IN UINT Bit ) { BOOL bStatus = bIsValidBit( Bit ); if( bStatus ) { _pBits[BitToIndex( Bit )] |= BitToMask( Bit ); } return bStatus; } BOOL TBitArray:: bReset( IN UINT Bit ) { BOOL bStatus = bIsValidBit( Bit ); if( bStatus ) { _pBits[BitToIndex( Bit )] &= ~BitToMask( Bit ); } return bStatus; } BOOL TBitArray:: bToggle( IN UINT Bit ) { BOOL bStatus = bIsValidBit( Bit ); if( bStatus ) { _pBits[BitToIndex( Bit )] ^= BitToMask( Bit ); } return bStatus; } // // Add one new bit to the end of the bit array. // If multiple bits need to be added the user of the // class should call this routine repeatedly. // BOOL TBitArray:: bAdd( VOID ) { BOOL bStatus = FALSE; UINT Bit = _nBits + 1; // // Check if there is room in the array for one more bit. // if( Bit <= nBitsToType( _nBits ) * kBitsInType ) { // // Update the current bit count and return true. // _nBits = Bit; bStatus = TRUE; } else { // // Grow the bit array. // bStatus = bGrow( Bit ); } return bStatus; } VOID TBitArray:: vSetAll( VOID ) { for( UINT i = 0; i < _nBits; i++ ) { bSet( i ); } } VOID TBitArray:: vResetAll( VOID ) { for( UINT i = 0; i < _nBits; i++ ) { bReset( i ); } } UINT TBitArray:: uNumBits( VOID ) const { return _nBits; } BOOL TBitArray:: bFindNextResetBit( IN UINT *puNextFreeBit ) { BOOL bStatus = bValid(); if( bStatus ) { BOOL bFound = FALSE; // // Locate the first type that contains at least one cleared bit. // for( UINT i = 0; i < nBitsToType( _nBits ); i++ ) { if( _pBits[i] != kBitsInTypeMask ) { // // Search for the bit that is cleared. // for( UINT j = 0; j < kBitsInType; j++ ) { if( !( _pBits[i] & BitToMask( j ) ) ) { *puNextFreeBit = i * kBitsInType + j; bFound = TRUE; break; } } } // // Free bit found terminate the search. // if( bFound ) { break; } } // // Free bit was not found then grow the bit array // if( !bFound ) { // // Assume a new bit will be added. // *puNextFreeBit = uNumBits(); // // Add a new bit. // bStatus = bAdd(); } } return bStatus; } /******************************************************************** Bit Array - private member functions. ********************************************************************/ BOOL TBitArray:: bClone( const TBitArray &rhs ) { BOOL bStatus = FALSE; if( this == &rhs ) { bStatus = TRUE; } else { Type *pTempBits = new Type [ nBitsToType( _nBits ) ]; if( pTempBits ) { memcpy( pTempBits, rhs._pBits, nBitsToType( _nBits ) * sizeof( Type ) ); delete [] _pBits; _pBits = pTempBits; _nBits = rhs._nBits; bStatus = TRUE; } } return bStatus; } BOOL TBitArray:: bGrow( IN UINT uBits ) { DBGMSG( DBG_TRACE, ( "TBitArray::bGrow\n" ) ); BOOL bStatus = FALSE; UINT uNewBits = uBits + _uGrowSize; DBGMSG( DBG_TRACE, ( "Grow to size %d Original size %d Buffer pointer %x\n", uNewBits, _nBits, _pBits ) ); // // We do support reducing the size of the bit array. // SPLASSERT( uNewBits > _nBits ); // // Allocate the enlarged bit array. // Type *pNewBits = new Type [ nBitsToType( uNewBits ) ]; if( pNewBits ) { // // Clear the new bits. // memset( pNewBits, 0, nBitsToType( uNewBits ) * sizeof( Type ) ); // // Copy the old bits to the new bit array. // memcpy( pNewBits, _pBits, nBitsToType( _nBits ) * sizeof( Type ) ); // // Release the old bit array and save the new pointer and size. // delete [] _pBits; _pBits = pNewBits; _nBits = uBits; // // Success. // bStatus = TRUE; } DBGMSG( DBG_TRACE, ( "New size %d Buffer pointer %x\n", _nBits, _pBits ) ); return bStatus; } UINT TBitArray:: nBitsToType( IN UINT uBits ) const { return ( uBits + kBitsInType - 1 ) / kBitsInType; } TBitArray::Type TBitArray:: BitToMask( IN UINT uBit ) const { return 1 << ( uBit % kBitsInType ); } UINT TBitArray:: BitToIndex( IN UINT uBit ) const { return uBit / kBitsInType; } BOOL TBitArray:: bIsValidBit( IN UINT uBit ) const { BOOL bStatus = ( uBit < _nBits ) && bValid(); if( !bStatus ) { DBGMSG( DBG_TRACE, ( "Invalid bit value %d\n", uBit ) ); } return bStatus; } /////////////////////////////////////////////////// // @@file@@ loadlib.cxx /////////////////////////////////////////////////// TLibrary:: TLibrary( LPCTSTR pszLibName ) { _hInst = LoadLibrary( pszLibName ); if( !_hInst ) { DBGMSG( DBG_WARN, ( "Library.ctr: unable to load "TSTR"\n", pszLibName )); } } TLibrary:: ~TLibrary( ) { if( bValid() ) { FreeLibrary( _hInst ); } } BOOL TLibrary:: bValid( VOID ) const { return _hInst != NULL; } FARPROC TLibrary:: pfnGetProc( IN LPCSTR pszProc ) const { FARPROC fpProc = bValid() ? GetProcAddress( _hInst, pszProc ) : NULL; if( !fpProc ) { DBGMSG( DBG_WARN, ( "Library.pfnGetProc: failed %s\n", pszProc )); } return fpProc; } FARPROC TLibrary:: pfnGetProc( IN UINT uOrdinal ) const { FARPROC fpProc = bValid() ? GetProcAddress( _hInst, (LPCSTR)MAKELPARAM( uOrdinal, 0 ) ) : NULL; if( !fpProc ) { DBGMSG( DBG_WARN, ( "Library.pfnGetProc: failed %d\n", uOrdinal )); } return fpProc; } HINSTANCE TLibrary:: hInst( VOID ) const { return _hInst; }