Logo ROOT   6.14/05
Reference Guide
TCheckHashRecursiveRemoveConsistency.h
Go to the documentation of this file.
1 // @(#)root/meta:$Id$
2 // Author: Rene Brun 07/01/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_TCheckHashRecursiveRemoveConsistency
13 #define ROOT_TCheckHashRecursiveRemoveConsistency
14 
15 #include "TBaseClass.h"
16 #include "TClass.h"
17 #include "TError.h"
18 #include "TMethod.h"
19 #include "TROOT.h"
20 
21 #include <list>
22 
23 #include <iostream>
24 #include <mutex>
25 
26 //////////////////////////////////////////////////////////////////////////
27 // //
28 // TCheckHashRecursiveRemoveConsistency //
29 // //
30 // Utility class to discover whether a class that overload //
31 // TObject::Hash also (as required) calls RecursiveRemove in its //
32 // destructor. //
33 // //
34 //////////////////////////////////////////////////////////////////////////
35 
36 namespace ROOT {
37 namespace Internal {
38 
40 public:
41  struct Value {
44  };
45  using Value_t = Value; // std::pair<ULong_t, TObject*>;
46 
47  std::list<Value> fCont;
48  std::mutex fMutex;
49 
50 public:
51  // Default constructor. Adds object to the list of
52  // cleanups.
54  {
56  gROOT->GetListOfCleanups()->Add(this);
57  }
58 
59  // Destructor. This class does not overload
60  // Hash so it can rely on the base class to call
61  // RecursiveRemove (and hence remove this from the list
62  // of cleanups).
64  {
65  // ... unless the mechanism is disabled in which case
66  // we need to do it explicitly.
67  if (!gROOT->MustClean())
68  gROOT->GetListOfCleanups()->Remove(this);
69  }
70 
71  void Add(TObject *obj)
72  {
73  obj->SetBit(kMustCleanup);
74  auto hashValue = obj->Hash(); // This might/will take the ROOT lock.
75 
76  std::unique_lock<std::mutex> lock(fMutex);
77  fCont.push_back(Value_t{hashValue, obj});
78  }
79 
81  {
82  // Since we use std::list, a remove (from another thread)
83  // would invalidate out iterator and taking the write lock
84  // 'only' inside the loop would suspend this thread and lead
85  // another reader or write go on; consequently we would need
86  // to re-find the object we are wanting to remove.
87  std::unique_lock<std::mutex> lock(fMutex);
88 
89  // std::cout << "Recursive Remove called for: " << obj << '\n';
90  for (auto p = fCont.begin(); p != fCont.end(); ++p) {
91  if (p->fObjectPtr == obj) {
92  // std::cout << " Found object with hash = " << p->fRecordedHash << '\n';
93  // std::cout << " Current hash = " << obj->Hash() << '\n';
94  if (p->fRecordedHash == obj->Hash())
95  fCont.erase(p);
96  // else
97  // std::cout << " Error: the recorded hash and the one returned by Hash are distinct.\n";
98  break;
99  }
100  }
101  }
102 
103  void SlowRemove(TObject *obj)
104  {
105  std::unique_lock<std::mutex> lock(fMutex);
106 
107  for (auto p = fCont.begin(); p != fCont.end(); ++p) {
108  if (p->fObjectPtr == obj) {
109  fCont.erase(p);
110  break;
111  }
112  }
113  }
114 
115  enum EResult {
119  };
120 
122  {
123  if (!classRef.HasDefaultConstructor() || classRef.Property() & kIsAbstract)
124  return kInconclusive; // okay that's probably a false negative ...
125 
126  auto size = fCont.size();
127  TObject *obj = (TObject *)classRef.DynamicCast(TObject::Class(), classRef.New(TClass::kDummyNew));
128  if (!obj || (!gROOT->MustClean() && obj->TestBit(kIsReferenced) && obj->GetUniqueID() != 0)) {
129  // Clean up is disable and the object is such that we wont be able to 'mark' it
130  // as needing a clean up anyway, so we can not actually test it.
131  return kInconclusive;
132  }
134  Add(obj);
135  delete obj;
136 
137  if (fCont.size() != size) {
138  // std::cerr << "Error: old= " << size << " new=" << fCont.size() << '\n';
139  // std::cerr << "Error " << classRef.GetName() <<
140  // " or one of its base classes override TObject::Hash but does not call TROOT::CallRecursiveRemoveIfNeeded
141  // in its destructor.\n";
142  SlowRemove(obj);
143  return kInconsistent;
144  } else {
145  return kConsistent;
146  }
147  }
148 
150  {
151 
152  if (classRef.HasLocalHashMember() && CheckRecursiveRemove(classRef) != kConsistent) {
153  return &classRef;
154  }
155 
156  for (auto base : ROOT::Detail::TRangeStaticCast<TBaseClass>(classRef.GetListOfBases())) {
157  TClass *baseCl = base->GetClassPointer();
158  TClass *res = FindMissingRecursiveRemove(*baseCl);
159  if (res)
160  return res;
161  }
162  return nullptr;
163  }
164 
165  bool VerifyRecursiveRemove(const char *classname)
166  {
167  TClass *classPtr = TClass::GetClass(classname);
168  if (classPtr)
169  return VerifyRecursiveRemove(*classPtr);
170  else
171  return true;
172  }
173 
175  {
176  // Use except if the class is non-default/abstract and HasLocalHashMember.
177  if (classRef.fRuntimeProperties) {
178  // We already did this testing for this class.
179  return classRef.HasConsistentHashMember() ? kConsistent : kInconsistent;
180  }
181 
182  if (classRef.HasLocalHashMember())
183  return CheckRecursiveRemove(classRef);
184 
185  EResult baseResult = kConsistent;
186  for (auto base : ROOT::Detail::TRangeStaticCast<TBaseClass>(classRef.GetListOfBases())) {
187  TClass *baseCl = base->GetClassPointer();
188 
189  if (baseCl->HasLocalHashMember() &&
190  (!baseCl->HasDefaultConstructor() || baseCl->Property() & kIsAbstract))
191  {
192  // We won't be able to check the base class, we need to (try) to check
193  // this class even-though it does not have a local HashMember.
194  return CheckRecursiveRemove(classRef);
195  }
196  auto baseConsistency = HasConsistentHashMember(*baseCl);
197  if (baseConsistency == kInconsistent) {
198  baseResult = kInconsistent;
199  } else if (baseConsistency == kInconclusive) {
200  return CheckRecursiveRemove(classRef);
201  }
202  }
203  return baseResult;
204  }
205 
207  {
208  // If the class does not inherit from TObject, the setup is always 'correct'
209  // (or more exactly does not matter).
210  if (!classRef.IsTObject())
211  return true;
212 
213  if (classRef.HasLocalHashMember() &&
214  (!classRef.HasDefaultConstructor() || classRef.Property() & kIsAbstract))
215  // We won't be able to check, so assume the worst but don't issue any
216  // error message.
217  return false;
218 
219  if (HasConsistentHashMember(classRef) != kConsistent) {
220  TClass *failing = FindMissingRecursiveRemove(classRef);
221 
222  // Because ClassDefInline does not yet support class template on all platforms,
223  // we have no ClassDef and thus can not get a good message from TObject::Error.
224  constexpr const char *funcName = "ROOT::Internal::TCheckHashRecursiveRemoveConsistency::CheckRecursiveRemove";
225  if (failing) {
226  ::Error(funcName,
227  "The class %s overrides TObject::Hash but does not call TROOT::RecursiveRemove in its destructor (seen while checking %s).",
228  failing->GetName(),classRef.GetName());
229  } else {
230  ::Error(funcName, "The class %s "
231  "or one of its base classes override TObject::Hash but does not call "
232  "TROOT::CallRecursiveRemoveIfNeeded in its destructor.\n",
233  classRef.GetName());
234  }
235  return false;
236  }
237  return true;
238  }
239 
240  static bool Check(TClass &classRef)
241  {
243  return checker.VerifyRecursiveRemove(classRef);
244  }
245 
247 };
248 
249 } // namespace Internal
250 } // namespace ROOT
251 
252 #endif // ROOT__TCheckHashRecursiveRemoveConsistency
virtual const char * GetName() const
Returns name of object.
Definition: TNamed.h:47
virtual UInt_t GetUniqueID() const
Return the unique object id.
Definition: TObject.cxx:375
TList * GetListOfBases()
Return list containing the TBaseClass(es) of a class.
Definition: TClass.cxx:3507
virtual ULong_t Hash() const
Return hash value for this object.
Definition: TObject.cxx:433
Namespace for new ROOT classes and functions.
Definition: StringConv.hxx:21
#define gROOT
Definition: TROOT.h:410
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition: TObject.h:172
TRangeStaticCast is an adaptater class that allows the typed iteration through a TCollection.
Definition: TCollection.h:382
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition: TObject.cxx:694
void Class()
Definition: Class.C:29
Bool_t HasLocalHashMember() const
Bool_t HasDefaultConstructor() const
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:880
The ROOT global object gROOT contains a list of all defined classes.
Definition: TClass.h:75
std::atomic< UChar_t > fRuntimeProperties
Definition: TClass.h:238
Long_t Property() const
Set TObject::fBits and fStreamerType to cache information about the class.
Definition: TClass.cxx:5768
if object destructor must call RecursiveRemove()
Definition: TObject.h:60
Bool_t HasConsistentHashMember()
Return &#39;true&#39; if we can guarantee that if this class (or any class in this class inheritance hierarch...
Definition: TClass.h:466
ClassDefInline(TCheckHashRecursiveRemoveConsistency, 0)
unsigned long ULong_t
Definition: RtypesCore.h:51
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition: TClass.cxx:2887
Mother of all ROOT objects.
Definition: TObject.h:37
Bool_t IsTObject() const
Return kTRUE is the class inherits from TObject.
Definition: TClass.cxx:5668
void RecursiveRemove(TObject *obj)
Recursively remove this object from a list.
if object is referenced by a TRef or TRefArray
Definition: TObject.h:61
void * DynamicCast(const TClass *base, void *obj, Bool_t up=kTRUE)
Cast obj of this class type up to baseclass cl if up is true.
Definition: TClass.cxx:4729
void SetRequireCleanup(TObject &obj)
Definition: TROOT.h:385
const char * Value
Definition: TXMLSetup.cxx:72
void * New(ENewType defConstructor=kClassNew, Bool_t quiet=kFALSE) const
Return a pointer to a newly allocated object of this class.
Definition: TClass.cxx:4792