ATLAS Offline Software
Loading...
Searching...
No Matches
IdentifiableCacheBase.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// $Id: IdentifiableCacheBase.cxx 791541 2017-01-09 10:43:53Z smh $
12
13// null --- default state.
14// ABORTED -- conversion failed or returned null
15// ptr > null && ptr < ABORTED --- Have payload
16// INVALID --- Conversion in progress or intention to add soon.
17
19#include <bit>
20
21namespace EventContainers {
22
23const void* const INVALID = std::bit_cast<const void*>(IdentifiableCacheBase::INVALIDflag);
24const void* const ABORTED = std::bit_cast<const void*>(IdentifiableCacheBase::ABORTEDflag);
25
26
27
29 const IMaker* maker)
30 : m_vec(maxHash),
31 m_maker (maker),
33{
34}
35
36
38
39int IdentifiableCacheBase::tryLock(IdentifierHash hash, IDC_WriteHandleBase &lock, std::vector<IdentifierHash> &wait){
40 const void *ptr1 =nullptr;
41
42 if(m_vec[hash].compare_exchange_strong(ptr1, INVALID, std::memory_order_relaxed, std::memory_order_relaxed)){//atomic swap (replaces ptr1 with value)
43 //First call
44 //Setup the IDC_WriteHandle to "lock" on this hash's pointer
45 lock.LockOn(&m_vec[hash]);
46 return 0;
47 }
48
49 if(ptr1 == INVALID){
50 //Second call while not finished
51 wait.emplace_back(hash);
52 return 1;
53 }
54 if(ptr1 == ABORTED) return 3;
55 return 2; //Already completed
56}
60{
61 size_t s = m_vec.size();
62 if(0 != m_currentHashes.load(std::memory_order_relaxed)){
63 for (size_t i=0; i<s ;i++) {
64 const void* ptr = m_vec[i].load(std::memory_order_relaxed);
65 m_vec[i].store(nullptr, std::memory_order_relaxed);
66 if (ptr && ptr < ABORTED){
67 deleter (ptr);
68 }
69 }
70 m_currentHashes.store(0, std::memory_order_relaxed);
71 }else{
72 for (size_t i=0; i<s ;i++) m_vec[i].store(nullptr, std::memory_order_relaxed);//Need to clear incase of aborts
73 }
74}
75
77//Does not lock or clear atomics to allow faster destruction
80 if(0 != m_currentHashes.load(std::memory_order_acquire)){ //Reduce overhead if cache was unused
81 size_t s = m_vec.size();
82 for (size_t i=0; i<s ;i++) {
83 const void* p = m_vec[i].load(std::memory_order_relaxed);
84 if(p && p < ABORTED) deleter (p);
85 }
86 }
87}
90 const void* p = m_vec[hash].load(std::memory_order_relaxed); //Relaxed because it is not returning a pointer to anything
91 return (p == ABORTED);
92}
93
94
96 const void* p = m_vec[hash].load(std::memory_order_relaxed); //Relaxed because it is not returning a pointer to anything
97 return (p == INVALID);
98}
99
100
102{
103 if (hash >= m_vec.size()) [[unlikely]] return nullptr;
104 const void* p = m_vec[hash].load(std::memory_order_acquire);
105 if (p >= ABORTED)
106 return nullptr;
107 return p;
108}
109
111{
112 std::atomic<const void*> &myatomic = m_vec[hash];
113 const void* item = myatomic.load(std::memory_order_acquire);
114 //Wait until pointer is set then retrieve and verify
115 while(item == INVALID){//Loop to check for spurious wakeups
116 myatomic.wait(item, std::memory_order_relaxed);
117 item = myatomic.load(std::memory_order_acquire);
118 }
119 return item;
120}
121
123{
124 if (hash >= m_vec.size()) [[unlikely]] return nullptr;
125 const void* p = waitFor(hash);
126 if(p>=ABORTED) return nullptr;
127 return p;
128}
129
131{
132 m_vec[hash].notify_all();
133}
134
136{
137 // If it's there already, return directly without locking.
138 const void* ptr = nullptr;
139 if (hash >= m_vec.size()) [[unlikely]] return ptr;
140
141 if(m_vec[hash].compare_exchange_strong(ptr, INVALID) ) {//Exchanges ptr with current value!!
142 // Make the payload.
143 if(m_maker == nullptr){
144 m_vec[hash].store( ABORTED );
145 return nullptr;
146 }
147 uniqueLock lock(m_mutex, std::defer_lock);
148 if(!m_maker->m_IsReEntrant) lock.lock();//Allow reentrant or non reentrant makers
149
150 try {
151 ptr = m_maker->typelessMake (hash).release();
152 }
153 catch (...) {
154 m_vec[hash].store( ABORTED );
155 notifyHash(hash);
156 throw;
157 }
158 assert(m_vec[hash] == INVALID);
159 if(ptr){
160 m_vec[hash].store( ptr );
162 }else{
163 m_vec[hash].store( ABORTED );
164 }
165 notifyHash(hash);
166 }
167 else if(ptr == INVALID){
168 ptr= waitFor(hash);
169 }
170 if(ptr == ABORTED) return nullptr;
171 assert(ptr < ABORTED);
172 return ptr;
173}
174
175void IdentifiableCacheBase::createSet (const std::vector<IdentifierHash>& hashes, std::vector<bool> &mask){
176 assert(mask.size() == fullSize());
177 for(IdentifierHash hash : hashes){
178 const void* ptr = get(hash);
179 if(ptr !=nullptr) mask[hash] = true;
180 }
181}
182
183
185{
186 return m_currentHashes.load(std::memory_order_relaxed); //Not to be used for syncing
187}
188
189std::vector<IdentifierHash> IdentifiableCacheBase::ids()
190{
191 std::vector<IdentifierHash> ret;
192 ret.reserve (m_currentHashes.load(std::memory_order_relaxed));
193 size_t s = m_vec.size();
194 for (size_t i =0; i<s; i++) {
195 const void* p = m_vec[i].load(std::memory_order_relaxed);
196 if (p && p < ABORTED)
197 ret.push_back (i);
198 }
199 return ret;
200}
201
202
203std::pair<bool, const void*> IdentifiableCacheBase::add (IdentifierHash hash, const void* p) noexcept
204{
205 if (hash >= m_vec.size()) [[unlikely]] return std::make_pair(false, nullptr);
206 if(p==nullptr) return std::make_pair(false, nullptr);
207 const void* nul=nullptr;
208 if(m_vec[hash].compare_exchange_strong(nul, p, std::memory_order_release, std::memory_order_relaxed)){
209 m_currentHashes.fetch_add(1, std::memory_order_relaxed);
210 return std::make_pair(true, p);
211 }
212 const void* invalid = INVALID;
213 if(m_vec[hash].compare_exchange_strong(invalid, p, std::memory_order_release, std::memory_order_acquire)){
214 m_currentHashes.fetch_add(1, std::memory_order_relaxed);
215 notifyHash(hash);
216 return std::make_pair(true, p);
217 }
218 return std::make_pair(false, invalid);
219}
220
221
222std::pair<bool, const void*> IdentifiableCacheBase::addLock (IdentifierHash hash, const void* p) noexcept
223{ //Same as method above except we check for invalid state first,
224 // more optimal for calling using writehandle lock method
225 assert(hash < m_vec.size());
226 if(p==nullptr) return std::make_pair(false, nullptr);
227 const void* invalid = INVALID;
228 if(m_vec[hash].compare_exchange_strong(invalid, p, std::memory_order_release, std::memory_order_relaxed)){
229 m_currentHashes.fetch_add(1, std::memory_order_relaxed);
230 notifyHash(hash);
231 return std::make_pair(true, p);
232 }
233 const void* nul=nullptr;
234 if(m_vec[hash].compare_exchange_strong(nul, p, std::memory_order_release, std::memory_order_acquire)){
235 m_currentHashes.fetch_add(1, std::memory_order_relaxed);
236 return std::make_pair(true, p);
237 }
238 return std::make_pair(false, nul);
239}
240
241std::pair<bool, const void*> IdentifiableCacheBase::addLock (IdentifierHash hash,
242 void_unique_ptr p) noexcept
243{
244 std::pair<bool, const void*> b = addLock(hash, p.get());
245 if(b.first) p.release();
246 return b;
247}
248
249
250std::pair<bool, const void*> IdentifiableCacheBase::add (IdentifierHash hash,
251 void_unique_ptr p) noexcept
252{
253 std::pair<bool, const void*> b = add(hash, p.get());
254 if(b.first) p.release();
255 return b;
256}
257
258
259
260} // namespace EventContainers
261
262
virtual void lock()=0
Interface to allow an object to lock itself when made const in SG.
std::vector< std::atomic< const void * > > m_vec
std::atomic< size_t > m_currentHashes
Holds the number of valid hashes in container, in concurrent use it is not guaranteed to be up to dat...
const void * waitFor(IdentifierHash)
Halts the thread until the require hash is completed or aborted.
size_t numberOfHashes()
In a concurrent situation this number isn't necessarily perfectly synchronised with ids()....
int itemAborted(IdentifierHash)
Returns 1 is the item has been aborted otherwise 0.
int itemInProgress(IdentifierHash)
Returns 1 is the item is inprogress otherwise 0.
std::vector< IdentifierHash > ids()
In a threaded situation this collection will be valid but will not container hashes later added.
const void * get(IdentifierHash hash)
Try to make payload if not there.
std::pair< bool, const void * > addLock(IdentifierHash hash, const void *p) noexcept
int tryLock(IdentifierHash, IDC_WriteHandleBase &, std::vector< IdentifierHash > &)
Checks if the item is completed if it is not started it extablishes lock (returns 0),...
void createSet(const std::vector< IdentifierHash > &hashes, std::vector< bool > &mask)
Create a set of hashes, updates an IDC mask as appropriate.
IdentifiableCacheBase(IdentifierHash maxHash, const IMaker *maker)
std::pair< bool, const void * > add(IdentifierHash hash, const void *p) noexcept
const void * findWait(IdentifierHash hash)
Retrieve ptr, will wait if there is something in progress.
const void * find(IdentifierHash hash) noexcept
Return payload if there, null if not there.
This is a "hash" representation of an Identifier.
bool add(const std::string &hname, TKey *tobj)
Definition fastadd.cxx:55
const void *const INVALID
const void *const ABORTED
#define unlikely(x)