Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TProcessID.cxx
Go to the documentation of this file.
1 // @(#)root/cont:$Id$
2 // Author: Rene Brun 28/09/2001
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 /** \class TProcessID
13 \ingroup Base
14 
15 A TProcessID identifies a ROOT job in a unique way in time and space.
16 The TProcessID title consists of a TUUID object which provides a globally
17 unique identifier (for more see TUUID.h).
18 
19 A TProcessID is automatically created by the TROOT constructor.
20 When a TFile contains referenced objects (see TRef), the TProcessID
21 object is written to the file.
22 If a file has been written in multiple sessions (same machine or not),
23 a TProcessID is written for each session.
24 These objects are used by the class TRef to uniquely identified
25 any TObject pointed by a TRef.
26 
27 When a referenced object is read from a file (its bit kIsReferenced is set),
28 this object is entered into the objects table of the corresponding TProcessID.
29 Each TFile has a list of TProcessIDs (see TFile::fProcessIDs) also
30 accessible via TProcessID::fgPIDs (for all files).
31 When this object is deleted, it is removed from the table via the cleanup
32 mechanism invoked by the TObject destructor.
33 
34 Each TProcessID has a table (TObjArray *fObjects) that keeps track
35 of all referenced objects. If a referenced object has a fUniqueID set,
36 a pointer to this unique object may be found via fObjects->At(fUniqueID).
37 In the same way, when a TRef::GetObject is called, GetObject uses
38 its own fUniqueID to find the pointer to the referenced object.
39 See TProcessID::GetObjectWithID and PutObjectWithID.
40 
41 When a referenced object is deleted, its slot in fObjects is set to null.
42 //
43 See also TProcessUUID: a specialized TProcessID to manage the single list
44 of TUUIDs.
45 */
46 
47 #include "TProcessID.h"
48 #include "TROOT.h"
49 #include "TObjArray.h"
50 #include "TExMap.h"
51 #include "TVirtualMutex.h"
52 #include "TError.h"
53 
54 TObjArray *TProcessID::fgPIDs = 0; //pointer to the list of TProcessID
55 TProcessID *TProcessID::fgPID = 0; //pointer to the TProcessID of the current session
56 std::atomic_uint TProcessID::fgNumber(0); //Current referenced object instance count
57 TExMap *TProcessID::fgObjPIDs= 0; //Table (pointer,pids)
58 ClassImp(TProcessID);
59 
60 static std::atomic<TProcessID *> gIsValidCache;
61 using PIDCacheContent_t = std::pair<Int_t, TProcessID*>;
62 static std::atomic<PIDCacheContent_t *> gGetProcessWithUIDCache;
63 
64 ////////////////////////////////////////////////////////////////////////////////
65 /// Return hash value for this object.
66 
67 static inline ULong_t Void_Hash(const void *ptr)
68 {
69  return TString::Hash(&ptr, sizeof(void*));
70 }
71 
72 ////////////////////////////////////////////////////////////////////////////////
73 /// Default constructor.
74 
75 TProcessID::TProcessID()
76 {
77  // MSVC doesn't support fSpinLock=ATOMIC_FLAG_INIT; in the class definition
78  // and Apple LLVM version 7.3.0 (clang-703.0.31) warns about:
79  // fLock(ATOMIC_FLAG_INIT)
80  // ^~~~~~~~~~~~~~~~
81  // c++/v1/atomic:1779:26: note: expanded from macro 'ATOMIC_FLAG_INIT'
82  // #define ATOMIC_FLAG_INIT {false}
83  // So reset the flag instead.
84  std::atomic_flag_clear( &fLock );
85 
86  fCount = 0;
87  fObjects = 0;
88 }
89 
90 ////////////////////////////////////////////////////////////////////////////////
91 /// Destructor.
92 
93 TProcessID::~TProcessID()
94 {
95  delete fObjects;
96  fObjects = 0;
97 
98  TProcessID *This = this; // We need a referencable value for the 1st argument
99  gIsValidCache.compare_exchange_strong(This, nullptr);
100 
101  auto current = gGetProcessWithUIDCache.load();
102  if (current && current->second == this) {
103  gGetProcessWithUIDCache.compare_exchange_strong(current, nullptr);
104  delete current;
105  }
106 
107  R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
108  fgPIDs->Remove(this);
109 }
110 
111 ////////////////////////////////////////////////////////////////////////////////
112 /// Static function to add a new TProcessID to the list of PIDs.
113 
114 TProcessID *TProcessID::AddProcessID()
115 {
116  R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
117 
118  if (fgPIDs && fgPIDs->GetEntriesFast() >= 65534) {
119  if (fgPIDs->GetEntriesFast() == 65534) {
120  ::Warning("TProcessID::AddProcessID","Maximum number of TProcessID (65535) is almost reached (one left). TRef will stop being functional when the limit is reached.");
121  } else {
122  ::Fatal("TProcessID::AddProcessID","Maximum number of TProcessID (65535) has been reached. TRef are not longer functional.");
123  }
124  }
125 
126  TProcessID *pid = new TProcessID();
127 
128  if (!fgPIDs) {
129  fgPID = pid;
130  fgPIDs = new TObjArray(10);
131  gROOT->GetListOfCleanups()->Add(fgPIDs);
132  }
133  UShort_t apid = fgPIDs->GetEntriesFast();
134  pid->IncrementCount();
135 
136  fgPIDs->Add(pid);
137  // if (apid == 0) for(int incr=0; incr < 65533; ++incr) fgPIDs->Add(0); // NOTE: DEBUGGING ONLY MUST BE REMOVED!
138  char name[20];
139  snprintf(name,20,"ProcessID%d",apid);
140  pid->SetName(name);
141  pid->SetUniqueID((UInt_t)apid);
142  TUUID u;
143  //apid = fgPIDs->GetEntriesFast();
144  pid->SetTitle(u.AsString());
145  return pid;
146 }
147 
148 ////////////////////////////////////////////////////////////////////////////////
149 /// static function returning the ID assigned to obj
150 /// If the object is not yet referenced, its kIsReferenced bit is set
151 /// and its fUniqueID set to the current number of referenced objects so far.
152 
153 UInt_t TProcessID::AssignID(TObject *obj)
154 {
155  R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
156 
157  UInt_t uid = obj->GetUniqueID() & 0xffffff;
158  if (obj == fgPID->GetObjectWithID(uid)) return uid;
159  if (obj->TestBit(kIsReferenced)) {
160  fgPID->PutObjectWithID(obj,uid);
161  return uid;
162  }
163  if (fgNumber >= 16777215) {
164  // This process id is 'full', we need to use a new one.
165  fgPID = AddProcessID();
166  fgNumber = 0;
167  for(Int_t i = 0; i < fgPIDs->GetLast()+1; ++i) {
168  TProcessID *pid = (TProcessID*)fgPIDs->At(i);
169  if (pid && pid->fObjects && pid->fObjects->GetEntries() == 0) {
170  pid->Clear();
171  }
172  }
173  }
174  fgNumber++;
175  obj->SetBit(kIsReferenced);
176  uid = fgNumber;
177  // if (fgNumber<10) fgNumber = 16777213; // NOTE: DEBUGGING ONLY MUST BE REMOVED!
178  if ( fgPID->GetUniqueID() < 255 ) {
179  obj->SetUniqueID( (uid & 0xffffff) + (fgPID->GetUniqueID()<<24) );
180  } else {
181  obj->SetUniqueID( (uid & 0xffffff) + 0xff000000 /* 255 << 24 */ );
182  }
183  fgPID->PutObjectWithID(obj,uid);
184  return uid;
185 }
186 
187 ////////////////////////////////////////////////////////////////////////////////
188 /// Initialize fObjects.
189 
190 void TProcessID::CheckInit()
191 {
192  if (!fObjects) {
193  while (fLock.test_and_set(std::memory_order_acquire)); // acquire lock
194  if (!fObjects) fObjects = new TObjArray(100);
195  fLock.clear(std::memory_order_release);
196  }
197 }
198 
199 ////////////////////////////////////////////////////////////////////////////////
200 /// static function (called by TROOT destructor) to delete all TProcessIDs
201 
202 void TProcessID::Cleanup()
203 {
204  R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
205 
206  fgPIDs->Delete();
207  gROOT->GetListOfCleanups()->Remove(fgPIDs);
208  delete fgPIDs;
209  fgPIDs = 0;
210 }
211 
212 ////////////////////////////////////////////////////////////////////////////////
213 /// delete the TObjArray pointing to referenced objects
214 /// this function is called by TFile::Close("R")
215 
216 void TProcessID::Clear(Option_t *)
217 {
218  if (GetUniqueID()>254 && fObjects && fgObjPIDs) {
219  // We might have many references registered in the map
220  for(Int_t i = 0; i < fObjects->GetSize(); ++i) {
221  TObject *obj = fObjects->UncheckedAt(i);
222  if (obj) {
223  ULong64_t hash = Void_Hash(obj);
224  fgObjPIDs->Remove(hash,(Long64_t)obj);
225  (*fObjects)[i] = 0;
226  }
227  }
228  }
229  delete fObjects; fObjects = 0;
230 }
231 
232 ////////////////////////////////////////////////////////////////////////////////
233 /// The reference fCount is used to delete the TProcessID
234 /// in the TFile destructor when fCount = 0
235 
236 Int_t TProcessID::DecrementCount()
237 {
238  fCount--;
239  if (fCount < 0) fCount = 0;
240  return fCount;
241 }
242 
243 ////////////////////////////////////////////////////////////////////////////////
244 /// static function returning a pointer to TProcessID number pid in fgPIDs
245 
246 TProcessID *TProcessID::GetProcessID(UShort_t pid)
247 {
248  return (TProcessID*)fgPIDs->At(pid);
249 }
250 
251 ////////////////////////////////////////////////////////////////////////////////
252 /// Return the (static) number of process IDs.
253 
254 UInt_t TProcessID::GetNProcessIDs()
255 {
256  return fgPIDs ? fgPIDs->GetLast()+1 : 0;
257 }
258 
259 ////////////////////////////////////////////////////////////////////////////////
260 /// static function returning a pointer to TProcessID with its pid
261 /// encoded in the highest byte of uid
262 
263 TProcessID *TProcessID::GetProcessWithUID(UInt_t uid, const void *obj)
264 {
265 
266  Int_t pid = (uid>>24)&0xff;
267  if (pid==0xff) {
268  // Look up the pid in the table (pointer,pid)
269  if (fgObjPIDs==0) return 0;
270  ULong_t hash = Void_Hash(obj);
271 
272  R__READ_LOCKGUARD(ROOT::gCoreMutex);
273  pid = fgObjPIDs->GetValue(hash,(Long_t)obj);
274  return (TProcessID*)fgPIDs->At(pid);
275  } else {
276  auto current = gGetProcessWithUIDCache.load();
277  if (current && current->first == pid)
278  return current->second;
279 
280  R__READ_LOCKGUARD(ROOT::gCoreMutex);
281  auto res = (TProcessID*)fgPIDs->At(pid);
282 
283  auto next = new PIDCacheContent_t(pid, res);
284  auto old = gGetProcessWithUIDCache.exchange(next);
285  delete old;
286 
287  return res;
288  }
289 }
290 
291 ////////////////////////////////////////////////////////////////////////////////
292 /// static function returning a pointer to TProcessID with its pid
293 /// encoded in the highest byte of obj->GetUniqueID()
294 
295 TProcessID *TProcessID::GetProcessWithUID(const TObject *obj)
296 {
297  return GetProcessWithUID(obj->GetUniqueID(),obj);
298 }
299 
300 ////////////////////////////////////////////////////////////////////////////////
301 /// static function returning the pointer to the session TProcessID
302 
303 TProcessID *TProcessID::GetSessionProcessID()
304 {
305  return fgPID;
306 }
307 
308 ////////////////////////////////////////////////////////////////////////////////
309 /// Increase the reference count to this object.
310 
311 Int_t TProcessID::IncrementCount()
312 {
313  CheckInit();
314  ++fCount;
315  return fCount;
316 }
317 
318 ////////////////////////////////////////////////////////////////////////////////
319 /// Return the current referenced object count
320 /// fgNumber is incremented every time a new object is referenced
321 
322 UInt_t TProcessID::GetObjectCount()
323 {
324  return fgNumber;
325 }
326 
327 ////////////////////////////////////////////////////////////////////////////////
328 /// returns the TObject with unique identifier uid in the table of objects
329 
330 TObject *TProcessID::GetObjectWithID(UInt_t uidd)
331 {
332  Int_t uid = uidd & 0xffffff; //take only the 24 lower bits
333 
334  if (fObjects==0 || uid >= fObjects->GetSize()) return 0;
335  return fObjects->UncheckedAt(uid);
336 }
337 
338 ////////////////////////////////////////////////////////////////////////////////
339 /// static: returns pointer to current TProcessID
340 
341 TProcessID *TProcessID::GetPID()
342 {
343  return fgPID;
344 }
345 
346 ////////////////////////////////////////////////////////////////////////////////
347 /// static: returns array of TProcessIDs
348 
349 TObjArray *TProcessID::GetPIDs()
350 {
351  return fgPIDs;
352 }
353 
354 
355 ////////////////////////////////////////////////////////////////////////////////
356 /// static function. return kTRUE if pid is a valid TProcessID
357 
358 Bool_t TProcessID::IsValid(TProcessID *pid)
359 {
360  if (gIsValidCache == pid)
361  return kTRUE;
362 
363  R__READ_LOCKGUARD(ROOT::gCoreMutex);
364 
365  if (fgPIDs==0) return kFALSE;
366  if (fgPIDs->IndexOf(pid) >= 0) {
367  gIsValidCache = pid;
368  return kTRUE;
369  }
370  if (pid == (TProcessID*)gROOT->GetUUIDs()) {
371  gIsValidCache = pid;
372  return kTRUE;
373  }
374  return kFALSE;
375 }
376 
377 ////////////////////////////////////////////////////////////////////////////////
378 /// stores the object at the uid th slot in the table of objects
379 /// The object uniqued is set as well as its kMustCleanup bit
380 
381 void TProcessID::PutObjectWithID(TObject *obj, UInt_t uid)
382 {
383  R__LOCKGUARD_IMT(gROOTMutex); // Lock for parallel TTree I/O
384 
385  if (uid == 0) uid = obj->GetUniqueID() & 0xffffff;
386 
387  if (!fObjects) fObjects = new TObjArray(100);
388  fObjects->AddAtAndExpand(obj,uid);
389 
390  obj->SetBit(kMustCleanup);
391  if ( (obj->GetUniqueID()&0xff000000)==0xff000000 ) {
392  // We have more than 255 pids we need to store this
393  // pointer in the table(pointer,pid) since there is no
394  // more space in fUniqueID
395  if (fgObjPIDs==0) fgObjPIDs = new TExMap;
396  ULong_t hash = Void_Hash(obj);
397 
398  // We use operator() rather than Add() because
399  // if the address has already been registered, we want to
400  // update it's uniqueID (this can easily happen when the
401  // referenced object have been stored in a TClonesArray.
402  (*fgObjPIDs)(hash, (Long_t)obj) = GetUniqueID();
403  }
404 }
405 
406 ////////////////////////////////////////////////////////////////////////////////
407 /// called by the object destructor
408 /// remove reference to obj from the current table if it is referenced
409 
410 void TProcessID::RecursiveRemove(TObject *obj)
411 {
412  if (!fObjects) return;
413  if (!obj->TestBit(kIsReferenced)) return;
414  UInt_t uid = obj->GetUniqueID() & 0xffffff;
415  if (obj == GetObjectWithID(uid)) {
416  R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
417  if (fgObjPIDs) {
418  ULong64_t hash = Void_Hash(obj);
419  fgObjPIDs->Remove(hash,(Long64_t)obj);
420  }
421  (*fObjects)[uid] = 0; // Avoid recalculation of fLast (compared to ->RemoveAt(uid))
422  }
423 }
424 
425 
426 ////////////////////////////////////////////////////////////////////////////////
427 /// static function to set the current referenced object count
428 /// fgNumber is incremented every time a new object is referenced
429 
430 void TProcessID::SetObjectCount(UInt_t number)
431 {
432  fgNumber = number;
433 }