Logo ROOT   6.16/01
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
36namespace ROOT {
37namespace Internal {
38
40public:
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
50public:
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
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.
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
void Class()
Definition: Class.C:29
unsigned long ULong_t
Definition: RtypesCore.h:51
@ kIsAbstract
Definition: TDictionary.h:71
#define gROOT
Definition: TROOT.h:410
TRangeStaticCast is an adaptater class that allows the typed iteration through a TCollection.
Definition: TCollection.h:382
void RecursiveRemove(TObject *obj)
Recursively remove this object from a list.
ClassDefInline(TCheckHashRecursiveRemoveConsistency, 0)
The ROOT global object gROOT contains a list of all defined classes.
Definition: TClass.h:75
void * New(ENewType defConstructor=kClassNew, Bool_t quiet=kFALSE) const
Return a pointer to a newly allocated object of this class.
Definition: TClass.cxx:4824
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:4761
Bool_t HasDefaultConstructor() const
@ kDummyNew
Definition: TClass.h:101
TList * GetListOfBases()
Return list containing the TBaseClass(es) of a class.
Definition: TClass.cxx:3505
Bool_t IsTObject() const
Return kTRUE is the class inherits from TObject.
Definition: TClass.cxx:5700
Bool_t HasLocalHashMember() const
Bool_t HasConsistentHashMember()
Return 'true' if we can guarantee that if this class (or any class in this class inheritance hierarch...
Definition: TClass.h:471
Long_t Property() const
Set TObject::fBits and fStreamerType to cache information about the class.
Definition: TClass.cxx:5800
std::atomic< UChar_t > fRuntimeProperties
Definition: TClass.h:238
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:2885
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:172
virtual UInt_t GetUniqueID() const
Return the unique object id.
Definition: TObject.cxx:375
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition: TObject.cxx:694
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:880
virtual ULong_t Hash() const
Return hash value for this object.
Definition: TObject.cxx:433
@ 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:385
Namespace for new ROOT classes and functions.
Definition: StringConv.hxx:21
const char * Value
Definition: TXMLSetup.cxx:72