/** @file CbmTofMemory.cxx ** @author Christian Simon ** @date 2017-06-01 **/ #include "CbmTofMemory.h" #include "CbmTofCharge.h" #include "CbmTofWall.h" #include "CbmTofCounter.h" #include "FairLogger.h" #include "TMath.h" #include "TThread.h" #include // ----- initialisation of static member variables ----------------------- CbmTofMemory* CbmTofMemory::fgInstance = NULL; Int_t CbmTofMemory::fiThreadIndices[CbmTofMemory::fiMaximumNThreads] = {-1}; TMutex CbmTofMemory::fDoMutex; TCondition CbmTofMemory::fDoSummation(&CbmTofMemory::fDoMutex); TMutex CbmTofMemory::fFinishedMutex; TCondition CbmTofMemory::fFinishedSummation(&CbmTofMemory::fFinishedMutex); Bool_t CbmTofMemory::fbNoSummation[CbmTofMemory::fiMaximumNThreads] = {kTRUE}; Bool_t CbmTofMemory::fbNotFinished = kTRUE; Double_t CbmTofMemory::fdCurrentMemoryCoefficient = 1.; Double_t CbmTofMemory::fdCurrentMCHitTime = 0.; Double_t CbmTofMemory::fdCurrentMCAcrossStripPosition = 0.; Double_t CbmTofMemory::fdCurrentMCAlongStripPosition = 0.; std::vector>* CbmTofMemory::fCurrentCounterMemory = NULL; Bool_t CbmTofMemory::fbRemoveHitsFromMemory = kFALSE; Double_t CbmTofMemory::fdHitRemovalCriterion = 0.001; Int_t CbmTofMemory::fiActualNThreads = 1; Int_t CbmTofMemory::fiFinishedThreads = 0; Double_t CbmTofMemory::fdChargeSamplingLimit = 100.; Double_t CbmTofMemory::fdHitDistanceScale = 1.; Double_t CbmTofMemory::fdRelaxationTimeConstant = 1.; // --------------------------------------------------------------------------- // ----- definition of static member functions --------------------------- // --------------------------------------------------------------------------- CbmTofMemory* CbmTofMemory::Instance() { if(!fgInstance) { fgInstance = new CbmTofMemory(); } return fgInstance; } // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- void CbmTofMemory::cleanup(void* arg) { // TThread::Printf("Cancelling thread %d ...",*(static_cast(arg))); // The mutex is used by several threads. Upon killing a thread in wait state // the remaining threads to be killed cannot unlock the mutex because the // thread that locked the mutex last is gone. fDoMutex.CleanUp(); } // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- void* CbmTofMemory::process(void* arg) { Int_t iThreadIndex = *(static_cast(arg)); TThread::CleanUpPush((void*) &cleanup, arg); TThread::SetCancelOn(); TThread::SetCancelDeferred(); for( ; ; ) { Double_t dPartialMemoryCoefficient(0.); for(auto itHit = (fCurrentCounterMemory->at(iThreadIndex)).begin(); itHit != (fCurrentCounterMemory->at(iThreadIndex)).end(); ) { Double_t dCurrentHitChargeWeight = (*itHit).GetCharge()/fdChargeSamplingLimit*TMath::Exp(-(fdCurrentMCHitTime - (*itHit).GetTime())/fdRelaxationTimeConstant); if( fbRemoveHitsFromMemory ) { if( fdHitRemovalCriterion > dCurrentHitChargeWeight ) { // remove hit from memory // Calling std::vector::erase on an element in the middle of the container // evokes a left-shift reallocation of all elements on the right-hand side // of the deleted one. To avoid this inefficiency, we overwrite the current // element with the last element and then pop the latter. // N.B.: The iterator is not incremented because otherwise the last and now // the current element would not be considered. *itHit = (fCurrentCounterMemory->at(iThreadIndex)).back(); (fCurrentCounterMemory->at(iThreadIndex)).pop_back(); continue; } } Double_t dQuadraticDistanceToCurrentHit = TMath::Power(fdCurrentMCAcrossStripPosition - (*itHit).GetAcrossPosition(), 2.) +TMath::Power(fdCurrentMCAlongStripPosition - (*itHit).GetAlongPosition(), 2.); dPartialMemoryCoefficient += 1./(1. + dQuadraticDistanceToCurrentHit/TMath::Power(fdHitDistanceScale, 2.))*dCurrentHitChargeWeight; ++itHit; } TThread::Lock(); fbNoSummation[iThreadIndex] = kTRUE; fdCurrentMemoryCoefficient -= dPartialMemoryCoefficient; TThread::UnLock(); while( fbNoSummation[iThreadIndex] ) { // For reasons of thread safety, all child threads shall have entered // their wait state as soon as possible after signalling the main // thread to continue. fDoMutex.Lock(); fiFinishedThreads++; if( fiActualNThreads == fiFinishedThreads ) { fiFinishedThreads = 0; fFinishedMutex.Lock(); fbNotFinished = kFALSE; fFinishedSummation.Signal(); fFinishedMutex.UnLock(); // FIXME: A race condition between the main thread waking up and the // child thread entering wait state cannot be fully avoided here. It // is, however, guaranteed that the last child thread to enter wait // state will execute a single instruction after unlocking the mutex // the main thread needs to lock to continue. } fDoSummation.Wait(); fDoMutex.UnLock(); } } return (0); } // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- CbmTofMemory::~CbmTofMemory() { /* if(fbRunMultiThreaded) { for( Int_t iThread = 0; iThread < fiActualNThreads; iThread++ ) { TThread::Printf("Killing ToF memory thread %d ...", iThread); fThreadPool.at(iThread)->Kill(); fThreadPool.at(iThread)->Join(); // TThread::Delete(fThreadPool.at(iThread)); // TODO } } */ } // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- void CbmTofMemory::ClearMemory() { for(auto & itCounter : fCounterMemory) { Int_t iTotalNCharges(0); CbmTofCounter* tCounter = CbmTofWall::Instance()->GetCounter(itCounter.first); std::vector>& tThreadChargeVector = itCounter.second; LOG(INFO)<GetModuleType(), tCounter->GetModuleIndex(), tCounter->GetCounterIndex())<second; fdCurrentMCHitTime = dTime; fdCurrentMCAcrossStripPosition = dAcrossPosition; fdCurrentMCAlongStripPosition = dAlongPosition; fdChargeSamplingLimit = dChargeLimit; fdHitDistanceScale = dDistanceScale; fdRelaxationTimeConstant = dTimeConstant; fdCurrentMemoryCoefficient = 1.; if(fbRunMultiThreaded) { fbNotFinished = kTRUE; fDoMutex.Lock(); fDoSummation.Broadcast(); while( fbNotFinished ) { fFinishedMutex.Lock(); for(Int_t iThread = 0; iThread < fiActualNThreads; iThread++) { fbNoSummation[iThread] = kFALSE; } fDoMutex.UnLock(); // FIXME: A race condition between the child threads woken up and the // main thread cannot be fully avoided here. fFinishedSummation.Wait(); fFinishedMutex.UnLock(); } } else { Double_t dPartialMemoryCoefficient(0.); for(auto itHit = (fCurrentCounterMemory->at(0)).begin(); itHit != (fCurrentCounterMemory->at(0)).end(); ) { Double_t dCurrentHitChargeWeight = (*itHit).GetCharge()/fdChargeSamplingLimit*TMath::Exp(-(fdCurrentMCHitTime - (*itHit).GetTime())/fdRelaxationTimeConstant); if(fbRemoveHitsFromMemory) { if(fdHitRemovalCriterion > dCurrentHitChargeWeight) { // remove hit from memory // Calling std::vector::erase on an element in the middle of the container // evokes a left-shift reallocation of all elements on the right-hand side // of the deleted one. To avoid this inefficiency, we overwrite the current // element with the last element and then pop the latter. // N.B.: The iterator is not incremented because otherwise the last and now // the current element would not be considered. *itHit = (fCurrentCounterMemory->at(0)).back(); (fCurrentCounterMemory->at(0)).pop_back(); continue; } } Double_t dQuadraticDistanceToCurrentHit = TMath::Power(fdCurrentMCAcrossStripPosition - (*itHit).GetAcrossPosition(), 2.) +TMath::Power(fdCurrentMCAlongStripPosition - (*itHit).GetAlongPosition(), 2.); dPartialMemoryCoefficient += 1./(1. + dQuadraticDistanceToCurrentHit/TMath::Power(fdHitDistanceScale, 2.))*dCurrentHitChargeWeight; ++itHit; } fdCurrentMemoryCoefficient -= dPartialMemoryCoefficient; } (fCurrentCounterMemory->at((itNHits->second)%fiActualNThreads)).emplace_back(dTime, fdCurrentMemoryCoefficient*dCharge, dAcrossPosition, dAlongPosition); (itNHits->second)++; return fdCurrentMemoryCoefficient; } // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- void CbmTofMemory::Initialize(const std::map& CounterMap) { for(auto const & itCounter : CounterMap) { Int_t iCounterAddress = itCounter.first; fCounterMemory.emplace(iCounterAddress, std::vector>(fiActualNThreads, std::vector())); fiNCounterHits.emplace(iCounterAddress, 0); for(Int_t iThread = 0; iThread < fiActualNThreads; iThread++) { ((fCounterMemory.at(iCounterAddress)).at(iThread)).reserve(1000000/fiActualNThreads + 1); } } if(fbRunMultiThreaded) { fThreadPool.resize(fiActualNThreads, NULL); for(Int_t iThread = 0; iThread < fiActualNThreads; iThread++) { fiThreadIndices[iThread] = iThread; // The pointer 'fCurrentCounterMemory' needs to point to something prior // to starting threads. Here, the memory of the first counter registered // with 'CbmTofMemory' is arbitrarily chosen for this purpose. fCurrentCounterMemory = &(fCounterMemory.begin()->second); fThreadPool.at(iThread) = new TThread(Form("tof_memory_thread_%d",iThread), process, &fiThreadIndices[iThread]); fThreadPool.at(iThread)->Run(); } sleep(2); TThread::Printf("Started %d ToF memory threads.", fiActualNThreads); } fbIsInitialized = kTRUE; } // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- void CbmTofMemory::SetNThreads(Int_t iNThreads) { if(fbIsInitialized) { LOG(WARNING)<<"CbmTofMemory singleton instance has already been initialized."<= iNThreads) { fiActualNThreads = 1; fbRunMultiThreaded = kFALSE; } else if(fiMaximumNThreads < iNThreads) { LOG(WARNING)<GetCounter(itCounter.first); const std::vector>& tThreadChargeVector = itCounter.second; LOG(INFO)<GetModuleType(), tCounter->GetModuleIndex(), tCounter->GetCounterIndex())<: pthread_cond_destroy error (No such file or directory) // SysError in : pthread_mutex_destroy error (No such file or directory) if(fbRunMultiThreaded) { for( Int_t iThread = 0; iThread < fiActualNThreads; iThread++ ) { TThread::Printf("Killing ToF memory thread %d ...", iThread); fThreadPool.at(iThread)->Kill(); fThreadPool.at(iThread)->Join(); TThread::Delete(fThreadPool.at(iThread)); } } } // --------------------------------------------------------------------------- // ----- default constructor --------------------------------------------- CbmTofMemory::CbmTofMemory() : fCounterMemory(), fiNCounterHits(), fThreadPool(), fbRunMultiThreaded(kFALSE), fbIsInitialized(kFALSE) { } // --------------------------------------------------------------------------- ClassImp(CbmTofMemory)