CMS 3D CMS Logo

externalGenerator.cc
Go to the documentation of this file.
1 #include "boost/program_options.hpp"
2 
3 #include <atomic>
4 #include <csignal>
5 #include <iostream>
6 #include <string>
7 #include <thread>
8 #include <memory>
9 
11 
19 
21 
28 
29 static char const* const kMemoryNameOpt = "memory-name";
30 static char const* const kMemoryNameCommandOpt = "memory-name,m";
31 static char const* const kUniqueIDOpt = "unique-id";
32 static char const* const kUniqueIDCommandOpt = "unique-id,i";
33 static char const* const kHelpOpt = "help";
34 static char const* const kHelpCommandOpt = "help,h";
35 static char const* const kVerboseOpt = "verbose";
36 static char const* const kVerboseCommandOpt = "verbose,v";
37 
38 //NOTE: Can use TestProcessor as the harness for the worker
39 
40 using namespace edm::shared_memory;
41 class Harness {
42 public:
43  Harness(std::string const& iConfig, edm::ServiceToken iToken)
44  : tester_(edm::test::TestProcessor::Config{iConfig}, iToken) {}
45 
47  auto lumi = tester_.testBeginLuminosityBlock(iLumi);
48  ExternalGeneratorLumiInfo returnValue;
49  returnValue.header_ = *lumi.get<GenLumiInfoHeader>();
50  return returnValue;
51  }
52 
54  ExternalGeneratorEventInfo returnValue;
55  auto event = tester_.test();
56  returnValue.hepmc_ = *event.get<edm::HepMCProduct>("unsmeared");
57  returnValue.eventInfo_ = *event.get<GenEventInfoProduct>();
58  returnValue.keepEvent_ = event.modulePassed();
59  return returnValue;
60  }
61 
63  auto lumi = tester_.testEndLuminosityBlock();
64  return *lumi.get<GenLumiInfoProduct>();
65  }
66 
68  auto run = tester_.testEndRun();
69  return *run.get<GenRunInfoProduct>();
70  }
71 
72 private:
74 };
75 
76 template <typename T>
78 
79 namespace {
80  //needed for atexit handling
81  boost::interprocess::scoped_lock<boost::interprocess::named_mutex>* s_sharedLock = nullptr;
82 
83  void atexit_handler() {
84  if (s_sharedLock) {
85  std::cerr << "early exit called: unlock\n";
86  s_sharedLock->unlock();
87  }
88  }
89 } // namespace
90 
91 int main(int argc, char* argv[]) {
92  std::string descString(argv[0]);
93  descString += " [--";
94  descString += kMemoryNameOpt;
95  descString += "] memory_name";
96  boost::program_options::options_description desc(descString);
97 
98  desc.add_options()(kHelpCommandOpt, "produce help message")(
99  kMemoryNameCommandOpt, boost::program_options::value<std::string>(), "memory name")(
100  kUniqueIDCommandOpt, boost::program_options::value<std::string>(), "unique id")(kVerboseCommandOpt,
101  "verbose output");
102 
103  boost::program_options::positional_options_description p;
104  p.add(kMemoryNameOpt, 1);
105  p.add(kUniqueIDOpt, 2);
106 
107  boost::program_options::options_description all_options("All Options");
108  all_options.add(desc);
109 
110  boost::program_options::variables_map vm;
111  try {
112  store(boost::program_options::command_line_parser(argc, argv).options(all_options).positional(p).run(), vm);
113  notify(vm);
114  } catch (boost::program_options::error const& iException) {
115  std::cout << argv[0] << ": Error while trying to process command line arguments:\n"
116  << iException.what() << "\nFor usage and an options list, please do 'cmsRun --help'.";
117  return 1;
118  }
119 
120  if (vm.count(kHelpOpt)) {
121  std::cout << desc << std::endl;
122  return 0;
123  }
124 
125  bool verbose = false;
126  if (vm.count(kVerboseOpt)) {
127  verbose = true;
128  }
129 
130  if (!vm.count(kMemoryNameOpt)) {
131  std::cout << " no argument given" << std::endl;
132  return 1;
133  }
134 
135  if (!vm.count(kUniqueIDOpt)) {
136  std::cout << " no second argument given" << std::endl;
137  return 1;
138  }
139 
140  WorkerMonitorThread monitorThread;
141 
142  monitorThread.startThread();
143 
144  try {
145  std::string const memoryName(vm[kMemoryNameOpt].as<std::string>());
146  std::string const uniqueID(vm[kUniqueIDOpt].as<std::string>());
147  {
148  //This class is holding the lock
149  WorkerChannel communicationChannel(memoryName, uniqueID);
150 
151  WriteBuffer sm_buffer{memoryName, communicationChannel.fromWorkerBufferInfo()};
152  ReadBuffer sm_readbuffer{std::string("Rand") + memoryName, communicationChannel.toWorkerBufferInfo()};
153  int counter = 0;
154 
155  //The lock must be released if there is a catastrophic signal
156  auto lockPtr = communicationChannel.accessLock();
157 
158  monitorThread.setAction([lockPtr]() {
159  if (lockPtr) {
160  std::cerr << "SIGNAL CAUGHT: unlock\n";
161  lockPtr->unlock();
162  }
163  });
164 
165  //be sure to unset the address of the shared lock before the lock goes away
166  s_sharedLock = lockPtr;
167  auto unsetLockPtr = [](void*) { s_sharedLock = nullptr; };
168  std::unique_ptr<decltype(s_sharedLock), decltype(unsetLockPtr)> sharedLockGuard{&s_sharedLock, unsetLockPtr};
169  std::atexit(atexit_handler);
170  auto releaseLock = []() {
171  if (s_sharedLock) {
172  std::cerr << "terminate called: unlock\n";
173  s_sharedLock->unlock();
174  s_sharedLock = nullptr;
175  //deactivate the abort signal
176 
177  struct sigaction act;
178  act.sa_sigaction = nullptr;
179  act.sa_flags = SA_SIGINFO;
180  sigemptyset(&act.sa_mask);
181  sigaction(SIGABRT, &act, nullptr);
182  std::abort();
183  }
184  };
185  std::set_terminate(releaseLock);
186 
187  Serializer<ExternalGeneratorEventInfo> serializer(sm_buffer);
188  Serializer<ExternalGeneratorLumiInfo> bl_serializer(sm_buffer);
189  Serializer<GenLumiInfoProduct> el_serializer(sm_buffer);
190  Serializer<GenRunInfoProduct> er_serializer(sm_buffer);
191 
192  ROOTDeserializer<edm::RandomNumberGeneratorState, ReadBuffer> random_deserializer(sm_readbuffer);
193 
194  std::cerr << uniqueID << " process: initializing " << std::endl;
195  int nlines;
196  std::cin >> nlines;
197 
199  for (int i = 0; i < nlines; ++i) {
200  std::string c;
201  std::getline(std::cin, c);
202  if (verbose) {
203  std::cerr << c << "\n";
204  }
205  configuration += c + "\n";
206  }
207 
209  auto serviceToken =
210  edm::ServiceRegistry::createContaining(std::unique_ptr<edm::RandomNumberGenerator>(randomService));
211  Harness harness(configuration, serviceToken);
212 
213  //Some generator libraries override the signal handlers
214  monitorThread.setupSignalHandling();
215  std::set_terminate(releaseLock);
216 
217  if (verbose) {
218  std::cerr << uniqueID << " process: done initializing" << std::endl;
219  }
220  communicationChannel.workerSetupDone();
221 
222  if (verbose)
223  std::cerr << uniqueID << " process: waiting " << counter << std::endl;
224  communicationChannel.handleTransitions([&](edm::Transition iTransition, unsigned long long iTransitionID) {
225  ++counter;
226  switch (iTransition) {
228  if (verbose)
229  std::cerr << uniqueID << " process: start beginRun " << std::endl;
230  if (verbose)
231  std::cerr << uniqueID << " process: end beginRun " << std::endl;
232 
233  break;
234  }
236  if (verbose)
237  std::cerr << uniqueID << " process: start beginLumi " << std::endl;
238  auto randState = random_deserializer.deserialize();
239  if (verbose)
240  std::cerr << uniqueID << " random " << randState.state_.size() << " " << randState.seed_ << std::endl;
241  randomService->setState(randState.state_, randState.seed_);
242  auto value = harness.getBeginLumiValue(iTransitionID);
243  value.randomState_.state_ = randomService->getState();
244  value.randomState_.seed_ = randomService->mySeed();
245 
246  bl_serializer.serialize(value);
247  if (verbose)
248  std::cerr << uniqueID << " process: end beginLumi " << std::endl;
249  if (verbose)
250  std::cerr << uniqueID << " rand " << value.randomState_.state_.size() << " " << value.randomState_.seed_
251  << std::endl;
252  break;
253  }
254  case edm::Transition::Event: {
255  if (verbose)
256  std::cerr << uniqueID << " process: event " << counter << std::endl;
257  auto randState = random_deserializer.deserialize();
258  randomService->setState(randState.state_, randState.seed_);
259  auto value = harness.getEventValue();
260  value.randomState_.state_ = randomService->getState();
261  value.randomState_.seed_ = randomService->mySeed();
262 
263  if (verbose)
264  std::cerr << uniqueID << " process: event " << counter << std::endl;
265 
266  serializer.serialize(value);
267  if (verbose)
268  std::cerr << uniqueID << " process: "
269  << " " << counter << std::endl;
270  //usleep(10000000);
271  break;
272  }
274  if (verbose)
275  std::cerr << uniqueID << " process: start endLumi " << std::endl;
276  auto value = harness.getEndLumiValue();
277 
278  el_serializer.serialize(value);
279  if (verbose)
280  std::cerr << uniqueID << " process: end endLumi " << std::endl;
281 
282  break;
283  }
285  if (verbose)
286  std::cerr << uniqueID << " process: start endRun " << std::endl;
287  auto value = harness.getEndRunValue();
288 
289  er_serializer.serialize(value);
290  if (verbose)
291  std::cerr << uniqueID << " process: end endRun " << std::endl;
292 
293  break;
294  }
295  default: {
296  assert(false);
297  }
298  }
299  if (verbose)
300  std::cerr << uniqueID << " process: notifying and waiting " << counter << std::endl;
301  });
302  }
303  } catch (std::exception const& iExcept) {
304  std::cerr << "caught exception \n" << iExcept.what() << "\n";
305  return 1;
306  } catch (...) {
307  std::cerr << "caught unknown exception";
308  return 1;
309  }
310  return 0;
311 }
edmtest::ThingCollection getEndLumiValue()
Definition: interprocess.cc:47
edmtest::ThingCollection getEventValue()
Definition: interprocess.cc:42
boost::interprocess::scoped_lock< boost::interprocess::named_mutex > * accessLock()
the lock is made accessible so that the WorkerMonitorThread can be used to unlock it in the event of ...
Definition: WorkerChannel.h:48
ExternalGeneratorEventInfo getEventValue()
static char const *const kHelpOpt
static char const *const kMemoryNameCommandOpt
edmtest::ThingCollection getBeginLumiValue(unsigned int iLumi)
Definition: interprocess.cc:37
void setState(std::vector< unsigned long > const &, long seed)
GenRunInfoProduct getEndRunValue()
void setupSignalHandling()
Sets the unix signal handler which communicates with the thread.
static char const *const kMemoryNameOpt
void setAction(std::function< void()> iFunc)
static char const *const kHelpCommandOpt
GenLumiInfoProduct getEndLumiValue()
BufferInfo * toWorkerBufferInfo()
This can be used with ReadBuffer to keep Controller and Worker in sync.
Definition: WorkerChannel.h:51
void workerSetupDone()
Matches the ControllerChannel::setupWorker call.
Definition: WorkerChannel.h:56
Transition
Definition: Transition.h:12
static ServiceToken createContaining(std::unique_ptr< T > iService)
create a service token that holds the service defined by iService
static char const *const kVerboseOpt
ExternalGeneratorLumiInfo getBeginLumiValue(unsigned int iLumi)
Definition: value.py:1
static char const *const kVerboseCommandOpt
BufferInfo * fromWorkerBufferInfo()
This can be used with WriteBuffer to keep Controller and Worker in sync.
Definition: WorkerChannel.h:53
static char const *const kUniqueIDCommandOpt
Harness(std::string const &iConfig, edm::ServiceToken iToken)
edmtest::ThingCollection getEndRunValue()
Definition: interprocess.cc:52
static char const *const kUniqueIDOpt
HLT enums.
static std::atomic< unsigned int > counter
Definition: Config.py:1
int main(int argc, char *argv[])