Files
geant4/source/global/management/include/G4MTBarrier.hh
2020-06-26 10:23:25 +02:00

160 lines
6.8 KiB
C++

//
// ********************************************************************
// * License and Disclaimer *
// * *
// * The Geant4 software is copyright of the Copyright Holders of *
// * the Geant4 Collaboration. It is provided under the terms and *
// * conditions of the Geant4 Software License, included in the file *
// * LICENSE and available at http://cern.ch/geant4/license . These *
// * include a list of copyright holders. *
// * *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work make any representation or warranty, express or implied, *
// * regarding this software system or assume any liability for its *
// * use. Please see the license in the file LICENSE and URL above *
// * for the full disclaimer and the limitation of liability. *
// * *
// * This code implementation is the result of the scientific and *
// * technical work of the GEANT4 collaboration. *
// * By using, copying, modifying or distributing the software (or *
// * any work based on the software) you agree to acknowledge its *
// * use in resulting scientific publications, and indicate your *
// * acceptance of all terms of the Geant4 Software license. *
// ********************************************************************
//
// G4MTBarrier
//
// Class description:
//
// This class defines a synchronization point between threads: a master
// and a pool of workers.
// A barrier is a (shared) instance of this class. Master sets the number
// of active threads to wait for, then it waits for workers to become ready
// calling the method WaitForReadyWorkers().
// The master thread will block on this call.
// Each of the workers calls ThisWorkerReady() when it is ready to continue.
// It will block on this call.
// When all worker threads have called ThisWorkerReady and are waiting the
// master will release the barrier and execution will continue.
//
// User code can implement more advanced barriers that require exchange
// of a message between master and threads inheriting from this class as in:
// class Derived : public G4MTBarrier {
// G4Mutex mutexForMessage;
// SomeType message;
// void MethodCalledByWorkers() {
// G4MTBarrirer::ThisWorkerReady();
// G4AutoLock l(&mutexForMessage);
// [... process message ...]
// }
// void WaitForReadyWorkers() override {
// Wait(); <== Mandatory
// [.. process message ...] <== User code between the two calls
// ReleaseBarrier(); <== Mandatory
// }
// void MethodCalledByMaster() { WaitForReadyWorkers(); }
// }
// User code can also achieve the same results as before using the granular
// methods LoopWaitingWorkers and ResetCounterAndBroadcast methods in the
// master. For examples of usage of this class see G4MTRunManager
//
// =====================================
// Barriers mechanism
// =====================================
// We define a barrier has a point in which threads synchronize.
// When workers threads reach a barrier they wait for the master thread a
// signal that they can continue. The master thread broadcast this signal
// only when all worker threads have reached this point.
// Currently only three points require this sync in the life-time of a G4
// application: just before and just after the for-loop controlling the
// thread event-loop and between runs.
//
// The basic algorithm of each barrier works like this:
// In the master:
// WaitWorkers()
// {
// while (true)
// {
// G4AutoLock l(&counterMutex); || Mutex is locked
// (1) if ( counter == nActiveThreads ) break; G4CONDITIONWAIT(
// &conditionOnCounter, &counterMutex); || Mutex is atomically released and
// wait, upon return locked (2)
// } || unlock mutex
// G4AutoLock l(&counterMutex); || lock again mutex
// (3) G4CONDITIONBROADCAST( &doSomethingCanStart ); || Here mutex
// is locked (4)
// } || final unlock (5)
// In the workers:
// WaitSignalFromMaster()
// {
// G4AutoLock l(&counterMutex); || (6)
// ++counter;
// G4CONDITIONBROADCAST(&conditionOnCounter); || (7)
// G4CONDITIONWAIT( &doSomethingCanStart , &counterMutex);|| (8)
// }
// Each barrier requires 2 conditions and one mutex, plus a counter.
// Important note: the thread calling broadcast should hold the mutex
// before calling broadcast to obtain predictible behavior
// http://pubs.opengroup.org/onlinepubs/7908799/xsh/pthread_cond_broadcast.html
// Also remember that the wait for condition will atomically release the mutex
// and wait on condition, but it will lock again on mutex when returning
// Here it is how the control flows.
// Imagine master starts and only one worker (nActiveThreads==1)
// Master | Worker | counter | Who holds mutex
// Gets to (1) | Blocks on (6) | 0 | M
// Waits in (2) | | 0 | -
// | Arrives to (7) | 1 | W
// | Waits in (8) | 1 | -
// Gets to (1) | | 1 | M
// Jumps to (3) | | 1 | M
// End | | 1 | -
// | End | 1 | -
// Similarly for more than one worker threads or if worker starts
// Author: A.Dotti (SLAC), 10 February 2016
// --------------------------------------------------------------------
#ifndef G4MTBARRIER_HH
#define G4MTBARRIER_HH
#include "G4Threading.hh"
class G4MTBarrier
{
public:
G4MTBarrier()
: G4MTBarrier(1)
{}
virtual ~G4MTBarrier() {}
G4MTBarrier(const G4MTBarrier&) = delete;
G4MTBarrier& operator=(const G4MTBarrier&) = delete;
// on explicitly defaulted move at
// https://msdn.microsoft.com/en-us/library/dn457344.aspx
// G4MTBarrier(G4MTBarrier&&) = default;
// G4MTBarrier& operator=(G4MTBarrier&&) = default;
G4MTBarrier(unsigned int numThreads);
void ThisWorkerReady();
virtual void WaitForReadyWorkers();
inline void SetActiveThreads(unsigned int val) { m_numActiveThreads = val; }
void ResetCounter();
unsigned int GetCounter();
void Wait();
void ReleaseBarrier();
inline void Wait(unsigned int numt)
{
SetActiveThreads(numt);
Wait();
}
private:
unsigned int m_numActiveThreads = 0;
unsigned int m_counter = 0;
G4Mutex m_mutex;
G4Condition m_counterChanged;
G4Condition m_continue;
};
#endif