    // @(#)root/cont:$Name:  $:$Id: TProcessID.cxx,v 1.27 2005/06/23 06:24:27 brun Exp $
    // 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 "TFile.h"
    #include "TObjArray.h"
    #include "TExMap.h"
    #include "TVirtualMutex.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)
    ClassImp(TProcessID)

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

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

    //______________________________________________________________________________
     TProcessID::TProcessID()
    {
       fCount = 0;
       fObjects = 0;
    }

    //______________________________________________________________________________
     TProcessID::~TProcessID()
    {
       delete fObjects;
       fObjects = 0;
       R__LOCKGUARD2(gROOTMutex);
       fgPIDs->Remove(this);
    }

    //______________________________________________________________________________
     TProcessID::TProcessID(const TProcessID &ref) : TNamed(ref)
    {
       // TProcessID copy ctor.
    }

    //______________________________________________________________________________
     TProcessID *TProcessID::AddProcessID()
    {
       // static function to add a new TProcessID to the list of PIDs

       R__LOCKGUARD2(gROOTMutex);

       TProcessID *pid = new TProcessID();

       if (!fgPIDs) {
          fgPID  = pid;
          fgPIDs = new TObjArray(10);
          gROOT->GetListOfCleanups()->Add(fgPIDs);
       }
       UShort_t apid = fgPIDs->GetEntriesFast();
       pid->IncrementCount();

       fgPIDs->Add(pid);
       char name[20];
       sprintf(name,"ProcessID%d",apid);
       pid->SetName(name);
       TUUID u;
       apid = fgPIDs->GetEntriesFast();
       pid->SetTitle(u.AsString());
       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.

       R__LOCKGUARD2(gROOTMutex);

       UInt_t uid = obj->GetUniqueID() & 0xffffff;
       if (obj == fgPID->GetObjectWithID(uid)) return uid;
       if (obj->TestBit(kIsReferenced)) {
          fgPID->PutObjectWithID(obj,uid);
          return uid;
       }
       fgNumber++;
       obj->SetBit(kIsReferenced);
       uid = fgNumber;
       obj->SetUniqueID(uid);
       fgPID->PutObjectWithID(obj,uid);
       return uid;
    }

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

       R__LOCKGUARD2(gROOTMutex);

       fgPIDs->Delete();
       gROOT->GetListOfCleanups()->Remove(fgPIDs);
       delete fgPIDs;
    }

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

       delete fObjects; fObjects = 0;
    }

    //______________________________________________________________________________
     Int_t TProcessID::DecrementCount()
    {

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

       fCount--;
       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);
    }

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

       R__LOCKGUARD2(gROOTMutex);

       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::GetSessionProcessID()
    {
       // static function returning the pointer to the session TProcessID

       return fgPID;
    }

    //______________________________________________________________________________
     Int_t TProcessID::IncrementCount()
    {

       if (!fObjects) fObjects = new TObjArray(100);
       fCount++;
       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
       //if (!fObjects) fObjects = new TObjArray(100);

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

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

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

       R__LOCKGUARD2(gROOTMutex);

       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 (!fObjects) fObjects = new TObjArray(100);

       if (uid == 0) uid = obj->GetUniqueID() & 0xffffff;
       fObjects->AddAtAndExpand(obj,uid);
       obj->SetBit(kMustCleanup);
       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 obejct have been stored in a TClonesArray.
          (*fgObjPIDs)(hash, (Long_t)obj) = GetUniqueID();
       }
    }

    //______________________________________________________________________________
     TProcessID  *TProcessID::ReadProcessID(UShort_t pidf, TFile *file)
    {
       //The TProcessID with number pidf is read from file. (static function)
       //If the object is not already entered in the gROOT list, it is added.

       if (!file) {
          if (!pidf) return fgPID; //may happen when cloning an object
          return 0;
       }
       TObjArray *pids = file->GetListOfProcessIDs();
       TProcessID *pid = 0;
       if (pidf < pids->GetSize()) pid = (TProcessID *)pids->UncheckedAt(pidf);
       if (pid) {
          if (!pid->fObjects) pid->fObjects = new TObjArray(100);
          return pid;
       }

       //check if fProcessIDs[uid] is set in file
       //if not set, read the process uid from file
       char pidname[32];
       sprintf(pidname,"ProcessID%d",pidf);
       TDirectory *dirsav = gDirectory;
       file->cd();
       pid = (TProcessID *)file->Get(pidname);
       if (dirsav) dirsav->cd();
       if (gDebug > 0) {
          printf("ReadProcessID, name=%s, file=%s, pid=%lx\n",pidname,file->GetName(),(Long_t)pid);
       }
       if (!pid) {
          //file->Error("ReadProcessID","Cannot find %s in file %s",pidname,file->GetName());
          return 0;
       }
          //check that a similar pid is not already registered in fgPIDs
       TIter next(fgPIDs);
       TProcessID *p;
       while ((p = (TProcessID*)next())) {
          if (!strcmp(p->GetTitle(),pid->GetTitle())) {
             delete pid;
             pids->AddAtAndExpand(p,pidf);
             p->IncrementCount();
             return p;
          }
       }
       pids->AddAtAndExpand(pid,pidf);
       pid->IncrementCount();
       fgPIDs->Add(pid);
       Int_t ind = fgPIDs->IndexOf(pid);
       pid->SetUniqueID((UInt_t)ind);
       return pid;
    }

    //______________________________________________________________________________
     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)) fObjects->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;
    }

    //______________________________________________________________________________
     UShort_t TProcessID::WriteProcessID(TProcessID *pidd, TFile *file)
    {
       // static function
       // Check if the ProcessID pid is already in the file.
       // if not, add it and return the index  number in the local file list

       if (!file) return 0;
       TProcessID *pid = pidd;
       if (!pid) pid = fgPID;
       TObjArray *pids = file->GetListOfProcessIDs();
       Int_t npids = file->GetNProcessIDs();
       for (Int_t i=0;i<npids;i++) {
          if (pids->At(i) == pid) return (UShort_t)i;
       }

       TDirectory *dirsav = gDirectory;
       file->cd();
       file->SetBit(TFile::kHasReferences);
       pids->AddAtAndExpand(pid,npids);
       pid->IncrementCount();
       char name[32];
       sprintf(name,"ProcessID%d",npids);
       pid->Write(name);
       file->IncrementProcessIDs();
       if (gDebug > 0) {
          printf("WriteProcessID, name=%s, file=%s\n",name,file->GetName());
       }
       if (dirsav) dirsav->cd();
       return (UShort_t)npids;
    }