ATLAS Offline Software
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 $
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 
21 namespace EventContainers {
22 
23 const void* const INVALID = reinterpret_cast<const void*>(IdentifiableCacheBase::INVALIDflag);
24 const void* const ABORTED = reinterpret_cast<const void*>(IdentifiableCacheBase::ABORTEDflag);
25 
26 
27 
29  const IMaker* maker)
30  : m_vec(maxHash),
31  m_maker (maker),
32  m_currentHashes(0)
33 {
34 }
35 
36 
38 
39 int 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 }
57 
58 
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 
76 
77 //Does not lock or clear atomics to allow faster destruction
79 {
80  std::atomic_thread_fence(std::memory_order_acquire);
81  if(0 != m_currentHashes.load(std::memory_order_relaxed)){ //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  }
88 }
89 
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  // FIXME: Can this be done with RAII?
156  notifyHash(hash);
157  throw;
158  }
159  assert(m_vec[hash] == INVALID);
160  if(ptr){
161  m_vec[hash].store( ptr );
162  m_currentHashes++;
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 
176 void 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 
190 std::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 
204 std::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 
223 std::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 
242 std::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 
251 std::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 
EventContainers::ABORTED
const void *const ABORTED
Definition: IdentifiableCacheBase.cxx:24
python.root_lsr_rank.hashes
hashes
Definition: root_lsr_rank.py:34
IdentifiableCacheBase.h
SGTest::store
TestStore store
Definition: TestStore.cxx:23
EventContainers::IdentifiableCacheBase::findWait
const void * findWait(IdentifierHash hash)
Retrieve ptr, will wait if there is something in progress.
Definition: IdentifiableCacheBase.cxx:123
python.SystemOfUnits.s
int s
Definition: SystemOfUnits.py:131
EventContainers::IdentifiableCacheBase::get
const void * get(IdentifierHash hash)
Try to make payload if not there.
Definition: IdentifiableCacheBase.cxx:136
EventContainers::IdentifiableCacheBase::ABORTEDflag
static constexpr uintptr_t ABORTEDflag
Definition: IdentifiableCacheBase.h:31
EventContainers::INVALID
const void *const INVALID
Definition: IdentifiableCacheBase.cxx:23
EventContainers::IDC_WriteHandleBase::LockOn
void LockOn(std::atomic< const void * > *in) noexcept
Definition: IDC_WriteHandleBase.h:21
EventContainers::IdentifiableCacheBase::IMaker::m_IsReEntrant
bool m_IsReEntrant
Definition: IdentifiableCacheBase.h:40
EventContainers::IdentifiableCacheBase::numberOfHashes
size_t numberOfHashes()
In a concurrent situation this number isn't necessarily perfectly synchronised with ids()....
Definition: IdentifiableCacheBase.cxx:185
EventContainers::IdentifiableCacheBase::m_mutex
mutex_t m_mutex
Definition: IdentifiableCacheBase.h:102
EventContainers::IdentifiableCacheBase::m_maker
const IMaker * m_maker
Definition: IdentifiableCacheBase.h:97
ATH_UNLIKELY
#define ATH_UNLIKELY(x)
Definition: AthUnlikelyMacros.h:17
EventContainers
Definition: T_AthenaPoolCreateFuncs.h:33
dbg::ptr
void * ptr(T *p)
Definition: SGImplSvc.cxx:74
python.utils.AtlRunQueryLookup.mask
string mask
Definition: AtlRunQueryLookup.py:460
AthUnlikelyMacros.h
PyPoolBrowser.item
item
Definition: PyPoolBrowser.py:129
EventContainers::IdentifiableCacheBase::~IdentifiableCacheBase
~IdentifiableCacheBase()
python.utils.AtlRunQueryDQUtils.p
p
Definition: AtlRunQueryDQUtils.py:210
lumiFormat.i
int i
Definition: lumiFormat.py:85
deleter_f
void deleter_f(const void *p)
Definition: deleter.h:12
add
bool add(const std::string &hname, TKey *tobj)
Definition: fastadd.cxx:55
EventContainers::IdentifiableCacheBase::IdentifiableCacheBase
IdentifiableCacheBase(IdentifierHash maxHash, const IMaker *maker)
Definition: IdentifiableCacheBase.cxx:28
EventContainers::IdentifiableCacheBase::tryLock
int tryLock(IdentifierHash, IDC_WriteHandleBase &, std::vector< IdentifierHash > &)
Checks if the item is completed if it is not started it extablishes lock (returns 0),...
Definition: IdentifiableCacheBase.cxx:39
EventContainers::IdentifiableCacheBase::fullSize
size_t fullSize() const
Definition: IdentifiableCacheBase.h:84
EventContainers::IdentifiableCacheBase::uniqueLock
std::unique_lock< mutex_t > uniqueLock
Definition: IdentifiableCacheBase.h:101
EventContainers::IdentifiableCacheBase::clear
void clear(deleter_f *deleter)
Definition: IdentifiableCacheBase.cxx:59
EventContainers::IdentifiableCacheBase::itemAborted
int itemAborted(IdentifierHash)
Returns 1 is the item has been aborted otherwise 0.
Definition: IdentifiableCacheBase.cxx:90
plotBeamSpotMon.b
b
Definition: plotBeamSpotMon.py:77
EventContainers::IdentifiableCacheBase::notifyHash
void notifyHash(IdentifierHash hash)
Definition: IdentifiableCacheBase.cxx:131
EventContainers::IdentifiableCacheBase::m_currentHashes
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...
Definition: IdentifiableCacheBase.h:104
item
Definition: ItemListSvc.h:43
EventContainers::IdentifiableCacheBase::m_vec
std::vector< std::atomic< const void * > > m_vec
Definition: IdentifiableCacheBase.h:95
EventContainers::IdentifiableCacheBase::createSet
void createSet(const std::vector< IdentifierHash > &hashes, std::vector< bool > &mask)
Create a set of hashes, updates an IDC mask as appropriate.
Definition: IdentifiableCacheBase.cxx:176
void_unique_ptr
Definition: deleter.h:16
EventContainers::IdentifiableCacheBase::IMaker::typelessMake
virtual void_unique_ptr typelessMake(IdentifierHash hash) const =0
EventContainers::IdentifiableCacheBase::itemInProgress
int itemInProgress(IdentifierHash)
Returns 1 is the item is inprogress otherwise 0.
Definition: IdentifiableCacheBase.cxx:96
CaloCondBlobAlgs_fillNoiseFromASCII.hash
dictionary hash
Definition: CaloCondBlobAlgs_fillNoiseFromASCII.py:109
EventContainers::IdentifiableCacheBase::find
const void * find(IdentifierHash hash) noexcept
Return payload if there, null if not there.
Definition: IdentifiableCacheBase.cxx:102
EventContainers::IdentifiableCacheBase::waitFor
const void * waitFor(IdentifierHash)
Halts the thread until the require hash is completed or aborted.
Definition: IdentifiableCacheBase.cxx:111
EventContainers::IdentifiableCacheBase::add
std::pair< bool, const void * > add(IdentifierHash hash, const void *p) noexcept
Definition: IdentifiableCacheBase.cxx:204
EventContainers::IdentifiableCacheBase::ids
std::vector< IdentifierHash > ids()
In a threaded situation this collection will be valid but will not container hashes later added.
Definition: IdentifiableCacheBase.cxx:190
EventContainers::IDC_WriteHandleBase
Definition: IDC_WriteHandleBase.h:13
EventContainers::IdentifiableCacheBase::IMaker
Definition: IdentifiableCacheBase.h:39
IdentifierHash
This is a "hash" representation of an Identifier. This encodes a 32 bit index which can be used to lo...
Definition: IdentifierHash.h:25
EventContainers::IdentifiableCacheBase::INVALIDflag
static constexpr uintptr_t INVALIDflag
Definition: IdentifiableCacheBase.h:30
EventContainers::IdentifiableCacheBase::addLock
std::pair< bool, const void * > addLock(IdentifierHash hash, const void *p) noexcept
Definition: IdentifiableCacheBase.cxx:223
EventContainers::IdentifiableCacheBase::cleanUp
void cleanUp(deleter_f *deleter)
Definition: IdentifiableCacheBase.cxx:78