diff --git a/Base/Progress/DelayedProgressCounter.cpp b/Base/Progress/DelayedProgressCounter.cpp index 2e14ef5e5f2c3c171271867d10ab39adc940f483..1a97403d2e25863f3fa6c8cfe2be5c1c4e41bbff 100644 --- a/Base/Progress/DelayedProgressCounter.cpp +++ b/Base/Progress/DelayedProgressCounter.cpp @@ -14,12 +14,15 @@ #include "Base/Progress/DelayedProgressCounter.h" #include "Base/Progress/ProgressHandler.h" +#include <stdexcept> -DelayedProgressCounter::DelayedProgressCounter(ProgressHandler* p_progress, size_t interval) - : m_progress(p_progress) +DelayedProgressCounter::DelayedProgressCounter(ProgressHandler* progress, size_t interval) + : m_progress(progress) , m_interval(interval) , m_count(0) { + if (!progress->alive()) + throw std::runtime_error("dead process (user interrupt?)"); } void DelayedProgressCounter::stepProgress() diff --git a/Base/Progress/DelayedProgressCounter.h b/Base/Progress/DelayedProgressCounter.h index c7f42b9c51105ab24f8fe8857d5bea7573b11cae..ec75ed41ba3be142e05a8e7b23b28240b69ea4d3 100644 --- a/Base/Progress/DelayedProgressCounter.h +++ b/Base/Progress/DelayedProgressCounter.h @@ -26,7 +26,7 @@ class ProgressHandler; class DelayedProgressCounter { public: - DelayedProgressCounter(ProgressHandler* p_progress, size_t interval); + DelayedProgressCounter(ProgressHandler* progress, size_t interval); ~DelayedProgressCounter() = default; //! Increments inner counter; at regular intervals updates progress handler. diff --git a/Sim/Computation/ComputationStatus.h b/Sim/Computation/ComputationStatus.h deleted file mode 100644 index 396e303dc6d7fd28ab95135f994a90c07b623731..0000000000000000000000000000000000000000 --- a/Sim/Computation/ComputationStatus.h +++ /dev/null @@ -1,50 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file Sim/Computation/ComputationStatus.h -//! @brief Defines and implements interface class ComputationStatus. -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2018 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifdef SWIG -#error no need to expose this header to Swig -#endif // SWIG -#ifndef BORNAGAIN_SIM_COMPUTATION_COMPUTATIONSTATUS_H -#define BORNAGAIN_SIM_COMPUTATION_COMPUTATIONSTATUS_H - -#include <string> - -//! Completion status (flag and text) of a numeric computation. - -class ComputationStatus { -public: - ComputationStatus() - : m_status(IDLE) - { - } - - bool isCompleted() const { return m_status == COMPLETED; } - std::string errorMessage() const { return m_error_message; } - - void setRunning() { m_status = RUNNING; } - void setCompleted() { m_status = COMPLETED; } - void setFailed(const std::string& message) - { - m_error_message = message; - m_status = FAILED; - } - -private: - enum ESimulationStatus { IDLE, RUNNING, COMPLETED, FAILED }; - - ESimulationStatus m_status; - std::string m_error_message; -}; - -#endif // BORNAGAIN_SIM_COMPUTATION_COMPUTATIONSTATUS_H diff --git a/Sim/Computation/DWBAComputation.cpp b/Sim/Computation/DWBAComputation.cpp index a04acc56263e507a4a15a1740d7fb8fc7be97c3c..99d7ffe3a962c34e179e3bcdeb66b414ab075a47 100644 --- a/Sim/Computation/DWBAComputation.cpp +++ b/Sim/Computation/DWBAComputation.cpp @@ -43,8 +43,6 @@ DWBAComputation::~DWBAComputation() = default; void DWBAComputation::runProtected() { for (auto it = m_begin_it; it != m_end_it; ++it) { - if (!m_progress->alive()) - break; DiffuseElement& ele = *it; const Fluxes fluxes_in = m_re_sample.fluxesIn(ele.getKi()); diff --git a/Sim/Computation/IComputation.cpp b/Sim/Computation/IComputation.cpp index da177adccf18b8645e6bb98cb92ee958e6d5fa5a..e48a22f9faafd7ff6cd657f5bcdddc5fbc39151c 100644 --- a/Sim/Computation/IComputation.cpp +++ b/Sim/Computation/IComputation.cpp @@ -21,35 +21,13 @@ IComputation::IComputation(const ReSample& re_sample, const SimulationOptions& o ProgressHandler& progress) : m_re_sample(re_sample) , m_options(options) - , m_progress(&progress) + , m_progress_counter(std::make_unique<DelayedProgressCounter>(&progress, 100)) { } IComputation::~IComputation() = default; -void IComputation::setProgressHandler(ProgressHandler* progress) const -{ - m_progress_counter = std::make_unique<DelayedProgressCounter>(progress, 100); -} - void IComputation::stepProgress() const { - if (m_progress_counter) - m_progress_counter->stepProgress(); -} - -void IComputation::compute() -{ - m_status.setRunning(); - try { - if (!m_progress->alive()) - return; - setProgressHandler(m_progress); - - runProtected(); // <--- here the main work is done - - m_status.setCompleted(); - } catch (const std::exception& ex) { - m_status.setFailed(ex.what()); - } + m_progress_counter->stepProgress(); } diff --git a/Sim/Computation/IComputation.h b/Sim/Computation/IComputation.h index 448e9aa714232938f7817fd0a5d567934c5543fe..be27585d4c57bf434611f9a7ca3f8058195019e7 100644 --- a/Sim/Computation/IComputation.h +++ b/Sim/Computation/IComputation.h @@ -18,11 +18,9 @@ #ifndef BORNAGAIN_SIM_COMPUTATION_ICOMPUTATION_H #define BORNAGAIN_SIM_COMPUTATION_ICOMPUTATION_H -#include "Sim/Computation/ComputationStatus.h" #include <memory> class DelayedProgressCounter; -class MultiLayer; class ReSample; class ProgressHandler; class SimulationOptions; @@ -38,26 +36,17 @@ public: ProgressHandler& progress); virtual ~IComputation(); - //! Calls runProtected(), catches exceptions, sets m_status. - void compute(); - - bool isCompleted() const { return m_status.isCompleted(); } - std::string errorMessage() const { return m_status.errorMessage(); } + //! Runs computation. May throw. + virtual void runProtected() = 0; protected: - void setProgressHandler(ProgressHandler* progress) const; void stepProgress() const; const ReSample& m_re_sample; const SimulationOptions& m_options; - ProgressHandler* m_progress; private: - //! Runs computation. May throw. To be called from runProtected(), which catches exceptions. - virtual void runProtected() = 0; - mutable std::unique_ptr<DelayedProgressCounter> m_progress_counter; - ComputationStatus m_status; }; #endif // BORNAGAIN_SIM_COMPUTATION_ICOMPUTATION_H diff --git a/Sim/Simulation/ISimulation.cpp b/Sim/Simulation/ISimulation.cpp index 904f9b287e8941fd4951d41a008dc1c322cc62c1..1809d28eb0fc6ce515899aa051a56f5c4746c3b0 100644 --- a/Sim/Simulation/ISimulation.cpp +++ b/Sim/Simulation/ISimulation.cpp @@ -25,6 +25,7 @@ #include "Sim/Computation/IComputation.h" #include <gsl/gsl_errno.h> #include <iostream> +#include <mutex> #include <thread> namespace { @@ -130,7 +131,7 @@ SimulationResult ISimulation::simulate() prepareSimulation(); - const auto re_sample = ReSample::make(*m_sample, options(), force_polarized()); + const ReSample re_sample = ReSample::make(*m_sample, options(), force_polarized()); const size_t total_size = numberOfElements(); size_t param_combinations = distributionHandler().getTotalNumberOfSamples(); @@ -217,41 +218,44 @@ void ISimulation::runSingleSimulation(const ReSample& re_sample, size_t batch_st if (n_threads == 1) { // Run computation in current thread. - const auto& c = createComputation(re_sample, batch_start, batch_size); - c->compute(); // <---- here most work is done (unthreaded case)! - if (!c->isCompleted()) - throw std::runtime_error("Unexpected error in simulation:\n" + c->errorMessage()); + try { + const auto& c = createComputation(re_sample, batch_start, batch_size); + c->runProtected(); // <---- here most work is done (unthreaded case)! + } catch (const std::exception& ex) { + throw std::runtime_error(std::string("Unexpected error in simulation:\n") + ex.what()); + } } else { - // Create batch computations. - std::vector<std::unique_ptr<IComputation>> computations; + // Launch computation threads. + std::vector<std::unique_ptr<std::thread>> threads; + std::vector<std::string> failure_messages; + std::mutex mutex; for (size_t i_thread = 0; i_thread < n_threads; ++i_thread) { const size_t thread_start = batch_start + startIndex(n_threads, i_thread, batch_size); const size_t thread_size = batchSize(n_threads, i_thread, batch_size); if (thread_size == 0) break; - computations.emplace_back(createComputation(re_sample, thread_start, thread_size)); + threads.emplace_back(new std::thread( + [this, &re_sample, &failure_messages, &mutex, thread_start, thread_size]() { + try { + const auto& c = createComputation(re_sample, thread_start, thread_size); + c->runProtected(); // <---- here most work is done (threaded case)! + } catch (const std::exception& ex) { + mutex.lock(); + failure_messages.push_back(ex.what()); + mutex.unlock(); + } + })); } - // Run computations in several threads. - std::vector<std::unique_ptr<std::thread>> threads; - for (const auto& c : computations) - threads.emplace_back(new std::thread([&c]() { - c->compute(); // <---- here most work is done (threaded case)! - })); - // Wait for threads to complete. for (auto& thread : threads) thread->join(); // Check successful completion. - std::vector<std::string> failure_messages; - for (const auto& c : computations) - if (!c->isCompleted()) - failure_messages.push_back(c->errorMessage()); if (!failure_messages.empty()) - throw std::runtime_error("Unexpected error in simulation thread(s):\n" - + BaseUtils::String::join(failure_messages, " --- ")); + throw std::runtime_error("Unexpected error(s) in simulation thread(s):\n" + + BaseUtils::String::join(failure_messages, "\n")); } normalize(batch_start, batch_size);