From d965898bc8a20f590f786cb3c7c9ae248059639e Mon Sep 17 00:00:00 2001
From: Nils Krumnack <krumnack@iastate.edu>
Date: Mon, 25 Jan 2021 11:52:13 -0600
Subject: [PATCH] add AnaReentrantAlgorithm

Mostly a copy-and-paste of AnaAlgorithm, with cleanup and updates to
inherit from AthReentrantAlgorithm.
---
 .../AnaAlgorithm/AnaAlgorithmDict.h           |   3 +
 .../AnaAlgorithm/AnaReentrantAlgorithm.h      | 243 ++++++++++++++++++
 .../AnaReentrantAlgorithmConfig.h             |  96 +++++++
 .../AnaReentrantAlgorithmWrapper.h            |  80 ++++++
 .../AnaAlgorithm/AnaAlgorithm/Global.h        |   3 +
 .../AnaAlgorithm/AnaAlgorithm/selection.xml   |   3 +
 .../Root/AnaReentrantAlgorithm.cxx            | 174 +++++++++++++
 .../Root/AnaReentrantAlgorithmConfig.cxx      |  66 +++++
 .../Root/AnaReentrantAlgorithmWrapper.cxx     | 138 ++++++++++
 .../D3PDTools/EventLoop/EventLoop/Job.h       |   1 +
 .../D3PDTools/EventLoop/Root/Job.cxx          |  10 +
 .../EventLoopTest/EventLoopTestDict.h         |   1 +
 .../EventLoopTest/UnitTestAlg6.h              |  49 ++++
 .../EventLoopTest/EventLoopTest/selection.xml |   1 +
 .../EventLoopTest/Root/UnitTestAlg6.cxx       |  69 +++++
 .../test/gt_AnaAlgorithmWrapper.cxx           |  26 --
 .../test/gt_AnaReentrantAlgorithm.cxx         | 143 +++++++++++
 17 files changed, 1080 insertions(+), 26 deletions(-)
 create mode 100644 PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaReentrantAlgorithm.h
 create mode 100644 PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaReentrantAlgorithmConfig.h
 create mode 100644 PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaReentrantAlgorithmWrapper.h
 create mode 100644 PhysicsAnalysis/D3PDTools/AnaAlgorithm/Root/AnaReentrantAlgorithm.cxx
 create mode 100644 PhysicsAnalysis/D3PDTools/AnaAlgorithm/Root/AnaReentrantAlgorithmConfig.cxx
 create mode 100644 PhysicsAnalysis/D3PDTools/AnaAlgorithm/Root/AnaReentrantAlgorithmWrapper.cxx
 create mode 100644 PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/UnitTestAlg6.h
 create mode 100644 PhysicsAnalysis/D3PDTools/EventLoopTest/Root/UnitTestAlg6.cxx
 delete mode 100644 PhysicsAnalysis/D3PDTools/EventLoopTest/test/gt_AnaAlgorithmWrapper.cxx
 create mode 100644 PhysicsAnalysis/D3PDTools/EventLoopTest/test/gt_AnaReentrantAlgorithm.cxx

diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaAlgorithmDict.h b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaAlgorithmDict.h
index dcd2748c0f84..7272b827d21b 100644
--- a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaAlgorithmDict.h
+++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaAlgorithmDict.h
@@ -6,9 +6,12 @@
 #endif
 
 #include "AnaAlgorithm/AnaAlgorithm.h"
+#include "AnaAlgorithm/AnaReentrantAlgorithm.h"
 #ifdef ROOTCORE
 #include "AnaAlgorithm/AnaAlgorithmConfig.h"
 #include "AnaAlgorithm/AnaAlgorithmWrapper.h"
+#include "AnaAlgorithm/AnaReentrantAlgorithmConfig.h"
+#include "AnaAlgorithm/AnaReentrantAlgorithmWrapper.h"
 #endif
 
 #endif
diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaReentrantAlgorithm.h b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaReentrantAlgorithm.h
new file mode 100644
index 000000000000..dac78f83b7bf
--- /dev/null
+++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaReentrantAlgorithm.h
@@ -0,0 +1,243 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+#ifndef ANA_ALGORITHM__ANA_REENTRANT_ALGORITHM_H
+#define ANA_ALGORITHM__ANA_REENTRANT_ALGORITHM_H
+
+#include <AnaAlgorithm/Global.h>
+
+#ifdef XAOD_STANDALONE
+#include <AsgTools/AsgComponent.h>
+#include <AsgTools/SgTEvent.h>
+#include <memory>
+#include <vector>
+#else
+#include <AthenaBaseComps/AthReentrantAlgorithm.h>
+#include <AsgMessaging/MessageCheck.h>
+#endif
+
+class EventContext;
+class ISvcLocator;
+
+namespace EL
+{
+#ifdef XAOD_STANDALONE
+  class IWorker;
+#endif
+
+  /// \brief the base class for EventLoop reentrant algorithms
+  ///
+  /// Technically EventLoop doesn't use that algorithms are reentrant,
+  /// but when writing a dual-use algorithm it is preferable for it to
+  /// be reentrant in Athena, and for that it needs its own interface
+  /// class.
+  ///
+  /// Please note that if you use this algorithm base class you
+  /// absolutely must use data handles for accessing the event store.
+  /// While it makes zero difference in EventLoop, it doesn't work
+  /// otherwise in AthenaMT, which defeats the whole purpose of using
+  /// this class.
+
+  class AnaReentrantAlgorithm
+#ifdef XAOD_STANDALONE
+    : public asg::AsgComponent
+#else
+    : public AthReentrantAlgorithm
+#endif
+  {
+    //
+    // public interface
+    //
+
+    /// \brief constructor with parameters
+    ///
+    /// This matches the Athena algorithm constructor (for dual-use
+    /// purposes).  Within EventLoop the `pSvcLocator` will always be
+    /// `nullptr` (unless we ever have dual-use services).
+    ///
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   out of memory II
+  public:
+    AnaReentrantAlgorithm (const std::string& name,
+                           ISvcLocator* pSvcLocator);
+
+    /// \brief standard (virtual) destructor
+    /// \par Guarantee
+    ///   no-fail
+  public:
+    virtual ~AnaReentrantAlgorithm() noexcept;
+
+
+
+    //
+    // services interface
+    //
+
+#ifdef XAOD_STANDALONE
+    /// \brief the filter worker interface
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   no filter worker configured
+    /// \post result != nullptr
+  public:
+    IFilterWorker *filterWorker () const;
+
+
+    /// \brief whether the current algorithm passed its filter
+    /// criterion for the current event
+    /// \par Guarantee
+    ///   no-fail
+  public:
+    bool filterPassed() const;
+
+    /// \brief set the value of \ref filterPassed
+    /// \par Guarantee
+    ///   no-fail
+  public:
+    void setFilterPassed (bool val_filterPassed);
+
+
+    /// \brief the worker that is controlling us (if working in
+    /// EventLoop)
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   job not running in EventLoop
+    /// \post result != nullptr
+  public:
+    IWorker *wk () const;
+#endif
+
+
+
+    //
+    // virtual interface
+    //
+
+    /// \brief initialize this algorithm
+    ///
+    /// Note that unlike the original EventLoop algorithms, this gets
+    /// called before any events are in memory (or at least it can
+    /// be).  As such you should *not* try to access the current event
+    /// in here.
+  protected:
+    virtual ::StatusCode initialize ();
+
+    /// \brief execute this algorithm
+    ///
+    /// This gets called once on every event and is where the bulk of
+    /// the processing ought to be happening.
+  protected:
+    virtual ::StatusCode execute (const EventContext& ctx);
+
+    /// \brief finalize this algorithm
+    ///
+    /// This gets called after event processing has finished.  The
+    /// last event may no longer be in memory, and the code should not
+    /// try to access it.
+  protected:
+    virtual ::StatusCode finalize ();
+
+
+
+    //
+    // framework interface
+    //
+
+#ifdef XAOD_STANDALONE
+    /// \brief call \ref initialize
+  public:
+    ::StatusCode sysInitialize ();
+
+    /// \brief call \ref execute
+  public:
+    ::StatusCode sysExecute (const EventContext& ctx);
+
+    /// \brief call \ref finalize
+  public:
+    ::StatusCode sysFinalize ();
+
+
+    /// \brief get the (main) event store for this algorithm
+    ///
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   job not configured for xAODs
+    /// \post result != nullptr
+    ///
+    /// \warn The user should *not* be calling this function directly,
+    /// ever, and it may very well disappear at any point without
+    /// notice, and it does *not* exist in Athena which defeats the
+    /// whole purpose of having a reentrant algorithm.
+    ///
+    /// It is essentially here because the stand-alone data handles
+    /// need it.  Basically there are three solutions I considered:
+    /// * make it a public member function for compatibility and warn all
+    ///   users against using it directly
+    /// * make it a private member function, and make all data handles
+    ///   friends, but that would be a lot of friends
+    /// * change the implementation of all data handles to no longer require
+    ///   this function, which would be quite an intrusive change
+  public:
+    asg::SgTEvent *evtStore() const;
+
+    /// \brief set the value of \ref evtStore
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   service already configured
+  public:
+    void setEvtStore (asg::SgTEvent *val_evtStore);
+
+    /// \brief set the value of \ref filterWorker
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   service already configured
+  public:
+    void setFilterWorker (IFilterWorker *val_filterWorker);
+
+    /// \brief set the value of \ref wk
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   service already configured
+  public:
+    void setWk (IWorker *val_wk);
+#endif
+
+
+
+    //
+    // private interface
+    //
+
+#ifdef XAOD_STANDALONE
+    /// \brief the value of \ref evtStore
+  private:
+    asg::SgTEvent *m_evtStore = nullptr;
+#endif
+
+#ifdef XAOD_STANDALONE
+    /// \brief the value of \ref filterWorker
+  private:
+    IFilterWorker *m_filterWorker = nullptr;
+#endif
+
+#ifdef XAOD_STANDALONE
+    /// \brief the value of \ref wk
+  private:
+    IWorker *m_wk = nullptr;
+#endif
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaReentrantAlgorithmConfig.h b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaReentrantAlgorithmConfig.h
new file mode 100644
index 000000000000..aba9597a4224
--- /dev/null
+++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaReentrantAlgorithmConfig.h
@@ -0,0 +1,96 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef ANA_ALGORITHM__ANA_REENTRANT_ALGORITHM_CONFIG_H
+#define ANA_ALGORITHM__ANA_REENTRANT_ALGORITHM_CONFIG_H
+
+#ifndef ROOTCORE
+#error only include this header in AnalysisBase
+#endif
+
+#include <AnaAlgorithm/Global.h>
+
+#include <AsgTools/AsgComponentConfig.h>
+
+namespace EL
+{
+  struct AlgorithmWorkerData;
+
+  /// \brief an object that can create a \ref AnaReentrantAlgorithm
+
+  class AnaReentrantAlgorithmConfig : public asg::AsgComponentConfig
+  {
+    //
+    // public interface
+    //
+
+    /// \brief test the invariant of this object
+    /// \par Guarantee
+    ///   no-fail
+  public:
+    void testInvariant () const;
+
+
+    /// \brief standard constructor
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   out of memory I
+  public:
+    AnaReentrantAlgorithmConfig ();
+
+
+    /// \brief initializing constructor
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   out of memory II
+  public:
+    explicit AnaReentrantAlgorithmConfig (const std::string& val_typeAndName);
+
+
+    /// \brief Virtual destructor, to make PyROOT happy
+    ///
+    /// Without it ROOT 6.22+ does not allow Python classes to inherit from this
+    /// type.
+    ///
+  public:
+    virtual ~AnaReentrantAlgorithmConfig() = default;
+
+
+    /// \brief make an algorithm from this configuration
+    ///
+    /// Note that generally users won't call this function.  The
+    /// typical workflow is to fully configure this object and then
+    /// hand it over to EventLoop which will take care of creating the
+    /// actual algorithm.
+    ///
+    /// An exception may be unit tests.  If this turns out to be too
+    /// cumbersome for unit testing we may need to revisit it at that
+    /// point.  For now it is unclear whether we'll even be able to do
+    /// algorithm unit testing either way.
+    ///
+    /// \par Guarantee
+    ///   strong
+    /// \par Failures
+    ///   configuration errors\n
+    ///   algorithm creation/initialization errors
+  public:
+    ::StatusCode
+    makeAlgorithm (std::unique_ptr<AnaReentrantAlgorithm>& algorithm,
+                   const AlgorithmWorkerData& workerData) const;
+
+
+
+    //
+    // private interface
+    //
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaReentrantAlgorithmWrapper.h b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaReentrantAlgorithmWrapper.h
new file mode 100644
index 000000000000..0a07964a3773
--- /dev/null
+++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/AnaReentrantAlgorithmWrapper.h
@@ -0,0 +1,80 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+#ifndef ANA_ALGORITHM__ANA_REENTRANT_ALGORITHM_WRAPPER_H
+#define ANA_ALGORITHM__ANA_REENTRANT_ALGORITHM_WRAPPER_H
+
+#include <AnaAlgorithm/Global.h>
+
+#include <AnaAlgorithm/AnaReentrantAlgorithm.h>
+#include <AnaAlgorithm/AnaReentrantAlgorithmConfig.h>
+#include <AnaAlgorithm/IAlgorithmWrapper.h>
+
+namespace EL
+{
+  /// \brief an \ref IAlgorithmWrapper for \ref AnaReentrantAlgorithm
+
+  class AnaReentrantAlgorithmWrapper final : public IAlgorithmWrapper
+  {
+  public:
+
+    /// Public Members
+    /// ==============
+
+    /// \brief test the invariant of this object
+    void testInvariant () const;
+
+    /// \brief standard default constructor for serialization
+    AnaReentrantAlgorithmWrapper () {};
+
+    /// \brief standard constructor
+    AnaReentrantAlgorithmWrapper (AnaReentrantAlgorithmConfig val_config);
+
+
+
+    /// Inherited Members
+    /// =================
+
+    virtual std::string_view getName () const override;
+
+    virtual bool hasName (const std::string& name) const override;
+
+    virtual std::unique_ptr<IAlgorithmWrapper> makeClone() const override;
+
+    virtual StatusCode initialize (const AlgorithmWorkerData& workerData) override;
+
+    virtual StatusCode execute () override;
+
+    virtual StatusCode finalize () override;
+
+    virtual ::StatusCode fileExecute () override;
+
+    virtual ::StatusCode beginInputFile () override;
+
+    virtual ::StatusCode endInputFile () override;
+
+
+
+    /// Private Members
+    /// ===============
+
+  private:
+
+    /// \brief the algorithm configuration
+    AnaReentrantAlgorithmConfig m_config;
+
+    /// \brief the actual algorithm (once instantiated)
+    ///
+    /// Please note (and do not remove) the comment at the end of the
+    /// line.  This specifically excludes this member from
+    /// serialization.
+    std::unique_ptr<AnaReentrantAlgorithm> m_algorithm; //!
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/Global.h b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/Global.h
index f13ec53864e4..ae509877dda9 100644
--- a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/Global.h
+++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/Global.h
@@ -12,11 +12,14 @@
 namespace EL
 {
   class AnaAlgorithm;
+  class AnaReentrantAlgorithm;
 
 #ifdef ROOTCORE
   struct AlgorithmWorkerData;
   class AnaAlgorithmConfig;
   class AnaAlgorithmWrapper;
+  class AnaReentrantAlgorithmConfig;
+  class AnaReentrantAlgorithmWrapper;
   class IAlgorithmWrapper;
   class IFilterWorker;
   class IHistogramWorker;
diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/selection.xml b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/selection.xml
index a5a5cda36bfa..0784b579a72f 100644
--- a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/selection.xml
+++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/AnaAlgorithm/selection.xml
@@ -3,6 +3,9 @@
    <class name="EL::AnaAlgorithm" />
    <class name="EL::AnaAlgorithmConfig" />
    <class name="EL::AnaAlgorithmWrapper" />
+   <class name="EL::AnaReentrantAlgorithm" />
+   <class name="EL::AnaReentrantAlgorithmConfig" />
+   <class name="EL::AnaReentrantAlgorithmWrapper" />
    <class name="EL::IAlgorithmWrapper" />
    
 </lcgdict>
diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/Root/AnaReentrantAlgorithm.cxx b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/Root/AnaReentrantAlgorithm.cxx
new file mode 100644
index 000000000000..a47218a346a2
--- /dev/null
+++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/Root/AnaReentrantAlgorithm.cxx
@@ -0,0 +1,174 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <AnaAlgorithm/AnaReentrantAlgorithm.h>
+
+#include <AsgMessaging/MessageCheck.h>
+#include <RootCoreUtils/Assert.h>
+#include <stdexcept>
+
+#ifdef XAOD_STANDALONE
+#include <AnaAlgorithm/IFilterWorker.h>
+#endif
+
+//
+// method implementations
+//
+
+namespace EL
+{
+  AnaReentrantAlgorithm ::
+  AnaReentrantAlgorithm (const std::string& name,
+                         [[maybe_unused]] ISvcLocator *pSvcLocator)
+#ifdef XAOD_STANDALONE
+    : AsgComponent (name)
+#else
+    : AthReentrantAlgorithm (name, pSvcLocator)
+#endif
+  {
+    ANA_MSG_DEBUG ("AnaReentrantAlgorithm: " << name);
+  }
+
+
+
+  AnaReentrantAlgorithm ::
+  ~AnaReentrantAlgorithm () noexcept
+  {}
+
+
+
+#ifdef XAOD_STANDALONE
+  bool AnaReentrantAlgorithm ::
+  filterPassed() const
+  {
+    return filterWorker()->filterPassed();
+  }
+
+
+
+  void AnaReentrantAlgorithm ::
+  setFilterPassed (bool val_filterPassed)
+  {
+    filterWorker()->setFilterPassed (val_filterPassed);
+  }
+
+
+
+  IFilterWorker *AnaReentrantAlgorithm ::
+  filterWorker () const
+  {
+    if (!m_filterWorker)
+      throw std::logic_error ("no filter worker set on algorithm " + name());
+    return m_filterWorker;
+  }
+
+
+
+  IWorker *AnaReentrantAlgorithm ::
+  wk () const
+  {
+    if (!m_wk)
+      throw std::logic_error ("no worker set on algorithm " + name());
+    return m_wk;
+  }
+#endif
+
+
+
+  ::StatusCode AnaReentrantAlgorithm ::
+  initialize ()
+  {
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  ::StatusCode AnaReentrantAlgorithm ::
+  execute (const EventContext& /*ctx*/)
+  {
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  ::StatusCode AnaReentrantAlgorithm ::
+  finalize ()
+  {
+    return StatusCode::SUCCESS;
+  }
+
+
+
+#ifdef XAOD_STANDALONE
+  ::StatusCode AnaReentrantAlgorithm ::
+  sysInitialize ()
+  {
+    return initialize ();
+  }
+
+
+
+  ::StatusCode AnaReentrantAlgorithm ::
+  sysExecute (const EventContext& ctx)
+  {
+    return execute (ctx);
+  }
+
+
+
+  ::StatusCode AnaReentrantAlgorithm ::
+  sysFinalize ()
+  {
+    return finalize ();
+  }
+
+
+
+  asg::SgTEvent *AnaReentrantAlgorithm ::
+  evtStore () const
+  {
+    if (!m_evtStore)
+      throw std::logic_error ("no evtStore set on algorithm " + name());
+    return m_evtStore;
+  }
+
+
+
+  void AnaReentrantAlgorithm ::
+  setEvtStore (asg::SgTEvent *val_evtStore)
+  {
+    if (m_evtStore)
+      throw std::logic_error ("set evtStore twice on algorithm " + name());
+    m_evtStore = val_evtStore;
+  }
+
+
+
+  void AnaReentrantAlgorithm ::
+  setFilterWorker (IFilterWorker *val_filterWorker)
+  {
+    if (m_filterWorker)
+      throw std::logic_error ("set filter worker twice on algorithm " + name());
+    m_filterWorker = val_filterWorker;
+  }
+
+
+
+  void AnaReentrantAlgorithm ::
+  setWk (IWorker *val_wk)
+  {
+    if (m_wk)
+      throw std::logic_error ("set wk twice on algorithm " + name());
+    m_wk = val_wk;
+  }
+#endif
+}
diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/Root/AnaReentrantAlgorithmConfig.cxx b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/Root/AnaReentrantAlgorithmConfig.cxx
new file mode 100644
index 000000000000..6cd45ac8d93b
--- /dev/null
+++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/Root/AnaReentrantAlgorithmConfig.cxx
@@ -0,0 +1,66 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <AnaAlgorithm/AnaReentrantAlgorithmConfig.h>
+
+#include <AnaAlgorithm/AnaReentrantAlgorithm.h>
+#include <AnaAlgorithm/AlgorithmWorkerData.h>
+#include <AnaAlgorithm/MessageCheck.h>
+#include <AsgTools/AsgTool.h>
+#include <RootCoreUtils/Assert.h>
+
+//
+// method implementations
+//
+
+namespace EL
+{
+  void AnaReentrantAlgorithmConfig ::
+  testInvariant () const
+  {
+  }
+
+
+
+  AnaReentrantAlgorithmConfig ::
+  AnaReentrantAlgorithmConfig ()
+  {
+    RCU_NEW_INVARIANT (this);
+  }
+
+
+
+  AnaReentrantAlgorithmConfig ::
+  AnaReentrantAlgorithmConfig (const std::string& val_typeAndName)
+    : AsgComponentConfig (val_typeAndName)
+  {
+    RCU_NEW_INVARIANT (this);
+  }
+
+
+
+  ::StatusCode AnaReentrantAlgorithmConfig ::
+  makeAlgorithm (std::unique_ptr<AnaReentrantAlgorithm>& algorithm,
+                 const AlgorithmWorkerData& workerData) const
+  {
+    RCU_READ_INVARIANT (this);
+    using namespace msgAlgorithmConfig;
+
+    ANA_CHECK (makeComponentExpert (algorithm, "new %1% (\"%2%\", nullptr)", true, ""));
+    algorithm->setFilterWorker (workerData.m_filterWorker);
+    algorithm->setWk (workerData.m_wk);
+    if (workerData.m_evtStore)
+      algorithm->setEvtStore (workerData.m_evtStore);
+    ANA_CHECK (algorithm->sysInitialize());
+    return StatusCode::SUCCESS;
+  }
+}
diff --git a/PhysicsAnalysis/D3PDTools/AnaAlgorithm/Root/AnaReentrantAlgorithmWrapper.cxx b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/Root/AnaReentrantAlgorithmWrapper.cxx
new file mode 100644
index 000000000000..d1bd7f8d878a
--- /dev/null
+++ b/PhysicsAnalysis/D3PDTools/AnaAlgorithm/Root/AnaReentrantAlgorithmWrapper.cxx
@@ -0,0 +1,138 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <AnaAlgorithm/AnaReentrantAlgorithmWrapper.h>
+
+#include <AnaAlgorithm/MessageCheck.h>
+#include <AsgTools/CurrentContext.h>
+#include <RootCoreUtils/Assert.h>
+
+//
+// method implementations
+//
+
+namespace EL
+{
+  void AnaReentrantAlgorithmWrapper ::
+  testInvariant () const
+  {
+    RCU_INVARIANT (!m_config.name().empty());
+  }
+
+
+
+  AnaReentrantAlgorithmWrapper ::
+  AnaReentrantAlgorithmWrapper (AnaReentrantAlgorithmConfig val_config)
+    : m_config (std::move (val_config))
+  {
+    RCU_NEW_INVARIANT (this);
+  }
+
+
+
+  std::string_view AnaReentrantAlgorithmWrapper ::
+  getName () const
+  {
+    RCU_READ_INVARIANT (this);
+    return m_config.name();
+  }
+
+
+
+  bool AnaReentrantAlgorithmWrapper ::
+  hasName (const std::string& name) const
+  {
+    RCU_READ_INVARIANT (this);
+    return m_config.name() == name;
+  }
+
+
+
+  std::unique_ptr<IAlgorithmWrapper> AnaReentrantAlgorithmWrapper ::
+  makeClone() const
+  {
+    RCU_READ_INVARIANT (this);
+    return std::make_unique<AnaReentrantAlgorithmWrapper> (m_config);
+  }
+
+
+
+  StatusCode AnaReentrantAlgorithmWrapper ::
+  initialize (const AlgorithmWorkerData& workerData)
+  {
+    using namespace msgAlgorithmConfig;
+    RCU_CHANGE_INVARIANT (this);
+    if (m_config.makeAlgorithm (m_algorithm, workerData).isFailure())
+    {
+      ANA_MSG_ERROR ("failed to create AnaReentrantAlgorithm: " << m_config.name());
+      return StatusCode::FAILURE;
+    }
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode AnaReentrantAlgorithmWrapper ::
+  execute ()
+  {
+    using namespace msgAlgorithmConfig;
+    RCU_READ_INVARIANT (this);
+    if (m_algorithm->sysExecute(Gaudi::Hive::currentContext()).isFailure())
+    {
+      ANA_MSG_ERROR ("failed to call execute() on algorithm: " << m_config.name());
+      return StatusCode::FAILURE;
+    }
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode AnaReentrantAlgorithmWrapper ::
+  finalize ()
+  {
+    using namespace msgAlgorithmConfig;
+    RCU_READ_INVARIANT (this);
+    if (m_algorithm->sysFinalize().isFailure())
+    {
+      ANA_MSG_ERROR ("failed to call finalize() on algorithm: " << m_config.name());
+      return StatusCode::FAILURE;
+    }
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode AnaReentrantAlgorithmWrapper ::
+  fileExecute ()
+  {
+    // no-op
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  StatusCode AnaReentrantAlgorithmWrapper ::
+  beginInputFile ()
+  {
+    // no-op
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  ::StatusCode AnaReentrantAlgorithmWrapper ::
+  endInputFile ()
+  {
+    // no-op
+    return StatusCode::SUCCESS;
+  }
+}
diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/Job.h b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/Job.h
index c46cf2c5626d..50fe426a4765 100644
--- a/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/Job.h
+++ b/PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/Job.h
@@ -93,6 +93,7 @@ namespace EL
     void algsAdd (std::unique_ptr<Algorithm> val_algorithm);
     void algsAdd (Algorithm *alg_swallow);
     void algsAdd (const AnaAlgorithmConfig& config);
+    void algsAdd (const AnaReentrantAlgorithmConfig& config);
 
 
     /// \brief add a clone of the given algorithm
diff --git a/PhysicsAnalysis/D3PDTools/EventLoop/Root/Job.cxx b/PhysicsAnalysis/D3PDTools/EventLoop/Root/Job.cxx
index aae81d06efe2..e44cf89fde3a 100644
--- a/PhysicsAnalysis/D3PDTools/EventLoop/Root/Job.cxx
+++ b/PhysicsAnalysis/D3PDTools/EventLoop/Root/Job.cxx
@@ -11,6 +11,7 @@
 
 #include <memory>
 #include <AnaAlgorithm/AnaAlgorithmWrapper.h>
+#include <AnaAlgorithm/AnaReentrantAlgorithmWrapper.h>
 #include <EventLoop/MessageCheck.h>
 #include <EventLoop/Algorithm.h>
 #include <EventLoop/AlgorithmWrapper.h>
@@ -264,6 +265,15 @@ namespace EL
 
 
 
+  void Job ::
+  algsAdd (const AnaReentrantAlgorithmConfig& config)
+  {
+    // no invariant used
+    algsAdd (std::make_unique<AnaReentrantAlgorithmWrapper> (config));
+  }
+
+
+
   void Job ::
   algsAddClone (const Algorithm& alg)
   {
diff --git a/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/EventLoopTestDict.h b/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/EventLoopTestDict.h
index 8dfe14a56758..9472bf63c650 100644
--- a/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/EventLoopTestDict.h
+++ b/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/EventLoopTestDict.h
@@ -6,5 +6,6 @@
 #endif
 
 #include "EventLoopTest/UnitTestAlg5.h"
+#include "EventLoopTest/UnitTestAlg6.h"
 
 #endif
diff --git a/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/UnitTestAlg6.h b/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/UnitTestAlg6.h
new file mode 100644
index 000000000000..3ad66ad01b01
--- /dev/null
+++ b/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/UnitTestAlg6.h
@@ -0,0 +1,49 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef EVENT_LOOP_UNIT_TEST_ALG6_H
+#define EVENT_LOOP_UNIT_TEST_ALG6_H
+
+#include <EventLoopTest/Global.h>
+
+#include <AnaAlgorithm/AnaReentrantAlgorithm.h>
+#include <EventLoopTest/IUnitTestTool.h>
+#include <AsgTools/ToolHandle.h>
+
+namespace EL
+{
+  class UnitTestAlg6 final : public AnaReentrantAlgorithm
+  {
+  public:
+    UnitTestAlg6 (const std::string& name, ISvcLocator* pSvcLocator);
+
+  public:
+    virtual ::StatusCode initialize () override;
+
+  public:
+    virtual ::StatusCode execute (const EventContext& ctx) override;
+
+  public:
+    virtual ::StatusCode finalize () override;
+
+
+    /// \brief the float property our owner sets
+  public:
+    float m_property = 0;
+
+    /// \brief the string property our owner sets
+  public:
+    std::string m_string_property;
+
+    /// \brief the tool handle property our owner sets
+  public:
+    ToolHandle<IUnitTestTool> m_toolHandle {"", this};
+
+    /// \brief whether initialize has been called
+  public:
+    bool m_wasInitialized = false;
+  };
+}
+
+#endif
diff --git a/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/selection.xml b/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/selection.xml
index 209766bd7235..8cd781929c2c 100644
--- a/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/selection.xml
+++ b/PhysicsAnalysis/D3PDTools/EventLoopTest/EventLoopTest/selection.xml
@@ -2,5 +2,6 @@
 
    <!-- Unit test types: -->
    <class name="EL::UnitTestAlg5" />
+   <class name="EL::UnitTestAlg6" />
 
 </lcgdict>
diff --git a/PhysicsAnalysis/D3PDTools/EventLoopTest/Root/UnitTestAlg6.cxx b/PhysicsAnalysis/D3PDTools/EventLoopTest/Root/UnitTestAlg6.cxx
new file mode 100644
index 000000000000..ee9fb8cd47f7
--- /dev/null
+++ b/PhysicsAnalysis/D3PDTools/EventLoopTest/Root/UnitTestAlg6.cxx
@@ -0,0 +1,69 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+//
+// includes
+//
+
+#include <EventLoopTest/UnitTestAlg6.h>
+
+//
+// method implementations
+//
+
+namespace EL
+{
+  UnitTestAlg6 ::
+  UnitTestAlg6 (const std::string& name, ISvcLocator* pSvcLocator)
+    : AnaReentrantAlgorithm (name, pSvcLocator)
+  {
+    declareProperty ("property", m_property, "test property");
+    declareProperty ("string_property", m_string_property, "test string property");
+    declareProperty ("toolHandle", m_toolHandle, "ToolHandle property");
+  }
+
+
+
+  ::StatusCode UnitTestAlg6 ::
+  initialize ()
+  {
+    if (m_wasInitialized)
+    {
+      ANA_MSG_FATAL ("initialized twice");
+      return StatusCode::FAILURE;
+    }
+
+    if (!m_toolHandle.empty())
+      ANA_CHECK (m_toolHandle.retrieve());
+
+    m_wasInitialized = true;
+    return ::StatusCode::SUCCESS;
+  }
+
+
+
+  ::StatusCode UnitTestAlg6 ::
+  execute (const EventContext& /*ctx*/)
+  {
+    if (!m_wasInitialized)
+    {
+      ANA_MSG_FATAL ("never initialized");
+      return StatusCode::FAILURE;
+    }
+    return ::StatusCode::SUCCESS;
+  }
+
+
+
+  ::StatusCode UnitTestAlg6 ::
+  finalize ()
+  {
+    if (!m_wasInitialized)
+    {
+      ANA_MSG_FATAL ("never initialized");
+      return StatusCode::FAILURE;
+    }
+    return ::StatusCode::SUCCESS;
+  }
+}
diff --git a/PhysicsAnalysis/D3PDTools/EventLoopTest/test/gt_AnaAlgorithmWrapper.cxx b/PhysicsAnalysis/D3PDTools/EventLoopTest/test/gt_AnaAlgorithmWrapper.cxx
deleted file mode 100644
index 9462707d9cba..000000000000
--- a/PhysicsAnalysis/D3PDTools/EventLoopTest/test/gt_AnaAlgorithmWrapper.cxx
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
-*/
-
-/// @author Nils Krumnack
-
-
-
-//
-// includes
-//
-
-#include <EventLoop/Global.h>
-
-#include <RootCoreUtils/Assert.h>
-
-using namespace EL;
-
-//
-// unit test
-//
-
-int main ()
-{
-  return 0;
-}
diff --git a/PhysicsAnalysis/D3PDTools/EventLoopTest/test/gt_AnaReentrantAlgorithm.cxx b/PhysicsAnalysis/D3PDTools/EventLoopTest/test/gt_AnaReentrantAlgorithm.cxx
new file mode 100644
index 000000000000..3a0684628c8f
--- /dev/null
+++ b/PhysicsAnalysis/D3PDTools/EventLoopTest/test/gt_AnaReentrantAlgorithm.cxx
@@ -0,0 +1,143 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+/// @author Nils Krumnack
+
+
+
+//
+// includes
+//
+
+#include <AnaAlgorithm/AnaReentrantAlgorithm.h>
+#include <AnaAlgorithm/AnaReentrantAlgorithmConfig.h>
+
+#include <AnaAlgorithm/AlgorithmWorkerData.h>
+#include <EventLoopTest/UnitTestAlg6.h>
+#include <AsgTools/ToolHandle.h>
+#include <AsgMessaging/MessageCheck.h>
+#include <AsgTesting/UnitTest.h>
+#include <cmath>
+#include <gtest/gtest.h>
+
+#ifdef ROOTCORE
+#include <xAODRootAccess/Init.h>
+#endif
+
+#include <RootCoreUtils/Assert.h>
+
+using namespace EL;
+
+//
+// unit test
+//
+
+TEST (AnaReentrantAlgorithmTest, create_basic)
+{
+  AlgorithmWorkerData workerData;
+  AnaReentrantAlgorithmConfig config;
+  config.setName ("name");
+  config.setType ("EL::AnaReentrantAlgorithm");
+  std::unique_ptr<AnaReentrantAlgorithm> alg;
+  ASSERT_SUCCESS (config.makeAlgorithm (alg, workerData));
+  ASSERT_NE (nullptr, alg.get());
+  ASSERT_EQ ("name", alg->name());
+}
+
+TEST (AnaReentrantAlgorithmTest, newAlg)
+{
+  std::unique_ptr<UnitTestAlg6> alg (new UnitTestAlg6 ("name", nullptr));
+}
+
+TEST (AnaReentrantAlgorithmTest, create)
+{
+  AlgorithmWorkerData workerData;
+  AnaReentrantAlgorithmConfig config;
+  config.setName ("name");
+  config.setType ("EL::UnitTestAlg6");
+  std::unique_ptr<AnaReentrantAlgorithm> alg;
+  ASSERT_SUCCESS (config.makeAlgorithm (alg, workerData));
+  ASSERT_NE (nullptr, alg.get());
+  ASSERT_EQ ("name", alg->name());
+}
+
+TEST (AnaReentrantAlgorithmTest, setProperty_string)
+{
+  AlgorithmWorkerData workerData;
+  AnaReentrantAlgorithmConfig config;
+  config.setName ("name");
+  config.setType ("EL::UnitTestAlg6");
+  ASSERT_SUCCESS (config.setProperty ("string_property", "42"));
+  std::unique_ptr<AnaReentrantAlgorithm> alg;
+  ASSERT_SUCCESS (config.makeAlgorithm (alg, workerData));
+  UnitTestAlg6 *myalg = dynamic_cast<UnitTestAlg6*>(alg.get());
+  ASSERT_NE (nullptr, myalg);
+  ASSERT_EQ ("42", myalg->m_string_property);
+}
+
+TEST (AnaReentrantAlgorithmTest, setProperty)
+{
+  AlgorithmWorkerData workerData;
+  AnaReentrantAlgorithmConfig config;
+  config.setName ("name");
+  config.setType ("EL::UnitTestAlg6");
+  ASSERT_SUCCESS (config.setProperty ("property", 42));
+  std::unique_ptr<AnaReentrantAlgorithm> alg;
+  ASSERT_SUCCESS (config.makeAlgorithm (alg, workerData));
+  UnitTestAlg6 *myalg = dynamic_cast<UnitTestAlg6*>(alg.get());
+  ASSERT_NE (nullptr, myalg);
+  ASSERT_EQ (42, myalg->m_property);
+}
+
+TEST (AnaReentrantAlgorithmTest, setOutputLevel)
+{
+  AlgorithmWorkerData workerData;
+  AnaReentrantAlgorithmConfig config;
+  config.setName ("name");
+  config.setType ("EL::UnitTestAlg6");
+  ASSERT_SUCCESS (config.setProperty ("OutputLevel", MSG::Level::VERBOSE));
+  std::unique_ptr<AnaReentrantAlgorithm> alg;
+  ASSERT_SUCCESS (config.makeAlgorithm (alg, workerData));
+  UnitTestAlg6 *myalg = dynamic_cast<UnitTestAlg6*>(alg.get());
+  ASSERT_NE (nullptr, myalg);
+  ASSERT_EQ (MSG::Level::VERBOSE, static_cast<int>(myalg->msg().level()));
+}
+
+TEST (AnaReentrantAlgorithmTest, setSubTool)
+{
+  AlgorithmWorkerData workerData;
+  AnaReentrantAlgorithmConfig config;
+  config.setName ("name");
+  config.setType ("EL::UnitTestAlg6");
+  ASSERT_SUCCESS (config.createPrivateTool ("toolHandle", "EL::UnitTestTool"));
+  ASSERT_SUCCESS (config.setProperty ("toolHandle.propertyInt", 17));
+  std::unique_ptr<AnaReentrantAlgorithm> alg;
+  ASSERT_SUCCESS (config.makeAlgorithm (alg, workerData));
+  UnitTestAlg6 *myalg = dynamic_cast<UnitTestAlg6*>(alg.get());
+  ASSERT_NE (nullptr, myalg);
+  ASSERT_NE (nullptr, &*myalg->m_toolHandle);
+  ASSERT_EQ (17, myalg->m_toolHandle->getPropertyInt());
+  ASSERT_EQ (nullptr, myalg->m_toolHandle->getSubtool());
+}
+
+TEST (AnaReentrantAlgorithmTest, setSubSubTool)
+{
+  AlgorithmWorkerData workerData;
+  AnaReentrantAlgorithmConfig config;
+  config.setName ("name");
+  config.setType ("EL::UnitTestAlg6");
+  ASSERT_SUCCESS (config.createPrivateTool ("toolHandle", "EL::UnitTestTool"));
+  ASSERT_SUCCESS (config.createPrivateTool ("toolHandle.subtool", "EL::UnitTestTool"));
+  ASSERT_SUCCESS (config.setProperty ("toolHandle.subtool.propertyInt", 17));
+  std::unique_ptr<AnaReentrantAlgorithm> alg;
+  ASSERT_SUCCESS (config.makeAlgorithm (alg, workerData));
+  UnitTestAlg6 *myalg = dynamic_cast<UnitTestAlg6*>(alg.get());
+  ASSERT_NE (nullptr, myalg);
+  ASSERT_NE (nullptr, &*myalg->m_toolHandle);
+  ASSERT_EQ (0, myalg->m_toolHandle->getPropertyInt());
+  ASSERT_NE (nullptr, myalg->m_toolHandle->getSubtool());
+  ASSERT_EQ (17, myalg->m_toolHandle->getSubtool()->getPropertyInt());
+}
+
+ATLAS_GOOGLE_TEST_MAIN
-- 
GitLab