Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TCollection.h
Go to the documentation of this file.
1 // @(#)root/cont:$Id$
2 // Author: Fons Rademakers 13/08/95
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #ifndef ROOT_TCollection
13 #define ROOT_TCollection
14 
15 
16 //////////////////////////////////////////////////////////////////////////
17 // //
18 // TCollection //
19 // //
20 // Collection abstract base class. This class inherits from TObject //
21 // because we want to be able to have collections of collections. //
22 // //
23 //////////////////////////////////////////////////////////////////////////
24 
25 #include "TObject.h"
26 
27 #include "TIterator.h"
28 
29 #include "TString.h"
30 
31 #include "TVirtualRWMutex.h"
32 
33 #include <assert.h>
34 
35 class TClass;
36 class TObjectTable;
37 class TVirtualMutex;
38 class TIter;
39 
40 const Bool_t kIterForward = kTRUE;
41 const Bool_t kIterBackward = !kIterForward;
42 
43 R__EXTERN TVirtualMutex *gCollectionMutex;
44 
45 // #define R__CHECK_COLLECTION_MULTI_ACCESS
46 
47 // When R__CHECK_COLLECTION_MULTI_ACCESS is turned on (defined),
48 // the normal (not locked) ROOT TCollections are instrumented with a
49 // pseudo read-write lock which does not halt the execution but detects
50 // and report concurrent access to the same collections.
51 // Multiple readers are allowed.
52 // Multiple concurrent writer is reported as a Conflict
53 // Readers access while a write is running is reported as Conflict
54 // Re-entrant writing call by the same Writer thread are allowed.
55 // Entering a writing section by a single Reader thread is allowed.
56 
57 #ifdef R__CHECK_COLLECTION_MULTI_ACCESS
58 #include <atomic>
59 #include <thread>
60 #include <unordered_set>
61 #endif
62 
63 class TCollection : public TObject {
64 
65 #ifdef R__CHECK_COLLECTION_MULTI_ACCESS
66 public:
67  class TErrorLock {
68  // Warn when multiple thread try to acquire the same 'lock'
69  std::atomic<std::thread::id> fWriteCurrent;
70  std::atomic<size_t> fWriteCurrentRecurse;
71  std::atomic<size_t> fReadCurrentRecurse;
72  std::unordered_multiset<std::thread::id> fReadSet;
73  std::atomic_flag fSpinLockFlag;
74 
75  void Lock(const TCollection *collection, const char *function);
76 
77  void Unlock();
78 
79  void ReadLock(const TCollection *collection, const char *function);
80 
81  void ReadUnlock();
82 
83  void ConflictReport(std::thread::id holder, const char *accesstype, const TCollection *collection,
84  const char *function);
85 
86  public:
87  TErrorLock() : fWriteCurrent(), fWriteCurrentRecurse(0), fReadCurrentRecurse(0)
88  {
89  std::atomic_flag_clear(&fSpinLockFlag);
90  }
91 
92  class WriteGuard {
93  TErrorLock *fLock;
94 
95  public:
96  WriteGuard(TErrorLock &lock, const TCollection *collection, const char *function) : fLock(&lock)
97  {
98  fLock->Lock(collection, function);
99  }
100  ~WriteGuard() { fLock->Unlock(); }
101  };
102 
103  class ReadGuard {
104  TErrorLock *fLock;
105 
106  public:
107  ReadGuard(TErrorLock &lock, const TCollection *collection, const char *function) : fLock(&lock)
108  {
109  fLock->ReadLock(collection, function);
110  }
111  ~ReadGuard() { fLock->ReadUnlock(); }
112  };
113  };
114 
115  mutable TErrorLock fLock; //! Special 'lock' to detect multiple access to a collection.
116 
117 #define R__COLLECTION_WRITE_GUARD() TCollection::TErrorLock::WriteGuard wg(fLock, this, __PRETTY_FUNCTION__)
118 #define R__COLLECTION_READ_GUARD() TCollection::TErrorLock::ReadGuard rg(fLock, this, __PRETTY_FUNCTION__)
119 
120 #define R__COLLECTION_ITER_GUARD(collection) \
121  TCollection::TErrorLock::ReadGuard rg(collection->fLock, collection, __PRETTY_FUNCTION__)
122 
123 #else
124 
125 #define R__COLLECTION_WRITE_GUARD()
126 #define R__COLLECTION_READ_GUARD()
127 #define R__COLLECTION_ITER_GUARD(collection)
128 
129 #endif
130 
131 private:
132  static TCollection *fgCurrentCollection; //used by macro R__FOR_EACH
133  static TObjectTable *fgGarbageCollection; //used by garbage collector
134  static Bool_t fgEmptyingGarbage; //used by garbage collector
135  static Int_t fgGarbageStack; //used by garbage collector
136 
137  TCollection(const TCollection &); //private and not-implemented, collections
138  void operator=(const TCollection &); //are too complex to be automatically copied
139 
140 protected:
141  enum EStatusBits {
142  kIsOwner = BIT(14),
143  // BIT(15) is used by TClonesArray and TMap
144  kUseRWLock = BIT(16)
145  };
146 
147  TString fName; //name of the collection
148  Int_t fSize; //number of elements in collection
149 
150  TCollection() : fName(), fSize(0) { }
151 
152  virtual void PrintCollectionHeader(Option_t* option) const;
153  virtual const char* GetCollectionEntryName(TObject* entry) const;
154  virtual void PrintCollectionEntry(TObject* entry, Option_t* option, Int_t recurse) const;
155 
156 public:
157  enum { kInitCapacity = 16, kInitHashTableCapacity = 17 };
158 
159  virtual ~TCollection();
160  virtual void Add(TObject *obj) = 0;
161  void AddVector(TObject *obj1, ...);
162  virtual void AddAll(const TCollection *col);
163  Bool_t AssertClass(TClass *cl) const;
164  void Browse(TBrowser *b);
165  Int_t Capacity() const { return fSize; }
166  virtual void Clear(Option_t *option="") = 0;
167  virtual TObject *Clone(const char *newname="") const;
168  Int_t Compare(const TObject *obj) const;
169  Bool_t Contains(const char *name) const { return FindObject(name) != 0; }
170  Bool_t Contains(const TObject *obj) const { return FindObject(obj) != 0; }
171  virtual void Delete(Option_t *option="") = 0;
172  virtual void Draw(Option_t *option="");
173  virtual void Dump() const ;
174  virtual TObject *FindObject(const char *name) const;
175  TObject *operator()(const char *name) const;
176  virtual TObject *FindObject(const TObject *obj) const;
177  virtual Int_t GetEntries() const { return GetSize(); }
178  virtual const char *GetName() const;
179  virtual TObject **GetObjectRef(const TObject *obj) const = 0;
180  /// Return the *capacity* of the collection, i.e. the current total amount of space that has been allocated so far.
181  /// Same as `Capacity`. Use `GetEntries` to get the number of elements currently in the collection.
182  virtual Int_t GetSize() const { return fSize; }
183  virtual Int_t GrowBy(Int_t delta) const;
184  ULong_t Hash() const { return fName.Hash(); }
185  Bool_t IsArgNull(const char *where, const TObject *obj) const;
186  virtual Bool_t IsEmpty() const { return GetSize() <= 0; }
187  virtual Bool_t IsFolder() const { return kTRUE; }
188  Bool_t IsOwner() const { return TestBit(kIsOwner); }
189  Bool_t IsSortable() const { return kTRUE; }
190  virtual void ls(Option_t *option="") const ;
191  virtual Bool_t Notify();
192  virtual TIterator *MakeIterator(Bool_t dir = kIterForward) const = 0;
193  virtual TIterator *MakeReverseIterator() const { return MakeIterator(kIterBackward); }
194  virtual void Paint(Option_t *option="");
195  virtual void Print(Option_t *option="") const;
196  virtual void Print(Option_t *option, Int_t recurse) const;
197  virtual void Print(Option_t *option, const char* wildcard, Int_t recurse=1) const;
198  virtual void Print(Option_t *option, TPRegexp& regexp, Int_t recurse=1) const;
199  virtual void RecursiveRemove(TObject *obj);
200  virtual TObject *Remove(TObject *obj) = 0;
201  virtual void RemoveAll(TCollection *col);
202  void RemoveAll() { Clear(); }
203  void SetCurrentCollection();
204  void SetName(const char *name) { fName = name; }
205  virtual void SetOwner(Bool_t enable = kTRUE);
206  virtual bool UseRWLock();
207  virtual Int_t Write(const char *name=0, Int_t option=0, Int_t bufsize=0);
208  virtual Int_t Write(const char *name=0, Int_t option=0, Int_t bufsize=0) const;
209 
210  R__ALWAYS_INLINE Bool_t IsUsingRWLock() const { return TestBit(TCollection::kUseRWLock); }
211 
212  static TCollection *GetCurrentCollection();
213  static void StartGarbageCollection();
214  static void GarbageCollect(TObject *obj);
215  static void EmptyGarbageCollection();
216 
217  TIter begin() const;
218  TIter end() const;
219 
220  ClassDef(TCollection,3) //Collection abstract base class
221 };
222 
223 
224 //////////////////////////////////////////////////////////////////////////
225 // //
226 // TIter //
227 // //
228 // Iterator wrapper. Type of iterator used depends on type of //
229 // collection. //
230 // //
231 //////////////////////////////////////////////////////////////////////////
232 
233 class TIter {
234 
235 private:
236  TIterator *fIterator; //collection iterator
237 
238 protected:
239  TIter() : fIterator(nullptr) { }
240 
241 public:
242  TIter(const TCollection *col, Bool_t dir = kIterForward)
243  : fIterator(col ? col->MakeIterator(dir) : 0) { }
244  TIter(TIterator *it) : fIterator(it) { }
245  TIter(const TIter &iter);
246  TIter &operator=(const TIter &rhs);
247  virtual ~TIter() { SafeDelete(fIterator); }
248  TObject *operator()() { return Next(); }
249  TObject *Next() { return fIterator ? fIterator->Next() : nullptr; }
250  const TCollection *GetCollection() const { return fIterator ? fIterator->GetCollection() : nullptr; }
251  Option_t *GetOption() const { return fIterator ? fIterator->GetOption() : ""; }
252  void Reset() { if (fIterator) fIterator->Reset(); }
253  TIter &operator++() { Next(); return *this; }
254  Bool_t operator==(const TIter &aIter) const {
255  if (fIterator == nullptr)
256  return aIter.fIterator == nullptr || **aIter.fIterator == nullptr;
257  if (aIter.fIterator == nullptr)
258  return fIterator == nullptr || **fIterator == nullptr;
259  return *fIterator == *aIter.fIterator;
260  }
261  Bool_t operator!=(const TIter &aIter) const {
262  return !(*this == aIter);
263  }
264  TObject *operator*() const { return fIterator ? *(*fIterator): nullptr; }
265  TIter &Begin();
266  static TIter End();
267 
268  ClassDef(TIter,0) //Iterator wrapper
269 };
270 
271 template <class T>
272 class TIterCategory: public TIter, public std::iterator_traits<typename T::Iterator_t> {
273 
274 public:
275  TIterCategory(const TCollection *col, Bool_t dir = kIterForward) : TIter(col, dir) { }
276  TIterCategory(TIterator *it) : TIter(it) { }
277  virtual ~TIterCategory() { }
278  TIterCategory &Begin() { TIter::Begin(); return *this; }
279  static TIterCategory End() { return TIterCategory(static_cast<TIterator*>(nullptr)); }
280 };
281 
282 
283 inline TIter TCollection::begin() const { return ++(TIter(this)); }
284 inline TIter TCollection::end() const { return TIter::End(); }
285 
286 namespace ROOT {
287 namespace Internal {
288 
289 const TCollection &EmptyCollection();
290 bool ContaineeInheritsFrom(TClass *cl, TClass *base);
291 
292 /// @brief Internal help class implmenting an iterator for TRangeDynCast.
293 template <class Containee> // Containee must derive from TObject.
294 class TRangeDynCastIterator : public TIter {
295  static_assert(std::is_base_of<TObject, Containee>::value, "Containee type must inherit from TObject");
296 
297  /// This is a workaround against ClassDefInline not supporting classes
298  /// missing their default constructor or having them private.
299  template <class T>
300  friend class ROOT::Internal::ClassDefGenerateInitInstanceLocalInjector;
301 
302  TRangeDynCastIterator() = default;
303 
304 public:
305  using TIter::TIter;
306  TRangeDynCastIterator(const TIter &iter) : TIter(iter) {}
307 
308  Containee *operator()() = delete;
309 
310  Containee *Next() { return dynamic_cast<Containee *>(TIter::Next()); }
311  Containee *operator*() const { return dynamic_cast<Containee *>(TIter::operator*()); }
312 
313  ClassDefInline(TRangeDynCastIterator, 0);
314 };
315 
316 } // namespace Internal
317 
318 namespace Detail {
319 
320 /// @brief TTypedIter is a typed version of TIter.
321 ///
322 /// This requires the collection to contains elements of the type requested
323 /// (or a derived class). Any deviation from this expectation
324 /// will only be caught/reported by an assert in debug builds.
325 ///
326 /// This is best used with a TClonesArray, for other cases prefered TRangeDynCast.
327 ///
328 /// The typical use is:
329 /// ```{.cpp}
330 /// TTypedIter<TBaseClass> next(tbaseClassClonesArrayPtr);
331 /// while(auto bcl = next()) {
332 /// ... use bcl as a TBaseClass*
333 /// }
334 /// ```
335 template <class Containee> // Containee must derive from TObject.
336 class TTypedIter : public TIter {
337  static_assert(std::is_base_of<TObject, Containee>::value, "Containee type must inherit from TObject");
338 
339  /// This is a workaround against ClassDefInline not supporting classes
340  /// missing their default constructor or having them private.
341  template <class T>
342  friend class ROOT::Internal::ClassDefGenerateInitInstanceLocalInjector;
343 
344  TTypedIter() = default;
345 
346  static Containee *StaticCast(TObject *obj)
347  {
348  assert(!obj || ROOT::Internal::ContaineeInheritsFrom(obj->IsA(), Containee::Class()));
349  return static_cast<Containee *>(obj);
350  }
351 
352 public:
353  using TIter::TIter;
354  TTypedIter(const TIter &iter) : TIter(iter) {}
355 
356  Containee *operator()() { return StaticCast(TIter::Next()); }
357  Containee *Next() { return StaticCast(TIter::Next()); }
358  Containee *operator*() const { return StaticCast(TIter::operator*()); }
359 
360  ClassDefInline(TTypedIter, 0);
361 };
362 
363 /// @brief TRangeStaticCast is an adaptater class that allows the typed iteration
364 /// through a TCollection. This requires the collection to contains element
365 /// of the type requested (or a derived class). Any deviation from this expectation
366 /// will only be caught/reported by an assert in debug builds.
367 ///
368 /// This is best used with a TClonesArray, for other cases prefered TRangeDynCast.
369 ///
370 /// The typical use is:
371 /// ```{.cpp}
372 /// for(auto bcl : TRangeStaticCast<TBaseClass>( *tbaseClassClonesArrayPtr )) {
373 /// assert(bcl && bcl->IsA()->InheritsFrom(TBaseClass::Class()));
374 /// ... use bcl as a TBaseClass*
375 /// }
376 /// for(auto bcl : TRangeStaticCast<TBaseClass>( tbaseClassClonesArrayPtr )) {
377 /// assert(bcl && bcl->IsA()->InheritsFrom(TBaseClass::Class()));
378 /// ... use bcl as a TBaseClass*
379 /// }
380 /// ```
381 template <class T>
382 class TRangeStaticCast {
383  const TCollection &fCollection;
384 
385 public:
386  TRangeStaticCast(const TCollection &col) : fCollection(col) {}
387  TRangeStaticCast(const TCollection *col) : fCollection(col != nullptr ? *col : ROOT::Internal::EmptyCollection()) {}
388 
389  TTypedIter<T> begin() const { return fCollection.begin(); }
390  TTypedIter<T> end() const { return fCollection.end(); }
391 };
392 
393 } // namespace Detail
394 } // namespace ROOT
395 
396 /// @brief TRangeDynCast is an adaptater class that allows the typed iteration
397 /// through a TCollection.
398 ///
399 /// The typical use is:
400 /// ```{.cpp}
401 /// for(auto bcl : TRangeDynCast<TBaseClass>( *cl->GetListOfBases() )) {
402 /// if (!bcl) continue;
403 /// ... use bcl as a TBaseClass*
404 /// }
405 /// for(auto bcl : TRangeDynCast<TBaseClass>( cl->GetListOfBases() )) {
406 /// if (!bcl) continue;
407 /// ... use bcl as a TBaseClass*
408 /// }
409 /// ```
410 template <class T>
411 class TRangeDynCast {
412  const TCollection &fCollection;
413 
414 public:
415  TRangeDynCast(const TCollection &col) : fCollection(col) {}
416  TRangeDynCast(const TCollection *col) : fCollection(col != nullptr ? *col : ROOT::Internal::EmptyCollection()) {}
417 
418  ROOT::Internal::TRangeDynCastIterator<T> begin() const { return fCollection.begin(); }
419  ROOT::Internal::TRangeDynCastIterator<T> end() const { return fCollection.end(); }
420 };
421 
422 // Zero overhead macros in case not compiled with thread support
423 #if defined (_REENTRANT) || defined (WIN32)
424 
425 #define R__COLL_COND_MUTEX(mutex) this->IsUsingRWLock() ? mutex : nullptr
426 
427 #define R__COLLECTION_READ_LOCKGUARD(mutex) ::ROOT::TReadLockGuard _R__UNIQUE_(R__readguard)(R__COLL_COND_MUTEX(mutex))
428 #define R__COLLECTION_READ_LOCKGUARD_NAMED(name,mutex) ::ROOT::TReadLockGuard _NAME2_(R__readguard,name)(R__COLL_COND_MUTEX(mutex))
429 
430 #define R__COLLECTION_WRITE_LOCKGUARD(mutex) ::ROOT::TWriteLockGuard _R__UNIQUE_(R__readguard)(R__COLL_COND_MUTEX(mutex))
431 #define R__COLLECTION_WRITE_LOCKGUARD_NAMED(name,mutex) ::ROOT::TWriteLockGuard _NAME2_(R__readguard,name)(R__COLL_COND_MUTEX(mutex))
432 
433 #else
434 
435 #define R__COLLECTION_READ_LOCKGUARD(mutex) (void)mutex
436 #define R__COLLECTION_COLLECTION_READ_LOCKGUARD_NAMED(name,mutex) (void)mutex
437 
438 #define R__COLLECTION_WRITE_LOCKGUARD(mutex) (void)mutex
439 #define R__COLLECTION_WRITE_LOCKGUARD_NAMED(name,mutex) (void)mutex
440 
441 #endif
442 
443 //---- R__FOR_EACH macro -------------------------------------------------------
444 
445 // Macro to loop over all elements of a list of type "type" while executing
446 // procedure "proc" on each element
447 
448 #define R__FOR_EACH(type,proc) \
449  SetCurrentCollection(); \
450  TIter _NAME3_(nxt_,type,proc)(TCollection::GetCurrentCollection()); \
451  type *_NAME3_(obj_,type,proc); \
452  while ((_NAME3_(obj_,type,proc) = (type*) _NAME3_(nxt_,type,proc)())) \
453  _NAME3_(obj_,type,proc)->proc
454 
455 #endif