ATLAS Offline Software
Loading...
Searching...
No Matches
DataProxy.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5
6#include "SGTools/DataProxy.h"
10
11#include "GaudiKernel/DataObject.h"
12#include "GaudiKernel/IConverter.h"
13#include "GaudiKernel/GenericAddress.h"
14#include "GaudiKernel/MsgStream.h"
15#include "GaudiKernel/EventContext.h"
16#include "GaudiKernel/GaudiException.h"
17
19#include "SGTools/T2pMap.h"
24#include <algorithm>
25#include <cassert>
26#include <print>
27#include <stdexcept>
28
29using SG::DataProxy;
31using std::find;
32
33
34namespace SG {
35 typedef IProxyDict** getDataSourcePointerFunc_t (const std::string&);
37
38 class DataProxyHolder
39 {
40 public:
41 static void resetCachedSource();
42 };
43}
44
45namespace {
46
47class PushStore
48{
49public:
50 PushStore (IProxyDict* store)
51 {
52 static std::string storeName = "StoreGateSvc";
53 m_storePtr = (*SG::getDataSourcePointerFunc) (storeName);
54 m_store = *m_storePtr;
55 if (store && store != m_store) {
56 *m_storePtr = store;
58 }
59 }
60
61 ~PushStore()
62 {
63 if (*m_storePtr != m_store) {
64 *m_storePtr = m_store;
66 }
67 }
68
69private:
70 IProxyDict** m_storePtr;
71 IProxyDict* m_store;
72};
73
74}
75
76
77namespace {
79 template <class GAUDIREF>
80 void setGaudiRef(GAUDIREF* pgref, GAUDIREF*& pMember) {
81 if (0 != pgref) pgref->addRef();
82 if (0 != pMember) pMember->release();
83 pMember = pgref;
84 }
85
87 template <class GAUDIREF>
88 void resetGaudiRef(GAUDIREF*& pMember) { setGaudiRef((GAUDIREF*)0, pMember); }
89
90} //end of unnamed namespace
91
92// Default Constructor
96
97// DataProxy constructor with Transient Address
98// (typically called from Proxy Provider)
100 IConverter* svc,
101 bool constFlag, bool resetOnly)
102 : DataProxy (std::move(*tAddr),
103 svc, constFlag, resetOnly)
104{
105 delete tAddr;
106}
107
108// DataProxy constructor with Transient Address
109// (typically called from Proxy Provider)
110DataProxy::DataProxy(std::unique_ptr<TransientAddress> tAddr,
111 IConverter* svc,
112 bool constFlag, bool resetOnly)
113 : DataProxy (std::move(*tAddr), svc, constFlag, resetOnly)
114{
115 //assert( tAddr->clID() != 0 );
116 if (svc) svc->addRef();
117}
118
120 IConverter* svc,
121 bool constFlag, bool resetOnly):
123 m_const(constFlag),
124 m_origConst(constFlag),
125 m_tAddress(std::move(tAddr)),
126 m_dataLoader(svc)
127{
128 //assert( tAddr->clID() != 0 );
129 if (svc) svc->addRef();
130}
131
132// with Data Object:
133// (typically called from a StoreGate record
134DataProxy::DataProxy(DataObject* dObject,
135 TransientAddress* tAddr,
136 bool constFlag, bool resetOnly):
138 m_const(constFlag),
139 m_origConst(constFlag),
140 m_tAddress(std::move(*tAddr))
141{
142 setObject(dObject);
143 delete tAddr;
144}
145
146DataProxy::DataProxy(DataObject* dObject,
147 TransientAddress&& tAddr,
148 bool constFlag, bool resetOnly):
150 m_const(constFlag),
151 m_origConst(constFlag),
152 m_tAddress(std::move(tAddr))
153{
154 setObject(dObject);
155}
156
157// Destructor
162
164{
166 m_t2p = t2p;
167}
168
169
177{
178 objLock_t objLock (m_objMutex);
180 if (!m_const) {
181 m_const = true;
182 this->lock (objLock, m_dObject);
183 }
184}
185
187 assert(ir);
189 if (ir->isSet()) {
190 return false;
191 } else {
192 m_handles.push_back(ir);
193 m_boundHandles = true;
194 if (IProxyDict* store = m_store)
195 store->boundHandle(ir);
196 return true;
197 }
198}
199
200
202inline
204{
205 // Skip calling resetGaudiRef for the case where the proxy has never
206 // been defererenced.
207 if (m_dObject) {
208 DataObject* dobj = m_dObject;
209 resetGaudiRef(dobj);
210 m_dObject = dobj;
211 }
212 m_tAddress.reset();
214}
215
216
217void DataProxy::reset (bool hard /*= false*/)
218{
219 resetBoundHandles (hard);
220
221 objLock_t objLock (m_objMutex);
223 resetRef();
224}
225
226
228{
229 handleList_t handles;
230 {
231 objLock_t objLock (m_objMutex);
233 m_const=false; //hack to force the resetting of proxy ptr in VarHandleBase
234
235 handles = m_handles;
236
237 DataObject* dobj = m_dObject;
238 resetGaudiRef(dobj);
239 m_dObject = dobj;
240 resetGaudiRef(m_dataLoader);
241
242 if (m_handles.empty()) {
243 m_boundHandles = false;
244 }
245 }
246
247 for (auto ih: handles) {
248 if (0 != ih) ih->finalReset();
249 }
250}
251
254 handleList_t handles;
255 {
257 // Early exit if the list is empty.
258 if (!m_boundHandles) return;
259
260 // Make a copy and drop the lock, so we're not holding the lock
261 // during the callback.
262 handles = m_handles;
263 }
264
265 for (IResetable* h : handles) {
266 h->reset(hard);
267 }
268}
269
271 assert(ir);
273 auto ifr = find(m_handles.begin(), m_handles.end(), ir );
274 if (ifr != m_handles.end()) {
275 m_handles.erase(ifr);
276 if (IProxyDict* store = m_store)
277 store->unboundHandle(ir);
278 }
279 m_boundHandles = !m_handles.empty();
280}
281
283unsigned long DataProxy::refCount() const
284{
286 return m_refCount;
287}
288
290unsigned long DataProxy::addRef()
291{
293 return ++m_refCount;
294}
295
297unsigned long DataProxy::release()
298{
299 unsigned long count;
300 {
302 count = --m_refCount;
303 }
304 if ( 0 == count ) delete this;
305 return count;
306}
307
308
330bool DataProxy::requestRelease(bool force, bool hard) {
331
332 if (m_boundHandles) {
333 resetBoundHandles(hard);
334 }
335 bool canRelease = force;
336 if (!m_resetFlag) canRelease = true;
337#if 0
338 MsgStream gLog(m_ims, "DataProxy");
339 if (gLog.level() <= MSG::VERBOSE) {
340 gLog << MSG::VERBOSE << "requestRelease(): "
341 << (canRelease ? " release " : " reset")
342 <<" object "
343 << name() << " CLID " << clID() << " address " << MSG::hex
344 << object() << MSG::dec << endmsg;
345 }
346#endif
347 if (!canRelease) {
348 resetRef();
349 }
350 return canRelease;
351}
352
356void DataProxy::setObject(objLock_t& objLock, DataObject* dObject, bool doreg)
357{
358 DataObject* dobj = m_dObject.exchange (nullptr);
359 std::atomic_thread_fence (std::memory_order_seq_cst);
360 setGaudiRef(dObject, dobj);
361 if (0 != dobj) {
362 if (doreg) dobj->setRegistry(this);
363 if (m_const) this->lock (objLock, dobj);
364 }
365 // Be sure the settings above happen before we set m_dObject.
366 // Otherwise other threads may be able to see the object before those
367 // settings have been made.
368 std::atomic_thread_fence (std::memory_order_seq_cst);
369 m_dObject = dobj;
370}
371
372
374void DataProxy::setObject(DataObject* dObject, bool doreg /*= true*/)
375{
376 objLock_t objLock (m_objMutex);
377 setObject (objLock, dObject, doreg);
378}
379
380
381// set IOpaqueAddress
382void DataProxy::setAddress(IOpaqueAddress* address)
383{
385 m_tAddress.setAddress(address);
386}
387
388// set IOpaqueAddress
394
395
396
409std::unique_ptr<DataObject> DataProxy::readData()
410{
411 // Public wrapper for readData().
412 objLock_t objLock (m_objMutex);
413 return readData (objLock, nullptr);
414}
415
416
428std::unique_ptr<DataObject> DataProxy::readData (objLock_t&, ErrNo* errNo)
429{
430 if (errNo) {
431 if (*errNo == RECURSIVEREAD) {
432 // This can end up being called from noexcept functions.
433 // Creating a MsgStream can throw an exception --- don't let
434 // it escape.
435 try {
436 MsgStream gLog(m_ims, "DataProxy");
437 gLog << MSG::ERROR
438 << "readData: ERROR recursive read for object"
439 <<m_tAddress.clID() << '/' << m_tAddress.name() << '\n'
440 <<" Returning NULL DataObject pointer " << endmsg;
441 }
442 catch (const GaudiException&) {
443 std::println (std::cerr, "DataProxy::readData: Problem creating MsgStream");
444 }
445 return nullptr;
446 }
448 }
449
450 IConverter* dataLoader;
452 IOpaqueAddress* address;
453 {
455 if (0 == m_dataLoader) {
456 //MsgStream gLog(m_ims, "DataProxy");
457 //gLog << MSG::WARNING
458 // << "accessData: IConverter ptr not set" <<endmsg;
459 if (errNo) *errNo=NOCNVSVC;
460 return nullptr;
461 }
462
463 dataLoader = m_dataLoader;
464 store = m_store;
465 address = m_tAddress.address();
466 }
467
468 if (!isValidAddress()) {
469 //MsgStream gLog(m_ims, "DataProxy");
470 //gLog << MSG::WARNING
471 // << "accessData: IOA pointer not set" <<endmsg;
472 if (errNo) *errNo=NOIOA;
473 return nullptr;
474 }
475
476 SG::CurrentEventStore::Push push (store);
477
478 DataObject* obj = nullptr;
479 StatusCode sc;
480 if (store)
481 sc = store->createObj (dataLoader, address, obj);
482 else
483 sc = dataLoader->createObj (address, obj);
484 if (sc.isSuccess()) {
485 if (errNo && *errNo == RECURSIVEREAD) *errNo = ALLOK;
486 return std::unique_ptr<DataObject>(obj);
487 }
488 if (!address) {
489 MsgStream gLog(m_ims, "DataProxy");
490 gLog << MSG::ERROR
491 << "readData: no address for " << clID() << "/" << name()
492 << " but validAddress returned true. You may be trying to retrieve"
493 << " alignments during initialization without having defined a campaign."
494 << endmsg;
495 }
496 if (errNo) *errNo = CNVFAILED;
497 return nullptr;
498}
499
500
503{
504 // This is done in the inlined accessData().
505 //if (0 != m_dObject) return m_dObject; // cached object
506
507 objLock_t objLock (m_objMutex);
508 // Check again after acquiring the lock.
509 if (0 != m_dObject) return m_dObject; // cached object
510
511 if (isValidAddress()) {
512 // An address provider called by isValidAddress may have set the object
513 // pointer directly, rather than filling in the address. So check
514 // the cached object pointer again.
515 if (0 != m_dObject) return m_dObject; // cached object
516 }
517
518 std::unique_ptr<DataObject> obju = readData (objLock, &m_errno);
519 if (!obju) {
520 if (m_errno == NOIOA) {
521 MsgStream gLog(m_ims, "DataProxy");
522 gLog << MSG::WARNING
523 << "accessData: IOA pointer not set" <<endmsg;
524 }
525 else if (m_errno == CNVFAILED) {
526 MsgStream gLog(m_ims, "DataProxy");
527 gLog << MSG::WARNING
528 << "accessData: conversion failed for data object "
529 <<m_tAddress.clID() << '/' << m_tAddress.name() << '\n'
530 <<" Returning NULL DataObject pointer " << endmsg;
531 }
532 setObject(objLock, 0, true);
533 return 0;
534 }
535
536 DataBucketBase* bucket = dynamic_cast<DataBucketBase*>(obju.get());
537 if (m_t2p) {
538 if (bucket) {
539 void* payload = bucket->object();
540 m_t2p->t2pRegister(payload, this);
541 m_errno=ALLOK;
542
543 // Register bases as well.
545 if (bi) {
546 std::vector<CLID> base_clids = bi->get_bases();
547 for (unsigned i=0; i < base_clids.size(); ++i) {
548 // nb. DataProxy_cast here will give an infinite recursion!
549 void* bobj = SG::Storable_cast (obju.get(), base_clids[i], nullptr, true);
550 if (bobj && bobj != payload)
551 m_t2p->t2pRegister (bobj, this);
552 }
553 }
554 }
555 else {
556 MsgStream gLog(m_ims, "DataProxy");
557 gLog << MSG::ERROR
558 << "accessData: ERROR registering object in t2p map"
559 <<m_tAddress.clID() << '/' << m_tAddress.name() << '\n'
560 <<" Returning NULL DataObject pointer " << endmsg;
561 obju.reset();
562 m_errno=T2PREGFAILED;
563 }
564 }
565
566 // Must get everything else set up before setting m_dObject, because
567 // once we set it, it's immediately visible to other threads.
568 // In particular, we were previously doing this before the t2p registration.
569 // This meant that another thread could retrieve the pointer from
570 // the proxy, but then fail trying to map the pointer back to the proxy.
571 // See ATEAM-1126.
572 std::atomic_thread_fence (std::memory_order_seq_cst);
573 setObject(objLock, obju.release(), true);
574
575 return m_dObject;
576}
577
578
579/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
581{
582 // Looking up the context is relatively expensive.
583 // So first try isValid() without the context.
584 {
586 if (const_cast<DataProxy*>(this)->m_tAddress.isValid(nullptr)) {
587 return true;
588 }
589 }
590 // Get the context. (Must not be holding m_mutex here.)
591 const EventContext& ctx = contextFromStore();
592 // Try again with the context.
594 return const_cast<DataProxy*>(this)->m_tAddress.isValid(&ctx);
595}
596
598{
599 // Be sure to get the context before acquiring the lock.
600 const EventContext& ctx = contextFromStore();
602 return m_tAddress.isValid(&ctx, true);
603}
604
614void* SG::DataProxy_cast (SG::DataProxy* proxy, CLID clid, const std::type_info* tinfo /*= nullptr*/)
615{
616 if (nullptr == proxy || !proxy->isValid()) {
617 return nullptr;
618 }
619
620 DataObject* pObject = proxy->accessData();
621 if (nullptr == pObject) {
622#ifndef NDEBUG
623 MsgStream gLog(Athena::getMessageSvc(), "SG::DataProxy_cast");
624 gLog << MSG::WARNING
625 << "this proxy " << MSG::hex << proxy
626 << MSG::dec << " has a NULL data object ptr" << endmsg;
627#endif
628 return nullptr;
629 }
630
631 void* result = SG::Storable_cast (pObject, clid, tinfo, true, proxy, proxy->isConst());
632 if (nullptr == result) {
633 //if result is null, probably CLID is neither the type the object was
634 // stored with, nor it inherits from it.
635 // Before giving up let's check its transient CLIDs
636 DataBucketBase* db(0);
637 if (proxy->transientID(clid) &&
638 nullptr != (db = dynamic_cast<DataBucketBase*>(pObject)) )
639 {
640 //it is a symlink after all. Let's hard cast and keep our fingers Xed
641 // But first: if this is a non-const proxy, then the cast
642 // may have failed because it needed to go via a copying conversion
643 // that's not allowed for non-const objects. So try the conversion
644 // again as const; if that works, then don't do the hard cast.
645 if (!proxy->isConst() &&
646 SG::Storable_cast(pObject, clid, tinfo, true, proxy, true) != nullptr)
647 {
648#ifndef NDEBUG
649 MsgStream gLog(Athena::getMessageSvc(), "SG::DataProxy_cast");
650 gLog << MSG::WARNING
651 << "Request for a non-const object via copying conversion; "
652 << "requested CLID = " << clid
653 << ", proxy primary ID is " << proxy->clID() << endmsg ;
654#endif
655 }
656 else {
657 // ok, hard cast.
658 result = db->object();
659 }
660 }
661 else {
662#ifndef NDEBUG
663 MsgStream gLog(Athena::getMessageSvc(), "SG::DataProxy_cast");
664 gLog << MSG::WARNING
665 << "Request for an invalid object; requested CLID = "
666 << clid
667 << ", proxy primary ID is " << proxy->clID() << endmsg ;
668#endif
669 } //try symlink
670 } //result 0
671
672 return result;
673}
674
675
681{
683 if (m_t2p)
684 m_t2p->t2pRegister (p, this);
685}
686
687
693void DataProxy::lock (objLock_t&, DataObject* dobj)
694{
695 DataBucketBase* bucket = dynamic_cast<DataBucketBase*>(dobj);
696 if (bucket)
697 bucket->lock();
698}
699
700
710const EventContext& DataProxy::contextFromStore() const
711{
713 if (store) {
714 static const SG::sgkey_t ctxkey =
715 store->stringToKey ("EventContext", ClassID_traits<EventContext>::ID());
716 SG::DataProxy* proxy = store->proxy_exact (ctxkey);
717 if (proxy && proxy->object()) {
718 EventContext* ctx = SG::DataProxy_cast<EventContext> (proxy);
719 if (ctx) return *ctx;
720 }
721 }
722 static const EventContext emptyContext;
723 return emptyContext;
724}
#define endmsg
Hold a pointer to the current event store.
Helpers for retrieving the payload held by a DataProxy.
Assign a CLID to EventContext.
uint32_t CLID
The Class ID type.
static Double_t sc
Header file for AthHistogramAlgorithm.
Simple smart pointer for Gaudi-style refcounted objects.
A non-templated base class for DataBucket, allows to access the transient object address as a void*.
virtual void lock()=0
If the held object derives from ILockable, call lock() on it.
virtual void * object()=0
a resetable object (e.g.
Definition IResetable.h:15
The non-template portion of the BaseInfo implementation.
static const BaseInfoBase * find(CLID clid)
Find the BaseInfoBase instance for clid.
Definition BaseInfo.cxx:587
const std::vector< CLID > & get_bases() const
Return the class IDs of all known bases of T (that have class IDs).
Definition BaseInfo.cxx:307
Manage DataProxy reference in ElementLink/DataLink.
static void resetCachedSource()
CLID clID() const
Retrieve clid.
void resetBoundHandles(bool hard)
reset the bound DataHandles If HARD is true, then the bound objects should also clear any data that d...
std::lock_guard< objMutex_t > objLock_t
Definition DataProxy.h:355
handleList_t m_handles
Definition DataProxy.h:333
virtual const name_type & name() const override final
Retrieve data object key == string.
unsigned int m_refCount
Definition DataProxy.h:306
ErrNo errNo() const
std::vector< IResetable * > handleList_t
list of bound DataHandles
Definition DataProxy.h:332
IConverter * m_dataLoader
Definition DataProxy.h:327
void setConst()
Mark this object as const.
DataObject * accessDataOol()
Out-of-line part of accessData().
bool requestRelease(bool force, bool hard)
Reset/release a proxy at the end of an event.
virtual void setAddress(IOpaqueAddress *ioa) override final
set an IOpaqueAddress
std::unique_ptr< DataObject > readData()
Read in a new copy of the object referenced by this proxy.
bool m_resetFlag
reset and not delete: default is true
Definition DataProxy.h:309
bool m_boundHandles
True if there are any bound handles.
Definition DataProxy.h:316
virtual void registerTransient(void *p) override final
Register a transient object in a t2p map.
virtual IOpaqueAddress * address() const override final
Retrieve IOpaqueAddress.
virtual unsigned long release() override final
release reference to object
void finalReset()
void reset(bool hard=false)
Other methods of DataProxy (not in Interface IRegistry):
void setObject(DataObject *obj, bool doreg=true)
set DataObject If doreg is true, then call setRegistry to set the backpointer from obj to the proxt.
IProxyDict * store()
Return the store of which we're a part.
bool isValidAddress() const
is the address valid?
void resetRef()
Drop the reference to the data object.
TransientAddress m_tAddress
Definition DataProxy.h:325
void resetOnly(const bool &flag)
set the reset only flag: Clear Store will reset and not delete.
void lock(objLock_t &, DataObject *dobj)
Lock the data object we're holding, if any.
std::atomic< IProxyDict * > m_store
The store of which we are a part.
Definition DataProxy.h:347
Athena::IMessageSvcHolder m_ims
Definition DataProxy.h:344
std::atomic< DataObject * > m_dObject
Definition DataProxy.h:323
void setT2p(T2pMap *t2p)
void unbindHandle(IResetable *ir)
std::atomic< bool > m_const
Is the proxy currently const?
Definition DataProxy.h:319
unsigned long refCount() const
return refCount
virtual ~DataProxy()
bool m_origConst
Was the proxy created as const?
Definition DataProxy.h:321
bool bindHandle(IResetable *ir)
const EventContext & contextFromStore() const
Retrieve the EventContext saved in the owning store.
objMutex_t m_objMutex
Definition DataProxy.h:356
bool updateAddress()
std::lock_guard< mutex_t > lock_t
Definition DataProxy.h:341
mutex_t m_mutex
Definition DataProxy.h:342
virtual unsigned long addRef() override final
Add reference to object.
T2pMap * m_t2p
Definition DataProxy.h:329
int ir
counter of the current depth
Definition fastadd.cxx:49
singleton-like access to IMessageSvc via open function and helper
std::string find(const std::string &s)
return a remapped string
Definition hcg.cxx:140
int count(std::string s, const std::string &regx)
count how many occurances of a regx are in a string
Definition hcg.cxx:148
IMessageSvc * getMessageSvc(bool quiet=false)
Forward declaration.
DATA * DataProxy_cast(DataProxy *proxy)
cast the proxy into the concrete data object it proxies
T * Storable_cast(DataObject *pDObj, bool quiet=true, IRegisterTransient *irt=0, bool isConst=true)
uint32_t sgkey_t
Type used for hashed StoreGate key+CLID pairs.
Definition sgkey_t.h:32
IProxyDict ** getDataSourcePointerFunc_t(const std::string &)
Definition DataProxy.cxx:35
getDataSourcePointerFunc_t * getDataSourcePointerFunc
virtual void lock() override
Lock the container.
STL namespace.