Logo ROOT  
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
20#include <list>
21#include <utility>
22#include <iostream>
23#include <mutex>
24
25//////////////////////////////////////////////////////////////////////////
26// //
27// TCheckHashRecursiveRemoveConsistency //
28// //
29// Utility class to discover whether a class that overload //
30// TObject::Hash also (as required) calls RecursiveRemove in its //
31// destructor. //
32// //
33//////////////////////////////////////////////////////////////////////////
34
35namespace ROOT {
36namespace Internal {
37
39public:
40 struct Value {
43 };
44 using Value_t = Value; // std::pair<ULong_t, TObject*>;
45
46 std::list<Value> fCont;
47 std::mutex fMutex;
48
49public:
50 // Default constructor. Adds object to the list of
51 // cleanups.
53 {
55 gROOT->GetListOfCleanups()->Add(this);
56 }
57
58 // Destructor. This class does not overload
59 // Hash so it can rely on the base class to call
60 // RecursiveRemove (and hence remove this from the list
61 // of cleanups).
63 {
64 // ... unless the mechanism is disabled in which case
65 // we need to do it explicitly.
66 if (!gROOT->MustClean())
67 gROOT->GetListOfCleanups()->Remove(this);
68 }
69
70 void Add(TObject *obj)
71 {
72 obj->SetBit(kMustCleanup);
73 auto hashValue = obj->Hash(); // This might/will take the ROOT lock.
74
75 std::unique_lock<std::mutex> lock(fMutex);
76 fCont.push_back(Value_t{hashValue, obj});
77 }
78
80 {
81 // Since we use std::list, a remove (from another thread)
82 // would invalidate out iterator and taking the write lock
83 // 'only' inside the loop would suspend this thread and lead
84 // another reader or write go on; consequently we would need
85 // to re-find the object we are wanting to remove.
86 std::unique_lock<std::mutex> lock(fMutex);
87
88 // std::cout << "Recursive Remove called for: " << obj << '\n';
89 for (auto p = fCont.begin(); p != fCont.end(); ++p) {
90 if (p->fObjectPtr == obj) {
91 // std::cout << " Found object with hash = " << p->fRecordedHash << '\n';
92 // std::cout << " Current hash = " << obj->Hash() << '\n';
93 if (p->fRecordedHash == obj->Hash())
94 fCont.erase(p);
95 // else
96 // std::cout << " Error: the recorded hash and the one returned by Hash are distinct.\n";
97 break;
98 }
99 }
100 }
101
103 {
104 std::unique_lock<std::mutex> lock(fMutex);
105
106 for (auto p = fCont.begin(); p != fCont.end(); ++p) {
107 if (p->fObjectPtr == obj) {
108 fCont.erase(p);
109 break;
110 }
111 }
112 }
113
114 enum EResult {
118 };
119
121 {
122 if (!classRef.HasDefaultConstructor() || classRef.Property() & kIsAbstract)
123 return kInconclusive; // okay that's probably a false negative ...
124
125 auto size = fCont.size();
126 TObject *obj = (TObject *)classRef.DynamicCast(TObject::Class(), classRef.New(TClass::kDummyNew));
127 if (!obj || (!gROOT->MustClean() && obj->TestBit(kIsReferenced) && obj->GetUniqueID() != 0)) {
128 // Clean up is disable and the object is such that we wont be able to 'mark' it
129 // as needing a clean up anyway, so we can not actually test it.
130 return kInconclusive;
131 }
133 Add(obj);
134 delete obj;
135
136 if (fCont.size() != size) {
137 // std::cerr << "Error: old= " << size << " new=" << fCont.size() << '\n';
138 // std::cerr << "Error " << classRef.GetName() <<
139 // " or one of its base classes override TObject::Hash but does not call TROOT::CallRecursiveRemoveIfNeeded
140 // in its destructor.\n";
141 SlowRemove(obj);
142 return kInconsistent;
143 } else {
144 return kConsistent;
145 }
146 }
147
149 {
150
151 if (classRef.HasLocalHashMember() && CheckRecursiveRemove(classRef) != kConsistent) {
152 return &classRef;
153 }
154
155 for (auto base : ROOT::Detail::TRangeStaticCast<TBaseClass>(classRef.GetListOfBases())) {
156 TClass *baseCl = base->GetClassPointer();
157 TClass *res = FindMissingRecursiveRemove(*baseCl);
158 if (res)
159 return res;
160 }
161 return nullptr;
162 }
163
164 bool VerifyRecursiveRemove(const char *classname)
165 {
166 TClass *classPtr = TClass::GetClass(classname);
167 if (classPtr)
168 return VerifyRecursiveRemove(*classPtr);
169 else
170 return true;
171 }
172
174 {
175 // Use except if the class is non-default/abstract and HasLocalHashMember.
176 if (classRef.fRuntimeProperties) {
177 // We already did this testing for this class.
179 }
180
181 if (classRef.HasLocalHashMember())
182 return CheckRecursiveRemove(classRef);
183
184 EResult baseResult = kConsistent;
185 for (auto base : ROOT::Detail::TRangeStaticCast<TBaseClass>(classRef.GetListOfBases())) {
186 TClass *baseCl = base->GetClassPointer();
187
188 if (baseCl->HasLocalHashMember() &&
189 (!baseCl->HasDefaultConstructor() || baseCl->Property() & kIsAbstract))
190 {
191 // We won't be able to check the base class, we need to (try) to check
192 // this class even-though it does not have a local HashMember.
193 return CheckRecursiveRemove(classRef);
194 }
195 auto baseConsistency = HasConsistentHashMember(*baseCl);
196 if (baseConsistency == kInconsistent) {
197 baseResult = kInconsistent;
198 } else if (baseConsistency == kInconclusive) {
199 return CheckRecursiveRemove(classRef);
200 }
201 }
202 return baseResult;
203 }
204
206 {
207 // If the class does not inherit from TObject, the setup is always 'correct'
208 // (or more exactly does not matter).
209 if (!classRef.IsTObject())
210 return true;
211
212 if (classRef.HasLocalHashMember() &&
213 (!classRef.HasDefaultConstructor() || classRef.Property() & kIsAbstract))
214 // We won't be able to check, so assume the worst but don't issue any
215 // error message.
216 return false;
217
218 if (HasConsistentHashMember(classRef) != kConsistent) {
219 TClass *failing = FindMissingRecursiveRemove(classRef);
220
221 // Because ClassDefInline does not yet support class template on all platforms,
222 // we have no ClassDef and thus can not get a good message from TObject::Error.
223 constexpr const char *funcName = "ROOT::Internal::TCheckHashRecursiveRemoveConsistency::CheckRecursiveRemove";
224 if (failing) {
225 ::Error(funcName,
226 "The class %s overrides TObject::Hash but does not call TROOT::RecursiveRemove in its destructor (seen while checking %s).",
227 failing->GetName(),classRef.GetName());
228 } else {
229 ::Error(funcName, "The class %s "
230 "or one of its base classes override TObject::Hash but does not call "
231 "TROOT::CallRecursiveRemoveIfNeeded in its destructor.\n",
232 classRef.GetName());
233 }
234 return false;
235 }
236 return true;
237 }
238
239 static bool Check(TClass &classRef)
240 {
242 return checker.VerifyRecursiveRemove(classRef);
243 }
244
246};
247
248} // namespace Internal
249} // namespace ROOT
250
251#endif // ROOT__TCheckHashRecursiveRemoveConsistency
unsigned long ULong_t
Definition: CPyCppyy.h:51
void Class()
Definition: Class.C:29
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
@ kIsAbstract
Definition: TDictionary.h:71
#define gROOT
Definition: TROOT.h:404
TRangeStaticCast is an adaptater class that allows the typed iteration through a TCollection.
Definition: TCollection.h:389
void RecursiveRemove(TObject *obj)
Recursively remove this object from a list.
ClassDefInline(TCheckHashRecursiveRemoveConsistency, 0)
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition: TClass.h:80
void * New(ENewType defConstructor=kClassNew, Bool_t quiet=kFALSE) const
Return a pointer to a newly allocated object of this class.
Definition: TClass.cxx:4971
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:4908
@ kDummyNew
Definition: TClass.h:107
TList * GetListOfBases()
Return list containing the TBaseClass(es) of a class.
Definition: TClass.cxx:3629
Bool_t IsTObject() const
Return kTRUE is the class inherits from TObject.
Definition: TClass.cxx:5931
Bool_t HasLocalHashMember() const
Returns true if this class has an definition and/or overload of the member function Hash.
Definition: TClass.cxx:7369
Bool_t HasConsistentHashMember()
Return 'true' if we can guarantee that if this class (or any class in this class inheritance hierarch...
Definition: TClass.h:498
Long_t Property() const
Returns the properties of the TClass as a bit field stored as a Long_t value.
Definition: TClass.cxx:6038
Bool_t HasDefaultConstructor(Bool_t testio=kFALSE) const
Return true if we have access to a constructor usable for I/O.
Definition: TClass.cxx:7341
std::atomic< UChar_t > fRuntimeProperties
Definition: TClass.h:270
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:2955
virtual const char * GetName() const
Returns name of object.
Definition: TNamed.h:47
Mother of all ROOT objects.
Definition: TObject.h:37
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition: TObject.h:187
virtual UInt_t GetUniqueID() const
Return the unique object id.
Definition: TObject.cxx:377
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition: TObject.cxx:696
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:893
virtual ULong_t Hash() const
Return hash value for this object.
Definition: TObject.cxx:435
@ kIsReferenced
if object is referenced by a TRef or TRefArray
Definition: TObject.h:61
@ kMustCleanup
if object destructor must call RecursiveRemove()
Definition: TObject.h:60
void SetRequireCleanup(TObject &obj)
Definition: TROOT.h:379
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
Definition: RNumpyDS.hxx:30
const char * Value
Definition: TXMLSetup.cxx:73