From 26c9420ccf47d8ac2356032b91846929f7b94987 Mon Sep 17 00:00:00 2001
From: scott snyder <scott.snyder@cern.ch>
Date: Thu, 20 Apr 2017 18:05:24 +0200
Subject: [PATCH] AthenaBaseComps: Extend algorithm base class to deal with
 handles to symlinked objects.

---
 .../AthenaBaseComps/AthAlgorithm.h            |  13 ++
 .../AthenaBaseComps/AthReentrantAlgorithm.h   |  15 +-
 Control/AthenaBaseComps/CMakeLists.txt        |  10 ++
 .../share/AthAlgorithmDHUpdate_test.ref       |  32 ++++
 .../share/AthAlgorithm_test.ref               |  33 ++++
 Control/AthenaBaseComps/src/AthAlgorithm.cxx  |  27 +++
 .../src/AthAlgorithmDHUpdate.cxx              |  94 ++++++++++
 .../src/AthAlgorithmDHUpdate.h                |  85 +++++++++
 .../src/AthReentrantAlgorithm.cxx             |  26 +++
 .../test/AthAlgorithmDHUpdate_test.cxx        | 164 ++++++++++++++++++
 .../test/AthAlgorithm_test.cxx                | 131 ++++++++++++++
 .../test/AthReentrantAlgorithm_test.cxx       |  19 +-
 12 files changed, 646 insertions(+), 3 deletions(-)
 create mode 100644 Control/AthenaBaseComps/share/AthAlgorithmDHUpdate_test.ref
 create mode 100644 Control/AthenaBaseComps/share/AthAlgorithm_test.ref
 create mode 100644 Control/AthenaBaseComps/src/AthAlgorithmDHUpdate.cxx
 create mode 100644 Control/AthenaBaseComps/src/AthAlgorithmDHUpdate.h
 create mode 100644 Control/AthenaBaseComps/test/AthAlgorithmDHUpdate_test.cxx
 create mode 100644 Control/AthenaBaseComps/test/AthAlgorithm_test.cxx

diff --git a/Control/AthenaBaseComps/AthenaBaseComps/AthAlgorithm.h b/Control/AthenaBaseComps/AthenaBaseComps/AthAlgorithm.h
index e79468713bfb..6ca6da831830 100644
--- a/Control/AthenaBaseComps/AthenaBaseComps/AthAlgorithm.h
+++ b/Control/AthenaBaseComps/AthenaBaseComps/AthAlgorithm.h
@@ -234,6 +234,15 @@ public:
   }
 
   
+  /**
+   * @brief Return the list of extra output dependencies.
+   *
+   * This list is extended to include symlinks implied by inheritance
+   * relations.
+   */
+  virtual const DataObjIDColl& extraOutputDeps() const override;
+
+
   /////////////////////////////////////////////////////////////////// 
   // Non-const methods: 
   /////////////////////////////////////////////////////////////////// 
@@ -266,6 +275,10 @@ public:
   typedef ServiceHandle<IUserDataSvc> UserDataSvc_t;
   /// Pointer to IUserDataSvc
   mutable UserDataSvc_t m_userStore;
+
+  /// Extra output dependency collection, extended by AthAlgorithmDHUpdate
+  /// to add symlinks.  Empty if no symlinks were found.
+  DataObjIDColl m_extendedExtraObjects;
 }; 
 
 /////////////////////////////////////////////////////////////////// 
diff --git a/Control/AthenaBaseComps/AthenaBaseComps/AthReentrantAlgorithm.h b/Control/AthenaBaseComps/AthenaBaseComps/AthReentrantAlgorithm.h
index e21626d3e063..0dd08e060b71 100644
--- a/Control/AthenaBaseComps/AthenaBaseComps/AthReentrantAlgorithm.h
+++ b/Control/AthenaBaseComps/AthenaBaseComps/AthReentrantAlgorithm.h
@@ -281,7 +281,16 @@ public:
 
   }
 
-  
+
+  /**
+   * @brief Return the list of extra output dependencies.
+   *
+   * This list is extended to include symlinks implied by inheritance
+   * relations.
+   */
+  virtual const DataObjIDColl& extraOutputDeps() const override;
+
+
   /////////////////////////////////////////////////////////////////// 
   // Non-const methods: 
   /////////////////////////////////////////////////////////////////// 
@@ -314,6 +323,10 @@ public:
   typedef ServiceHandle<IUserDataSvc> UserDataSvc_t;
   /// Pointer to IUserDataSvc
   mutable UserDataSvc_t m_userStore;
+
+  /// Extra output dependency collection, extended by AthAlgorithmDHUpdate
+  /// to add symlinks.  Empty if no symlinks were found.
+  DataObjIDColl m_extendedExtraObjects;
 }; 
 
 /////////////////////////////////////////////////////////////////// 
diff --git a/Control/AthenaBaseComps/CMakeLists.txt b/Control/AthenaBaseComps/CMakeLists.txt
index ece926545de7..fa62f1921cfa 100644
--- a/Control/AthenaBaseComps/CMakeLists.txt
+++ b/Control/AthenaBaseComps/CMakeLists.txt
@@ -38,3 +38,13 @@ atlas_add_test( AthReentrantAlgorithm_test
    SOURCES test/AthReentrantAlgorithm_test.cxx
    LINK_LIBRARIES StoreGateLib GaudiKernel TestTools AthenaBaseComps 
    ENVIRONMENT "JOBOPTSEARCHPATH=${CMAKE_CURRENT_SOURCE_DIR}/share" )
+
+atlas_add_test( AthAlgorithm_test
+   SOURCES test/AthAlgorithm_test.cxx
+   LINK_LIBRARIES StoreGateLib GaudiKernel TestTools AthenaBaseComps 
+   ENVIRONMENT "JOBOPTSEARCHPATH=${CMAKE_CURRENT_SOURCE_DIR}/share" )
+
+atlas_add_test( AthAlgorithmDHUpdate_test
+   SOURCES test/AthAlgorithmDHUpdate_test.cxx
+   LINK_LIBRARIES StoreGateLib GaudiKernel TestTools AthenaBaseComps
+   ENVIRONMENT "JOBOPTSEARCHPATH=${CMAKE_CURRENT_SOURCE_DIR}/share" )
diff --git a/Control/AthenaBaseComps/share/AthAlgorithmDHUpdate_test.ref b/Control/AthenaBaseComps/share/AthAlgorithmDHUpdate_test.ref
new file mode 100644
index 000000000000..d535188fd9f8
--- /dev/null
+++ b/Control/AthenaBaseComps/share/AthAlgorithmDHUpdate_test.ref
@@ -0,0 +1,32 @@
+
+
+Initializing Gaudi ApplicationMgr using job opts ../share/propertyHandling_test.txt
+JobOptionsSvc        INFO # =======> /afs/cern.ch/user/s/ssnyder/atlas-work7/Control/AthenaBaseComps/share/../share/propertyHandling_test.txt
+JobOptionsSvc        INFO # (1,1): alg.rkey = "FooSvc/aaa"
+JobOptionsSvc        INFO # (2,1): alg.wkey = "BarSvc/bbb"
+JobOptionsSvc        INFO # (3,1): alg.ukey = "ccc"
+JobOptionsSvc        INFO # (4,1): alg.rhandle = "FooSvc/ddd"
+JobOptionsSvc        INFO # (5,1): alg.whandle = "BarSvc/eee"
+JobOptionsSvc        INFO # (6,1): alg.uhandle = "fff"
+JobOptionsSvc        INFO # (8,1): tool.rkey = "FooSvc/taa"
+JobOptionsSvc        INFO # (9,1): tool.wkey = "BarSvc/tbb"
+JobOptionsSvc        INFO # (10,1): tool.ukey = "tcc"
+JobOptionsSvc        INFO # (11,1): tool.rhandle = "FooSvc/tdd"
+JobOptionsSvc        INFO # (12,1): tool.whandle = "BarSvc/tee"
+JobOptionsSvc        INFO # (13,1): tool.uhandle = "tff"
+JobOptionsSvc        INFO # (15,1): ralg.rkey = "FooSvc/aaa"
+JobOptionsSvc        INFO # (16,1): ralg.whandle = "BarSvc/eee"
+JobOptionsSvc        INFO Job options successfully read in from ../share/propertyHandling_test.txt
+ApplicationMgr    SUCCESS 
+====================================================================================================================================
+                                                   Welcome to ApplicationMgr (GaudiCoreSvc v28r1)
+                                          running on lxplus042.cern.ch on Thu Apr  6 17:12:06 2017
+====================================================================================================================================
+ApplicationMgr       INFO Application Manager Configured successfully
+EventLoopMgr      WARNING Unable to locate service "EventSelector" 
+EventLoopMgr      WARNING No events will be processed from external input.
+HistogramPersis...WARNING Histograms saving not required.
+ApplicationMgr       INFO Application Manager Initialized successfully
+ApplicationMgr Ready
+test1
+ClassIDSvc           INFO  getRegistryEntries: read 677 CLIDRegistry entries for module ALL
diff --git a/Control/AthenaBaseComps/share/AthAlgorithm_test.ref b/Control/AthenaBaseComps/share/AthAlgorithm_test.ref
new file mode 100644
index 000000000000..351f76b87752
--- /dev/null
+++ b/Control/AthenaBaseComps/share/AthAlgorithm_test.ref
@@ -0,0 +1,33 @@
+
+
+Initializing Gaudi ApplicationMgr using job opts ../share/propertyHandling_test.txt
+JobOptionsSvc        INFO # =======> /afs/cern.ch/user/s/ssnyder/atlas-work7/Control/AthenaBaseComps/share/../share/propertyHandling_test.txt
+JobOptionsSvc        INFO # (1,1): alg.rkey = "FooSvc/aaa"
+JobOptionsSvc        INFO # (2,1): alg.wkey = "BarSvc/bbb"
+JobOptionsSvc        INFO # (3,1): alg.ukey = "ccc"
+JobOptionsSvc        INFO # (4,1): alg.rhandle = "FooSvc/ddd"
+JobOptionsSvc        INFO # (5,1): alg.whandle = "BarSvc/eee"
+JobOptionsSvc        INFO # (6,1): alg.uhandle = "fff"
+JobOptionsSvc        INFO # (8,1): tool.rkey = "FooSvc/taa"
+JobOptionsSvc        INFO # (9,1): tool.wkey = "BarSvc/tbb"
+JobOptionsSvc        INFO # (10,1): tool.ukey = "tcc"
+JobOptionsSvc        INFO # (11,1): tool.rhandle = "FooSvc/tdd"
+JobOptionsSvc        INFO # (12,1): tool.whandle = "BarSvc/tee"
+JobOptionsSvc        INFO # (13,1): tool.uhandle = "tff"
+JobOptionsSvc        INFO # (15,1): ralg.rkey = "FooSvc/aaa"
+JobOptionsSvc        INFO # (16,1): ralg.whandle = "BarSvc/eee"
+JobOptionsSvc        INFO Job options successfully read in from ../share/propertyHandling_test.txt
+ApplicationMgr    SUCCESS 
+====================================================================================================================================
+                                                   Welcome to ApplicationMgr (GaudiCoreSvc v28r1)
+                                          running on lxplus042.cern.ch on Thu Apr  6 23:08:52 2017
+====================================================================================================================================
+ApplicationMgr       INFO Application Manager Configured successfully
+EventLoopMgr      WARNING Unable to locate service "EventSelector" 
+EventLoopMgr      WARNING No events will be processed from external input.
+HistogramPersis...WARNING Histograms saving not required.
+ApplicationMgr       INFO Application Manager Initialized successfully
+ApplicationMgr Ready
+test1
+ClassIDSvc           INFO  getRegistryEntries: read 727 CLIDRegistry entries for module ALL
+ClassIDSvc           INFO  getRegistryEntries: read 359 CLIDRegistry entries for module ALL
diff --git a/Control/AthenaBaseComps/src/AthAlgorithm.cxx b/Control/AthenaBaseComps/src/AthAlgorithm.cxx
index e419251f6e8a..35f87bce7e3f 100644
--- a/Control/AthenaBaseComps/src/AthAlgorithm.cxx
+++ b/Control/AthenaBaseComps/src/AthAlgorithm.cxx
@@ -11,12 +11,14 @@
 
 // AthenaBaseComps includes
 #include "AthenaBaseComps/AthAlgorithm.h"
+#include "AthAlgorithmDHUpdate.h"
 
 // STL includes
 
 // FrameWork includes
 #include "GaudiKernel/Property.h"
 
+
 /////////////////////////////////////////////////////////////////// 
 // Public methods: 
 /////////////////////////////////////////////////////////////////// 
@@ -61,6 +63,14 @@ AthAlgorithm::AthAlgorithm( const std::string& name,
                    m_userStore = UserDataSvc_t ("UserDataSvc/UserDataSvc", name),
                    "Handle to a UserDataSvc/UserDataSvc instance: it will be used to "
                    "retrieve user data during the course of the job" );
+
+  // Set up to run AthAlgorithmDHUpdate in sysInitialize before
+  // merging depedency lists.  This extends the output dependency
+  // list with any symlinks implied by inheritance relations.
+  m_updateDataHandles =
+    std::make_unique<AthenaBaseComps::AthAlgorithmDHUpdate>
+    (m_extendedExtraObjects,
+     std::move (m_updateDataHandles));
 }
 
 // Destructor
@@ -105,3 +115,20 @@ AthAlgorithm::msg_update_handler( Property& outputLevel )
       msg().setLevel( msgLevel() );
    }
 }
+
+
+/**
+ * @brief Return the list of extra output dependencies.
+ *
+ * This list is extended to include symlinks implied by inheritance
+ * relations.
+ */
+const DataObjIDColl& AthAlgorithm::extraOutputDeps() const
+{
+  // If we didn't find any symlinks to add, just return the collection
+  // from the base class.  Otherwise, return the extended collection.
+  if (!m_extendedExtraObjects.empty()) {
+    return m_extendedExtraObjects;
+  }
+  return Algorithm::extraOutputDeps();
+}
diff --git a/Control/AthenaBaseComps/src/AthAlgorithmDHUpdate.cxx b/Control/AthenaBaseComps/src/AthAlgorithmDHUpdate.cxx
new file mode 100644
index 000000000000..237817d08657
--- /dev/null
+++ b/Control/AthenaBaseComps/src/AthAlgorithmDHUpdate.cxx
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration.
+ */
+// $Id$
+/**
+ * @file AthenaBaseComps/src/AthAlgorithmDHUpdate.cxx
+ * @author scott snyder <snyder@bnl.gov>
+ * @date Apr, 2017
+ * @brief Update output dependencies to include symlinks.
+ */
+
+
+#include "AthAlgorithmDHUpdate.h"
+#include "SGTools/BaseInfo.h"
+
+
+namespace AthenaBaseComps {
+
+
+/**
+ * @brief Constructor.
+ * @param linkedObjs Set to which entries for symlinks will be added.
+ * @param chain Hook to call after this one completes.
+ */
+AthAlgorithmDHUpdate::AthAlgorithmDHUpdate (DataObjIDColl& linkedObjs,
+                                            std::unique_ptr<IDataHandleVisitor> chain)
+  : m_linkedObjs (linkedObjs),
+    m_chain (std::move (chain))
+{
+}
+
+
+/**
+ * @brief Walk over the dependencies of an algorithm.
+ * @param dhh The algorithm object.
+ *
+ * Finds output dependencies of the algorithms that have symlinks
+ * available and enters these links in @c linkedObjs.
+ */
+void AthAlgorithmDHUpdate::visit (const IDataHandleHolder* dhh)
+{
+  // Make a copy, as usually linkedObjs will be extraOutputDeps().
+  DataObjIDColl ex = dhh->extraOutputDeps();
+
+  // Process all output dependencies.
+  for (const Gaudi::DataHandle* h : dhh->outputHandles()) {
+    if (!h->objKey().empty())
+      handle (h->fullKey().clid(), h->objKey());
+  }
+
+  for (const DataObjID& dobj : ex) {
+    if (!dobj.key().empty())
+      handle (dobj.clid(), dobj.key());
+  }
+
+  for (const DataObjID& dobj : dhh->outputDataObjs()) {
+    if (!dobj.key().empty())
+      handle (dobj.clid(), dobj.key());
+  }
+
+  // If we put anything in linkedObjs, also add the contents
+  // of extraOutputDeps.
+  if (!m_linkedObjs.empty()) {
+    DataObjIDColl ex = dhh->extraOutputDeps();
+    m_linkedObjs.insert (ex.begin(), ex.end());
+  }
+
+  // Call the next hook function, if any.
+  if (m_chain) {
+    m_chain->visit (dhh);
+  }
+}
+
+
+/**
+ * @brief Handle one output dependency.
+ * @param clid The CLID of the output.
+ * @param key The SG key of the output.
+ *
+ * If CLID has symlinks available, enter these links in @c linkedObjs.
+ */
+void AthAlgorithmDHUpdate::handle (CLID clid, const std::string& key)
+{
+  const SG::BaseInfoBase* bib = SG::BaseInfoBase::find (clid);
+  if (!bib) return;
+  for (CLID clid2 : bib->get_bases()) {
+    if (clid2 != clid) {
+      m_linkedObjs.emplace (clid2, key);
+    }
+  }
+}
+
+
+} // namespace AthenaBaseComps
diff --git a/Control/AthenaBaseComps/src/AthAlgorithmDHUpdate.h b/Control/AthenaBaseComps/src/AthAlgorithmDHUpdate.h
new file mode 100644
index 000000000000..45036ce0ce7f
--- /dev/null
+++ b/Control/AthenaBaseComps/src/AthAlgorithmDHUpdate.h
@@ -0,0 +1,85 @@
+// This file's extension implies that it's C, but it's really -*- C++ -*-.
+/*
+ * Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration.
+ */
+// $Id$
+/**
+ * @file AthenaBaseComps/src/AthAlgorithmDHUpdate.h
+ * @author scott snyder <snyder@bnl.gov>
+ * @date Apr, 2017
+ * @brief Update output dependencies to include symlinks.
+ */
+
+
+#ifndef ATHENABASECOMPS_ATHALGORITHMDHUPDATE_H
+#define ATHENABASECOMPS_ATHALGORITHMDHUPDATE_H
+
+
+#include "GaudiKernel/IDataHandleHolder.h"
+#include "GaudiKernel/DataObjID.h"
+#include "GaudiKernel/ClassID.h"
+#include <memory>
+
+
+namespace AthenaBaseComps {
+
+
+/**
+ * @brief Update output dependencies to include symlinks.
+ *
+ * This is intended to be installed in an algorithm via the
+ * m_updateDataHandles hook, to run before Algorithm::sysInitialize
+ * merges the sets of input and output dependencies.
+ *
+ * It makes a first pass over all the output dependences.  For each,
+ * it examines the CLID to see if there are any other CLIDs to which
+ * it is convertable, and if so, adds them to the @c linkedObjs list.
+ * The intention is that the algorithm will then override
+ * @c extraOutputDeps so that it will include these extra entries.
+ */
+class AthAlgorithmDHUpdate
+  : public IDataHandleVisitor
+{
+public:
+  /**
+   * @brief Constructor.
+   * @param linkedObjs Set to which entries for symlinks will be added.
+   * @param chain Hook to call after this one completes.
+   */
+  AthAlgorithmDHUpdate (DataObjIDColl& linkedObjs,
+                        std::unique_ptr<IDataHandleVisitor> chain);
+  
+
+  /**
+   * @brief Walk over the dependencies of an algorithm.
+   * @param dhh The algorithm object.
+   *
+   * Finds output dependencies of the algorithms that have symlinks
+   * available and enters these links in @c linkedObjs.
+   */
+  virtual void visit(const IDataHandleHolder* dhh) override;
+
+  
+private:
+  /**
+   * @brief Handle one output dependency.
+   * @param clid The CLID of the output.
+   * @param key The SG key of the output.
+   *
+   * If CLID has symlinks available, enter these links in @c linkedObjs.
+   */
+  void handle (CLID clid, const std::string& key);
+
+
+  /// Set of additional output dependencies represented by links.
+  DataObjIDColl& m_linkedObjs;
+
+  /// Hook to call after this one.
+  std::unique_ptr<IDataHandleVisitor> m_chain;
+};
+
+
+} // namespace AthenaBaseComps
+
+
+#endif // not ATHENABASECOMPS_ATHALGORITHMDHUPDATE_H
diff --git a/Control/AthenaBaseComps/src/AthReentrantAlgorithm.cxx b/Control/AthenaBaseComps/src/AthReentrantAlgorithm.cxx
index bc09e1db6f48..7846761c4a22 100644
--- a/Control/AthenaBaseComps/src/AthReentrantAlgorithm.cxx
+++ b/Control/AthenaBaseComps/src/AthReentrantAlgorithm.cxx
@@ -11,6 +11,7 @@
 
 // AthenaBaseComps includes
 #include "AthenaBaseComps/AthReentrantAlgorithm.h"
+#include "AthAlgorithmDHUpdate.h"
 
 // FrameWork includes
 #include "GaudiKernel/Property.h"
@@ -62,6 +63,14 @@ AthReentrantAlgorithm::AthReentrantAlgorithm( const std::string& name,
                    m_userStore = UserDataSvc_t ("UserDataSvc/UserDataSvc", name),
                    "Handle to a UserDataSvc/UserDataSvc instance: it will be used to "
                    "retrieve user data during the course of the job" );
+
+  // Set up to run AthAlgorithmDHUpdate in sysInitialize before
+  // merging depedency lists.  This extends the output dependency
+  // list with any symlinks implied by inheritance relations.
+  m_updateDataHandles =
+    std::make_unique<AthenaBaseComps::AthAlgorithmDHUpdate>
+    (m_extendedExtraObjects,
+     std::move (m_updateDataHandles));
 }
 
 // Destructor
@@ -119,3 +128,20 @@ StatusCode AthReentrantAlgorithm::execute()
   return execute_r (Gaudi::Hive::currentContext());
 }
 #endif
+
+
+/**
+ * @brief Return the list of extra output dependencies.
+ *
+ * This list is extended to include symlinks implied by inheritance
+ * relations.
+ */
+const DataObjIDColl& AthReentrantAlgorithm::extraOutputDeps() const
+{
+  // If we didn't find any symlinks to add, just return the collection
+  // from the base class.  Otherwise, return the extended collection.
+  if (!m_extendedExtraObjects.empty()) {
+    return m_extendedExtraObjects;
+  }
+  return Algorithm::extraOutputDeps();
+}
diff --git a/Control/AthenaBaseComps/test/AthAlgorithmDHUpdate_test.cxx b/Control/AthenaBaseComps/test/AthAlgorithmDHUpdate_test.cxx
new file mode 100644
index 000000000000..e57749a5018f
--- /dev/null
+++ b/Control/AthenaBaseComps/test/AthAlgorithmDHUpdate_test.cxx
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration.
+ */
+
+// $Id$
+/**
+ * @file AthenaBaseComps/test/AthAlgorithmDHUpdate_test.cxx
+ * @author scott snyder <snyder@bnl.gov>
+ * @date Apr, 2017
+ * @brief Test property handling for AthReentrantAlgorithm.
+ */
+
+
+#undef NDEBUG
+#include "../src/AthAlgorithmDHUpdate.h"
+#include "SGTools/CLASS_DEF.h"
+#include "SGTools/BaseInfo.h"
+#include "TestTools/initGaudi.h"
+#include <cassert>
+#include <iostream>
+
+
+class A2 {};
+class A3 {};
+class A1 : public A2, public A3 {};
+CLASS_DEF (A1, 132102064, 0)
+CLASS_DEF (A2, 132102065, 0)
+CLASS_DEF (A3, 132102066, 0)
+SG_BASES2 (A1, A2, A3);
+
+class B2 {};
+class B1 : public B2 {};
+CLASS_DEF (B1, 132102074, 0)
+CLASS_DEF (B2, 132102075, 0)
+SG_BASE (B1, B2);
+
+class C2 {};
+class C1 : public C2 {};
+CLASS_DEF (C1, 132102084, 0)
+CLASS_DEF (C2, 132102085, 0)
+SG_BASE (C1, C2);
+
+class TestHolder
+  : public IDataHandleHolder
+{
+public:
+  virtual std::vector<Gaudi::DataHandle*> inputHandles() const override
+  { std::abort(); }
+  virtual const DataObjIDColl& extraInputDeps() const override
+  { std::abort(); }
+  virtual void acceptDHVisitor(IDataHandleVisitor*) const override
+  { std::abort(); }
+  virtual void commitHandles() override
+  { std::abort(); }
+  virtual const DataObjIDColl& inputDataObjs() const override
+  { std::abort(); }
+  virtual void addDependency(const DataObjID&, const Gaudi::DataHandle::Mode&) override
+  { std::abort(); }
+  virtual void declare(Gaudi::DataHandle&) override
+  { std::abort(); }
+  virtual void renounce(Gaudi::DataHandle&) override
+  { std::abort(); }
+  virtual unsigned long addRef() override
+  { std::abort(); }
+  virtual unsigned long release() override
+  { std::abort(); }
+  virtual StatusCode queryInterface(const InterfaceID&, void**) override
+  { std::abort(); }
+  virtual const std::string& name() const override
+  { std::abort(); }
+
+  virtual std::vector<Gaudi::DataHandle*> outputHandles() const override;
+  virtual const DataObjIDColl& extraOutputDeps() const override;
+  virtual const DataObjIDColl& outputDataObjs() const override;
+
+
+  std::vector<Gaudi::DataHandle*> handle_ptrs;
+  DataObjIDColl outDeps;
+  DataObjIDColl extraOutDeps;
+};
+
+
+std::vector<Gaudi::DataHandle*> TestHolder::outputHandles() const
+{
+  return handle_ptrs;
+}
+
+
+const DataObjIDColl& TestHolder::extraOutputDeps() const
+{
+  return extraOutDeps;
+}
+
+
+const DataObjIDColl& TestHolder::outputDataObjs() const
+{
+  return outDeps;
+}
+
+
+class TestChain
+  : public IDataHandleVisitor
+{
+public:
+  virtual void visit(const IDataHandleHolder*) override;
+
+  const IDataHandleHolder* visited = nullptr;
+};
+
+
+void TestChain::visit (const IDataHandleHolder* dhh) 
+{
+  visited = dhh;
+}
+
+
+void test1()
+{
+  std::cout << "test1\n";
+
+  TestHolder h;
+  Gaudi::DataHandle h1 (DataObjID (ClassID_traits<A1>::ID(), "a1"));
+  h.handle_ptrs.push_back (&h1);
+
+  h.outDeps.emplace (ClassID_traits<B1>::ID(), "b1");
+  h.outDeps.emplace (ClassID_traits<C1>::ID(), "c1");
+  h.extraOutDeps.emplace (ClassID_traits<C1>::ID(), "c1");
+
+  DataObjIDColl linkedObjs;
+  auto chain = std::make_unique<TestChain>();
+  TestChain* tc = chain.get();
+  AthenaBaseComps::AthAlgorithmDHUpdate dhu (linkedObjs, std::move (chain));
+  dhu.visit (&h);
+  assert (tc->visited == &h);
+
+  DataObjIDColl exp = {
+    { ClassID_traits<A2>::ID(), "a1" },
+    { ClassID_traits<A3>::ID(), "a1" },
+    { ClassID_traits<B2>::ID(), "b1" },
+    { ClassID_traits<C1>::ID(), "c1" },
+    { ClassID_traits<C2>::ID(), "c1" },
+  };
+
+  if (linkedObjs != exp) {
+    for (const DataObjID& o : linkedObjs) {
+        std::cout << "linked " << o.clid() << " " << o.key() << "\n";
+    }
+  }
+
+  // Quick test with null chain.
+  std::unique_ptr<IDataHandleVisitor> chain2;
+  AthenaBaseComps::AthAlgorithmDHUpdate dhu2 (linkedObjs, std::move (chain2));
+  dhu2.visit (&h);
+}
+
+
+int main()
+{
+  ISvcLocator* svcLoc = nullptr;
+  Athena_test::initGaudi ("propertyHandling_test.txt", svcLoc);
+
+  test1();
+  return 0;
+}
diff --git a/Control/AthenaBaseComps/test/AthAlgorithm_test.cxx b/Control/AthenaBaseComps/test/AthAlgorithm_test.cxx
new file mode 100644
index 000000000000..f0f2e494e004
--- /dev/null
+++ b/Control/AthenaBaseComps/test/AthAlgorithm_test.cxx
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration.
+ */
+
+// $Id$
+/**
+ * @file AthenaBaseComps/test/AthAlgorithm_test.cxx
+ * @author scott snyder <snyder@bnl.gov>
+ * @date Apr, 2017
+ * @brief Test property handling for AthAlgorithm.
+ */
+
+
+#undef NDEBUG
+#include "AthenaBaseComps/AthAlgorithm.h"
+#include "StoreGate/ReadHandleKey.h"
+#include "StoreGate/WriteHandle.h"
+#include "TestTools/initGaudi.h"
+#include "GaudiKernel/ThreadLocalContext.h"
+#include <vector>
+#include <cassert>
+#include <iostream>
+
+
+namespace AthenaBaseCompsTest {
+class MyBase {};
+class MyObj : public MyBase {};
+}
+CLASS_DEF (AthenaBaseCompsTest::MyBase, 293847296, 1)
+CLASS_DEF (AthenaBaseCompsTest::MyObj, 293847295, 1)
+SG_BASE (AthenaBaseCompsTest::MyObj, AthenaBaseCompsTest::MyBase);
+using AthenaBaseCompsTest::MyObj;
+
+
+class MyAlg
+  : public AthAlgorithm
+{
+public:
+  MyAlg (const std::string& name, ISvcLocator* svcLoc);
+
+  virtual StatusCode execute() override;
+
+  virtual void declare(Gaudi::DataHandle& hnd) override;
+
+  SG::ReadHandleKey<MyObj> rkey;
+  SG::WriteHandle<MyObj> whandle;
+
+  std::vector<Gaudi::DataHandle*> inputs;
+  std::vector<Gaudi::DataHandle*> outputs;
+};
+
+
+MyAlg::MyAlg  (const std::string& name, ISvcLocator* svcLoc)
+  : AthAlgorithm (name, svcLoc)
+{
+  declareProperty ("rkey", rkey);
+  declareProperty ("whandle", whandle);
+}
+
+
+StatusCode MyAlg::execute()
+{
+  return StatusCode::SUCCESS;
+}
+
+
+void MyAlg::declare(Gaudi::DataHandle& hnd) {
+  if (hnd.mode() & Gaudi::DataHandle::Reader) 
+    inputs.push_back( &hnd );
+  if (hnd.mode() & Gaudi::DataHandle::Writer) 
+    outputs.push_back( &hnd );
+  AthAlgorithm::declare (hnd);
+}
+
+
+void test1 (ISvcLocator* svcLoc)
+{
+  std::cout << "test1\n";
+
+  MyAlg alg ("ralg", svcLoc);  alg.addRef();
+  //assert (alg.setProperties().isSuccess());
+  assert (alg.sysInitialize().isSuccess());
+
+  assert (alg.rkey.clid() == 293847295);
+  assert (alg.rkey.key() == "aaa");
+  assert (alg.rkey.storeHandle().name() == "FooSvc");
+  assert (alg.rkey.mode() == Gaudi::DataHandle::Reader);
+
+  assert (alg.whandle.clid() == 293847295);
+  assert (alg.whandle.key() == "eee");
+  assert (alg.whandle.storeHandle().name() == "BarSvc");
+  assert (alg.whandle.mode() == Gaudi::DataHandle::Writer);
+
+  std::vector<std::string> inputKeys { "aaa"  };
+  assert (alg.inputs.size() == inputKeys.size());
+  for (size_t i = 0; i < inputKeys.size(); i++)
+    assert (alg.inputs[i]->objKey() == inputKeys[i]);
+
+  std::vector<std::string> outputKeys { "eee" };
+  assert (alg.outputs.size() == outputKeys.size());
+  for (size_t i = 0; i < outputKeys.size(); i++)
+    assert (alg.outputs[i]->objKey() == outputKeys[i]);
+
+  IProxyDict* xdict = &*alg.evtStore();
+  xdict = alg.evtStore()->hiveProxyDict();
+  EventContext ctx;
+  ctx.setProxy (xdict);
+  Gaudi::Hive::setCurrentContext (ctx);
+
+  assert (alg.execute().isSuccess());
+
+  DataObjIDColl exp = {
+    { ClassID_traits<AthenaBaseCompsTest::MyObj>::ID(), "eee" },
+    { ClassID_traits<AthenaBaseCompsTest::MyBase>::ID(), "eee" },
+  };
+  if (exp != alg.outputDataObjs()) {
+    for (const DataObjID& o : alg.outputDataObjs()) {
+      std::cout << "obj " << o.clid() << " " << o.key() << "\n";
+    }
+  }
+}
+
+
+int main()
+{
+  ISvcLocator* svcLoc = nullptr;
+  Athena_test::initGaudi ("propertyHandling_test.txt", svcLoc);
+
+  test1 (svcLoc);
+  return 0;
+}
diff --git a/Control/AthenaBaseComps/test/AthReentrantAlgorithm_test.cxx b/Control/AthenaBaseComps/test/AthReentrantAlgorithm_test.cxx
index d0d94d777810..51d4685b71cb 100644
--- a/Control/AthenaBaseComps/test/AthReentrantAlgorithm_test.cxx
+++ b/Control/AthenaBaseComps/test/AthReentrantAlgorithm_test.cxx
@@ -23,9 +23,12 @@
 
 
 namespace AthenaBaseCompsTest {
-class MyObj {};
+class MyBase {};
+class MyObj : public MyBase {};
 }
+CLASS_DEF (AthenaBaseCompsTest::MyBase, 293847296, 1)
 CLASS_DEF (AthenaBaseCompsTest::MyObj, 293847295, 1)
+SG_BASE (AthenaBaseCompsTest::MyObj, AthenaBaseCompsTest::MyBase);
 using AthenaBaseCompsTest::MyObj;
 
 
@@ -70,6 +73,7 @@ void MyAlg::declare(Gaudi::DataHandle& hnd) {
     inputs.push_back( &hnd );
   if (hnd.mode() & Gaudi::DataHandle::Writer) 
     outputs.push_back( &hnd );
+  AthReentrantAlgorithm::declare (hnd);
 }
 
 
@@ -78,7 +82,8 @@ void test1 (ISvcLocator* svcLoc)
   std::cout << "test1\n";
 
   MyAlg alg ("ralg", svcLoc);  alg.addRef();
-  assert (alg.setProperties().isSuccess());
+  //assert (alg.setProperties().isSuccess());
+  assert (alg.sysInitialize().isSuccess());
 
   assert (alg.rkey.clid() == 293847295);
   assert (alg.rkey.key() == "aaa");
@@ -108,6 +113,16 @@ void test1 (ISvcLocator* svcLoc)
 
   assert (alg.execute().isSuccess());
   assert (pdict == xdict);
+
+  DataObjIDColl exp = {
+    { ClassID_traits<AthenaBaseCompsTest::MyObj>::ID(), "eee" },
+    { ClassID_traits<AthenaBaseCompsTest::MyBase>::ID(), "eee" },
+  };
+  if (exp != alg.outputDataObjs()) {
+    for (const DataObjID& o : alg.outputDataObjs()) {
+      std::cout << "obj " << o.clid() << " " << o.key() << "\n";
+    }
+  }
 }
 
 
-- 
GitLab