diff --git a/Framework/Algorithms/inc/MantidAlgorithms/SpectrumAlgorithm.h b/Framework/Algorithms/inc/MantidAlgorithms/SpectrumAlgorithm.h index 1827259f0d7232f40c5dab8a01346916fdb7a133..4b8ebfaa3b7c018a25a1d618fc941c1a602884db 100644 --- a/Framework/Algorithms/inc/MantidAlgorithms/SpectrumAlgorithm.h +++ b/Framework/Algorithms/inc/MantidAlgorithms/SpectrumAlgorithm.h @@ -88,7 +88,7 @@ private: auto size = static_cast<int64_t>(indexSet.size()); API::Progress progress(this, 0.0, 1.0, size); - PARALLEL_FOR1((&workspace)) + PARALLEL_FOR_IF(Kernel::threadSafe(workspace)) for (int64_t i = 0; i < size; ++i) { PARALLEL_START_INTERUPT_REGION // Note the small but for now negligible overhead from the IndexSet access diff --git a/Framework/Algorithms/src/AlignDetectors.cpp b/Framework/Algorithms/src/AlignDetectors.cpp index 8d553002cf40c209660d5fae826ec1bad5f1df4b..799abab078eb4b6d4b3a9255ebfbb503059294b3 100644 --- a/Framework/Algorithms/src/AlignDetectors.cpp +++ b/Framework/Algorithms/src/AlignDetectors.cpp @@ -326,7 +326,7 @@ void AlignDetectors::exec() { void AlignDetectors::align(const ConversionFactors &converter, Progress &progress, MatrixWorkspace &outputWS) { - PARALLEL_FOR1((&outputWS)) + PARALLEL_FOR_IF(Kernel::threadSafe(outputWS)) for (int64_t i = 0; i < m_numberOfSpectra; ++i) { PARALLEL_START_INTERUPT_REGION try { diff --git a/Framework/Algorithms/src/BinaryOperation.cpp b/Framework/Algorithms/src/BinaryOperation.cpp index f4d7e154399e4dad36eee09d8a627728440420a6..2408a18fd52dd668977a88e0344204d54f3ead14 100644 --- a/Framework/Algorithms/src/BinaryOperation.cpp +++ b/Framework/Algorithms/src/BinaryOperation.cpp @@ -492,7 +492,7 @@ void BinaryOperation::doSingleValue() { if (m_eout) { // ---- The output is an EventWorkspace ------ - PARALLEL_FOR3(m_lhs, m_rhs, m_out) + PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out)) for (int64_t i = 0; i < numHists; ++i) { PARALLEL_START_INTERUPT_REGION m_out->setX(i, m_lhs->refX(i)); @@ -503,7 +503,7 @@ void BinaryOperation::doSingleValue() { PARALLEL_CHECK_INTERUPT_REGION } else { // ---- Histogram Output ----- - PARALLEL_FOR3(m_lhs, m_rhs, m_out) + PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out)) for (int64_t i = 0; i < numHists; ++i) { PARALLEL_START_INTERUPT_REGION m_out->setX(i, m_lhs->refX(i)); @@ -537,7 +537,7 @@ void BinaryOperation::doSingleColumn() { const int64_t numHists = m_lhs->getNumberHistograms(); if (m_eout) { // ---- The output is an EventWorkspace ------ - PARALLEL_FOR3(m_lhs, m_rhs, m_out) + PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out)) for (int64_t i = 0; i < numHists; ++i) { PARALLEL_START_INTERUPT_REGION const double rhsY = m_rhs->readY(i)[0]; @@ -553,7 +553,7 @@ void BinaryOperation::doSingleColumn() { PARALLEL_CHECK_INTERUPT_REGION } else { // ---- Histogram Output ----- - PARALLEL_FOR3(m_lhs, m_rhs, m_out) + PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out)) for (int64_t i = 0; i < numHists; ++i) { PARALLEL_START_INTERUPT_REGION const double rhsY = m_rhs->readY(i)[0]; @@ -599,7 +599,7 @@ void BinaryOperation::doSingleSpectrum() { // Now loop over the spectra of the left hand side calling the virtual // function const int64_t numHists = m_lhs->getNumberHistograms(); - PARALLEL_FOR3(m_lhs, m_rhs, m_out) + PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out)) for (int64_t i = 0; i < numHists; ++i) { PARALLEL_START_INTERUPT_REGION // m_out->setX(i,m_lhs->refX(i)); //unnecessary - that was copied @@ -622,7 +622,7 @@ void BinaryOperation::doSingleSpectrum() { // function const int64_t numHists = m_lhs->getNumberHistograms(); - PARALLEL_FOR3(m_lhs, m_rhs, m_out) + PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out)) for (int64_t i = 0; i < numHists; ++i) { PARALLEL_START_INTERUPT_REGION // m_out->setX(i,m_lhs->refX(i)); //unnecessary - that was copied @@ -648,7 +648,7 @@ void BinaryOperation::doSingleSpectrum() { // function const int64_t numHists = m_lhs->getNumberHistograms(); - PARALLEL_FOR3(m_lhs, m_rhs, m_out) + PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out)) for (int64_t i = 0; i < numHists; ++i) { PARALLEL_START_INTERUPT_REGION m_out->setX(i, m_lhs->refX(i)); @@ -693,7 +693,7 @@ void BinaryOperation::do2D(bool mismatchedSpectra) { // ------------ The rhs is ALSO an EventWorkspace --------------- // Now loop over the spectra of each one calling the virtual function const int64_t numHists = m_lhs->getNumberHistograms(); - PARALLEL_FOR3(m_lhs, m_rhs, m_out) + PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out)) for (int64_t i = 0; i < numHists; ++i) { PARALLEL_START_INTERUPT_REGION m_progress->report(this->name()); @@ -726,7 +726,7 @@ void BinaryOperation::do2D(bool mismatchedSpectra) { // Now loop over the spectra of each one calling the virtual function const int64_t numHists = m_lhs->getNumberHistograms(); - PARALLEL_FOR3(m_lhs, m_rhs, m_out) + PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out)) for (int64_t i = 0; i < numHists; ++i) { PARALLEL_START_INTERUPT_REGION m_progress->report(this->name()); @@ -763,7 +763,7 @@ void BinaryOperation::do2D(bool mismatchedSpectra) { // Now loop over the spectra of each one calling the virtual function const int64_t numHists = m_lhs->getNumberHistograms(); - PARALLEL_FOR3(m_lhs, m_rhs, m_out) + PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out)) for (int64_t i = 0; i < numHists; ++i) { PARALLEL_START_INTERUPT_REGION m_progress->report(this->name()); diff --git a/Framework/Algorithms/src/DetectorDiagnostic.cpp b/Framework/Algorithms/src/DetectorDiagnostic.cpp index 4c57f308791efde2811296ec9b4cd7d7de9a0791..dbb315c6bfb33b7ac14cbbbe97676f9e383caabb 100644 --- a/Framework/Algorithms/src/DetectorDiagnostic.cpp +++ b/Framework/Algorithms/src/DetectorDiagnostic.cpp @@ -621,7 +621,7 @@ std::vector<double> DetectorDiagnostic::calculateMedian( (instrument->getSample() != nullptr)); } - PARALLEL_FOR1((&input)) + PARALLEL_FOR_IF(Kernel::threadSafe(input)) for (int i = 0; i < nhists; ++i) { // NOLINT PARALLEL_START_INTERUPT_REGION diff --git a/Framework/Algorithms/src/DetectorEfficiencyVariation.cpp b/Framework/Algorithms/src/DetectorEfficiencyVariation.cpp index 101c657e0bb916de257aa2096c09fa5d80ca9192..dc2214496a3546402fbab56d3d8aedfe8500b80b 100644 --- a/Framework/Algorithms/src/DetectorEfficiencyVariation.cpp +++ b/Framework/Algorithms/src/DetectorEfficiencyVariation.cpp @@ -210,7 +210,7 @@ int DetectorEfficiencyVariation::doDetectorTests( const double deadValue(1.0); int numFailed(0); const auto &spectrumInfo = counts1->spectrumInfo(); - PARALLEL_FOR3(counts1, counts2, maskWS) + PARALLEL_FOR_IF(Kernel::threadSafe(*counts1, *counts2, *maskWS)) for (int i = 0; i < numSpec; ++i) { PARALLEL_START_INTERUPT_REGION // move progress bar diff --git a/Framework/Algorithms/src/NormaliseToMonitor.cpp b/Framework/Algorithms/src/NormaliseToMonitor.cpp index 1ab370ee0d21fcd82ab2fd8bb0227da69731f29c..fe915ac54f877febba39c1b36ae91f5d49d241a3 100644 --- a/Framework/Algorithms/src/NormaliseToMonitor.cpp +++ b/Framework/Algorithms/src/NormaliseToMonitor.cpp @@ -574,7 +574,8 @@ void NormaliseToMonitor::normaliseBinByBin( bool hasZeroDivision = false; Progress prog(this, 0.0, 1.0, numHists); // Loop over spectra - PARALLEL_FOR3(inputWorkspace, outputWorkspace, m_monitor) + PARALLEL_FOR_IF( + Kernel::threadSafe(*inputWorkspace, *outputWorkspace, *m_monitor)) for (int64_t i = 0; i < int64_t(numHists); ++i) { PARALLEL_START_INTERUPT_REGION prog.report(); diff --git a/Framework/Algorithms/src/PointByPointVCorrection.cpp b/Framework/Algorithms/src/PointByPointVCorrection.cpp index 2b2de14b3b68b74c5c506643b2a8cae89b21d240..ef1cf8094db94013ae874270bf74e544054b27f8 100644 --- a/Framework/Algorithms/src/PointByPointVCorrection.cpp +++ b/Framework/Algorithms/src/PointByPointVCorrection.cpp @@ -53,7 +53,7 @@ void PointByPointVCorrection::exec() { const int nHist = static_cast<int>(inputWS1->getNumberHistograms()); Progress prog(this, 0.0, 1.0, nHist); - PARALLEL_FOR3(inputWS1, inputWS2, outputWS) + PARALLEL_FOR_IF(Kernel::threadSafe(*inputWS1, *inputWS2, *outputWS)) for (int i = 0; i < nHist; i++) // Looping on all histograms { PARALLEL_START_INTERUPT_REGION diff --git a/Framework/Algorithms/src/Q1D2.cpp b/Framework/Algorithms/src/Q1D2.cpp index 5c97a099e5364eb60e32dcfd19ce684fcb2399b7..fb15129a72c62bad93e6ca50850d61bb13d9261a 100644 --- a/Framework/Algorithms/src/Q1D2.cpp +++ b/Framework/Algorithms/src/Q1D2.cpp @@ -146,7 +146,7 @@ void Q1D2::exec() { Progress progress(this, 0.05, 1.0, numSpec + 1); const auto &spectrumInfo = m_dataWS->spectrumInfo(); - PARALLEL_FOR3(m_dataWS, outputWS, pixelAdj) + PARALLEL_FOR_IF(Kernel::threadSafe(*m_dataWS, *outputWS, pixelAdj.get())) for (int i = 0; i < numSpec; ++i) { PARALLEL_START_INTERUPT_REGION if (!spectrumInfo.hasDetectors(i)) { diff --git a/Framework/Algorithms/src/Rebin.cpp b/Framework/Algorithms/src/Rebin.cpp index 474a29507493d61ff950fcc179a1950fc7b859f6..64388219cf672609ce49b1fa75e91050dc9d1845 100644 --- a/Framework/Algorithms/src/Rebin.cpp +++ b/Framework/Algorithms/src/Rebin.cpp @@ -179,7 +179,7 @@ void Rebin::exec() { Progress prog(this, 0.0, 1.0, histnumber); // Go through all the histograms and set the data - PARALLEL_FOR3(inputWS, eventInputWS, outputWS) + PARALLEL_FOR_IF(Kernel::threadSafe(*inputWS, *outputWS)) for (int i = 0; i < histnumber; ++i) { PARALLEL_START_INTERUPT_REGION diff --git a/Framework/Crystal/src/MaskPeaksWorkspace.cpp b/Framework/Crystal/src/MaskPeaksWorkspace.cpp index 3ba6cbe3eae5e0aec19f4e4bc5a6892989812b80..378be6b7113f2edc1b53afc1d796baaf688afb2d 100644 --- a/Framework/Crystal/src/MaskPeaksWorkspace.cpp +++ b/Framework/Crystal/src/MaskPeaksWorkspace.cpp @@ -89,7 +89,7 @@ void MaskPeaksWorkspace::exec() { // Loop over peaks const std::vector<Peak> &peaks = peaksW->getPeaks(); - PARALLEL_FOR3(m_inputW, peaksW, tablews) + PARALLEL_FOR_IF(Kernel::threadSafe(*m_inputW, *peaksW, *tablews)) for (int i = 0; i < static_cast<int>(peaks.size()); i++) { // NOLINT PARALLEL_START_INTERUPT_REGION const Peak &peak = peaks[i]; diff --git a/Framework/Crystal/src/PeakIntegration.cpp b/Framework/Crystal/src/PeakIntegration.cpp index 218265137bb6def382e09a7249481ec1e2253857..c1524ad285c6567e5695450e65bda98c8f757349 100644 --- a/Framework/Crystal/src/PeakIntegration.cpp +++ b/Framework/Crystal/src/PeakIntegration.cpp @@ -124,7 +124,7 @@ void PeakIntegration::exec() { } Progress prog(this, MinPeaks, 1.0, NumberPeaks); - PARALLEL_FOR3(inputW, peaksW, outputW) + PARALLEL_FOR_IF(Kernel::threadSafe(*inputW, *peaksW, *outputW)) for (int i = MinPeaks; i < NumberPeaks; i++) { PARALLEL_START_INTERUPT_REGION diff --git a/Framework/Crystal/src/SCDCalibratePanels.cpp b/Framework/Crystal/src/SCDCalibratePanels.cpp index dbfefc0d28ed1e3c0865c15330c93d5a699d7841..dd4f20a3a277ee2c58a1d9a2178d013ca2bd80ad 100644 --- a/Framework/Crystal/src/SCDCalibratePanels.cpp +++ b/Framework/Crystal/src/SCDCalibratePanels.cpp @@ -371,7 +371,7 @@ void SCDCalibratePanels::exec() { DblMatrix UB = lattice.getUB(); // sort again since edge peaks can trace to other banks peaksWs->sort(criteria); - PARALLEL_FOR3(ColWksp, RowWksp, TofWksp) + PARALLEL_FOR_IF(Kernel::threadSafe(*ColWksp, *RowWksp, *TofWksp)) for (int i = 0; i < static_cast<int>(MyBankNames.size()); ++i) { PARALLEL_START_INTERUPT_REGION const std::string &bankName = *std::next(MyBankNames.begin(), i); diff --git a/Framework/CurveFitting/src/Algorithms/ConvolveWorkspaces.cpp b/Framework/CurveFitting/src/Algorithms/ConvolveWorkspaces.cpp index bceffdf53e839c586f11f7086b84124da8fc63e7..cdf6947d8a7cdf8e71abb73bfe6b06ee45527ad5 100644 --- a/Framework/CurveFitting/src/Algorithms/ConvolveWorkspaces.cpp +++ b/Framework/CurveFitting/src/Algorithms/ConvolveWorkspaces.cpp @@ -62,7 +62,7 @@ void ConvolveWorkspaces::exec() { prog = new Progress(this, 0.0, 1.0, numHists); // Now convolve the histograms - PARALLEL_FOR3(ws1, ws2, outputWS) + PARALLEL_FOR_IF(Kernel::threadSafe(*ws1, *ws2, *outputWS)) for (int l = 0; l < static_cast<int>(numHists); ++l) { PARALLEL_START_INTERUPT_REGION prog->report(); diff --git a/Framework/CurveFitting/src/Algorithms/VesuvioCalculateGammaBackground.cpp b/Framework/CurveFitting/src/Algorithms/VesuvioCalculateGammaBackground.cpp index cb3db378fc77bdb1c629190cf6c15ff2ffbed78e..dcbb982f1c7c744a57c4afd58e087ef8303ed3b3 100644 --- a/Framework/CurveFitting/src/Algorithms/VesuvioCalculateGammaBackground.cpp +++ b/Framework/CurveFitting/src/Algorithms/VesuvioCalculateGammaBackground.cpp @@ -115,7 +115,8 @@ void VesuvioCalculateGammaBackground::exec() { 10 + nhist * (m_npeaks + 2 * m_foils0.size() * NTHETA * NUP * m_npeaks); m_progress = new Progress(this, 0.0, 1.0, nreports); - PARALLEL_FOR3(m_inputWS, m_correctedWS, m_backgroundWS) + PARALLEL_FOR_IF( + Kernel::threadSafe(*m_inputWS, *m_correctedWS, *m_backgroundWS)) for (int64_t i = 0; i < nhist; ++i) { PARALLEL_START_INTERUPT_REGION const size_t outputIndex = i; diff --git a/Framework/Kernel/inc/MantidKernel/MultiThreaded.h b/Framework/Kernel/inc/MantidKernel/MultiThreaded.h index c7b8bc90f5a152bc4b9fca56b3122e3ae7351f65..07014cbba03a54ca45dede942876485463bc8b8b 100644 --- a/Framework/Kernel/inc/MantidKernel/MultiThreaded.h +++ b/Framework/Kernel/inc/MantidKernel/MultiThreaded.h @@ -1,11 +1,75 @@ #ifndef MANTID_KERNEL_MULTITHREADED_H_ #define MANTID_KERNEL_MULTITHREADED_H_ +#include "MantidKernel/DataItem.h" + #include <mutex> namespace Mantid { -namespace Kernel {} // namespace -} // namespace +namespace Kernel { + +/** Thread-safety check + * Checks the workspace to ensure it is suitable for multithreaded access. + * NULL workspaces are assumed suitable + * @param workspace pointer to workspace to verify. + * @return Whether workspace is threadsafe. + */ +template <typename Arg> +inline typename std::enable_if<std::is_pointer<Arg>::value, bool>::type +threadSafe(Arg workspace) { + static_assert( + std::is_base_of<DataItem, typename std::remove_pointer<Arg>::type>::value, + "Parameter must be derived from Mantid::Kernel::DataItem!"); + return !workspace || workspace->threadSafe(); +} + +/** Thread-safety check + * Checks the workspace to ensure it is suitable for multithreaded access. + * NULL workspaces are assumed suitable + * @param workspace pointer to workspace to verify. + * @param others pointers to all other workspaces which need to be checked. + * @return whether workspace is threadsafe. + */ +template <typename Arg, typename... Args> +inline typename std::enable_if<std::is_pointer<Arg>::value, bool>::type +threadSafe(Arg workspace, Args &&... others) { + static_assert( + std::is_base_of<DataItem, typename std::remove_pointer<Arg>::type>::value, + "Parameter must be derived from Mantid::Kernel::DataItem!"); + return (!workspace || workspace->threadSafe()) && + threadSafe(std::forward<Args>(others)...); +} + +/** Thread-safety check + * Checks the workspace to ensure it is suitable for multithreaded access. + * @param workspace reference to workspace to verify. + * @return Whether workspace is threadsafe. + */ +template <typename Arg> +inline typename std::enable_if<!std::is_pointer<Arg>::value, bool>::type +threadSafe(const Arg &workspace) { + static_assert(std::is_base_of<DataItem, Arg>::value, + "Parameter must be derived from Mantid::Kernel::DataItem!"); + return workspace.threadSafe(); +} + +/** Thread-safety check + * Checks the workspace to ensure it is suitable for multithreaded access. + * @param workspace reference to workspace to verify. + * @param others references or pointers to all other workspaces which need to be + * checked. + * @return whether workspace is threadsafe. + */ +template <typename Arg, typename... Args> +inline typename std::enable_if<!std::is_pointer<Arg>::value, bool>::type +threadSafe(const Arg &workspace, Args &&... others) { + static_assert(std::is_base_of<DataItem, Arg>::value, + "Parameter must be derived from Mantid::Kernel::DataItem!"); + return workspace.threadSafe() && threadSafe(std::forward<Args>(others)...); +} + +} // namespace Kernel +} // namespace Mantid // The syntax used to define a pragma within a macro is different on windows and // GCC @@ -98,16 +162,6 @@ namespace Kernel {} // namespace PRAGMA(omp parallel for if ( ( !workspace1 || workspace1->threadSafe() ) && \ ( !workspace2 || workspace2->threadSafe() ) )) -/** Includes code to add OpenMP commands to run the next for loop in parallel. -* All three workspaces are checked to ensure they are suitable for -*multithreaded access -* but NULL workspaces are assumed to be safe -*/ -#define PARALLEL_FOR3(workspace1, workspace2, workspace3) \ - PRAGMA(omp parallel for if ( (!workspace1 || workspace1->threadSafe()) && \ - ( !workspace2 || workspace2->threadSafe() ) && \ - ( !workspace3 || workspace3->threadSafe() ) )) - /** Ensures that the next execution line or block is only executed if * there are multple threads execting in this region */ @@ -164,7 +218,6 @@ namespace Kernel {} // namespace #define PARALLEL_FOR_NO_WSP_CHECK_FIRSTPRIVATE2(variable1, variable2) #define PARALLEL_FOR1(workspace1) #define PARALLEL_FOR2(workspace1, workspace2) -#define PARALLEL_FOR3(workspace1, workspace2, workspace3) #define IF_PARALLEL if (false) #define IF_NOT_PARALLEL #define PARALLEL_CRITICAL(name)