ATLAS Offline Software
RCUObject.icc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2025 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) {
37  quiescentOol (ctx);
38  }
39 }
40 
41 
42 /**
43  * @brief Return the mutex for this object.
44  */
45 inline
46 typename IRCUObject::mutex_t& IRCUObject::mutex()
47 {
48  return m_mutex;
49 }
50 
51 
52 //*************************************************************************
53 
54 
55 /**
56  * @brief Constructor, with RCUSvc.
57  * @param svc Service with which to register.
58  * @param args... Additional arguments to pass to the @c T constructor.
59  *
60  * The service will call @c quiescent at the end of each event.
61  */
62 template <class T>
63 template <typename... Args>
64 inline
65 RCUObject<T>::RCUObject (IRCUSvc& svc, Args&&... args)
66  : IRCUObject (svc),
67  m_objref (std::make_unique<T>(std::forward<Args>(args)...)),
68  m_obj (m_objref.get())
69 {
70 }
71 
72 
73 /**
74  * @brief Constructor, with number of slots.
75  * @param nslots Number of event slots.
76  * @param args... Additional arguments to pass to the @c T constructor.
77  */
78 template <class T>
79 template <typename... Args>
80 inline
81 RCUObject<T>::RCUObject (size_t nslots, Args&&... args)
82  : IRCUObject (nslots),
83  m_objref (std::make_unique<T>(std::forward<Args>(args)...)),
84  m_obj (m_objref.get())
85 {
86 }
87 
88 
89 /**
90  * @brief Constructor, with RCUSvc.
91  * @param svc Service with which to register.
92  *
93  * The service will call @c quiescent at the end of each event.
94  * No current object will be created.
95  */
96 template <class T>
97 inline
98 RCUObject<T>::RCUObject (IRCUSvc& svc, NoObjectEnum)
99  : IRCUObject (svc),
100  m_objref(),
101  m_obj (nullptr)
102 {
103 }
104 
105 
106 /**
107  * @brief Move constructor.
108  *
109  * Allow passing these objects via move.
110  */
111 template <class T>
112 RCUObject<T>::RCUObject (RCUObject&& other)
113  : IRCUObject (std::move (other)),
114  m_objref (std::move (other.m_objref)),
115  m_obj (static_cast<const T*> (other.m_obj)),
116  m_garbage (std::move (other.m_garbage))
117 {
118  other.m_obj = nullptr;
119 }
120 
121 
122 /**
123  * @brief Return a reader for this @c RCUObject.
124  */
125 template <class T>
126 inline
127 typename RCUObject<T>::Read_t RCUObject<T>::reader() const
128 {
129  return Read_t (*this);
130 }
131 
132 
133 /**
134  * @brief Return a reader for this @c RCUObject.
135  * When destroyed, this reader will declare
136  * the @c RCUObject as quiescent
137  *
138  * This version will read the global event context.
139  */
140 template <class T>
141 inline
142 typename RCUObject<T>::ReadQuiesce_t RCUObject<T>::readerQuiesce()
143 {
144  return ReadQuiesce_t (*this);
145 }
146 
147 
148 /**
149  * @brief Return a reader for this @c RCUObject.
150  * When destroyed, this reader will declare
151  * the @c RCUObject as quiescent
152  * @param ctx The event context.
153  */
154 template <class T>
155 inline
156 typename RCUObject<T>::ReadQuiesce_t
157 RCUObject<T>::readerQuiesce (const EventContext& ctx)
158 {
159  return ReadQuiesce_t (*this, ctx);
160 }
161 
162 
163 /**
164  * @brief Return an updater for this @c RCUObject.
165  *
166  * This version will read the global event context.
167  */
168 template <class T>
169 inline
170 typename RCUObject<T>::Update_t RCUObject<T>::updater()
171 {
172  return Update_t (*this);
173 }
174 
175 
176 /**
177  * @brief Return an updater for this @c RCUObject.
178  *
179  * This version will read the global event context.
180  */
181 template <class T>
182 inline
183 typename RCUObject<T>::Update_t
184 RCUObject<T>::updater (const EventContext& ctx)
185 {
186  return Update_t (*this, ctx);
187 }
188 
189 
190 /**
191  * @brief Queue an object for later deletion.
192  * @param p The object to delete.
193  *
194  * The object @c p will be queued for deletion once a grace period
195  * has passed for all slots. In contrast to using @c updater,
196  * this does not change the current object. It also does not mark
197  * the current slot as having completed the grace period (so this can
198  * be called by a thread running outside of a slot context).
199  */
200 template <class T>
201 void RCUObject<T>::discard (std::unique_ptr<const T> p)
202 {
203  lock_t g (mutex());
204  discard (g, std::move (p));
205 }
206 
207 
208 /**
209  * @brief Queue an object for later deletion.
210  * @param lock Lock object (external locking).
211  * @param p The object to delete.
212  */
213 template <class T>
214 void RCUObject<T>::discard (lock_t& lock, std::unique_ptr<const T> p)
215 {
216  makeOld (lock, m_garbage.size());
217  m_garbage.push_front (std::move (p));
218  setGrace (lock);
219 }
220 
221 
222 /**
223  * @brief Delete all objects.
224  * @param Lock object (external locking).
225  *
226  * The caller must be holding the mutex for this object.
227  */
228 template <class T>
229 inline
230 void RCUObject<T>::clearAll (lock_t& /*lock*/)
231 {
232  m_garbage.clear();
233 }
234 
235 
236 /**
237  * @brief Delete old objects.
238  * @param Lock object (external locking).
239  * @param nold Number of old objects to delete.
240  *
241  * The caller must be holding the mutex for this object.
242  * Returns true if there are no more objects pending deletion.
243  */
244 template <class T>
245 inline
246 bool RCUObject<T>::clearOld (lock_t& /*lock*/, size_t nold)
247 {
248  while (nold > 0) {
249  m_garbage.pop_back();
250  --nold;
251  }
252  return m_garbage.empty();
253 }
254 
255 
256 //*************************************************************************
257 
258 
259 /**
260  * @brief Constructor.
261  * @param rcuobj The @c RCUObject we're reading.
262  */
263 template <class T>
264 inline
265 RCURead<T>::RCURead (const RCUObject<T>& rcuobj)
266  : m_obj (rcuobj.m_obj)
267 {
268 }
269 
270 
271 /**
272  * @brief Access data.
273  */
274 template <class T>
275 inline
276 const T& RCURead<T>::operator*() const
277 {
278  return *m_obj;
279 }
280 
281 
282 /**
283  * @brief Access data.
284  */
285 template <class T>
286 inline
287 const T* RCURead<T>::operator->() const
288 {
289  return m_obj;
290 }
291 
292 
293 //*************************************************************************
294 
295 
296 /**
297  * @brief Constructor.
298  * @param rcuobj The @c RCUObject we're reading.
299  *
300  * This version will read the global event context.
301  */
302 template <class T>
303 inline
304 RCUReadQuiesce<T>::RCUReadQuiesce (RCUObject<T>& rcuobj)
305  : RCURead<T> (rcuobj),
306  m_rcuobj (rcuobj),
307  m_ctx (Gaudi::Hive::currentContext())
308 {
309 }
310 
311 
312 /**
313  * @brief Constructor.
314  * @param rcuobj The @c RCUObject we're reading.
315  * @param ctx The event context.
316  */
317 template <class T>
318 inline
319 RCUReadQuiesce<T>::RCUReadQuiesce (RCUObject<T>& rcuobj,
320  const EventContext& ctx)
321  : RCURead<T> (rcuobj),
322  m_rcuobj (rcuobj),
323  m_ctx (ctx)
324 {
325 }
326 
327 
328 /**
329  * @brief Destructor.
330  *
331  * Mark this event slot quiescent.
332  */
333 template <class T>
334 inline
335 RCUReadQuiesce<T>::~RCUReadQuiesce()
336 {
337  m_rcuobj.quiescent (m_ctx);
338 }
339 
340 
341 //*************************************************************************
342 
343 
344 /**
345  * @brief Constructor.
346  * @param rcuobj The @c RCUObject we're reading.
347  *
348  * This version will read the global event context.
349  */
350 template <class T>
351 inline
352 RCUUpdate<T>::RCUUpdate (RCUObject<T>& rcuobj)
353  : m_rcuobj (rcuobj),
354  m_ctx (Gaudi::Hive::currentContext()),
355  m_g (rcuobj.mutex())
356 {
357 }
358 
359 
360 /**
361  * @brief Constructor.
362  * @param rcuobj The @c RCUObject we're reading.
363  * @param ctx The event context.
364  */
365 template <class T>
366 inline
367 RCUUpdate<T>::RCUUpdate (RCUObject<T>& rcuobj, const EventContext& ctx)
368  : m_rcuobj (rcuobj),
369  m_ctx (ctx),
370  m_g (rcuobj.mutex())
371 {
372 }
373 
374 
375 /**
376  * @brief Access data.
377  */
378 template <class T>
379 inline
380 const T& RCUUpdate<T>::operator*() const
381 {
382  return *m_rcuobj.m_obj;
383 }
384 
385 
386 /**
387  * @brief Access data.
388  */
389 template <class T>
390 inline
391 const T* RCUUpdate<T>::operator->() const
392 {
393  return m_rcuobj.m_obj;
394 }
395 
396 
397 /**
398  * @brief Publish a new version of the data object.
399  * @param ptr The new data object.
400  */
401 template <class T>
402 void RCUUpdate<T>::update (std::unique_ptr<T> ptr)
403 {
404  // Queue the current object for deletion, if it exists.
405  if (m_rcuobj.m_objref) {
406  m_rcuobj.discard (m_g, std::move (m_rcuobj.m_objref));
407  }
408 
409  // Install the new object.
410  m_rcuobj.m_objref = std::move (ptr);
411  m_rcuobj.m_obj = m_rcuobj.m_objref.get();
412 
413  // Go ahead and end the grace period for the current slot.
414  m_rcuobj.endGrace(m_g, m_ctx);
415 }
416 
417 
418 } // namespace Athena