|
10 | 10 | // or submit itself to any jurisdiction. |
11 | 11 |
|
12 | 12 | #include "Generators/GeneratorHybrid.h" |
| 13 | +#include <chrono> |
13 | 14 | #include <fairlogger/Logger.h> |
14 | 15 | #include <algorithm> |
15 | 16 | #include <tbb/concurrent_queue.h> |
@@ -191,7 +192,24 @@ GeneratorHybrid::GeneratorHybrid(const std::string& inputgens) |
191 | 192 | GeneratorHybrid::~GeneratorHybrid() |
192 | 193 | { |
193 | 194 | LOG(info) << "Destructor of generator hybrid called"; |
| 195 | + // stop() is the designated shutdown hook (called via GeneratorService::stopGenerators), |
| 196 | + // normally running end-of-stream, in which case this is a no-op. It is not reached on every exit path, e.g. when the |
| 197 | + // driver quits before the event budget or the TTL is up, so it is repeated here. |
| 198 | + stop(); |
| 199 | +} |
| 200 | + |
| 201 | +void GeneratorHybrid::stop() |
| 202 | +{ |
194 | 203 | mStopFlag = true; |
| 204 | + // The worker lambdas reference mInputTaskQueue, mResultQueue and mTaskArena, which are |
| 205 | + // destroyed together with this object, so they must not be left running. |
| 206 | + // We wait for the thread to finish, which will happen once the worker lambdas notice mStopFlag. |
| 207 | + if (mTBBTaskPoolRunner.joinable()) { |
| 208 | + auto t0 = std::chrono::steady_clock::now(); |
| 209 | + mTBBTaskPoolRunner.join(); |
| 210 | + auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - t0).count(); |
| 211 | + LOG(info) << "HybridGen: stop() joined TBB task pool after " << ms << " ms"; |
| 212 | + } |
195 | 213 | } |
196 | 214 |
|
197 | 215 | Bool_t GeneratorHybrid::Init() |
@@ -320,7 +338,7 @@ Bool_t GeneratorHybrid::Init() |
320 | 338 | // } |
321 | 339 | } |
322 | 340 | bool isTriggered = false; |
323 | | - while (!isTriggered) { |
| 341 | + while (!isTriggered && !mStopFlag) { |
324 | 342 | generator->clearParticles(); |
325 | 343 | generator->generateEvent(); |
326 | 344 | generator->importParticles(); |
@@ -355,8 +373,8 @@ Bool_t GeneratorHybrid::Init() |
355 | 373 |
|
356 | 374 | // let the TBB task system run in it's own thread |
357 | 375 | mTBBTaskPoolRunner = std::thread([this, worker_function]() { mTaskArena.execute([&]() { tbb::parallel_for(0, mTaskArena.max_concurrency(), [&](int) { worker_function(); }); }); }); |
358 | | - mTBBTaskPoolRunner.detach(); // detaching because we don't like to wait on the thread to finish |
359 | | - // some of the generators might still be generating when we are done |
| 376 | + // NOTE: thread deliberately NOT detached. We never wait for it during the run, but the |
| 377 | + // pool must be joined in the destructor before the queues and the arena are destroyed. |
360 | 378 |
|
361 | 379 | // let's also push initial generation tasks for each event generator |
362 | 380 | for (size_t genindex = 0; genindex < gens.size(); ++genindex) { |
|
0 commit comments