From 7d9b176480bbbbc3baac8b6fdfab62794791b649 Mon Sep 17 00:00:00 2001
From: Sebastien Ponce <sebastien.ponce@cern.ch>
Date: Mon, 17 Jun 2024 16:13:14 +0200
Subject: [PATCH] Introduced ICallBackHolder and CallBackHolder wrapper

This allows to register callbacks are each State transition of a component.
It can be an alternative to overwriting initialize/start/stop/finalize methods in particular.
---
 GaudiKernel/include/Gaudi/FSMCallbackHolder.h | 69 +++++++++++++++++++
 .../Gaudi/Interfaces/IFSMCallbackHolder.h     | 30 ++++++++
 GaudiTestSuite/CMakeLists.txt                 |  1 +
 GaudiTestSuite/options/FSMCallbackTest.py     | 15 ++++
 .../src/FSMCallback/FSMCallbackTestAlgo.cpp   | 34 +++++++++
 .../qmtest/gauditestsuite.qms/callback.qmt    | 16 +++++
 .../tests/qmtest/refs/FSMCallbackTest.ref     | 26 +++++++
 7 files changed, 191 insertions(+)
 create mode 100644 GaudiKernel/include/Gaudi/FSMCallbackHolder.h
 create mode 100644 GaudiKernel/include/Gaudi/Interfaces/IFSMCallbackHolder.h
 create mode 100644 GaudiTestSuite/options/FSMCallbackTest.py
 create mode 100644 GaudiTestSuite/src/FSMCallback/FSMCallbackTestAlgo.cpp
 create mode 100644 GaudiTestSuite/tests/qmtest/gauditestsuite.qms/callback.qmt
 create mode 100644 GaudiTestSuite/tests/qmtest/refs/FSMCallbackTest.ref

diff --git a/GaudiKernel/include/Gaudi/FSMCallbackHolder.h b/GaudiKernel/include/Gaudi/FSMCallbackHolder.h
new file mode 100644
index 0000000000..e7ada4ff60
--- /dev/null
+++ b/GaudiKernel/include/Gaudi/FSMCallbackHolder.h
@@ -0,0 +1,69 @@
+/***********************************************************************************\
+* (c) Copyright 1998-2024 CERN for the benefit of the LHCb and ATLAS collaborations *
+*                                                                                   *
+* This software is distributed under the terms of the Apache version 2 licence,     *
+* copied verbatim in the file "LICENSE".                                            *
+*                                                                                   *
+* In applying this licence, CERN does not waive the privileges and immunities       *
+* granted to it by virtue of its status as an Intergovernmental Organization        *
+* or submit itself to any jurisdiction.                                             *
+\***********************************************************************************/
+#pragma once
+
+#include <Gaudi/Interfaces/IFSMCallbackHolder.h>
+#include <GaudiKernel/StateMachine.h>
+#include <GaudiKernel/extends.h>
+
+#include <map>
+#include <vector>
+
+namespace Gaudi {
+
+  /** Helper class to implement the IFSMCallbackHolder interface.
+   *
+   *  FSMCallbackHolder is used by components base classes (Algorithm, Tool,
+   *  etc.) to provide a default implementation the IFSMCallbackHolder interface.
+   *
+   *  When needing to implement the IFSMCallbackHolder interface in a class, it is
+   *  enough to wrap the base of the class with FSMCallbackHolder, as in
+   *
+   *  \code{.cpp}
+   *  class MyClass : public FSMCallbackHolder<BaseClass> {
+   *    // ...
+   *  };
+   *  \endcode
+   */
+  template <class BASE>
+  class FSMCallbackHolder : public extends<BASE, IFSMCallbackHolder> {
+
+  public:
+    using Parent = extends<BASE, IFSMCallbackHolder>;
+    using extends<BASE, IFSMCallbackHolder>::extends;
+
+    void registerCallBack( StateMachine::Transition s, IFSMCallbackHolder::CallBack c ) override {
+      m_callbacks[s].push_back( std::move( c ) );
+    }
+
+    StatusCode sysInitialize() override {
+      return Parent::sysInitialize().andThen( [&]() { handleCallBacks( StateMachine::INITIALIZE ); } );
+    }
+    StatusCode sysStart() override {
+      return Parent::sysStart().andThen( [&]() { handleCallBacks( StateMachine::START ); } );
+    }
+    StatusCode sysStop() override {
+      return Parent::sysStop().andThen( [&]() { handleCallBacks( StateMachine::STOP ); } );
+    }
+    StatusCode sysFinalize() override {
+      return Parent::sysFinalize().andThen( [&]() { handleCallBacks( StateMachine::FINALIZE ); } );
+    }
+
+  private:
+    void handleCallBacks( StateMachine::Transition state ) {
+      std::for_each( m_callbacks[state].begin(), m_callbacks[state].end(),
+                     []( IFSMCallbackHolder::CallBack const& c ) { c(); } );
+    }
+
+    std::map<StateMachine::Transition, std::vector<IFSMCallbackHolder::CallBack>> m_callbacks;
+  };
+
+} // namespace Gaudi
diff --git a/GaudiKernel/include/Gaudi/Interfaces/IFSMCallbackHolder.h b/GaudiKernel/include/Gaudi/Interfaces/IFSMCallbackHolder.h
new file mode 100644
index 0000000000..bb70724196
--- /dev/null
+++ b/GaudiKernel/include/Gaudi/Interfaces/IFSMCallbackHolder.h
@@ -0,0 +1,30 @@
+/***********************************************************************************\
+* (c) Copyright 1998-2024 CERN for the benefit of the LHCb and ATLAS collaborations *
+*                                                                                   *
+* This software is distributed under the terms of the Apache version 2 licence,     *
+* copied verbatim in the file "LICENSE".                                            *
+*                                                                                   *
+* In applying this licence, CERN does not waive the privileges and immunities       *
+* granted to it by virtue of its status as an Intergovernmental Organization        *
+* or submit itself to any jurisdiction.                                             *
+\***********************************************************************************/
+#pragma once
+
+#include <GaudiKernel/StateMachine.h>
+
+namespace Gaudi {
+  /**
+   *  Interface defining a CallBack registration functionality based on the
+   *  State Machine of Gaudi. Callbacks are std::function which can be registered
+   *  for each transition. They will then be called automatically just after
+   *  the transition took place in the order in which they have been registered.
+   */
+  struct IFSMCallbackHolder : virtual public IInterface {
+
+  public:
+    DeclareInterfaceID( IFSMCallbackHolder, 1, 0 );
+
+    using CallBack                                                      = std::function<void()>;
+    virtual void registerCallBack( StateMachine::Transition, CallBack ) = 0;
+  };
+} // namespace Gaudi
diff --git a/GaudiTestSuite/CMakeLists.txt b/GaudiTestSuite/CMakeLists.txt
index 4a84a4fd6b..0434723398 100644
--- a/GaudiTestSuite/CMakeLists.txt
+++ b/GaudiTestSuite/CMakeLists.txt
@@ -55,6 +55,7 @@ gaudi_add_module(GaudiTestSuiteComponents
                          src/ExtendedProperties/ExtendedProperties.cpp
                          src/ExtendedProperties/ExtendedProperties2.cpp
                          src/FileMgr/FileMgrTest.cpp
+                         src/FSMCallback/FSMCallbackTestAlgo.cpp
                          src/FunctionalAlgorithms/EventCounter.cpp
                          src/FunctionalAlgorithms/MakeAndConsume.cpp
                          src/FunctionalAlgorithms/merging_transformer.cpp
diff --git a/GaudiTestSuite/options/FSMCallbackTest.py b/GaudiTestSuite/options/FSMCallbackTest.py
new file mode 100644
index 0000000000..26e303452b
--- /dev/null
+++ b/GaudiTestSuite/options/FSMCallbackTest.py
@@ -0,0 +1,15 @@
+#####################################################################################
+# (c) Copyright 1998-2019 CERN for the benefit of the LHCb and ATLAS collaborations #
+#                                                                                   #
+# This software is distributed under the terms of the Apache version 2 licence,     #
+# copied verbatim in the file "LICENSE".                                            #
+#                                                                                   #
+# In applying this licence, CERN does not waive the privileges and immunities       #
+# granted to it by virtue of its status as an Intergovernmental Organization        #
+# or submit itself to any jurisdiction.                                             #
+#####################################################################################
+from Gaudi.Configuration import ApplicationMgr
+from Configurables import Gaudi__TestSuite__FSMCallbackTestAlgo as FSMCallbackTestAlgo
+
+alg = FSMCallbackTestAlgo("CallBackTestAlgo")
+app = ApplicationMgr(EvtMax=5, EvtSel="NONE", TopAlg=[alg])
diff --git a/GaudiTestSuite/src/FSMCallback/FSMCallbackTestAlgo.cpp b/GaudiTestSuite/src/FSMCallback/FSMCallbackTestAlgo.cpp
new file mode 100644
index 0000000000..ade8fcee63
--- /dev/null
+++ b/GaudiTestSuite/src/FSMCallback/FSMCallbackTestAlgo.cpp
@@ -0,0 +1,34 @@
+/***********************************************************************************\
+* (c) Copyright 1998-2024 CERN for the benefit of the LHCb and ATLAS collaborations *
+*                                                                                   *
+* This software is distributed under the terms of the Apache version 2 licence,     *
+* copied verbatim in the file "LICENSE".                                            *
+*                                                                                   *
+* In applying this licence, CERN does not waive the privileges and immunities       *
+* granted to it by virtue of its status as an Intergovernmental Organization        *
+* or submit itself to any jurisdiction.                                             *
+\***********************************************************************************/
+#include <Gaudi/Algorithm.h>
+#include <Gaudi/FSMCallbackHolder.h>
+
+namespace Gaudi::TestSuite {
+
+  /**
+   * Example of usage of callbacks, when using CallbackHolder
+   */
+  struct FSMCallbackTestAlgo : FSMCallbackHolder<Algorithm> {
+    FSMCallbackTestAlgo( const std::string& name, ISvcLocator* pSvcLocator )
+        : FSMCallbackHolder<Algorithm>( name, pSvcLocator ) {
+      registerCallBack( StateMachine::INITIALIZE, []() { std::cout << "Callback properly called at INITIALIZE\n"; } );
+      registerCallBack( StateMachine::START, []() { std::cout << "Callback properly called at START\n"; } );
+      registerCallBack( StateMachine::STOP, []() { std::cout << "Callback properly called at STOP\n"; } );
+      registerCallBack( StateMachine::FINALIZE, []() { std::cout << "Callback properly called at FINALIZE\n"; } );
+    }
+    StatusCode execute( const EventContext& ) const override {
+      std::cout << "Executing\n";
+      return StatusCode::SUCCESS;
+    }
+  };
+  DECLARE_COMPONENT( FSMCallbackTestAlgo );
+
+} // namespace Gaudi::TestSuite
diff --git a/GaudiTestSuite/tests/qmtest/gauditestsuite.qms/callback.qmt b/GaudiTestSuite/tests/qmtest/gauditestsuite.qms/callback.qmt
new file mode 100644
index 0000000000..1703df18a0
--- /dev/null
+++ b/GaudiTestSuite/tests/qmtest/gauditestsuite.qms/callback.qmt
@@ -0,0 +1,16 @@
+<?xml version="1.0" ?><!DOCTYPE extension  PUBLIC '-//QM/2.3/Extension//EN'  'http://www.codesourcery.com/qm/dtds/2.3/-//qm/2.3/extension//en.dtd'>
+<!--
+    (c) Copyright 1998-2024 CERN for the benefit of the LHCb and ATLAS collaborations
+
+    This software is distributed under the terms of the Apache version 2 licence,
+    copied verbatim in the file "LICENSE".
+
+    In applying this licence, CERN does not waive the privileges and immunities
+    granted to it by virtue of its status as an Intergovernmental Organization
+    or submit itself to any jurisdiction.
+-->
+<extension class="GaudiTest.GaudiExeTest" kind="test">
+  <argument name="program"><text>gaudirun.py</text></argument>
+  <argument name="args"><set><text>../../options/FSMCallbackTest.py</text></set></argument>
+  <argument name="reference"><text>refs/FSMCallbackTest.ref</text></argument>
+</extension>
diff --git a/GaudiTestSuite/tests/qmtest/refs/FSMCallbackTest.ref b/GaudiTestSuite/tests/qmtest/refs/FSMCallbackTest.ref
new file mode 100644
index 0000000000..287071a430
--- /dev/null
+++ b/GaudiTestSuite/tests/qmtest/refs/FSMCallbackTest.ref
@@ -0,0 +1,26 @@
+# setting LC_ALL to "C"
+# --> Including file '/home/sponce/master/Gaudi/GaudiTestSuite/options/CallBackTest.py'
+# <-- End of file '/home/sponce/master/Gaudi/GaudiTestSuite/options/CallBackTest.py'
+ApplicationMgr    SUCCESS
+====================================================================================================================================
+                                                   Welcome to ApplicationMgr (GaudiCoreSvc v38r2)
+                                          running on lblhcbpr11.cern.ch on Mon Jun 17 16:10:53 2024
+====================================================================================================================================
+ApplicationMgr       INFO Application Manager Configured successfully
+Callback properly called at INITIALIZE
+EventLoopMgr      WARNING Unable to locate service "EventSelector"
+EventLoopMgr      WARNING No events will be processed from external input.
+ApplicationMgr       INFO Application Manager Initialized successfully
+Callback properly called at START
+ApplicationMgr       INFO Application Manager Started successfully
+Executing
+Executing
+Executing
+Executing
+Executing
+Callback properly called at STOP
+ApplicationMgr       INFO Application Manager Stopped successfully
+Callback properly called at FINALIZE
+EventLoopMgr         INFO Histograms converted successfully according to request.
+ApplicationMgr       INFO Application Manager Finalized successfully
+ApplicationMgr       INFO Application Manager Terminated successfully
-- 
GitLab