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 std::atomic_thread_fence(std::memory_order_acquire);
82 if(0 != m_currentHashes.load(std::memory_order_relaxed)){ //Reduce overhead if cache was unused
83 size_t s = m_vec.size();
84 for (size_t i=0; i<s ;i++) {
85 const void* p = m_vec[i].load(std::memory_order_relaxed);
86 if(p && p < ABORTED) deleter (p);
87 }
88 }
92 const void* p = m_vec[hash].load(std::memory_order_relaxed); //Relaxed because it is not returning a pointer to anything
93 return (p == ABORTED);
94}
95
96
98 const void* p = m_vec[hash].load(std::memory_order_relaxed); //Relaxed because it is not returning a pointer to anything
99 return (p == INVALID);
100}
101
102
104{
105 if (ATH_UNLIKELY(hash >= m_vec.size())) return nullptr;
106 const void* p = m_vec[hash].load(std::memory_order_acquire);
107 if (p >= ABORTED)
108 return nullptr;
109 return p;
110}
111
113{
114 std::atomic<const void*> &myatomic = m_vec[hash];
115 const void* item = myatomic.load(std::memory_order_acquire);
116 //Wait until pointer is set then retrieve and verify
117 while(item == INVALID){//Loop to check for spurious wakeups
118 myatomic.wait(item, std::memory_order_relaxed);
119 item = myatomic.load(std::memory_order_acquire);
120 }
121 return item;
122}
123
125{
126 if (ATH_UNLIKELY(hash >= m_vec.size())) return nullptr;
127 const void* p = waitFor(hash);
128 if(p>=ABORTED) return nullptr;
129 return p;
130}
131
133{
134 m_vec[hash].notify_all();
135}
136
138{
139 // If it's there already, return directly without locking.
140 const void* ptr = nullptr;
141 if (ATH_UNLIKELY(hash >= m_vec.size())) return ptr;
142
143 if(m_vec[hash].compare_exchange_strong(ptr, INVALID) ) {//Exchanges ptr with current value!!
144 // Make the payload.
145 if(m_maker == nullptr){
146 m_vec[hash].store( ABORTED );
147 return nullptr;
148 }
149 uniqueLock lock(m_mutex, std::defer_lock);
150 if(!m_maker->m_IsReEntrant) lock.lock();//Allow reentrant or non reentrant makers
151
152 try {
153 ptr = m_maker->typelessMake (hash).release();
154 }
155 catch (...) {
156 // FIXME: Can this be done with RAII?
157 notifyHash(hash);
158 throw;
159 }
160 assert(m_vec[hash] == INVALID);
161 if(ptr){
162 m_vec[hash].store( ptr );
164 }else{
165 m_vec[hash].store( ABORTED );
166 }
167 notifyHash(hash);
168 }
169 else if(ptr == INVALID){
170 ptr= waitFor(hash);
171 }
172 if(ptr == ABORTED) return nullptr;
173 assert(ptr < ABORTED);
174 return ptr;
175}
176
177void IdentifiableCacheBase::createSet (const std::vector<IdentifierHash>& hashes, std::vector<bool> &mask){
178 assert(mask.size() == fullSize());
179 for(IdentifierHash hash : hashes){
180 const void* ptr = get(hash);
181 if(ptr !=nullptr) mask[hash] = true;
182 }
183}
184
185
187{
188 return m_currentHashes.load(std::memory_order_relaxed); //Not to be used for syncing
189}
190
191std::vector<IdentifierHash> IdentifiableCacheBase::ids()
192{
193 std::vector<IdentifierHash> ret;
194 ret.reserve (m_currentHashes.load(std::memory_order_relaxed));
195 size_t s = m_vec.size();
196 for (size_t i =0; i<s; i++) {
197 const void* p = m_vec[i].load(std::memory_order_relaxed);
198 if (p && p < ABORTED)
199 ret.push_back (i);
200 }
201 return ret;
202}
203
204
205std::pair<bool, const void*> IdentifiableCacheBase::add (IdentifierHash hash, const void* p) noexcept
206{
207 if (ATH_UNLIKELY(hash >= m_vec.size())) return std::make_pair(false, nullptr);
208 if(p==nullptr) return std::make_pair(false, nullptr);
209 const void* nul=nullptr;
210 if(m_vec[hash].compare_exchange_strong(nul, p, std::memory_order_release, std::memory_order_relaxed)){
211 m_currentHashes.fetch_add(1, std::memory_order_relaxed);
212 return std::make_pair(true, p);
213 }
214 const void* invalid = INVALID;
215 if(m_vec[hash].compare_exchange_strong(invalid, p, std::memory_order_release, std::memory_order_acquire)){
216 m_currentHashes.fetch_add(1, std::memory_order_relaxed);
217 notifyHash(hash);
218 return std::make_pair(true, p);
219 }
220 return std::make_pair(false, invalid);
221}
222
223
224std::pair<bool, const void*> IdentifiableCacheBase::addLock (IdentifierHash hash, const void* p) noexcept
225{ //Same as method above except we check for invalid state first,
226 // more optimal for calling using writehandle lock method
227 assert(hash < m_vec.size());
228 if(p==nullptr) return std::make_pair(false, nullptr);
229 const void* invalid = INVALID;
230 if(m_vec[hash].compare_exchange_strong(invalid, p, std::memory_order_release, std::memory_order_relaxed)){
231 m_currentHashes.fetch_add(1, std::memory_order_relaxed);
232 notifyHash(hash);
233 return std::make_pair(true, p);
234 }
235 const void* nul=nullptr;
236 if(m_vec[hash].compare_exchange_strong(nul, p, std::memory_order_release, std::memory_order_acquire)){
237 m_currentHashes.fetch_add(1, std::memory_order_relaxed);
238 return std::make_pair(true, p);
239 }
240 return std::make_pair(false, nul);
241}
242
243std::pair<bool, const void*> IdentifiableCacheBase::addLock (IdentifierHash hash,
244 void_unique_ptr p) noexcept
245{
246 std::pair<bool, const void*> b = addLock(hash, p.get());
247 if(b.first) p.release();
248 return b;
249}
250
251
252std::pair<bool, const void*> IdentifiableCacheBase::add (IdentifierHash hash,
253 void_unique_ptr p) noexcept
254{
255 std::pair<bool, const void*> b = add(hash, p.get());
256 if(b.first) p.release();
257 return b;
258}
259
260
261
262} // namespace EventContainers
263
264
#define ATH_UNLIKELY(x)
void LockOn(std::atomic< const void * > *in) noexcept
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