ATLAS Offline Software
RCUObject.icc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
3 */
4 /**
5  * @file AthenaKernel/RCUObject.icc
6  * @author scott snyder <snyder@bnl.gov>
7  * @date Aug, 2016
8  * @brief read-copy-update (RCU) style synchronization for Athena.
9  */
10 
11 
12 namespace Athena {
13 
14 
15 /**
16  * @brief Declare the current event slot of this object to be quiescent.
17  *
18  * This will take out a lock and possibly trigger object cleanup.
19  */
20 inline
21 void IRCUObject::quiescent()
22 {
23  quiescent (Gaudi::Hive::currentContext());
24 }
25 
26 
27 /**
28  * @brief Declare the event slot @c ctx of this object to be quiescent.
29  * @param ctx The event context.
30  *
31  * This will take out a lock and possibly trigger object cleanup.
32  */
33 inline
34 void IRCUObject::quiescent (const EventContext& ctx)
35 {
36  if (!m_dirty) return;
37  lock_t g (m_mutex);
38  if (!endGrace(g, ctx, m_grace)) {
39  clearAll(g);
40  m_nold = 0;
41  m_dirty = false;
42  }
43  else if (m_nold > 0 && !endGrace(g, ctx, m_oldGrace)) {
44  if (clearOld(g, m_nold)) {
45  m_dirty = false;
46  }
47  m_nold = 0;
48  }
49 }
50 
51 
52 /**
53  * @brief Declare that the grace period for a slot is ending.
54  * @param lock Lock object (external locking).
55  * @param ctx Event context for the slot.
56  * @returns true if any slot is still in a grace period.
57  * false if no slots are in a grace period.
58  *
59  * The caller must be holding the mutex for this object.
60  */
61 inline
62 bool IRCUObject::endGrace (lock_t& lock, const EventContext& ctx)
63 {
64  return endGrace (lock, ctx, m_grace);
65 }
66 
67 
68 /**
69  * @brief Declare that all slots are in a grace period.
70  * @param Lock object (external locking).
71  *
72  * The caller must be holding the mutex for this object.
73  */
74 inline
75 void IRCUObject::setGrace (lock_t& /*lock*/)
76 {
77  m_grace.set();
78  if (!m_dirty) m_dirty = true;
79 }
80 
81 
82 /**
83  * @brief Return the mutex for this object.
84  */
85 inline
86 typename IRCUObject::mutex_t& IRCUObject::mutex()
87 {
88  return m_mutex;
89 }
90 
91 
92 /**
93  * @brief Make existing pending objects old, if possible.
94  * @param lock Lock object (external locking).
95  * @param garbageSize Present number of objects pending deletion.
96  *
97  * A new object is about to be added to the list of objects pending deletion.
98  * If there are any existing pending objects and there are no existing
99  * old objects, make the current pending objects old.
100  */
101 inline
102 void IRCUObject::makeOld (lock_t& /*lock*/, size_t garbageSize)
103 {
104  if (garbageSize && m_nold == 0) {
105  m_oldGrace = m_grace;
106  m_nold = garbageSize;
107  }
108 }
109 
110 
111 /**
112  * @brief Declare that the grace period for a slot is ending.
113  * @param Lock object (external locking).
114  * @param ctx Event context for the slot.
115  * @param grace Bitmask tracking grace periods.
116  * @returns true if any slot is still in a grace period.
117  * false if no slots are in a grace period.
118  *
119  * The caller must be holding the mutex for this object.
120  */
121 inline
122 bool IRCUObject::endGrace (lock_t& /*lock*/,
123  const EventContext& ctx,
124  boost::dynamic_bitset<>& grace) const
125 {
126  EventContext::ContextID_t slot = ctx.slot();
127  if (slot == EventContext::INVALID_CONTEXT_ID) return false;
128  if (slot >= grace.size()) std::abort();
129  grace[slot] = false;
130  return grace.any();
131 }
132 
133 
134 //*************************************************************************
135 
136 
137 /**
138  * @brief Constructor, with RCUSvc.
139  * @param svc Service with which to register.
140  * @param args... Additional arguments to pass to the @c T constructor.
141  *
142  * The service will call @c quiescent at the end of each event.
143  */
144 template <class T>
145 template <typename... Args>
146 inline
147 RCUObject<T>::RCUObject (IRCUSvc& svc, Args&&... args)
148  : IRCUObject (svc),
149  m_objref (std::make_unique<T>(std::forward<Args>(args)...)),
150  m_obj (m_objref.get())
151 {
152 }
153 
154 
155 /**
156  * @brief Constructor, with number of slots.
157  * @param nslots Number of event slots.
158  * @param args... Additional arguments to pass to the @c T constructor.
159  */
160 template <class T>
161 template <typename... Args>
162 inline
163 RCUObject<T>::RCUObject (size_t nslots, Args&&... args)
164  : IRCUObject (nslots),
165  m_objref (std::make_unique<T>(std::forward<Args>(args)...)),
166  m_obj (m_objref.get())
167 {
168 }
169 
170 
171 /**
172  * @brief Constructor, with RCUSvc.
173  * @param svc Service with which to register.
174  *
175  * The service will call @c quiescent at the end of each event.
176  * No current object will be created.
177  */
178 template <class T>
179 inline
180 RCUObject<T>::RCUObject (IRCUSvc& svc, NoObjectEnum)
181  : IRCUObject (svc),
182  m_objref(),
183  m_obj (nullptr)
184 {
185 }
186 
187 
188 /**
189  * @brief Move constructor.
190  *
191  * Allow passing these objects via move.
192  */
193 template <class T>
194 RCUObject<T>::RCUObject (RCUObject&& other)
195  : IRCUObject (std::move (other)),
196  m_objref (std::move (other.m_objref)),
197  m_obj (static_cast<const T*> (other.m_obj)),
198  m_garbage (std::move (other.m_garbage))
199 {
200  other.m_obj = nullptr;
201 }
202 
203 
204 /**
205  * @brief Return a reader for this @c RCUObject.
206  */
207 template <class T>
208 inline
209 typename RCUObject<T>::Read_t RCUObject<T>::reader() const
210 {
211  return Read_t (*this);
212 }
213 
214 
215 /**
216  * @brief Return a reader for this @c RCUObject.
217  * When destroyed, this reader will declare
218  * the @c RCUObject as quiescent
219  *
220  * This version will read the global event context.
221  */
222 template <class T>
223 inline
224 typename RCUObject<T>::ReadQuiesce_t RCUObject<T>::readerQuiesce()
225 {
226  return ReadQuiesce_t (*this);
227 }
228 
229 
230 /**
231  * @brief Return a reader for this @c RCUObject.
232  * When destroyed, this reader will declare
233  * the @c RCUObject as quiescent
234  * @param ctx The event context.
235  */
236 template <class T>
237 inline
238 typename RCUObject<T>::ReadQuiesce_t
239 RCUObject<T>::readerQuiesce (const EventContext& ctx)
240 {
241  return ReadQuiesce_t (*this, ctx);
242 }
243 
244 
245 /**
246  * @brief Return an updater for this @c RCUObject.
247  *
248  * This version will read the global event context.
249  */
250 template <class T>
251 inline
252 typename RCUObject<T>::Update_t RCUObject<T>::updater()
253 {
254  return Update_t (*this);
255 }
256 
257 
258 /**
259  * @brief Return an updater for this @c RCUObject.
260  *
261  * This version will read the global event context.
262  */
263 template <class T>
264 inline
265 typename RCUObject<T>::Update_t
266 RCUObject<T>::updater (const EventContext& ctx)
267 {
268  return Update_t (*this, ctx);
269 }
270 
271 
272 /**
273  * @brief Queue an object for later deletion.
274  * @param p The object to delete.
275  *
276  * The object @c p will be queued for deletion once a grace period
277  * has passed for all slots. In contrast to using @c updater,
278  * this does not change the current object. It also does not mark
279  * the current slot as having completed the grace period (so this can
280  * be called by a thread running outside of a slot context).
281  */
282 template <class T>
283 void RCUObject<T>::discard (std::unique_ptr<const T> p)
284 {
285  lock_t g (mutex());
286  discard (g, std::move (p));
287 }
288 
289 
290 /**
291  * @brief Queue an object for later deletion.
292  * @param lock Lock object (external locking).
293  * @param p The object to delete.
294  */
295 template <class T>
296 void RCUObject<T>::discard (lock_t& lock, std::unique_ptr<const T> p)
297 {
298  makeOld (lock, m_garbage.size());
299  m_garbage.push_front (std::move (p));
300  setGrace (lock);
301 }
302 
303 
304 /**
305  * @brief Delete all objects.
306  * @param Lock object (external locking).
307  *
308  * The caller must be holding the mutex for this object.
309  */
310 template <class T>
311 inline
312 void RCUObject<T>::clearAll (lock_t& /*lock*/)
313 {
314  m_garbage.clear();
315 }
316 
317 
318 /**
319  * @brief Delete old objects.
320  * @param Lock object (external locking).
321  * @param nold Number of old objects to delete.
322  *
323  * The caller must be holding the mutex for this object.
324  * Returns true if there are no more objects pending deletion.
325  */
326 template <class T>
327 inline
328 bool RCUObject<T>::clearOld (lock_t& /*lock*/, size_t nold)
329 {
330  while (nold > 0) {
331  m_garbage.pop_back();
332  --nold;
333  }
334  return m_garbage.empty();
335 }
336 
337 
338 //*************************************************************************
339 
340 
341 /**
342  * @brief Constructor.
343  * @param rcuobj The @c RCUObject we're reading.
344  */
345 template <class T>
346 inline
347 RCURead<T>::RCURead (const RCUObject<T>& rcuobj)
348  : m_obj (rcuobj.m_obj)
349 {
350 }
351 
352 
353 /**
354  * @brief Access data.
355  */
356 template <class T>
357 inline
358 const T& RCURead<T>::operator*() const
359 {
360  return *m_obj;
361 }
362 
363 
364 /**
365  * @brief Access data.
366  */
367 template <class T>
368 inline
369 const T* RCURead<T>::operator->() const
370 {
371  return m_obj;
372 }
373 
374 
375 //*************************************************************************
376 
377 
378 /**
379  * @brief Constructor.
380  * @param rcuobj The @c RCUObject we're reading.
381  *
382  * This version will read the global event context.
383  */
384 template <class T>
385 inline
386 RCUReadQuiesce<T>::RCUReadQuiesce (RCUObject<T>& rcuobj)
387  : RCURead<T> (rcuobj),
388  m_rcuobj (rcuobj),
389  m_ctx (Gaudi::Hive::currentContext())
390 {
391 }
392 
393 
394 /**
395  * @brief Constructor.
396  * @param rcuobj The @c RCUObject we're reading.
397  * @param ctx The event context.
398  */
399 template <class T>
400 inline
401 RCUReadQuiesce<T>::RCUReadQuiesce (RCUObject<T>& rcuobj,
402  const EventContext& ctx)
403  : RCURead<T> (rcuobj),
404  m_rcuobj (rcuobj),
405  m_ctx (ctx)
406 {
407 }
408 
409 
410 /**
411  * @brief Destructor.
412  *
413  * Mark this event slot quiescent.
414  */
415 template <class T>
416 inline
417 RCUReadQuiesce<T>::~RCUReadQuiesce()
418 {
419  m_rcuobj.quiescent (m_ctx);
420 }
421 
422 
423 //*************************************************************************
424 
425 
426 /**
427  * @brief Constructor.
428  * @param rcuobj The @c RCUObject we're reading.
429  *
430  * This version will read the global event context.
431  */
432 template <class T>
433 inline
434 RCUUpdate<T>::RCUUpdate (RCUObject<T>& rcuobj)
435  : m_rcuobj (rcuobj),
436  m_ctx (Gaudi::Hive::currentContext()),
437  m_g (rcuobj.mutex())
438 {
439 }
440 
441 
442 /**
443  * @brief Constructor.
444  * @param rcuobj The @c RCUObject we're reading.
445  * @param ctx The event context.
446  */
447 template <class T>
448 inline
449 RCUUpdate<T>::RCUUpdate (RCUObject<T>& rcuobj, const EventContext& ctx)
450  : m_rcuobj (rcuobj),
451  m_ctx (ctx),
452  m_g (rcuobj.mutex())
453 {
454 }
455 
456 
457 /**
458  * @brief Access data.
459  */
460 template <class T>
461 inline
462 const T& RCUUpdate<T>::operator*() const
463 {
464  return *m_rcuobj.m_obj;
465 }
466 
467 
468 /**
469  * @brief Access data.
470  */
471 template <class T>
472 inline
473 const T* RCUUpdate<T>::operator->() const
474 {
475  return m_rcuobj.m_obj;
476 }
477 
478 
479 /**
480  * @brief Publish a new version of the data object.
481  * @param ptr The new data object.
482  */
483 template <class T>
484 void RCUUpdate<T>::update (std::unique_ptr<T> ptr)
485 {
486  // Queue the current object for deletion, if it exists.
487  if (m_rcuobj.m_objref) {
488  m_rcuobj.discard (m_g, std::move (m_rcuobj.m_objref));
489  }
490 
491  // Install the new object.
492  m_rcuobj.m_objref = std::move (ptr);
493  m_rcuobj.m_obj = m_rcuobj.m_objref.get();
494 
495  // Go ahead and end the grace period for the current slot.
496  m_rcuobj.endGrace(m_g, m_ctx);
497 }
498 
499 
500 } // namespace Athena