Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TRef.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 TRef
13\ingroup Base
14
15Persistent Reference link to a TObject
16A TRef is a lightweight object pointing to any TObject.
17This object can be used instead of normal C++ pointers in case
18
19 - the referenced object R and the pointer P are not written to the same file
20 - P is read before R
21 - R and P are written to different Tree branches
22
23When a top level object (eg Event *event) is a tree/graph of many objects,
24the normal ROOT Streaming mechanism ensures that only one copy of each object
25in the tree/graph is written to the output buffer to avoid circular
26dependencies.
27
28However if the object event is split into several files or into several
29branches of one or more Trees, normal C++ pointers cannot be used because
30each I/O operation will write the referenced objects.
31
32When a TRef is used to point to a TObject *robj, for example in a class with
33~~~ {.cpp}
34 TRef fRef;
35~~~
36one can do:
37~~~ {.cpp}
38 fRef = robj; //to set the pointer
39~~~
40This TRef and robj can be written with two different I/O calls
41in the same or different files, in the same or different branches of a Tree.
42
43If the TRef is read and the referenced object has not yet been read,
44the TRef will return a null pointer. As soon as the referenced object
45will be read, the TRef will point to it. If the referenced object is
46contained in a TTree it can be auto-loaded using the TBranchRef mechanism,
47which is set up by simply calling TTree::BranchRef().
48
49TRef also supports the complex situation where a TFile is updated
50multiple times on the same machine or a different machine.
51
52## How does it work
53
54A TRef is itself a TObject with an additional transient pointer fPID.
55When the statement fRef = robj is executed, the following actions happen:
56
57 - The pointer fPID is set to the current TProcessID.
58 - The current ObjectNumber (see below) is incremented by one.
59 - robj::fUniqueID is set to ObjectNumber.
60 - In the fPID object, the element fObjects[ObjectNumber] is set to robj
61 - ref::fUniqueID is also set to ObjectNumber.
62
63After having set fRef, one can immediately return the value of robj
64using fRef.GetObject(). This function returns directly fObjects[fUniqueID]
65from the fPID object.
66
67When the TRef is written, the process id number pidf of fPID is written
68in addition to the TObject part of TRef (fBits,fUniqueID).
69
70When the TRef is read, its pointer fPID is set to the value
71stored in the TObjArray of TFile::fProcessIDs (fProcessIDs[pidf]).
72The pidf is stored as a UShort_t limiting a file to 65535 distinct
73ProcessID objects.
74
75The pidf is stored in the bits 24->31 of the fUniqueID of the TRef.
76This implies that the number of TRefs in a single ProcessID should not
77exceed 2**24 = 16777216. For pidf greater than 254, the value 0xff is
78stored in those bits and we use the table TProcessID::fgObjPIDs which
79links the referenced object's address to its ProcessID.
80
81See section "ObjectNumber" below for a recipe to minimize the object count.
82If the object-number exceeds this limit, it could be the sign that:
83
84 - The object count is never reset (see below)
85 - TRef is misused.
86
87When a referenced object robj is written, TObject::Streamer writes
88in addition to the standard (fBits,fUniqueID) the pidf.
89When this robj is read by TObject::Streamer, the pidf is read.
90At this point, robj is entered into the table of objects of the TProcessID
91corresponding to pidf.
92
93### WARNING1:
94If MyClass is the class of the referenced object, The TObject
95part of MyClass must be Streamed. One should not
96call MyClass::Class()->IgnoreTObjectStreamer()
97
98### WARNING2:
99A TRef cannot point to another TRef.
100
101## ObjectNumber
102
103When an object is referenced (see TRef assignment operator or TRefArray::Add)
104a unique identifier is computed and stored in both the fUniqueID of the
105referenced and referencing object. This uniqueID is computed by incrementing
106by one the static global in TProcessID::fgNumber. fUniqueID is some sort of
107serial object number in the current session. One can retrieve at any time
108the current value of fgNumber by calling the static function TProcessID::GetObjectCount
109or set this number via TProcessID::SetObjectCount.
110
111To avoid a growing table of fObjects in TProcessID, in case, for example,
112one processes many events in a loop, it might be necessary to reset the
113ObjectNumber at the end of processing of one event. See an example
114in $ROOTSYS/test/Event.cxx (look at function Build).
115
116The value of ObjectNumber (say saveNumber=TProcessID::GetObjectCount()) may be
117saved at the beginning of one event and reset to this original value
118at the end of the event via TProcessID::SetObjectCount(saveNumber). These
119actions may be stacked.
120
121## Action on Demand
122
123The normal behaviour of a TRef has been described above. In addition,
124TRef supports also "Actions on Demand". It may happen that the object
125referenced is not yet in memory, on a separate file or not yet computed.
126In this case TRef is able to automatically execute an action:
127
128 - call to a compiled function (static function of member function)
129 - call to an interpreted function
130 - execution of a C++ script
131
132How to select this option?
133In the definition of the TRef data member in the original class, do:
134~~~ {.cpp}
135 TRef fRef; //EXEC:execName. points to something
136~~~
137When the special keyword "EXEC:" is found in the comment field of the member,
138the next string is assumed to be the name of a TExec object.
139When a file is connected, the dictionary of the classes on the file
140is read in memory (see TFile::ReadStreamerInfo). When the TStreamerElement
141object is read, a TExec object is automatically created with the name
142specified after the keyword "EXEC:" in case a TExec with a same name does
143not already exist.
144
145The action to be executed via this TExec can be specified with:
146
147 - a call to the TExec constructor, if the constructor is called before
148 opening the file.
149 - a call to TExec::SetAction at any time.
150 One can compute a pointer to an existing TExec with a name with:
151~~~ {.cpp}
152 TExec *myExec = gROOT->GetExec(execName);
153 myExec->SetAction(actionCommand);
154~~~
155 where actionCommand is a string containing a C++ instruction. Examples:
156~~~ {.cpp}
157 myExec->SetAction("LoadHits()");
158 myExec->SetAction(".x script.C");
159~~~
160
161When a TRef is dereferenced via TRef::GetObject, its TExec will be
162automatically executed. In the function/script being executed, one or more
163of the following actions can be executed:
164
165 - load a file containing the referenced object. This function typically
166 looks in the file catalog (GRID).
167 - compute a pointer to the referenced object and communicate this pointer
168 back to the calling function TRef::GetObject via:
169~~~ {.cpp}
170 TRef::SetStaticObject(object).
171~~~
172 When the TExec is called, it has access to the dereferencing TRef
173 by calling GetStaticObject() (TRef::GetObject() sets fgObject to "this"
174 before the call to TExec). This can be useful for accessing the TRef's
175 fUniqueID.
176
177As soon as an object is returned to GetObject, the fUniqueID of the TRef is set
178to the fUniqueID of the referenced object. At the next call to GetObject,
179the pointer stored in fPid:fObjects[fUniqueID] will be returned directly.
180
181An example of action on demand is shown in $ROOTSYS/test/Event.h with
182the member:
183~~~ {.cpp}
184 TRef fWebHistogram; //EXEC:GetWebHistogram
185~~~
186When calling fWebHistogram.GetObject(), the function GetObject
187will automatically invoke a script GetWebHistogram.C via the interpreter.
188
189An example of a GetWebHistogram.C script is shown below
190~~~ {.cpp}
191 void GetWebHistogram() {
192 TFile *f= TFile::Open("http://root.cern/files/pippa.root");
193 f->cd("DM/CJ");
194 TH1 *h6 = (TH1*)gDirectory->Get("h6");
195 h6->SetDirectory(0);
196 delete f;
197 TRef::SetStaticObject(h6);
198 }
199~~~
200In the above example, a call to fWebHistogram.GetObject() executes the
201script with the function GetWebHistogram. This script connects a file
202with histograms: pippa.root on the ROOT Web site and returns the object h6
203to TRef::GetObject.
204
205Note that if the definition of the TRef fWebHistogram had been:
206~~~ {.cpp}
207 TRef fWebHistogram; //EXEC:GetWebHistogram()
208~~~
209then, the compiled or interpreted function GetWebHistogram() would have
210been called instead of the C++ script GetWebHistogram.C
211
212## Special case of a TRef pointing to an object with a TUUID
213
214If the referenced object has a TUUID, its bit kHasUUID has been set.
215This case is detected by the TRef assignment operator.
216(For example, TFile and TDirectory have a TUUID)
217The TRef fPID points directly to the single object TProcessUUID (deriving
218from TProcessID) and managing the list of TUUIDs for a process.
219The TRef kHasUUID bit is set and its fUniqueID is set to the fUniqueID
220of the referenced object.
221
222When the TRef is streamed to a buffer, the corresponding TUUID is also
223streamed with the TRef. When a TRef is read from a buffer, the corresponding
224TUUID is also read and entered into the global list of TUUIDs (if not
225already there). The TRef fUniqueID is set to the UUIDNumber.
226see TProcessUUID for more details.
227
228## Array of TRef
229
230The special class TRefArray should be used to store multiple references.
231A TRefArray has one single pointer fPID for all objects in the array.
232It has a dynamic compact table of fUniqueIDs. Use a TRefArray rather
233then a collection of TRefs if all TRefs stem from the same process.
234
235Example:
236
237Suppose a TObjArray *mytracks containing a list of Track objects
238Suppose a TRefArray *pions containing pointers to the pion tracks in mytracks.
239This list is created with statements like: pions->Add(track);
240Suppose a TRefArray *muons containing pointers to the muon tracks in mytracks.
241The 3 arrays mytracks,pions and muons may be written separately.
242*/
243
244#include "TRef.h"
245#include "TROOT.h"
246#include "TBuffer.h"
247#include "TClass.h"
248#include "TProcessUUID.h"
249#include "TRefTable.h"
250#include "TObjArray.h"
251#include "TExec.h"
252#include "TObjString.h"
253
254TObjArray *TRef::fgExecs = nullptr;
255TObject *TRef::fgObject = nullptr;
256
258
259////////////////////////////////////////////////////////////////////////////////
260/// Create a ref to obj.
261
263{
264 *this = obj;
265}
266
267////////////////////////////////////////////////////////////////////////////////
268/// TRef copy ctor.
269
270TRef::TRef(const TRef &ref) : TObject(ref)
271{
272 *this = ref;
273}
274
275////////////////////////////////////////////////////////////////////////////////
276/// Assign object to reference.
277
279{
280 UInt_t uid = 0;
281 fPID = nullptr;
282 if (obj) {
283 if (obj->IsA()->CanIgnoreTObjectStreamer()) {
284 Error("operator= ","Class: %s IgnoreTObjectStreamer. Cannot reference object",obj->ClassName());
285 return;
286 }
287 if (obj->TestBit(kHasUUID)) {
288 fPID = gROOT->GetUUIDs();
289 obj->SetBit(kIsReferenced);
291 uid = obj->GetUniqueID();
292 } else {
293 if (!obj->TestBit(kIsReferenced)) {
295 }
296 uid = obj->GetUniqueID();
299 }
300 }
301 SetUniqueID(uid);
302}
303
304////////////////////////////////////////////////////////////////////////////////
305/// TRef assignment operator.
306
308{
309 if (this != &ref) {
311 fPID = ref.fPID;
313 }
314 return *this;
315}
316
317////////////////////////////////////////////////////////////////////////////////
318/// Return kTRUE if r1 and r2 point to the same object.
319
320Bool_t operator==(const TRef &r1, const TRef &r2)
321{
322 if (r1.GetPID() == r2.GetPID() && r1.GetUniqueID() == r2.GetUniqueID()) return kTRUE;
323 else return kFALSE;
324}
325
326////////////////////////////////////////////////////////////////////////////////
327/// Return kTRUE if r1 and r2 do not point to the same object.
328
329Bool_t operator!=(const TRef &r1, const TRef &r2)
330{
331 if (r1.GetPID() == r2.GetPID() && r1.GetUniqueID() == r2.GetUniqueID()) return kFALSE;
332 else return kTRUE;
333}
334
335////////////////////////////////////////////////////////////////////////////////
336/// If Exec with name does not exist in the list of Execs, it is created.
337/// returns the index of the Exec in the list.
338
340{
341#ifdef R__COMPLETE_MEM_TERMINATION
342 if (!fgExecs) GetListOfExecs();
343#else
344 if (!fgExecs) fgExecs = new TObjArray(10);
345#endif
346
347 TExec *exec = (TExec*)fgExecs->FindObject(name);
348 if (!exec) {
349 // we register this Exec to the list of Execs.
350 exec = new TExec(name,"");
351 fgExecs->Add(exec);
352 }
353 return fgExecs->IndexOf(exec);
354}
355
356////////////////////////////////////////////////////////////////////////////////
357/// Return a pointer to the static TObjArray holding the list of Execs.
358
360{
361#ifdef R__COMPLETE_MEM_TERMINATION
362 static TObjArray listOfExecs(10);
363 if (!fgExecs) {
364 listOfExecs.SetOwner(kTRUE);
365 fgExecs = &listOfExecs;
366 }
367#else
368 if (!fgExecs) fgExecs = new TObjArray(10);
369#endif
370 return fgExecs;
371
372}
373
374////////////////////////////////////////////////////////////////////////////////
375/// Return a pointer to the referenced object.
376
378{
379 //TObject *obj = 0;
380 if (!fPID) return nullptr;
381 if (!TProcessID::IsValid(fPID)) return nullptr;
382 UInt_t uid = GetUniqueID();
383
384 //the reference may be in the TRefTable
386 if (table) {
388 table->SetUID(uid, fPID);
389 table->Notify();
390 }
391
392 //Try to find the object from the table of the corresponding PID
393 TObject *obj = fPID->GetObjectWithID(uid);
394
395 //if object not found, then exec action if an action has been defined
396 if (!obj) {
397 //execid in the first 8 bits
398 Int_t execid = TestBits(0xff0000);
399 if (execid > 0) {
400 execid = execid>>16;
402 TExec *exec = (TExec*)fgExecs->At(execid-1);
403 if (exec) {
404 //we expect the object to be returned via TRef::SetStaticObject
405 fgObject = const_cast<TRef*>(this);
406 exec->Exec();
407 if ((const TRef*)fgObject != this)
408 obj = fgObject;
409 else obj=nullptr;
410 if (obj){
411 uid = TProcessID::AssignID(obj);
412 ((TRef*)this)->SetUniqueID(uid);
413 fPID->PutObjectWithID(obj,uid);
414 } else {
415 //well may be the Exec has loaded the object
416 obj = fPID->GetObjectWithID(uid);
417 }
418 }
419 }
420 }
421
422 return obj;
423}
424
425////////////////////////////////////////////////////////////////////////////////
426/// Store the exec number (in the ROOT list of Execs)
427/// into the fBits of this TRef.
428
429void TRef::SetAction(const char *name)
430{
432 if (!exec) {
433 Error("SetAction","Unknown TExec: %s",name);
434 return;
435 }
436 Int_t execid = 1 + fgExecs->IndexOf(exec);
437 SetBit(execid << 16);
438}
439
440////////////////////////////////////////////////////////////////////////////////
441/// Find the action to be executed in the dictionary of the parent class
442/// and store the corresponding exec number into fBits.
443/// This function searches a data member in the class of parent with an
444/// offset corresponding to this.
445/// If a comment "TEXEC:" is found in the comment field of the data member,
446/// the function stores the exec identifier of the exec statement
447/// following this keyword.
448
450{
451 if (!parent) return;
452 if (gDirectory) gDirectory->SetTRefAction(this,parent);
453}
454
455 ///////////////////////////////////////////////////////////////////////////////
456 /// Returns the static object.
457
459 return fgObject;
460}
461
462////////////////////////////////////////////////////////////////////////////////
463/// static Obsolete function kept for back compatibility.
464/// In the near future will print a Warning, then will be deleted.
465
467{
468 SetStaticObject(obj);
469}
470
471////////////////////////////////////////////////////////////////////////////////
472/// Static function to set the object found on the Action on Demand function.
473/// This function may be called by the user in the function called
474/// when a "EXEC:" keyword is specified in the data member field of the TRef.
475/// The function can get access to the dereferencing TRef (i.e. this)using
476/// the static function GetStaticObject().
477
479{
480 fgObject = obj;
481}
482
483////////////////////////////////////////////////////////////////////////////////
484/// Stream an object of class TRef.
485
487{
488 UShort_t pidf;
489 if (R__b.IsReading()) {
490 TObject::Streamer(R__b);
491 if (TestBit(kHasUUID)) {
492 TString s;
493 s.Streamer(R__b);
494 TProcessUUID *pid = gROOT->GetUUIDs();
495 UInt_t number = pid->AddUUID(s.Data());
496 fPID = pid;
497 SetUniqueID(number);
498 if (gDebug > 1) {
499 printf("Reading TRef (HasUUID) uid=%d, obj=%zx\n",GetUniqueID(),(size_t)GetObject());
500 }
501 } else {
502 R__b >> pidf;
503 pidf += R__b.GetPidOffset();
504 fPID = R__b.ReadProcessID(pidf);
505 //The execid has been saved in the unique id of the TStreamerElement
506 //being read by TStreamerElement::Streamer
507 //The current element (fgElement) is set as a static global
508 //by TStreamerInfo::ReadBuffer (Clones) when reading this TRef
509 Int_t execid = R__b.GetTRefExecId();
510 if (execid) SetBit(execid<<16);
511 if (gDebug > 1) {
512 printf("Reading TRef, pidf=%d, fPID=%zx, uid=%d, obj=%zx\n",pidf,(size_t)fPID,GetUniqueID(),(size_t)GetObject());
513 }
514 }
515 } else {
516 TObject::Streamer(R__b);
517
518 if (TestBit(kHasUUID)) {
519 TObjString *objs = gROOT->GetUUIDs()->FindUUID(GetUniqueID());
520 objs->String().Streamer(R__b);
521 if (gDebug > 1) {
522 printf("Writing TRef (HasUUID) uid=%d, obj=%zx\n",GetUniqueID(),(size_t)GetObject());
523 }
524 } else {
525 pidf = R__b.WriteProcessID(fPID);
526 R__b << pidf;
527 if (gDebug > 1) {
528 printf("Writing TRef, pidf=%d, fPID=%zx, uid=%d, obj=%zx\n",pidf,(size_t)fPID,GetUniqueID(),(size_t)GetObject());
529 }
530 }
531 }
532}
unsigned short UShort_t
Definition RtypesCore.h:40
constexpr Bool_t kFALSE
Definition RtypesCore.h:94
constexpr Bool_t kTRUE
Definition RtypesCore.h:93
#define ClassImp(name)
Definition Rtypes.h:382
#define gDirectory
Definition TDirectory.h:384
char name[80]
Definition TGX11.cxx:110
Int_t gDebug
Definition TROOT.cxx:597
#define gROOT
Definition TROOT.h:406
Bool_t operator==(const TRef &r1, const TRef &r2)
Return kTRUE if r1 and r2 point to the same object.
Definition TRef.cxx:320
Bool_t operator!=(const TRef &r1, const TRef &r2)
Return kTRUE if r1 and r2 do not point to the same object.
Definition TRef.cxx:329
#define R__WRITE_LOCKGUARD(mutex)
Buffer base class used for serializing objects.
Definition TBuffer.h:43
virtual UInt_t GetTRefExecId()=0
virtual TProcessID * ReadProcessID(UShort_t pidf)=0
Return the current Process-ID.
Definition TBuffer.cxx:344
virtual UShort_t WriteProcessID(TProcessID *pid)=0
Always return 0 (current processID).
Definition TBuffer.cxx:353
virtual UShort_t GetPidOffset() const =0
Bool_t IsReading() const
Definition TBuffer.h:86
Bool_t CanIgnoreTObjectStreamer()
Definition TClass.h:393
virtual void SetOwner(Bool_t enable=kTRUE)
Set whether this collection is the owner (enable==true) of its content.
TExec is a utility class that can be used to execute a C++ command when some event happens in a pad.
Definition TExec.h:26
virtual void Exec(const char *command="")
Execute the command referenced by this object.
Definition TExec.cxx:143
An array of TObjects.
Definition TObjArray.h:31
Int_t IndexOf(const TObject *obj) const override
TObject * At(Int_t idx) const override
Definition TObjArray.h:164
TObject * FindObject(const char *name) const override
Find an object in this collection using its name.
void Add(TObject *obj) override
Definition TObjArray.h:68
Collectable string class.
Definition TObjString.h:28
TString & String()
Definition TObjString.h:48
Mother of all ROOT objects.
Definition TObject.h:41
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition TObject.h:199
virtual UInt_t GetUniqueID() const
Return the unique object id.
Definition TObject.cxx:462
virtual void Streamer(TBuffer &)
Stream an object of class TObject.
Definition TObject.cxx:894
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:213
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:786
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:993
virtual void SetUniqueID(UInt_t uid)
Set the unique object id.
Definition TObject.cxx:797
Int_t TestBits(UInt_t f) const
Definition TObject.h:200
virtual TClass * IsA() const
Definition TObject.h:243
void ResetBit(UInt_t f)
Definition TObject.h:198
@ kHasUUID
if object has a TUUID (its fUniqueID=UUIDNumber)
Definition TObject.h:66
@ kIsReferenced
if object is referenced by a TRef or TRefArray
Definition TObject.h:65
static Bool_t IsValid(TProcessID *pid)
static function. return kTRUE if pid is a valid TProcessID
static UInt_t AssignID(TObject *obj)
static function returning the ID assigned to obj If the object is not yet referenced,...
void PutObjectWithID(TObject *obj, UInt_t uid=0)
stores the object at the uid th slot in the table of objects The object uniqued is set as well as its...
static TProcessID * GetProcessWithUID(const TObject *obj)
static function returning a pointer to TProcessID with its pid encoded in the highest byte of obj->Ge...
TObject * GetObjectWithID(UInt_t uid)
returns the TObject with unique identifier uid in the table of objects
This class is a specialized TProcessID managing the list of UUIDs.
UInt_t AddUUID(TUUID &uuid, TObject *obj)
Add uuid to the table of UUIDs The TObject *obj has its uniqueID set to the UUID number return entry ...
TList * GetUUIDs() const
A TRefTable maintains the association between a referenced object and the parent object supporting th...
Definition TRefTable.h:35
virtual void SetUID(UInt_t uid, TProcessID *context=nullptr)
Definition TRefTable.h:91
static TRefTable * GetRefTable()
Static function returning the current TRefTable.
Bool_t Notify() override
This function is called by TRef::Streamer or TStreamerInfo::ReadBuffer when reading a reference.
Persistent Reference link to a TObject A TRef is a lightweight object pointing to any TObject.
Definition TRef.h:32
void Streamer(TBuffer &) override
Stream an object of class TRef.
Definition TRef.cxx:486
static void SetObject(TObject *obj)
static Obsolete function kept for back compatibility.
Definition TRef.cxx:466
static void SetStaticObject(TObject *obj)
Static function to set the object found on the Action on Demand function.
Definition TRef.cxx:478
void operator=(TObject *obj)
Assign object to reference.
Definition TRef.cxx:278
static TObject * fgObject
Definition TRef.h:38
TRef()
Definition TRef.h:42
TProcessID * GetPID() const
Definition TRef.h:50
static TObject * GetStaticObject()
Returns the static object.
Definition TRef.cxx:458
virtual void SetAction(const char *name)
Store the exec number (in the ROOT list of Execs) into the fBits of this TRef.
Definition TRef.cxx:429
TObject * GetObject() const
Return a pointer to the referenced object.
Definition TRef.cxx:377
TProcessID * fPID
Definition TRef.h:35
static TObjArray * GetListOfExecs()
Return a pointer to the static TObjArray holding the list of Execs.
Definition TRef.cxx:359
static Int_t AddExec(const char *name)
If Exec with name does not exist in the list of Execs, it is created.
Definition TRef.cxx:339
static TObjArray * fgExecs
Pointer to ProcessID when TRef was written.
Definition TRef.h:37
Basic string class.
Definition TString.h:139
const char * Data() const
Definition TString.h:376
virtual void Streamer(TBuffer &)
Stream a string object.
Definition TString.cxx:1412
R__EXTERN TVirtualRWMutex * gCoreMutex