2 Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
5 * @file AthenaKernel/RCUObject.icc
6 * @author scott snyder <snyder@bnl.gov>
8 * @brief read-copy-update (RCU) style synchronization for Athena.
16 * @brief Declare the current event slot of this object to be quiescent.
18 * This will take out a lock and possibly trigger object cleanup.
21 void IRCUObject::quiescent()
23 quiescent (Gaudi::Hive::currentContext());
28 * @brief Declare the event slot @c ctx of this object to be quiescent.
29 * @param ctx The event context.
31 * This will take out a lock and possibly trigger object cleanup.
34 void IRCUObject::quiescent (const EventContext& ctx)
38 if (!endGrace(g, ctx, m_grace)) {
43 else if (m_nold > 0 && !endGrace(g, ctx, m_oldGrace)) {
44 if (clearOld(g, m_nold)) {
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.
59 * The caller must be holding the mutex for this object.
62 bool IRCUObject::endGrace (lock_t& lock, const EventContext& ctx)
64 return endGrace (lock, ctx, m_grace);
69 * @brief Declare that all slots are in a grace period.
70 * @param Lock object (external locking).
72 * The caller must be holding the mutex for this object.
75 void IRCUObject::setGrace (lock_t& /*lock*/)
78 if (!m_dirty) m_dirty = true;
83 * @brief Return the mutex for this object.
86 typename IRCUObject::mutex_t& IRCUObject::mutex()
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.
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.
102 void IRCUObject::makeOld (lock_t& /*lock*/, size_t garbageSize)
104 if (garbageSize && m_nold == 0) {
105 m_oldGrace = m_grace;
106 m_nold = garbageSize;
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.
119 * The caller must be holding the mutex for this object.
122 bool IRCUObject::endGrace (lock_t& /*lock*/,
123 const EventContext& ctx,
124 boost::dynamic_bitset<>& grace) const
126 EventContext::ContextID_t slot = ctx.slot();
127 if (slot == EventContext::INVALID_CONTEXT_ID) return false;
128 if (slot >= grace.size()) std::abort();
134 //*************************************************************************
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.
142 * The service will call @c quiescent at the end of each event.
145 template <typename... Args>
147 RCUObject<T>::RCUObject (IRCUSvc& svc, Args&&... args)
149 m_objref (std::make_unique<T>(std::forward<Args>(args)...)),
150 m_obj (m_objref.get())
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.
161 template <typename... Args>
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())
172 * @brief Constructor, with RCUSvc.
173 * @param svc Service with which to register.
175 * The service will call @c quiescent at the end of each event.
176 * No current object will be created.
180 RCUObject<T>::RCUObject (IRCUSvc& svc, NoObjectEnum)
189 * @brief Move constructor.
191 * Allow passing these objects via move.
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))
200 other.m_obj = nullptr;
205 * @brief Return a reader for this @c RCUObject.
209 typename RCUObject<T>::Read_t RCUObject<T>::reader() const
211 return Read_t (*this);
216 * @brief Return a reader for this @c RCUObject.
217 * When destroyed, this reader will declare
218 * the @c RCUObject as quiescent
220 * This version will read the global event context.
224 typename RCUObject<T>::ReadQuiesce_t RCUObject<T>::readerQuiesce()
226 return ReadQuiesce_t (*this);
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.
238 typename RCUObject<T>::ReadQuiesce_t
239 RCUObject<T>::readerQuiesce (const EventContext& ctx)
241 return ReadQuiesce_t (*this, ctx);
246 * @brief Return an updater for this @c RCUObject.
248 * This version will read the global event context.
252 typename RCUObject<T>::Update_t RCUObject<T>::updater()
254 return Update_t (*this);
259 * @brief Return an updater for this @c RCUObject.
261 * This version will read the global event context.
265 typename RCUObject<T>::Update_t
266 RCUObject<T>::updater (const EventContext& ctx)
268 return Update_t (*this, ctx);
273 * @brief Queue an object for later deletion.
274 * @param p The object to delete.
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).
283 void RCUObject<T>::discard (std::unique_ptr<const T> p)
286 discard (g, std::move (p));
291 * @brief Queue an object for later deletion.
292 * @param lock Lock object (external locking).
293 * @param p The object to delete.
296 void RCUObject<T>::discard (lock_t& lock, std::unique_ptr<const T> p)
298 makeOld (lock, m_garbage.size());
299 m_garbage.push_front (std::move (p));
305 * @brief Delete all objects.
306 * @param Lock object (external locking).
308 * The caller must be holding the mutex for this object.
312 void RCUObject<T>::clearAll (lock_t& /*lock*/)
319 * @brief Delete old objects.
320 * @param Lock object (external locking).
321 * @param nold Number of old objects to delete.
323 * The caller must be holding the mutex for this object.
324 * Returns true if there are no more objects pending deletion.
328 bool RCUObject<T>::clearOld (lock_t& /*lock*/, size_t nold)
331 m_garbage.pop_back();
334 return m_garbage.empty();
338 //*************************************************************************
342 * @brief Constructor.
343 * @param rcuobj The @c RCUObject we're reading.
347 RCURead<T>::RCURead (const RCUObject<T>& rcuobj)
348 : m_obj (rcuobj.m_obj)
354 * @brief Access data.
358 const T& RCURead<T>::operator*() const
365 * @brief Access data.
369 const T* RCURead<T>::operator->() const
375 //*************************************************************************
379 * @brief Constructor.
380 * @param rcuobj The @c RCUObject we're reading.
382 * This version will read the global event context.
386 RCUReadQuiesce<T>::RCUReadQuiesce (RCUObject<T>& rcuobj)
387 : RCURead<T> (rcuobj),
389 m_ctx (Gaudi::Hive::currentContext())
395 * @brief Constructor.
396 * @param rcuobj The @c RCUObject we're reading.
397 * @param ctx The event context.
401 RCUReadQuiesce<T>::RCUReadQuiesce (RCUObject<T>& rcuobj,
402 const EventContext& ctx)
403 : RCURead<T> (rcuobj),
413 * Mark this event slot quiescent.
417 RCUReadQuiesce<T>::~RCUReadQuiesce()
419 m_rcuobj.quiescent (m_ctx);
423 //*************************************************************************
427 * @brief Constructor.
428 * @param rcuobj The @c RCUObject we're reading.
430 * This version will read the global event context.
434 RCUUpdate<T>::RCUUpdate (RCUObject<T>& rcuobj)
436 m_ctx (Gaudi::Hive::currentContext()),
443 * @brief Constructor.
444 * @param rcuobj The @c RCUObject we're reading.
445 * @param ctx The event context.
449 RCUUpdate<T>::RCUUpdate (RCUObject<T>& rcuobj, const EventContext& ctx)
458 * @brief Access data.
462 const T& RCUUpdate<T>::operator*() const
464 return *m_rcuobj.m_obj;
469 * @brief Access data.
473 const T* RCUUpdate<T>::operator->() const
475 return m_rcuobj.m_obj;
480 * @brief Publish a new version of the data object.
481 * @param ptr The new data object.
484 void RCUUpdate<T>::update (std::unique_ptr<T> ptr)
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));
491 // Install the new object.
492 m_rcuobj.m_objref = std::move (ptr);
493 m_rcuobj.m_obj = m_rcuobj.m_objref.get();
495 // Go ahead and end the grace period for the current slot.
496 m_rcuobj.endGrace(m_g, m_ctx);
500 } // namespace Athena