ATLAS Offline Software
Loading...
Searching...
No Matches
IdentifiableCacheBase.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2017 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
20#include <bit>
21
22namespace EventContainers {
23
24const void* const INVALID = std::bit_cast<const void*>(IdentifiableCacheBase::INVALIDflag);
25const void* const ABORTED = std::bit_cast<const void*>(IdentifiableCacheBase::ABORTEDflag);
26
27
28
30 const IMaker* maker)
31 : m_vec(maxHash),
32 m_maker (maker),
34{
35}
36
37
39
40int IdentifiableCacheBase::tryLock(IdentifierHash hash, IDC_WriteHandleBase &lock, std::vector<IdentifierHash> &wait){
41 const void *ptr1 =nullptr;
42
43 if(m_vec[hash].compare_exchange_strong(ptr1, INVALID, std::memory_order_relaxed, std::memory_order_relaxed)){//atomic swap (replaces ptr1 with value)
44 //First call
45 //Setup the IDC_WriteHandle to "lock" on this hash's pointer
46 lock.LockOn(&m_vec[hash]);
47 return 0;
48 }
49
50 if(ptr1 == INVALID){
51 //Second call while not finished
52 wait.emplace_back(hash);
53 return 1;
54 }
55 if(ptr1 == ABORTED) return 3;
56 return 2; //Already completed
61{
62 size_t s = m_vec.size();
63 if(0 != m_currentHashes.load(std::memory_order_relaxed)){
64 for (size_t i=0; i<s ;i++) {
65 const void* ptr = m_vec[i].load(std::memory_order_relaxed);
66 m_vec[i].store(nullptr, std::memory_order_relaxed);
67 if (ptr && ptr < ABORTED){
68 deleter (ptr);
69 }
70 }
71 m_currentHashes.store(0, std::memory_order_relaxed);
72 }else{
73 for (size_t i=0; i<s ;i++) m_vec[i].store(nullptr, std::memory_order_relaxed);//Need to clear incase of aborts
74 }
75}
77
78//Does not lock or clear atomics to allow faster destruction
80{
81 if(0 != m_currentHashes.load(std::memory_order_acquire)){ //Reduce overhead if cache was unused
82 size_t s = m_vec.size();
83 for (size_t i=0; i<s ;i++) {
84 const void* p = m_vec[i].load(std::memory_order_relaxed);
85 if(p && p < ABORTED) deleter (p);
86 }
87 }
91 const void* p = m_vec[hash].load(std::memory_order_relaxed); //Relaxed because it is not returning a pointer to anything
92 return (p == ABORTED);
93}
94
95
97 const void* p = m_vec[hash].load(std::memory_order_relaxed); //Relaxed because it is not returning a pointer to anything
98 return (p == INVALID);
99}
100
101
103{
104 if (ATH_UNLIKELY(hash >= m_vec.size())) return nullptr;
105 const void* p = m_vec[hash].load(std::memory_order_acquire);
106 if (p >= ABORTED)
107 return nullptr;
108 return p;
109}
110
112{
113 std::atomic<const void*> &myatomic = m_vec[hash];
114 const void* item = myatomic.load(std::memory_order_acquire);
115 //Wait until pointer is set then retrieve and verify
116 while(item == INVALID){//Loop to check for spurious wakeups
117 myatomic.wait(item, std::memory_order_relaxed);
118 item = myatomic.load(std::memory_order_acquire);
119 }
120 return item;
121}
122
124{
125 if (ATH_UNLIKELY(hash >= m_vec.size())) return nullptr;
126 const void* p = waitFor(hash);
127 if(p>=ABORTED) return nullptr;
128 return p;
129}
130
132{
133 m_vec[hash].notify_all();
134}
135
137{
138 // If it's there already, return directly without locking.
139 const void* ptr = nullptr;
140 if (ATH_UNLIKELY(hash >= m_vec.size())) return ptr;
141
142 if(m_vec[hash].compare_exchange_strong(ptr, INVALID) ) {//Exchanges ptr with current value!!
143 // Make the payload.
144 if(m_maker == nullptr){
145 m_vec[hash].store( ABORTED );
146 return nullptr;
147 }
148 uniqueLock lock(m_mutex, std::defer_lock);
149 if(!m_maker->m_IsReEntrant) lock.lock();//Allow reentrant or non reentrant makers
150
151 try {
152 ptr = m_maker->typelessMake (hash).release();
153 }
154 catch (...) {
155 m_vec[hash].store( ABORTED );
156 notifyHash(hash);
157 throw;
158 }
159 assert(m_vec[hash] == INVALID);
160 if(ptr){
161 m_vec[hash].store( ptr );
163 }else{
164 m_vec[hash].store( ABORTED );
165 }
166 notifyHash(hash);
167 }
168 else if(ptr == INVALID){
169 ptr= waitFor(hash);
170 }
171 if(ptr == ABORTED) return nullptr;
172 assert(ptr < ABORTED);
173 return ptr;
174}
175
176void IdentifiableCacheBase::createSet (const std::vector<IdentifierHash>& hashes, std::vector<bool> &mask){
177 assert(mask.size() == fullSize());
178 for(IdentifierHash hash : hashes){
179 const void* ptr = get(hash);
180 if(ptr !=nullptr) mask[hash] = true;
181 }
182}
183
184
186{
187 return m_currentHashes.load(std::memory_order_relaxed); //Not to be used for syncing
188}
189
190std::vector<IdentifierHash> IdentifiableCacheBase::ids()
191{
192 std::vector<IdentifierHash> ret;
193 ret.reserve (m_currentHashes.load(std::memory_order_relaxed));
194 size_t s = m_vec.size();
195 for (size_t i =0; i<s; i++) {
196 const void* p = m_vec[i].load(std::memory_order_relaxed);
197 if (p && p < ABORTED)
198 ret.push_back (i);
199 }
200 return ret;
201}
202
203
204std::pair<bool, const void*> IdentifiableCacheBase::add (IdentifierHash hash, const void* p) noexcept
205{
206 if (ATH_UNLIKELY(hash >= m_vec.size())) return std::make_pair(false, nullptr);
207 if(p==nullptr) return std::make_pair(false, nullptr);
208 const void* nul=nullptr;
209 if(m_vec[hash].compare_exchange_strong(nul, p, std::memory_order_release, std::memory_order_relaxed)){
210 m_currentHashes.fetch_add(1, std::memory_order_relaxed);
211 return std::make_pair(true, p);
212 }
213 const void* invalid = INVALID;
214 if(m_vec[hash].compare_exchange_strong(invalid, p, std::memory_order_release, std::memory_order_acquire)){
215 m_currentHashes.fetch_add(1, std::memory_order_relaxed);
216 notifyHash(hash);
217 return std::make_pair(true, p);
218 }
219 return std::make_pair(false, invalid);
220}
221
222
223std::pair<bool, const void*> IdentifiableCacheBase::addLock (IdentifierHash hash, const void* p) noexcept
224{ //Same as method above except we check for invalid state first,
225 // more optimal for calling using writehandle lock method
226 assert(hash < m_vec.size());
227 if(p==nullptr) return std::make_pair(false, nullptr);
228 const void* invalid = INVALID;
229 if(m_vec[hash].compare_exchange_strong(invalid, p, std::memory_order_release, std::memory_order_relaxed)){
230 m_currentHashes.fetch_add(1, std::memory_order_relaxed);
231 notifyHash(hash);
232 return std::make_pair(true, p);
233 }
234 const void* nul=nullptr;
235 if(m_vec[hash].compare_exchange_strong(nul, p, std::memory_order_release, std::memory_order_acquire)){
236 m_currentHashes.fetch_add(1, std::memory_order_relaxed);
237 return std::make_pair(true, p);
238 }
239 return std::make_pair(false, nul);
240}
241
242std::pair<bool, const void*> IdentifiableCacheBase::addLock (IdentifierHash hash,
243 void_unique_ptr p) noexcept
244{
245 std::pair<bool, const void*> b = addLock(hash, p.get());
246 if(b.first) p.release();
247 return b;
248}
249
250
251std::pair<bool, const void*> IdentifiableCacheBase::add (IdentifierHash hash,
252 void_unique_ptr p) noexcept
253{
254 std::pair<bool, const void*> b = add(hash, p.get());
255 if(b.first) p.release();
256 return b;
257}
258
259
260
261} // namespace EventContainers
262
263
#define ATH_UNLIKELY(x)
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