// @(#)root/cont:$Id$
// Author: Rene Brun   28/09/2001

 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *

// TProcessID
// A TProcessID identifies a ROOT job in a unique way in time and space.
// The TProcessID title consists of a TUUID object which provides a globally
// unique identifier (for more see TUUID.h).
// A TProcessID is automatically created by the TROOT constructor.
// When a TFile contains referenced objects (see TRef), the TProcessID
// object is written to the file.
// If a file has been written in multiple sessions (same machine or not),
// a TProcessID is written for each session.
// These objects are used by the class TRef to uniquely identified
// any TObject pointed by a TRef.
// When a referenced object is read from a file (its bit kIsReferenced is set),
// this object is entered into the objects table of the corresponding TProcessID.
// Each TFile has a list of TProcessIDs (see TFile::fProcessIDs) also
// accessible via TProcessID::fgPIDs (for all files).
// When this object is deleted, it is removed from the table via the cleanup
// mechanism invoked by the TObject destructor.
// Each TProcessID has a table (TObjArray *fObjects) that keeps track
// of all referenced objects. If a referenced object has a fUniqueID set,
// a pointer to this unique object may be found via fObjects->At(fUniqueID).
// In the same way, when a TRef::GetObject is called, GetObject uses
// its own fUniqueID to find the pointer to the referenced object.
// See TProcessID::GetObjectWithID and PutObjectWithID.
// When a referenced object is deleted, its slot in fObjects is set to null.
// See also TProcessUUID: a specialized TProcessID to manage the single list
// of TUUIDs.

#include "TProcessID.h"
#include "TROOT.h"
#include "TObjArray.h"
#include "TExMap.h"
#include "TVirtualMutex.h"
#include "TError.h"

TObjArray  *TProcessID::fgPIDs   = 0; //pointer to the list of TProcessID
TProcessID *TProcessID::fgPID    = 0; //pointer to the TProcessID of the current session
UInt_t      TProcessID::fgNumber = 0; //Current referenced object instance count
TExMap     *TProcessID::fgObjPIDs= 0; //Table (pointer,pids)

static inline ULong_t Void_Hash(const void *ptr)
   // Return hash value for this object.

   return TString::Hash(&ptr, sizeof(void*));

   // Default constructor.

   fCount = 0;
   fObjects = 0;

   // Destructor.

   delete fObjects;
   fObjects = 0;

TProcessID *TProcessID::AddProcessID()
   // Static function to add a new TProcessID to the list of PIDs.


   if (fgPIDs && fgPIDs->GetEntriesFast() >= 65534) {
      if (fgPIDs->GetEntriesFast() == 65534) {
         ::Warning("TProcessID::AddProcessID","Maximum number of TProcessID (65535) is almost reached (one left).  TRef will stop being functional when the limit is reached.");
      } else {
         ::Fatal("TProcessID::AddProcessID","Maximum number of TProcessID (65535) has been reached.  TRef are not longer functional.");

   TProcessID *pid = new TProcessID();

   if (!fgPIDs) {
      fgPID  = pid;
      fgPIDs = new TObjArray(10);
   UShort_t apid = fgPIDs->GetEntriesFast();

   // if (apid == 0) for(int incr=0; incr < 65533; ++incr) fgPIDs->Add(0); // NOTE: DEBUGGING ONLY MUST BE REMOVED!
   char name[20];
   TUUID u;
   //apid = fgPIDs->GetEntriesFast();
   return pid;

UInt_t TProcessID::AssignID(TObject *obj)
   // static function returning the ID assigned to obj
   // If the object is not yet referenced, its kIsReferenced bit is set
   // and its fUniqueID set to the current number of referenced objects so far.


   UInt_t uid = obj->GetUniqueID() & 0xffffff;
   if (obj == fgPID->GetObjectWithID(uid)) return uid;
   if (obj->TestBit(kIsReferenced)) {
      return uid;
   if (fgNumber >= 16777215) {
      // This process id is 'full', we need to use a new one.
      fgPID = AddProcessID();
      fgNumber = 0;
      for(Int_t i = 0; i < fgPIDs->GetLast()+1; ++i) {
         TProcessID *pid = (TProcessID*)fgPIDs->At(i);
         if (pid && pid->fObjects && pid->fObjects->GetEntries() == 0) {
   uid = fgNumber;
   // if (fgNumber<10) fgNumber = 16777213; // NOTE: DEBUGGING ONLY MUST BE REMOVED!
   if ( fgPID->GetUniqueID() < 255 ) {
      obj->SetUniqueID( (uid & 0xffffff) + (fgPID->GetUniqueID()<<24) );
   } else {
      obj->SetUniqueID( (uid & 0xffffff) + 0xff000000 /* 255 << 24 */ );
   return uid;

void TProcessID::CheckInit()
   // Initialize fObjects.
   if (!fObjects) fObjects = new TObjArray(100);

void TProcessID::Cleanup()
   // static function (called by TROOT destructor) to delete all TProcessIDs


   delete fgPIDs;
   fgPIDs = 0;

void TProcessID::Clear(Option_t *)
   // delete the TObjArray pointing to referenced objects
   // this function is called by TFile::Close("R")

   if (GetUniqueID()>254 && fObjects && fgObjPIDs) {
      // We might have many references registered in the map
      for(Int_t i = 0; i < fObjects->GetSize(); ++i) {
         TObject *obj = fObjects->UncheckedAt(i);
         if (obj) {
            ULong64_t hash = Void_Hash(obj);
            (*fObjects)[i] = 0;
   delete fObjects; fObjects = 0;

Int_t TProcessID::DecrementCount()

   // the reference fCount is used to delete the TProcessID
   // in the TFile destructor when fCount = 0

   if (fCount < 0) fCount = 0;
   return fCount;

TProcessID *TProcessID::GetProcessID(UShort_t pid)
   // static function returning a pointer to TProcessID number pid in fgPIDs

   return (TProcessID*)fgPIDs->At(pid);

UInt_t TProcessID::GetNProcessIDs()
   // Return the (static) number of process IDs.
   return fgPIDs ? fgPIDs->GetLast()+1 : 0;

TProcessID *TProcessID::GetProcessWithUID(UInt_t uid, const void *obj)
   // static function returning a pointer to TProcessID with its pid
   // encoded in the highest byte of uid


   Int_t pid = (uid>>24)&0xff;
   if (pid==0xff) {
      // Look up the pid in the table (pointer,pid)
      if (fgObjPIDs==0) return 0;
      ULong_t hash = Void_Hash(obj);
      pid = fgObjPIDs->GetValue(hash,(Long_t)obj);
   return (TProcessID*)fgPIDs->At(pid);

TProcessID *TProcessID::GetProcessWithUID(const TObject *obj)
   // static function returning a pointer to TProcessID with its pid
   // encoded in the highest byte of obj->GetUniqueID()

   return GetProcessWithUID(obj->GetUniqueID(),obj);

TProcessID *TProcessID::GetSessionProcessID()
   // static function returning the pointer to the session TProcessID

   return fgPID;

Int_t TProcessID::IncrementCount()
   // Increase the reference count to this object.

   if (!fObjects) fObjects = new TObjArray(100);
   return fCount;

UInt_t TProcessID::GetObjectCount()
   // Return the current referenced object count
   // fgNumber is incremented everytime a new object is referenced

   return fgNumber;

TObject *TProcessID::GetObjectWithID(UInt_t uidd)
   //returns the TObject with unique identifier uid in the table of objects

   Int_t uid = uidd & 0xffffff;  //take only the 24 lower bits

   if (fObjects==0 || uid >= fObjects->GetSize()) return 0;
   return fObjects->UncheckedAt(uid);

TProcessID *TProcessID::GetPID()
   //static: returns pointer to current TProcessID

   return fgPID;

TObjArray *TProcessID::GetPIDs()
   //static: returns array of TProcessIDs

   return fgPIDs;

Bool_t TProcessID::IsValid(TProcessID *pid)
   // static function. return kTRUE if pid is a valid TProcessID


   if (fgPIDs==0) return kFALSE;
   if (fgPIDs->IndexOf(pid) >= 0) return kTRUE;
   if (pid == (TProcessID*)gROOT->GetUUIDs())  return kTRUE;
   return kFALSE;

void TProcessID::PutObjectWithID(TObject *obj, UInt_t uid)
   //stores the object at the uid th slot in the table of objects
   //The object uniqueid is set as well as its kMustCleanup bit

   if (uid == 0) uid = obj->GetUniqueID() & 0xffffff;

   if (!fObjects) fObjects = new TObjArray(100);

   if ( (obj->GetUniqueID()&0xff000000)==0xff000000 ) {
      // We have more than 255 pids we need to store this
      // pointer in the table(pointer,pid) since there is no
      // more space in fUniqueID
      if (fgObjPIDs==0) fgObjPIDs = new TExMap;
      ULong_t hash = Void_Hash(obj);

      // We use operator() rather than Add() because
      // if the address has already been registered, we want to
      // update it's uniqueID (this can easily happen when the
      // referenced object have been stored in a TClonesArray.
      (*fgObjPIDs)(hash, (Long_t)obj) = GetUniqueID();

void TProcessID::RecursiveRemove(TObject *obj)
   // called by the object destructor
   // remove reference to obj from the current table if it is referenced

   if (!fObjects) return;
   if (!obj->TestBit(kIsReferenced)) return;
   UInt_t uid = obj->GetUniqueID() & 0xffffff;
   if (obj == GetObjectWithID(uid)) {
      if (fgObjPIDs) {
         ULong64_t hash = Void_Hash(obj);
      (*fObjects)[uid] = 0; // Avoid recalculation of fLast (compared to ->RemoveAt(uid))

void TProcessID::SetObjectCount(UInt_t number)
   // static function to set the current referenced object count
   // fgNumber is incremented everytime a new object is referenced

   fgNumber = number;