Getting Started With Custom Event Data Persistency

Hi,

I’m working on a custom event-level data type (I want a graph of particle cascades/showers) that I want to collect and write to disk for each run. What would be a good example to show how to do this in a thread safe way? I’m envisioning at the run-level, I can have a vector of pointers to objects corresponding to each simulated event. The race condition comes into expanding and placing new pointers in the vector, I think.

A common trick is to pass your RunAction as a constructor argument to EventAction, which can then push a constructed event summary back into RunAction for accumulation.

Avoid the race condition by having each thread collect the vector itself, for example, as a data member of your RunAction. Then the worker thread RunActions, in their EndOfRunAction() function, can use a mutex to pass their summaries up to either a shared singleton or to the master-thread RunAction for merging.

This would limit the mutexes to just once per thread per run, instead of on every event.

That makes sense. If I use RunAction, would I need to code the mutex myself, or is that done by inheritance of the RunAction?

You apply the mutex yourself, using Geant4’s G4AutoLock class to make it platform independent. Keep in mind that in MT, you will have a separate master thread instance of RunAction vs. the worker thread instances. You may want to use the G4Threading::IfWorkerThread() function so that the master version doesn’t try to do something appropriate for the workers.

The documentation for multithreading stuff is in the Toolkit Guide: http://geant4-userdoc.web.cern.ch/geant4-userdoc/UsersGuides/ForToolkitDeveloper/html/OOAnalysisDesign/Multithreading/mt.html
with the mutex discussion in http://geant4-userdoc.web.cern.ch/geant4-userdoc/UsersGuides/ForToolkitDeveloper/html/OOAnalysisDesign/Multithreading/mt.html?highlight=multithread#types-and-functions-related-to-the-use-of-mutexes-and-conditions

However, that’s pretty low level. For what you need, there’s basically two lines of code involved. In your .cc file, create a file-scoped mutex object:

namespace { G4Mutex raMutex = G4MUTEX_INITIALIZER; }

Then within your code, create the mutex lock on the stack, so it is freed automatically:

  {
    some-code-is-here;
    more-code;

    G4AutoLock l(&raMutex);
    mutex-protected-activity;
  }

Mutex protection applies to reading from a non-const shared object, as well as writing, so if your “protected activity” is in an “if (shared-flag-is-false)” block, you need to have the mutex outside that if statement.

Coming back to that IfWorkerThread() test – in sequential running, that flag is always false; there’s only one RunAction, with the master and worker activity combined. You’ll need to keep this in mind in designing you collection system.

I wrote some code to test this. It seems to be working. Thanks so much for the introduction. This is very helpful.

I just got through a few weeks migrating our experiment’s sim framework to MT (yes, I let it go for six years…), so this stuff is fresh in my mind. There is good documentation available in the Toolkit Guide and in the CERN TWiki linked from there.