//------------------------------------------------------------------------------ // Desc: Functions for creating, starting, stopping, controlling threads. // Tabs: 3 // // Copyright (c) 2000-2007 Novell, Inc. All Rights Reserved. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; version 2.1 // of the License. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com. // // $Id$ //------------------------------------------------------------------------------ #include "ftksys.h" #ifdef FLM_UNIX pid_t getpid( void); #endif #ifdef FLM_LIBC_NLM void * threadStub( void * pvThread); #elif defined( FLM_RING_ZERO_NLM) void * threadStub( void * pvUnused, void * pvThread); #elif defined( FLM_WIN) unsigned __stdcall threadStub( void * pvThread); #elif defined( FLM_UNIX) extern "C" void * threadStub( void * pvThread); #endif /**************************************************************************** Desc: ****************************************************************************/ class F_ThreadMgr : public IF_ThreadMgr { public: F_ThreadMgr() { m_hMutex = F_MUTEX_NULL; m_pThreadList = NULL; m_uiNumThreads = 0; m_groupCounter = 0; } virtual ~F_ThreadMgr(); RCODE FLMAPI setupThreadMgr( void); FLMINT FLMAPI AddRef( void) { return( f_atomicInc( &m_refCnt)); } FLMINT FLMAPI Release( void) { FLMINT iRefCnt = f_atomicDec( &m_refCnt); if( !iRefCnt) { delete this; } return( iRefCnt); } RCODE FLMAPI createThread( IF_Thread ** ppThread, F_THREAD_FUNC fnThread, const char * pszThreadName, FLMUINT uiThreadGroup, FLMUINT uiAppId, void * pvParm1, void * pvParm2, FLMUINT uiStackSize); void FLMAPI shutdownThreadGroup( FLMUINT uiThreadGroup); void FLMAPI setThreadShutdownFlag( FLMUINT uiThreadId); RCODE FLMAPI findThread( IF_Thread ** ppThread, FLMUINT uiThreadGroup, FLMUINT uiAppId = 0, FLMBOOL bOkToFindMe = TRUE); RCODE FLMAPI getNextGroupThread( IF_Thread ** ppThread, FLMUINT uiThreadGroup, FLMUINT * puiThreadId); RCODE FLMAPI getThreadInfo( F_Pool * pPool, F_THREAD_INFO ** ppThreadInfo, FLMUINT * puiNumThreads); RCODE FLMAPI getThreadName( FLMUINT uiThreadId, char * pszThreadName, FLMUINT * puiLength); FLMUINT FLMAPI getThreadGroupCount( FLMUINT uiThreadGroup); FLMUINT FLMAPI allocGroupId( void); inline void lockMutex( void) { f_mutexLock( m_hMutex); } inline void unlockMutex( void) { f_mutexUnlock( m_hMutex); } void unlinkThread( IF_Thread * pThread, FLMBOOL bMutexLocked); RCODE getThread( FLMUINT uiThreadId, F_Thread ** ppThread); private: F_MUTEX m_hMutex; F_Thread * m_pThreadList; FLMUINT m_uiNumThreads; FLMATOMIC m_groupCounter; friend class F_Thread; }; /**************************************************************************** Desc: ****************************************************************************/ class F_Thread : public IF_Thread { public: F_Thread() { m_hMutex = F_MUTEX_NULL; m_pszThreadName = NULL; m_pszThreadStatus = NULL; m_uiStatusBufLen = 0; m_uiThreadGroup = F_INVALID_THREAD_GROUP; m_pPrev = NULL; m_pNext = NULL; cleanupThread(); } virtual ~F_Thread() { stopThread(); cleanupThread(); } FLMINT FLMAPI AddRef( void); FLMINT FLMAPI Release( void); RCODE FLMAPI startThread( F_THREAD_FUNC fnThread, const char * pszThreadName, FLMUINT uiThreadGroup, FLMUINT uiAppId, void * pvParm1, void * pvParm2, FLMUINT uiStackSize); void FLMAPI stopThread( void); FINLINE FLMUINT FLMAPI getThreadId( void) { return( m_uiThreadId); } FINLINE FLMBOOL FLMAPI getShutdownFlag( void) { return( m_bShutdown); } FINLINE RCODE FLMAPI getExitCode( void) { return( m_exitRc); } FINLINE void * FLMAPI getParm1( void) { return( m_pvParm1); } FINLINE void FLMAPI setParm1( void * pvParm) { m_pvParm1 = pvParm; } FINLINE void * FLMAPI getParm2( void) { return( m_pvParm2); } FINLINE void FLMAPI setParm2( void * pvParm) { m_pvParm2 = pvParm; } FINLINE void FLMAPI setShutdownFlag( void) { m_bShutdown = TRUE; } FINLINE FLMBOOL FLMAPI isThreadRunning( void) { return( m_bRunning); } void FLMAPI setThreadStatusStr( const char * pszStatus); void FLMAPI setThreadStatus( const char * pszBuffer, ...); void FLMAPI setThreadStatus( eThreadStatus genericStatus); FINLINE void FLMAPI setThreadAppId( FLMUINT uiAppId) { f_mutexLock( m_hMutex); m_uiAppId = uiAppId; f_mutexUnlock( m_hMutex); } FINLINE FLMUINT FLMAPI getThreadAppId( void) { return( m_uiAppId); } FINLINE FLMUINT FLMAPI getThreadGroup( void) { return( m_uiThreadGroup); } void FLMAPI cleanupThread( void); void FLMAPI sleep( FLMUINT uiMilliseconds); void FLMAPI waitToComplete( void); F_MUTEX m_hMutex; F_Thread * m_pPrev; F_Thread * m_pNext; char * m_pszThreadName; char * m_pszThreadStatus; FLMUINT m_uiStatusBufLen; FLMBOOL m_bShutdown; F_THREAD_FUNC m_fnThread; FLMBOOL m_bRunning; FLMUINT m_uiStackSize; void * m_pvParm1; void * m_pvParm2; FLMUINT m_uiThreadId; FLMUINT m_uiThreadGroup; FLMUINT m_uiAppId; FLMUINT m_uiStartTime; RCODE m_exitRc; friend class F_ThreadMgr; }; /**************************************************************************** Desc: ****************************************************************************/ class F_ThreadInfo : public IF_ThreadInfo { public: F_ThreadInfo() { m_pool.poolInit( 512); m_uiNumThreads = 0; m_pThreadInfoArray = NULL; } virtual ~F_ThreadInfo() { m_pool.poolFree(); } FLMUINT FLMAPI getNumThreads( void) { return( m_uiNumThreads); } FINLINE void FLMAPI getThreadInfo( FLMUINT uiThreadNum, FLMUINT * puiThreadId, FLMUINT * puiThreadGroup, FLMUINT * puiAppId, FLMUINT * puiStartTime, const char ** ppszThreadName, const char ** ppszThreadStatus) { if (uiThreadNum < m_uiNumThreads) { F_THREAD_INFO * pThrdInfo = &m_pThreadInfoArray [uiThreadNum]; *puiThreadId = pThrdInfo->uiThreadId; *puiThreadGroup = pThrdInfo->uiThreadGroup; *puiAppId = pThrdInfo->uiAppId; *puiStartTime = pThrdInfo->uiStartTime; *ppszThreadName = pThrdInfo->pszThreadName; *ppszThreadStatus = pThrdInfo->pszThreadStatus; } else { *puiThreadId = 0; *puiThreadGroup = 0; *puiAppId = 0; *puiStartTime = 0; *ppszThreadName = NULL; *ppszThreadStatus = NULL; } } F_Pool m_pool; F_THREAD_INFO * m_pThreadInfoArray; FLMUINT m_uiNumThreads; }; /**************************************************************************** Desc: ****************************************************************************/ RCODE f_allocThreadMgr( IF_ThreadMgr ** ppThreadMgr) { RCODE rc = NE_FLM_OK; F_ThreadMgr * pThreadMgr = NULL; if( (pThreadMgr = f_new F_ThreadMgr) == NULL) { rc = RC_SET( NE_FLM_MEM); } if( RC_BAD( rc = pThreadMgr->setupThreadMgr())) { goto Exit; } *ppThreadMgr = pThreadMgr; pThreadMgr = NULL; Exit: if( pThreadMgr) { pThreadMgr->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI FlmGetThreadMgr( IF_ThreadMgr ** ppThreadMgr) { *ppThreadMgr = f_getThreadMgrPtr(); (*ppThreadMgr)->AddRef(); return( NE_FLM_OK); } /**************************************************************************** Desc: Add a Reference to this object. ****************************************************************************/ FLMINT FLMAPI F_Thread::AddRef( void) { return( f_atomicInc( &m_refCnt)); } /**************************************************************************** Desc: Removes a reference to this object. ****************************************************************************/ FLMINT FLMAPI F_Thread::Release( void) { FLMINT iRefCnt = f_atomicDec( &m_refCnt); if( !iRefCnt) { delete this; } return( iRefCnt); } /**************************************************************************** Desc: Performs various setup work and starts a new thread ****************************************************************************/ RCODE FLMAPI F_Thread::startThread( F_THREAD_FUNC fnThread, const char * pszThreadName, FLMUINT uiThreadGroup, FLMUINT uiAppId, void * pvParm1, void * pvParm2, FLMUINT uiStackSize) { RCODE rc = NE_FLM_OK; F_ThreadMgr * pThreadMgr = (F_ThreadMgr *)f_getThreadMgrPtr(); FLMBOOL bManagerMutexLocked = FALSE; #ifdef FLM_LIBC_NLM pthread_attr_t thread_attr; pthread_t uiThreadId; #endif #ifdef FLM_RING_ZERO_NLM void * hThread = NULL; #endif #ifdef FLM_WIN unsigned uiThreadId; #endif #if defined( FLM_UNIX) pthread_attr_t thread_attr; pthread_t uiThreadId; #endif f_assert( fnThread != NULL && m_fnThread == NULL); f_assert( uiThreadGroup != F_INVALID_THREAD_GROUP); m_fnThread = fnThread; m_pvParm1 = pvParm1; m_pvParm2 = pvParm2; // Initialize the thread's mutex if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) { goto Exit; } // Set the stack size m_uiStackSize = (uiStackSize < F_THREAD_MIN_STACK_SIZE) ? F_THREAD_MIN_STACK_SIZE : uiStackSize; // Set the thread name if( pszThreadName && *pszThreadName) { FLMUINT uiNameLen = f_strlen( pszThreadName) + 1; if( RC_BAD( rc = f_alloc( uiNameLen, &m_pszThreadName))) { goto Exit; } f_memcpy( (void *)m_pszThreadName, pszThreadName, uiNameLen); } // Set the thread group ID and the application-specified thread ID m_uiThreadGroup = uiThreadGroup; m_uiAppId = uiAppId; // Set the thread's state to "running" -- if we fail to // start the thread, this will be set back to false when // the cleanupThread() method is called below. We set this // to TRUE here so that the stopThread() method won't get // stuck in an infinite loop if the thread was never started. m_bRunning = TRUE; // Lock the thread manager's mutex. f_mutexLock( pThreadMgr->m_hMutex); bManagerMutexLocked = TRUE; // Increment the active thread count pThreadMgr->m_uiNumThreads++; // Link the thread into the manager's list. We can't link threads in order // by thread ID at this point, because we don't know what the new thread's // ID will be. if( pThreadMgr->m_pThreadList) { pThreadMgr->m_pThreadList->m_pPrev = this; } m_pNext = pThreadMgr->m_pThreadList; pThreadMgr->m_pThreadList = this; // Increment the reference count of the thread object now // that it is linked into the thread manager's list. m_refCnt++; // Start the thread #ifdef FLM_WIN if( _beginthreadex( NULL, (unsigned int)m_uiStackSize, threadStub, (void *)this, 0, &uiThreadId) == 0) { rc = RC_SET( NE_FLM_COULD_NOT_START_THREAD); goto Exit; } m_uiThreadId = (FLMUINT)uiThreadId; #elif defined( FLM_LIBC_NLM) pthread_attr_init( &thread_attr); pthread_attr_setdetachstate( &thread_attr, PTHREAD_CREATE_DETACHED); if (pthread_create( &uiThreadId, &thread_attr, threadStub, this) != 0) { rc = RC_SET( NE_FLM_COULD_NOT_START_THREAD); goto Exit; } m_uiThreadId = (FLMUINT)uiThreadId; pthread_attr_destroy( &thread_attr); #elif defined( FLM_RING_ZERO_NLM) if( (hThread = kCreateThread( (BYTE *)((m_pszThreadName) ? (BYTE *)m_pszThreadName : (BYTE *)"FTK"), threadStub, NULL, (LONG)m_uiStackSize, (void *)this)) == NULL) { rc = RC_SET( NE_FLM_COULD_NOT_START_THREAD); goto Exit; } m_uiThreadId = (FLMUINT)hThread; if( kSetThreadLoadHandle( hThread, (LONG)f_getNLMHandle()) != 0) { (void)kDestroyThread( hThread); rc = RC_SET( NE_FLM_COULD_NOT_START_THREAD); goto Exit; } if( kScheduleThread( hThread) != 0) { (void)kDestroyThread( hThread); rc = RC_SET( NE_FLM_COULD_NOT_START_THREAD); goto Exit; } #elif defined( FLM_UNIX) pthread_attr_init( &thread_attr); pthread_attr_setdetachstate( &thread_attr, PTHREAD_CREATE_DETACHED); if (pthread_create( &uiThreadId, &thread_attr, threadStub, this) != 0) { rc = RC_SET( NE_FLM_COULD_NOT_START_THREAD); goto Exit; } m_uiThreadId = (FLMUINT)uiThreadId; pthread_attr_destroy( &thread_attr); #endif // Code is not designed to handle a thread ID of 0 f_assert( m_uiThreadId != 0); // Unlock the thread manager's mutex. f_mutexUnlock( pThreadMgr->m_hMutex); bManagerMutexLocked = FALSE; Exit: if( RC_BAD( rc)) { // Unlink the thread from the manager's list. This call // won't do anything if the thread was not linked above. pThreadMgr->unlinkThread( this, bManagerMutexLocked); // Reset the thread object back to its initial state cleanupThread(); } if( bManagerMutexLocked) { f_mutexUnlock( pThreadMgr->m_hMutex); } return( rc); } /**************************************************************************** Desc: Stop a running thread ****************************************************************************/ void FLMAPI F_Thread::stopThread( void) { // Set the shutdown flag and wait for the thread's // status to be something other than "running" m_bShutdown = TRUE; while( m_bRunning) { f_sleep( 10); } // Reset the shutdown flag in case this object is re-used. m_bShutdown = FALSE; } /**************************************************************************** Desc: ****************************************************************************/ void FLMAPI F_Thread::waitToComplete( void) { while( m_bRunning) { f_sleep( 10); } } /**************************************************************************** Desc: Begins a new thread of execution and calls the passed function. Performs generic thread init and cleanup functions. ****************************************************************************/ #ifdef FLM_LIBC_NLM void * threadStub( void * pvThread) #elif defined( FLM_RING_ZERO_NLM) void * threadStub( void * pvUnused, void * pvThread) #elif defined( FLM_WIN) unsigned __stdcall threadStub( void * pvThread) #elif defined( FLM_UNIX) void * threadStub( void * pvThread) #endif { F_Thread * pThread = (F_Thread *)pvThread; F_ThreadMgr * pThreadMgr = (F_ThreadMgr *)f_getThreadMgrPtr(); #ifdef FLM_RING_ZERO_NLM F_UNREFERENCED_PARM( pvUnused); #endif #if defined( FLM_UNIX) || defined( FLM_LIBC_NLM) // Block all signals (main thread will handle all signals) sigset_t mask; sigfillset(&mask); pthread_sigmask(SIG_SETMASK, &mask, 0); #endif // Lock the manager's mutex pThreadMgr->lockMutex(); // At this point, the thread ID must match. f_assert( pThread->m_uiThreadId == f_threadId()); // Set the start time f_timeGetSeconds( &pThread->m_uiStartTime); // Unlock the manager's mutex pThreadMgr->unlockMutex(); // Call the thread's function pThread->m_exitRc = pThread->m_fnThread( pThread); // Add a temporary reference to the thread object so // it doesn't go away when we unlink it from the // manager pThread->AddRef(); // Unlink the thread from the thread manager. pThreadMgr->unlinkThread( pThread, FALSE); // Set the running flag to FALSE pThread->m_bRunning = FALSE; // Release the temporary reference to the thread. Once the // reference is release, pThread must not be accessed because // the object may have gone away. pThread->Release(); pThread = NULL; // Terminate the thread #if defined( FLM_WIN) _endthreadex( 0); return( 0); #elif defined( FLM_RING_ZERO_NLM) kExitThread( NULL); #endif #if defined( FLM_NLM) || defined( FLM_UNIX) return( NULL); #endif } /**************************************************************************** Desc: Frees any resources allocated to the thread and resets member variables to their initial state ****************************************************************************/ void FLMAPI F_Thread::cleanupThread( void) { f_assert( !m_pPrev && !m_pNext); if( m_hMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hMutex); } if( m_pszThreadName) { f_free( &m_pszThreadName); } if( m_pszThreadStatus) { f_free( &m_pszThreadStatus); } m_uiStatusBufLen = 0; m_bShutdown = FALSE; m_fnThread = NULL; m_bRunning = FALSE; m_uiStackSize = 0; m_pvParm1 = NULL; m_pvParm2 = NULL; m_uiThreadId = 0; m_uiThreadGroup = F_INVALID_THREAD_GROUP; m_uiAppId = 0; m_uiStartTime = 0; m_exitRc = NE_FLM_OK; } /**************************************************************************** Desc: ****************************************************************************/ void FLMAPI F_Thread::sleep( FLMUINT uiMilliseconds) { FLMUINT uiTimeToSleep; if( !uiMilliseconds) { f_yieldCPU(); return; } while( uiMilliseconds && !m_bShutdown) { uiTimeToSleep = f_min( uiMilliseconds, 50); f_sleep( uiTimeToSleep); uiMilliseconds -= uiTimeToSleep; } } /**************************************************************************** Desc: Set the thread's status ****************************************************************************/ void FLMAPI F_Thread::setThreadStatusStr( const char * pszStatus) { FLMUINT uiStatusLen = f_strlen( pszStatus) + 1; if( m_uiStatusBufLen < uiStatusLen) { FLMUINT uiAllocSize = uiStatusLen < 128 ? 128 : uiStatusLen; if( m_pszThreadStatus != NULL) { f_free( &m_pszThreadStatus); } m_uiStatusBufLen = 0; if( RC_BAD( f_alloc( uiAllocSize, &m_pszThreadStatus))) { goto Exit; } m_uiStatusBufLen = uiAllocSize; } f_mutexLock( m_hMutex); f_memcpy( m_pszThreadStatus, pszStatus, uiStatusLen); f_mutexUnlock( m_hMutex); Exit: return; } /**************************************************************************** Desc: Set the thread's status ****************************************************************************/ void FLMAPI F_Thread::setThreadStatus( const char * pszFormat, ...) { char pucBuffer[ 128]; f_va_list args; f_va_start( args, pszFormat); f_vsprintf( pucBuffer, pszFormat, &args); f_va_end( args); setThreadStatusStr( pucBuffer); } /**************************************************************************** Desc: Set the thread's status to a generic string ****************************************************************************/ void FLMAPI F_Thread::setThreadStatus( eThreadStatus genericStatus) { const char * pszStatus = NULL; switch( genericStatus) { case FLM_THREAD_STATUS_INITIALIZING: pszStatus = "Initializing"; break; case FLM_THREAD_STATUS_RUNNING: pszStatus = "Running"; break; case FLM_THREAD_STATUS_SLEEPING: pszStatus = "Sleeping"; break; case FLM_THREAD_STATUS_TERMINATING: pszStatus = "Terminating"; break; case FLM_THREAD_STATUS_UNKNOWN: default: pszStatus = "Unknown"; break; } if( pszStatus) { setThreadStatusStr( pszStatus); } } /**************************************************************************** Desc: ****************************************************************************/ F_ThreadMgr::~F_ThreadMgr() { F_Thread * pTmpThread; if( m_hMutex != F_MUTEX_NULL) { f_mutexLock( m_hMutex); pTmpThread = m_pThreadList; while( pTmpThread) { pTmpThread->setShutdownFlag(); pTmpThread = pTmpThread->m_pNext; } while( m_pThreadList) { f_mutexUnlock( m_hMutex); f_sleep( 50); f_mutexLock( m_hMutex); } f_mutexUnlock( m_hMutex); f_mutexDestroy( &m_hMutex); } } /**************************************************************************** Desc: Allocates resources needed by the thread manager ****************************************************************************/ RCODE FLMAPI F_ThreadMgr::setupThreadMgr( void) { RCODE rc = NE_FLM_OK; f_assert( m_hMutex == F_MUTEX_NULL); if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Removes a thread from the thread manager's list Notes: This routine assumes that the manager's mutex is already locked. ****************************************************************************/ void F_ThreadMgr::unlinkThread( IF_Thread * ifpThread, FLMBOOL bMutexIsLocked) { F_Thread * pThread = (F_Thread *)ifpThread; // Lock the thread manager's mutex if( !bMutexIsLocked) { f_mutexLock( m_hMutex); } // If the thread isn't linked into the list, // don't do anything if( !pThread->m_pPrev && !pThread->m_pNext && m_pThreadList != pThread) { goto Exit; } // Decrement the active thread count f_assert( m_uiNumThreads); m_uiNumThreads--; if( pThread->m_pPrev) { pThread->m_pPrev->m_pNext = pThread->m_pNext; } else { m_pThreadList = pThread->m_pNext; } if( pThread->m_pNext) { pThread->m_pNext->m_pPrev = pThread->m_pPrev; } pThread->m_pNext = NULL; pThread->m_pPrev = NULL; // Release the thread object pThread->Release(); Exit: if( !bMutexIsLocked) { f_mutexUnlock( m_hMutex); } } /**************************************************************************** Desc: Signals all threads in a thread group to shut down and waits for them to terminate. ****************************************************************************/ void FLMAPI F_ThreadMgr::shutdownThreadGroup( FLMUINT uiThreadGroup) { F_Thread * pThread; FLMUINT uiCount; f_assert( uiThreadGroup != F_INVALID_THREAD_GROUP); for( ;;) { f_mutexLock( m_hMutex); uiCount = 0; pThread = m_pThreadList; while( pThread) { if( pThread->m_uiThreadGroup == uiThreadGroup) { pThread->setShutdownFlag(); uiCount++; } pThread = pThread->m_pNext; } f_mutexUnlock( m_hMutex); if( !uiCount) { break; } // The threads will automatically unlink themselves from // the manager before they terminate. Just sleep for // a few milliseconds and look through the list again to // verify that there are no more threads in the group. f_sleep( 200); } } /**************************************************************************** Desc: Signals a thread to shut down. ****************************************************************************/ void FLMAPI F_ThreadMgr::setThreadShutdownFlag( FLMUINT uiThreadId) { F_Thread * pThread; f_assert( uiThreadId != 0); f_mutexLock( m_hMutex); pThread = m_pThreadList; while( pThread) { if( pThread->m_uiThreadId == uiThreadId) { pThread->setShutdownFlag(); break; } pThread = pThread->m_pNext; } f_mutexUnlock( m_hMutex); } /**************************************************************************** Desc: Allocates an array of F_THREAD_INFO structures and populates them with information about the threads being managed by this object. ****************************************************************************/ RCODE FLMAPI F_ThreadMgr::getThreadInfo( F_Pool * pPool, F_THREAD_INFO ** ppThreadInfo, FLMUINT * puiNumThreads) { RCODE rc = NE_FLM_OK; FLMUINT uiOffset; FLMUINT uiLoop; FLMUINT uiSubLoop; FLMUINT uiLen; FLMBOOL bMutexLocked = FALSE; F_THREAD_INFO * pThreadInfo = NULL; F_THREAD_INFO tmpThreadInfo; F_Thread * pCurThread; void * pvMark = pPool->poolMark(); *ppThreadInfo = NULL; *puiNumThreads = 0; f_mutexLock( m_hMutex); bMutexLocked = TRUE; if( m_uiNumThreads == 0) { goto Exit; } if (RC_BAD( rc = pPool->poolCalloc( sizeof( F_THREAD_INFO) * m_uiNumThreads, (void **)&pThreadInfo))) { goto Exit; } uiOffset = 0; pCurThread = m_pThreadList; while( pCurThread) { f_assert( uiOffset < m_uiNumThreads); f_mutexLock( pCurThread->m_hMutex); pThreadInfo[ uiOffset].uiThreadId = pCurThread->m_uiThreadId; pThreadInfo[ uiOffset].uiThreadGroup = pCurThread->m_uiThreadGroup; pThreadInfo[ uiOffset].uiAppId = pCurThread->m_uiAppId; pThreadInfo[ uiOffset].uiStartTime = pCurThread->m_uiStartTime; if( pCurThread->m_pszThreadName) { uiLen = f_strlen( pCurThread->m_pszThreadName) + 1; if (RC_OK( pPool->poolCalloc( uiLen, (void **)&pThreadInfo[ uiOffset].pszThreadName))) { f_memcpy( (void *)pThreadInfo[ uiOffset].pszThreadName, pCurThread->m_pszThreadName, uiLen); } } if( pCurThread->m_pszThreadStatus) { uiLen = f_strlen( pCurThread->m_pszThreadStatus) + 1; if (RC_OK( pPool->poolCalloc( uiLen, (void **)&pThreadInfo[ uiOffset].pszThreadStatus))) { f_memcpy( (void *)(pThreadInfo[ uiOffset].pszThreadStatus), pCurThread->m_pszThreadStatus, uiLen); } } f_mutexUnlock( pCurThread->m_hMutex); uiOffset++; pCurThread = pCurThread->m_pNext; } f_assert( uiOffset == m_uiNumThreads); *puiNumThreads = m_uiNumThreads; f_mutexUnlock( m_hMutex); bMutexLocked = FALSE; // Sort the list by thread ID for( uiLoop = 0; uiLoop < *puiNumThreads; uiLoop++) { for( uiSubLoop = uiLoop + 1; uiSubLoop < *puiNumThreads; uiSubLoop++) { if( pThreadInfo[ uiLoop].uiThreadId > pThreadInfo[ uiSubLoop].uiThreadId) { f_memcpy( &tmpThreadInfo, &pThreadInfo[ uiLoop], sizeof( F_THREAD_INFO)); f_memcpy( &pThreadInfo[ uiLoop], &pThreadInfo[ uiSubLoop], sizeof( F_THREAD_INFO)); f_memcpy( &pThreadInfo[ uiSubLoop], &tmpThreadInfo, sizeof( F_THREAD_INFO)); } } } *ppThreadInfo = pThreadInfo; Exit: if( RC_BAD( rc)) { pPool->poolReset( pvMark); } if( bMutexLocked) { f_mutexUnlock( m_hMutex); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_ThreadMgr::getThreadName( FLMUINT uiThreadId, char * pszThreadName, FLMUINT * puiLength) { RCODE rc = NE_FLM_OK; F_Thread * pThread = NULL; FLMUINT uiCopyLen; if( RC_BAD( rc = getThread( uiThreadId, &pThread))) { goto Exit; } f_mutexLock( pThread->m_hMutex); if( pThread->m_pszThreadName) { uiCopyLen = f_min( *puiLength - 1, f_strlen( pThread->m_pszThreadName)); f_strncpy( pszThreadName, pThread->m_pszThreadName, uiCopyLen); *puiLength = uiCopyLen; } else { *pszThreadName = 0; } f_mutexUnlock( pThread->m_hMutex); Exit: if( pThread) { pThread->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_ThreadMgr::getThread( FLMUINT uiThreadId, F_Thread ** ppThread) { RCODE rc = NE_FLM_OK; FLMBOOL bMutexLocked = FALSE; F_Thread * pCurThread; f_assert( *ppThread == NULL); f_mutexLock( m_hMutex); bMutexLocked = TRUE; pCurThread = m_pThreadList; while( pCurThread) { if( pCurThread->m_uiThreadId == uiThreadId) { *ppThread = pCurThread; pCurThread->AddRef(); break; } pCurThread = pCurThread->m_pNext; } f_mutexUnlock( m_hMutex); bMutexLocked = FALSE; if( !pCurThread) { rc = RC_SET( NE_FLM_NOT_FOUND); goto Exit; } Exit: if( bMutexLocked) { f_mutexUnlock( m_hMutex); } return( rc); } /**************************************************************************** Desc: Finds a thread based on user-specified identifiers ****************************************************************************/ RCODE FLMAPI F_ThreadMgr::findThread( IF_Thread ** ppThread, FLMUINT uiThreadGroup, FLMUINT uiAppId, FLMBOOL bOkToFindMe) { RCODE rc = NE_FLM_OK; FLMBOOL bMutexLocked = FALSE; F_Thread * pCurThread; *ppThread = NULL; f_mutexLock( m_hMutex); bMutexLocked = TRUE; if( m_uiNumThreads == 0) { rc = RC_SET( NE_FLM_NOT_FOUND); goto Exit; } pCurThread = m_pThreadList; while( pCurThread) { f_mutexLock( pCurThread->m_hMutex); if( pCurThread->m_uiThreadGroup == uiThreadGroup && pCurThread->m_uiAppId == uiAppId) { if( bOkToFindMe || (!bOkToFindMe && pCurThread->m_uiThreadId != f_threadId())) { // Found a match. pCurThread->AddRef(); *ppThread = pCurThread; f_mutexUnlock( pCurThread->m_hMutex); goto Exit; } } f_mutexUnlock( pCurThread->m_hMutex); pCurThread = pCurThread->m_pNext; } rc = RC_SET( NE_FLM_NOT_FOUND); Exit: if( bMutexLocked) { f_mutexUnlock( m_hMutex); } return( rc); } /**************************************************************************** Desc: Finds a thread based on user-specified identifiers ****************************************************************************/ RCODE FLMAPI F_ThreadMgr::getNextGroupThread( IF_Thread ** ppThread, FLMUINT uiThreadGroup, FLMUINT * puiThreadId) { RCODE rc = NE_FLM_OK; FLMBOOL bMutexLocked = FALSE; F_Thread * pCurThread; F_Thread * pFoundThread = NULL; f_mutexLock( m_hMutex); bMutexLocked = TRUE; if( m_uiNumThreads == 0) { rc = RC_SET( NE_FLM_NOT_FOUND); goto Exit; } pCurThread = m_pThreadList; while( pCurThread) { if( pCurThread->m_uiThreadGroup == uiThreadGroup && pCurThread->m_uiThreadId > *puiThreadId) { // The threads are not kept in order by thread ID in the // manager's list. So, we need to make sure we get the // thread with the next ID beyond the ID passed into the // routine. if( !pFoundThread || pCurThread->m_uiThreadId < pFoundThread->m_uiThreadId) { pFoundThread = pCurThread; } } pCurThread = pCurThread->m_pNext; } if( !pFoundThread) { rc = RC_SET( NE_FLM_NOT_FOUND); goto Exit; } pFoundThread->AddRef(); *ppThread = pFoundThread; *puiThreadId = pFoundThread->m_uiThreadId; Exit: if( RC_BAD( rc)) { *ppThread = NULL; *puiThreadId = 0xFFFFFFFF; } if( bMutexLocked) { f_mutexUnlock( m_hMutex); } return( rc); } /**************************************************************************** Desc: Returns a count of the number of threads in a specified group ****************************************************************************/ FLMUINT FLMAPI F_ThreadMgr::getThreadGroupCount( FLMUINT uiThreadGroup) { F_Thread * pThread; FLMUINT uiCount; f_mutexLock( m_hMutex); uiCount = 0; pThread = m_pThreadList; while( pThread) { if( pThread->m_uiThreadGroup == uiThreadGroup) { uiCount++; } pThread = pThread->m_pNext; } f_mutexUnlock( m_hMutex); return( uiCount); } /**************************************************************************** Desc: ****************************************************************************/ FLMUINT FLMAPI F_ThreadMgr::allocGroupId( void) { return( f_atomicInc( &m_groupCounter)); } /**************************************************************************** Desc: Allocate a thread object and start the thread ****************************************************************************/ RCODE FLMAPI F_ThreadMgr::createThread( IF_Thread ** ppThread, F_THREAD_FUNC fnThread, const char * pszThreadName, FLMUINT uiThreadGroup, FLMUINT uiAppId, void * pvParm1, void * pvParm2, FLMUINT uiStackSize) { RCODE rc = NE_FLM_OK; F_Thread * pThread = NULL; if( ppThread) { *ppThread = NULL; } if( (pThread = f_new F_Thread) == NULL) { rc = RC_SET( NE_FLM_MEM); goto Exit; } if( RC_BAD( rc = pThread->startThread( fnThread, pszThreadName, uiThreadGroup, uiAppId, pvParm1, pvParm2, uiStackSize))) { goto Exit; } if( ppThread) { *ppThread = pThread; // Set pThread to NULL so that the object won't be released // below. The application has indicated (by passing in a // non-NULL ppThread) that it wants to keep a reference to // the thread. pThread = NULL; } Exit: if( pThread) { pThread->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI f_threadCreate( IF_Thread ** ppThread, F_THREAD_FUNC fnThread, const char * pszThreadName, FLMUINT uiThreadGroup, FLMUINT uiAppId, void * pvParm1, void * pvParm2, FLMUINT uiStackSize) { return( f_getThreadMgrPtr()->createThread( ppThread, fnThread, pszThreadName, uiThreadGroup, uiAppId, pvParm1, pvParm2, uiStackSize)); } /**************************************************************************** Desc: ****************************************************************************/ void FLMAPI f_threadDestroy( IF_Thread ** ppThread) { if( *ppThread != NULL) { (*ppThread)->stopThread(); (*ppThread)->Release(); *ppThread = NULL; } } /**************************************************************************** Desc: ****************************************************************************/ FLMUINT f_getpid( void) { #if defined( FLM_WIN) return _getpid(); #elif defined( FLM_UNIX) return getpid(); #elif defined( FLM_NLM) return( (FLMUINT)f_getNLMHandle()); #else #error "Unsupported Platform" #endif } /**************************************************************************** Desc: ****************************************************************************/ FLMUINT FLMAPI f_threadId( void) { #ifdef FLM_WIN return( (FLMUINT)_threadid); #elif defined( FLM_UNIX) || defined( FLM_LIBC_NLM) return( (FLMUINT)pthread_self()); #elif defined( FLM_RING_ZERO_NLM) return( (FLMUINT)kCurrentThread()); #else #error Platform not supprted #endif } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI FlmGetThreadInfo( IF_ThreadInfo ** ppThreadInfo) { RCODE rc = NE_FLM_OK; F_ThreadInfo * pThreadInfo = NULL; if( (pThreadInfo = f_new F_ThreadInfo) == NULL) { rc = RC_SET( NE_FLM_MEM); goto Exit; } if( RC_BAD( rc = f_getThreadMgrPtr()->getThreadInfo( &pThreadInfo->m_pool, &pThreadInfo->m_pThreadInfoArray, &pThreadInfo->m_uiNumThreads))) { goto Exit; } *ppThreadInfo = pThreadInfo; pThreadInfo = NULL; Exit: if( pThreadInfo) { pThreadInfo->Release(); } return( rc); }