// shmthread.h : // #if !defined(AGD_SHMTHREAD_H__C09C5E02_A3A0_4171_BA2E_045C3FE0C05C__INCLUDED_) #define AGD_SHMTHREAD_H__C09C5E02_A3A0_4171_BA2E_045C3FE0C05C__INCLUDED_ #include #include #include #include #ifndef _LIBBUILD #include #else // _LIBBUILD #include "shmvar.h" #endif // _LIBBUILD #define _SHM_SNEAK_PEEK 1 #define _TRACK_TIME 0 #define _TRACK_UPDATES 0 #define _SHM_SCAN_INTERVAL_MS 100 ///////////////////////////////////////////////////////////////////////////// #define _INVALID_THREAD_ID 0 ///////////////////////////////////////////////////////////////////////////// // shmthread.h - Declarations: template class CShmWatcher { public: CShmWatcher(HSHM hShm, T &rT, const void *pShm); virtual ~CShmWatcher(void); bool StartWatch(unsigned long nMsInterval = _SHM_SCAN_INTERVAL_MS); void StopWatch(void); static void* ShmWatchWorker(void *pParam); inline void Lock(void){ (*m_pfnLock)(m_hShm); } inline void Unlock(void){ (*m_pfnUnlock)(m_hShm); } void SetLockUnlockFunctions(PFN_GFA_IPC_LOCK_SHM pfnLock, PFN_GFA_IPC_UNLOCK_SHM pfnUnlock) { if(pfnLock && pfnUnlock) { Lock(); PFN_GFA_IPC_UNLOCK_SHM pfnUlTmp = m_pfnUnlock; m_pfnLock = pfnLock; m_pfnUnlock = pfnUnlock; (*pfnUlTmp)(m_hShm); } else { Lock(); PFN_GFA_IPC_UNLOCK_SHM pfnUlTmp = m_pfnUnlock; m_pfnLock = ::GfaIpcLockSHM; m_pfnUnlock = ::GfaIpcUnlockSHM; (*pfnUlTmp)(m_hShm); } } #if _SHM_SNEAK_PEEK inline bool ShmPeek(void){ Lock(); bool bRet = !!memcmp(m_pCache, m_pShm, sizeof(S)); Unlock(); return bRet;} inline void UpdateCache(void){ Lock(); memcpy(m_pCache, m_pShm, sizeof(S)); Unlock();} #endif // _SHM_SNEAK_PEEK private: struct timespec m_tsInterval; pthread_t m_threadID; HSHM m_hShm; T &m_rT; S *m_pCache; const void *m_pShm; PFN_GFA_IPC_LOCK_SHM m_pfnLock; PFN_GFA_IPC_UNLOCK_SHM m_pfnUnlock; }; ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// template CShmWatcher::CShmWatcher(HSHM hShm, T &rT, const void *pShm) : m_threadID(_INVALID_THREAD_ID), m_hShm(hShm), m_rT(rT), m_pCache(NULL), m_pShm(pShm), m_pfnLock(::GfaIpcLockSHM), m_pfnUnlock(::GfaIpcUnlockSHM) { #if _SHM_SNEAK_PEEK if(m_pShm) { m_pCache = new S; memcpy(m_pCache, pShm, sizeof(S)); } else { Q_ASSERT_X(false, "CShmWatcher::CShmWatcher", "Invalid SHM pointer!"); } #endif // _SHM_SNEAK_PEEK } template CShmWatcher::~CShmWatcher(void) { StopWatch(); #if _SHM_SNEAK_PEEK if(m_pCache) { delete m_pCache; m_pCache = NULL; } #endif // _SHM_SNEAK_PEEK } ///////////////////////////////////////////////////////////////////////////// template bool CShmWatcher::StartWatch(unsigned long nMsInterval) { Lock(); if(m_threadID != _INVALID_THREAD_ID) { Unlock(); return true; } Unlock(); m_tsInterval.tv_sec = nMsInterval / 1000; m_tsInterval.tv_nsec = (nMsInterval % 1000) * 1000000; bool bRet = !::pthread_create(&m_threadID, NULL, &CShmWatcher::ShmWatchWorker, reinterpret_cast(this)); if(!bRet) { m_threadID = _INVALID_THREAD_ID; qDebug() << "CShmWatcher::StartWatch failed!"; } else { qDebug() << "CShmWatcher::StartWatch success!"; } return bRet; } ///////////////////////////////////////////////////////////////////////////// template void CShmWatcher::StopWatch(void) { pthread_t threadID; Lock(); if((threadID = m_threadID) != _INVALID_THREAD_ID) m_threadID = _INVALID_THREAD_ID; Unlock(); if(threadID != _INVALID_THREAD_ID) { ::pthread_cancel(threadID); ::pthread_join(threadID, NULL); qDebug() << "CShmWatcher::StopWatch: thread exit!"; } } ///////////////////////////////////////////////////////////////////////////// template void* CShmWatcher::ShmWatchWorker(void *pParam) { if(pParam) { #if _TRACK_UPDATES CHECK_UPDATE_SHM_RETVAL rv; #endif // _TRACK_UPDATES #if _TRACK_TIME struct timespec tsStart, tsEnd; #endif // _TRACK_TIME #if _SHM_SNEAK_PEEK bool bUpdt; #endif // _SHM_SNEAK_PEEK CShmWatcher* pSelf = reinterpret_cast*>(pParam); do { #if _TRACK_TIME unsigned long long nStart, nEnd; double fTime; ::clock_gettime(CLOCK_MONOTONIC, &tsStart); #endif // _TRACK_TIME #if _SHM_SNEAK_PEEK bUpdt = pSelf->ShmPeek(); if(bUpdt) { #endif // _SHM_SNEAK_PEEK #if _TRACK_UPDATES rv.nRetval = pSelf->m_rT.CheckUpdateShm(); #else // _TRACK_UPDATES pSelf->m_rT.CheckUpdateShm(); #endif // _TRACK_UPDATES #if _SHM_SNEAK_PEEK pSelf->UpdateCache(); } #if _TRACK_UPDATES else { rv.nRetval = 0; } #endif // _TRACK_UPDATES #endif // _SHM_SNEAK_PEEK #if _TRACK_TIME ::clock_gettime(CLOCK_MONOTONIC, &tsEnd); nStart = tsStart.tv_sec * 1000000000 + tsStart.tv_nsec; nEnd = tsEnd.tv_sec * 1000000000 + tsEnd.tv_nsec; fTime = (double)(nEnd - nStart) / 1000000.0; qDebug() << "CShmWatcher::ShmWatchWorker: Scan / Update time: - " << fTime << "ms"; #endif // _TRACK_TIME #if _TRACK_UPDATES qDebug() << "CShmWatcher::ShmWatchWorker: Variables scanned / updated: - " << rv.nChecked << "/" << rv.nUpdated; #endif // _TRACK_UPDATES ::nanosleep(&pSelf->m_tsInterval, NULL); } while(true); pSelf->Lock(); pSelf->m_threadID = _INVALID_THREAD_ID; pSelf->Unlock(); qDebug() << "CShmWatcher::ShmWatchWorker: thread ended gracefully!"; } return NULL; } ///////////////////////////////////////////////////////////////////////////// #endif // !defined(AGD_SHMTHREAD_H__C09C5E02_A3A0_4171_BA2E_045C3FE0C05C__INCLUDED_)