Skip to content
Snippets Groups Projects
Commit 26c52313 authored by scott snyder's avatar scott snyder
Browse files

Simplify varhandle initialization for handles that are possibly ignored.

If a varhandle is used, we must call initialize on it; if it is not used,
the key must be cleared.  Combine these by extending initialize() to take
a flag.  If true, we proceed as usual; if false, we clear the key and
return success without doing anything else.

Also some small cleanups to tests.
parent 444db604
No related merge requests found
Showing
with 211 additions and 176 deletions
This diff is collapsed.
......@@ -7,7 +7,7 @@
* @file ReadDataReentrant.h
* @author scott snyder <snyder@bnl.gov>
* @date Jan, 2016
* @brief
* @brief Testing reentrant algorithms.
*/
......@@ -33,6 +33,7 @@
#include "AthLinks/ElementLink.h"
#include "AthenaKernel/DefaultKey.h"
#include "AthenaKernel/errorcheck.h"
/////////////////////////////////////////////////////////////////////////////
......@@ -52,8 +53,9 @@ ReadDataReentrant::ReadDataReentrant(const std::string& name, ISvcLocator* pSvcL
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
StatusCode ReadDataReentrant::initialize(){
StatusCode ReadDataReentrant::initialize()
{
errorcheck::ReportMessage::hideErrorLocus();
ATH_MSG_INFO ("in initialize()");
......
......@@ -9,7 +9,7 @@
* @file ReadDataReentrant.h
* @author scott snyder <snyder@bnl.gov>
* @date Jan, 2016
* @brief
* @brief Testing reentrant algorithms.
*/
......
......@@ -7,7 +7,7 @@
* @file WriteDataReentrant.cxx
* @author scott snyder <snyder@bnl.gov>
* @date Jan, 2016
* @brief
* @brief Testing reentrant algorithms.
*/
......@@ -31,7 +31,6 @@
#include "AthenaKernel/DefaultKey.h"
#include "AthenaKernel/errorcheck.h"
#include "CxxUtils/make_unique.h"
/////////////////////////////////////////////////////////////////////////////
......@@ -56,6 +55,8 @@ WriteDataReentrant::WriteDataReentrant(const std::string& name,
StatusCode WriteDataReentrant::initialize()
{
errorcheck::ReportMessage::hideErrorLocus();
ATH_MSG_INFO ("in initialize()");
ATH_CHECK( m_dobjKey.initialize() );
ATH_CHECK( m_dobjKey2.initialize() );
......@@ -96,20 +97,20 @@ StatusCode WriteDataReentrant::execute_r (const EventContext& ctx) const
// Part 1: Recording objects to SG
SG::WriteHandle<MyDataObj> dobj (m_dobjKey, ctx);
dobj = CxxUtils::make_unique<MyDataObj>(1);
dobj = std::make_unique<MyDataObj>(1);
//now we create a second MyDataObj instance...
//...try to record it as we did for the first. Since dobj2 is also a
//MyDataObj we expect to see an error
ATH_MSG_WARNING ("we expect an error message here");
EXPECT_EXCEPTION (std::runtime_error,
dobj = CxxUtils::make_unique<MyDataObj>(2));
dobj = std::make_unique<MyDataObj>(2));
ATH_MSG_WARNING ("end of error message");
//here we go again...
//... but this time we register the dobj3 using this algo name as key
auto dobj3 = SG::makeHandle (m_dobjKey3, ctx);
dobj3 = CxxUtils::make_unique<MyDataObj>(3);
dobj3 = std::make_unique<MyDataObj>(3);
SG::WriteHandle<TestDataObject> testobj (m_testObjectKey, ctx);
if (m_testObject->refCount() != 1) std::abort();
......@@ -119,9 +120,9 @@ StatusCode WriteDataReentrant::execute_r (const EventContext& ctx) const
#if 0
{
SG::WriteHandle<MyDataObj> dobj4 (m_dobjKey4, ctx);
ATH_CHECK( dobj4.recordOrRetrieve (CxxUtils::make_unique<MyDataObj>(4)) );
ATH_CHECK( dobj4.recordOrRetrieve (std::make_unique<MyDataObj>(4)) );
MyDataObj* pp = &*dobj4;
ATH_CHECK( dobj4.recordOrRetrieve (CxxUtils::make_unique<MyDataObj>(4)) );
ATH_CHECK( dobj4.recordOrRetrieve (std::make_unique<MyDataObj>(4)) );
assert (pp == &*dobj4);
}
#endif
......@@ -131,20 +132,20 @@ StatusCode WriteDataReentrant::execute_r (const EventContext& ctx) const
// Part 2: storing collections in the SG
SG::WriteHandle<DataVector<MyContObj> > cobj (m_cobjKey, ctx);
ATH_CHECK( cobj.record (CxxUtils::make_unique<DataVector<MyContObj> >()) );
ATH_CHECK( cobj.record (std::make_unique<DataVector<MyContObj> >()) );
cobj->reserve(10);
cobj->push_back (CxxUtils::make_unique<MyContObj> (11.3, 132));
cobj->push_back (CxxUtils::make_unique<MyContObj> (41.7, 291));
cobj->push_back (std::make_unique<MyContObj> (11.3, 132));
cobj->push_back (std::make_unique<MyContObj> (41.7, 291));
// as above with a vector of integers
SG::WriteHandle<std::vector<float> > vFloat (m_vFloatKey, ctx);
vFloat = CxxUtils::make_unique<std::vector<float> >();
vFloat = std::make_unique<std::vector<float> >();
vFloat->push_back(1.0);
vFloat->push_back(2.0);
vFloat->push_back(3.0);
SG::WriteHandle<MapStringFloat> m (m_mKey, ctx);
ATH_CHECK( m.record (CxxUtils::make_unique<MapStringFloat>()) );
ATH_CHECK( m.record (std::make_unique<MapStringFloat>()) );
(*m)["uno"]=1.0;
(*m)["due"]=2.0;
......@@ -209,7 +210,7 @@ StatusCode WriteDataReentrant::execute_r (const EventContext& ctx) const
//since dobj is identifiable in the SG a reference to it is all we need
SG::WriteHandle<MyDataObj> dobj2 (m_dobjKey2, ctx);
dobj2 = CxxUtils::make_unique<MyDataObj> (2);
dobj2 = std::make_unique<MyDataObj> (2);
//Otherwise one could first create an empty link
DataLink<MyDataObj> dobjLink2;
......@@ -250,7 +251,7 @@ StatusCode WriteDataReentrant::execute_r (const EventContext& ctx) const
// toContainedElement!
SG::WriteHandle<std::list<VecElemLink> > pLinkList (m_pLinkListKey, ctx);
pLinkList = CxxUtils::make_unique<std::list<VecElemLink> >();
pLinkList = std::make_unique<std::list<VecElemLink> >();
pLinkList->push_back(aLink);
pLinkList->push_back(thirdElementLink);
......@@ -259,7 +260,7 @@ StatusCode WriteDataReentrant::execute_r (const EventContext& ctx) const
//
typedef ElementLink<MapStringFloat> MapElemLink;
SG::WriteHandle<std::vector<MapElemLink> > linkVector (m_linkVectorKey, ctx);
linkVector = CxxUtils::make_unique<std::vector<MapElemLink> >();
linkVector = std::make_unique<std::vector<MapElemLink> >();
linkVector->push_back(MapElemLink(*m, "uno"));
MapElemLink mLink;
mLink.toContainedElement(*m, (*m)["due"]);
......
......@@ -9,7 +9,7 @@
* @file WriteDataReentrant.h
* @author scott snyder <snyder@bnl.gov>
* @date Jan, 2016
* @brief
* @brief Testing reentrant algorithms.
*/
......
......@@ -227,6 +227,8 @@ namespace SG {
/**
* @brief Retrieve and cache all information managed by a handle.
* @param used If false, then this handle is not to be used.
* Instead of normal initialization, the key will be cleared.
*
* This will retrieve and cache the associated @c DataProxy.
*
......@@ -234,6 +236,7 @@ namespace SG {
* the proxy may not exist. We return Success in that case; however,
* @c isInitialized will still return false.
*/
StatusCode initialize (bool used);
StatusCode initialize();
......
......@@ -105,9 +105,13 @@ public:
/**
* @brief If this object is used as a property, then this should be called
* during the initialize phase. It will fail if the requested StoreGate
* service cannot be found or if the key is blank.
* during the initialize phase. It will fail if the requested
* StoreGate service cannot be found or if the key is blank.
*
* @param used If false, then this handle is not to be used.
* Instead of normal initialization, the key will be cleared.
*/
StatusCode initialize (bool used);
StatusCode initialize();
......
Initializing Gaudi ApplicationMgr using job opts ../share/VarHandleBase_test.txt
JobOptionsSvc INFO # =======> /home/sss/nobackup/atlas/build/../tests/../share/VarHandleBase_test.txt
JobOptionsSvc INFO # =======> /afs/cern.ch/user/s/ssnyder/atlas-work3/Control/StoreGate/share/../share/VarHandleBase_test.txt
JobOptionsSvc INFO # (1,1): ApplicationMgr.ExtSvc = ["StoreGateSvc/OtherStore"]
JobOptionsSvc INFO # (2,1): OtherStore.ProxyProviderSvc = ""
JobOptionsSvc INFO Job options successfully read in from ../share/VarHandleBase_test.txt
ApplicationMgr SUCCESS
====================================================================================================================================
Welcome to ApplicationMgr (GaudiCoreSvc v27r1p99)
running on karma on Wed Feb 15 21:44:17 2017
Welcome to ApplicationMgr (GaudiCoreSvc v28r1)
running on lxplus050.cern.ch on Fri Mar 17 15:08:28 2017
====================================================================================================================================
ApplicationMgr INFO Application Manager Configured successfully
ClassIDSvc INFO getRegistryEntries: read 277 CLIDRegistry entries for module ALL
ClassIDSvc INFO getRegistryEntries: read 216 CLIDRegistry entries for module ALL
ClassIDSvc ERROR uncheckedSetTypePackageForID: PyAnalysisExamples-00-00-00 can not set CLID <86839352> for type name MyObj: Known CLID for this name <293847295> It was set by StoreGate-00-00-00
EventLoopMgr WARNING Unable to locate service "EventSelector"
EventLoopMgr WARNING No events will be processed from external input.
......@@ -22,10 +22,10 @@ test1
ClassIDSvc INFO getRegistryEntries: read 867 CLIDRegistry entries for module ALL
ServiceManager FATAL No Service factory for BazSvc available.
VarHandleKey.Se... ERROR ServiceLocatorHelper::service: can not locate service BazSvc
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize()): code 0: m_storeHandle.retrieve()
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize(bool)): code 0: m_storeHandle.retrieve()
ServiceManager FATAL No Service factory for BazSvc available.
VarHandleKey.Se... ERROR ServiceLocatorHelper::service: can not locate service BazSvc
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize()): code 0: m_storeHandle.retrieve()
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize(bool)): code 0: m_storeHandle.retrieve()
test2
test3
VarHandle(FooSv...WARNING FILE:LINE (void*SG::VarHandleBase::typeless_dataPointer_impl(bool)): could not get proxy for key foox
......@@ -36,7 +36,7 @@ VarHandle(Store... ERROR FILE:LINE (const void*SG::VarHandleBase::get_impl(cons
test4
ServiceManager FATAL No Service factory for BazSvc available.
VarHandleKey.Se... ERROR ServiceLocatorHelper::service: can not locate service BazSvc
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize()): code 0: m_storeHandle.retrieve()
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize(bool)): code 0: m_storeHandle.retrieve()
ServiceManager FATAL No Service factory for BazSvc available.
VarHandleKey.Se... ERROR ServiceLocatorHelper::service: can not locate service BazSvc
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize()): code 0: m_storeHandle.retrieve()
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize(bool)): code 0: m_storeHandle.retrieve()
Initializing Gaudi ApplicationMgr using job opts ../share/VarHandleBase_test.txt
JobOptionsSvc INFO # =======> /home/sss/nobackup/atlas/build/../tests/../share/VarHandleBase_test.txt
JobOptionsSvc INFO # =======> /afs/cern.ch/user/s/ssnyder/atlas-work3/Control/StoreGate/share/../share/VarHandleBase_test.txt
JobOptionsSvc INFO # (1,1): ApplicationMgr.ExtSvc = ["StoreGateSvc/OtherStore"]
JobOptionsSvc INFO # (2,1): OtherStore.ProxyProviderSvc = ""
JobOptionsSvc INFO Job options successfully read in from ../share/VarHandleBase_test.txt
ApplicationMgr SUCCESS
====================================================================================================================================
Welcome to ApplicationMgr (GaudiCoreSvc v27r1p99)
running on karma on Mon Feb 6 22:46:45 2017
Welcome to ApplicationMgr (GaudiCoreSvc v28r1)
running on lxplus050.cern.ch on Fri Mar 17 15:25:16 2017
====================================================================================================================================
ApplicationMgr INFO Application Manager Configured successfully
ClassIDSvc INFO getRegistryEntries: read 276 CLIDRegistry entries for module ALL
ClassIDSvc INFO getRegistryEntries: read 217 CLIDRegistry entries for module ALL
ClassIDSvc ERROR uncheckedSetTypePackageForID: PyAnalysisExamples-00-00-00 can not set CLID <86839352> for type name MyObj: Known CLID for this name <293847295> It was set by StoreGate-00-00-00
EventLoopMgr WARNING Unable to locate service "EventSelector"
EventLoopMgr WARNING No events will be processed from external input.
HistogramPersis...WARNING Histograms saving not required.
......@@ -21,29 +22,29 @@ test1
ClassIDSvc INFO getRegistryEntries: read 867 CLIDRegistry entries for module ALL
ServiceManager FATAL No Service factory for BazSvc available.
VarHandleKey.Se... ERROR ServiceLocatorHelper::service: can not locate service BazSvc
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize()): code 0: m_storeHandle.retrieve()
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize(bool)): code 0: m_storeHandle.retrieve()
ServiceManager FATAL No Service factory for BazSvc available.
VarHandleKey.Se... ERROR ServiceLocatorHelper::service: can not locate service BazSvc
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize()): code 0: m_storeHandle.retrieve()
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize(bool)): code 0: m_storeHandle.retrieve()
test2
test3
test4
ServiceManager FATAL No Service factory for FooSvc available.
VarHandleKey.Se... ERROR ServiceLocatorHelper::service: can not locate service FooSvc
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize()): code 0: m_storeHandle.retrieve()
VarHandle(FooSv... FATAL FILE:LINE (StatusCode SG::VarHandleBase::initialize()): code 0: VarHandleKey::initialize()
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize(bool)): code 0: m_storeHandle.retrieve()
VarHandle(FooSv... FATAL FILE:LINE (StatusCode SG::VarHandleBase::initialize(bool)): code 0: VarHandleKey::initialize()
ERROR FILE:LINE (StatusCode SG::VarHandleKey::initialize(bool)): code 0: Cannot initialize a Read/Write/Update handle with a null key.
VarHandle(FooSv... FATAL FILE:LINE (StatusCode SG::VarHandleBase::initialize(bool)): code 0: VarHandleKey::initialize()
test5
ServiceManager FATAL No Service factory for FooSvc available.
VarHandleKey.Se... ERROR ServiceLocatorHelper::service: can not locate service FooSvc
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize()): code 0: m_storeHandle.retrieve()
VarHandle(FooSv... FATAL FILE:LINE (StatusCode SG::VarHandleBase::initialize()): code 0: VarHandleKey::initialize()
VarHandle(FooSv...WARNING FILE:LINE (void*SG::VarHandleBase::typeless_dataPointer_impl(bool)): could not get proxy for key foo
VarHandle(FooSv...WARNING FILE:LINE (void*SG::VarHandleBase::typeless_dataPointer_impl(bool)): try using a ReadHandle
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize(bool)): code 0: m_storeHandle.retrieve()
VarHandle(FooSv... FATAL FILE:LINE (StatusCode SG::VarHandleBase::initialize(bool)): code 0: VarHandleKey::initialize()
test6
test7
ServiceManager FATAL No Service factory for FooSvc available.
VarHandleKey.Se... ERROR ServiceLocatorHelper::service: can not locate service FooSvc
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize()): code 0: m_storeHandle.retrieve()
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize(bool)): code 0: m_storeHandle.retrieve()
VarHandle(FooSv... FATAL FILE:LINE (StatusCode SG::VarHandleBase::record_impl(std::unique_ptr<DataObject>, void*, bool, bool)): code 0: VarHandleKey::initialize()
VarHandle(FooSv... ERROR FILE:LINE (StatusCode SG::VarHandleBase::record_impl(std::unique_ptr<DataObject>, void*, bool, bool)): code 0: recordObject failed
VarHandle(FooSv... ERROR FILE:LINE (StatusCode SG::VarHandleBase::record_impl(std::unique_ptr<DataObject>, void*, bool, bool)): code 0: Attempt to record an object with a null key
......@@ -53,7 +54,7 @@ VarHandle(FooSv...WARNING FILE:LINE (void*SG::VarHandleBase::typeless_dataPointe
VarHandle(FooSv...WARNING FILE:LINE (void*SG::VarHandleBase::typeless_dataPointer_fromProxy(SG::DataProxy*, bool) const): Proxy [293847295/foo] is in an invalid state
VarHandle(FooSv...WARNING FILE:LINE (void*SG::VarHandleBase::typeless_dataPointer_fromProxy(SG::DataProxy*, bool) const): Request for an invalid object; requested CLID = 293847295, proxy primary ID is 293847296
test9
VarHandleBase @0x7fffbb5654d0 store=FooSvc, clid=293847295, key=foo----------- ptr@0, proxy@0
VarHandleBase @0x7ffd1bdcb870 store=FooSvc, clid=293847295, key=foo----------- ptr@0, proxy@0
test10
ServiceManager FATAL No Service factory for FooSvc available.
VarHandleKey.Se... ERROR ServiceLocatorHelper::service: can not locate service FooSvc
......
ApplicationMgr SUCCESS
====================================================================================================================================
Welcome to ApplicationMgr (GaudiCoreSvc v27r1p99)
running on karma on Mon Feb 6 22:54:18 2017
Welcome to ApplicationMgr (GaudiCoreSvc v28r1)
running on lxplus050.cern.ch on Fri Mar 17 15:15:13 2017
====================================================================================================================================
ApplicationMgr INFO Application Manager Configured successfully
EventLoopMgr WARNING Unable to locate service "EventSelector"
......@@ -10,9 +10,9 @@ HistogramPersis...WARNING Histograms saving not required.
ApplicationMgr INFO Application Manager Initialized successfully
ApplicationMgr Ready
test1
ClassIDSvc INFO getRegistryEntries: read 223 CLIDRegistry entries for module ALL
ClassIDSvc INFO getRegistryEntries: read 164 CLIDRegistry entries for module ALL
ClassIDSvc INFO getRegistryEntries: read 869 CLIDRegistry entries for module ALL
ServiceManager FATAL No Service factory for FooSvc available.
VarHandleKey.Se... ERROR ServiceLocatorHelper::service: can not locate service FooSvc
FATAL ../src/VarHandleKey.cxx:100 (StatusCode SG::VarHandleKey::initialize()): code 0: m_storeHandle.retrieve()
ERROR ../src/VarHandleKey.cxx:96 (StatusCode SG::VarHandleKey::initialize()): code 0: Cannot initialize a Read/Write/Update handle with a null key.
FATAL FILE:LINE (StatusCode SG::VarHandleKey::initialize(bool)): code 0: m_storeHandle.retrieve()
ERROR FILE:LINE (StatusCode SG::VarHandleKey::initialize(bool)): code 0: Cannot initialize a Read/Write/Update handle with a null key.
......@@ -377,6 +377,8 @@ namespace SG {
/**
* @brief Retrieve and cache all information managed by a handle.
* @param used If false, then this handle is not to be used.
* Instead of normal initialization, the key will be cleared.
*
* This will retrieve and cache the associated @c DataProxy.
*
......@@ -385,8 +387,13 @@ namespace SG {
* @c isInitialized will still return false.
*/
StatusCode
VarHandleBase::initialize()
VarHandleBase::initialize (bool used /*= true*/)
{
if (!used) {
CHECK( VarHandleKey::initialize (used) );
return StatusCode::SUCCESS;
}
if (!m_store) {
CHECK( VarHandleKey::initialize() );
m_store = &*(this->storeHandle());
......@@ -405,6 +412,7 @@ namespace SG {
return sc;
}
StatusCode VarHandleBase::initialize() { return initialize(true); } // temp
/**
......
......@@ -7,7 +7,7 @@
* @file StoreGate/src/VarHandleKey.cxx
* @author scott snyder <snyder@bnl.gov>
* @date Jan, 2016
* @brief
* @brief A property holding a SG store/key/clid from which a VarHandle is made.
*/
......@@ -87,11 +87,19 @@ StatusCode VarHandleKey::assign (const std::string& sgkey)
/**
* @brief If this object is used as a property, then this should be called
* during the initialize phase. It will fail if the requested StoreGate
* service cannot be found or if the key is blank.
* during the initialize phase. It will fail if the requested
* StoreGate service cannot be found or if the key is blank.
*
* @param used If false, then this handle is not to be used.
* Instead of normal initialization, the key will be cleared.
*/
StatusCode VarHandleKey::initialize()
StatusCode VarHandleKey::initialize (bool used /*= true*/)
{
if (!used) {
Gaudi::DataHandle::updateKey ("");
return StatusCode::SUCCESS;
}
if (Gaudi::DataHandle::objKey() == "") {
REPORT_ERROR (StatusCode::FAILURE)
<< "Cannot initialize a Read/Write/Update handle with a null key.";
......@@ -100,6 +108,7 @@ StatusCode VarHandleKey::initialize()
CHECK( m_storeHandle.retrieve() );
return StatusCode::SUCCESS;
}
StatusCode VarHandleKey::initialize() { return initialize(true); } // temp
/**
......
......@@ -314,6 +314,15 @@ void test4()
h2.finalReset();
assert (!h2.isInitialized());
assert (h2.m_store == 0);
TestHandle h3 (293847295, "", Gaudi::DataHandle::Writer, "FooSvc");
assert (h3.initialize().isFailure());
assert (h3.initialize(false).isSuccess());
TestHandle h4 (293847295, "foo", Gaudi::DataHandle::Writer, "FooSvc");
assert (h4.key() == "foo");
assert (h4.initialize(false).isSuccess());
assert (h4.key() == "");
}
......
......@@ -14,6 +14,7 @@
#undef NDEBUG
#include "StoreGate/VarHandleKey.h"
#include "StoreGate/exceptions.h"
#include "AthenaKernel/errorcheck.h"
#include "TestTools/initGaudi.h"
#include "TestTools/expect_exception.h"
#include <cassert>
......@@ -81,14 +82,21 @@ void test1()
assert (k4.storeHandle().name() == "StoreGateSvc");
assert (!k4.storeHandle().isSet());
assert (k4.initialize().isFailure());
assert (k4.initialize(false).isSuccess());
EXPECT_EXCEPTION (SG::ExcBadHandleKey,
SG::VarHandleKey (1237, "a/b/c", Gaudi::DataHandle::Updater));
SG::VarHandleKey k5 (1236, "BarSvc/ccc", Gaudi::DataHandle::Updater, "FooSvc");
assert (k5.key() == "ccc");
assert (k5.initialize(false).isSuccess());
assert (k5.key() == "");
}
int main()
{
errorcheck::ReportMessage::hideErrorLocus();
ISvcLocator* pDum;
Athena_test::initGaudi(pDum); //need MessageSvc
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment