Skip to content

Made reading/writing ROOT files thread safe #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
ed34f32
Fast and dirty version of setting special value in TObject::new
Dr15Jones Jan 13, 2014
36cf7d6
Removed use of TStorage::IsOnHeap
Dr15Jones Jan 13, 2014
f3139c6
Cleaned up TStorage changes
Dr15Jones Jan 13, 2014
fbe78ab
Encapsulate check on alloc in TStorage::FilledByObjectAlloc
Dr15Jones Jan 15, 2014
f4f81f0
Added suppression entries for determining if TObject on heap
Dr15Jones Jan 15, 2014
238d929
Don't reset global in TClass::New
Dr15Jones Dec 31, 2013
6a2db33
Made TClass::fgCallingNew a thread_local file scope static
Dr15Jones Jan 20, 2014
83658c7
Make variable used for GUID of TKeys atomic
Dr15Jones Jan 28, 2014
576c911
Protect Cintex structure with mutex
Dr15Jones Jan 30, 2014
ff062d6
Make zip globals thread local
Dr15Jones Jan 30, 2014
f93317a
Protect access to TROOT::GetListOfFiles()
Dr15Jones Feb 1, 2014
601716c
Protected threaded access to TClass::GetStreamerInfos()
Dr15Jones Feb 1, 2014
664b0aa
Thread safe caching of TStreamerInfo in TBranchSTL
Dr15Jones Feb 1, 2014
def94e4
Only set TStreamerInfo to unoptimized if it is optimized
Dr15Jones Feb 1, 2014
ed20b3c
Fix threading issues in TClass
Dr15Jones Feb 1, 2014
6481fca
Made TPluginManager thread-safe for I/O
Dr15Jones Feb 3, 2014
25d37fc
Mutex lock removed from TStorage::ObjectDealloc to avoid deadlocks
Dr15Jones Feb 3, 2014
3bc032b
Use thread_local for statics in TUUID
Dr15Jones Feb 3, 2014
7e2c9b2
Fix thread-safety issues with TError
Dr15Jones Feb 3, 2014
e3bbfe0
Use gCINTMutex to protect access to G__getgvp
Dr15Jones Feb 3, 2014
ee9fcd7
Use gCINTMutex to protect access to TBaseClass::Property
Dr15Jones Feb 3, 2014
e3b8c64
Fix thread-safety problems with cleanup of containers
Dr15Jones Feb 3, 2014
edf2280
Fix thread-safety issues of StreamerInfos
Dr15Jones Feb 3, 2014
44aa619
Made gTree thread_local
Dr15Jones Feb 3, 2014
5c8b6ac
Properly avoid repeated calls to TStreamerInfo::Build*
Dr15Jones Feb 5, 2014
37ca522
Added additional locks in TClass to protect CINT data structures
Dr15Jones Feb 5, 2014
1d55ff3
Made TROOT ReadingObject methods thread safe
Dr15Jones Feb 5, 2014
35e140c
Made global counters in TFile atomic
Dr15Jones Feb 5, 2014
fd69db9
Used std::atomic to protect TStreamerInfo counters
Dr15Jones Feb 5, 2014
b6465ba
Use std::atomic to protect access to TClass state
Dr15Jones Feb 5, 2014
e3aa20d
Need to reset kBuildOldUsed bit when reading TStreamerInfo
Dr15Jones Feb 7, 2014
e4409de
Need to use CINT mutex in Cintex::Callback::operator()
Dr15Jones Feb 12, 2014
afb5846
Minimize the time spent holding the Cintex mutex lock
Dr15Jones Feb 12, 2014
2f337da
Make last error string in TSystem a thread_local
Dr15Jones Feb 13, 2014
fd1d637
Thread safety fixes for TUnixSystem
Dr15Jones Feb 13, 2014
4fdfae4
Use Mutex to protect gObjectVersionRepository
Dr15Jones Feb 13, 2014
2cc289b
TStreamerElement cached strings changed to thread_local
Dr15Jones Feb 13, 2014
1416855
Fix thread-safety issues with string formating
Dr15Jones Feb 13, 2014
66593e6
fgIsA needs to be thread safe
Dr15Jones Feb 13, 2014
6bd5de8
Fix caching bug in ROOTClassEnhancer::IsA
Dr15Jones Feb 13, 2014
6704a68
Avoid holding gCINTMutex and gROOTMutex simultaneously in TPluginManager
Dr15Jones Feb 13, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cint/cintex/src/Cintex.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#include <iostream>

#include "TROOT.h"
#include "TInterpreter.h" //gCINTMutex
#include "TVirtualMutex.h"

using namespace ROOT::Reflex;
using namespace ROOT::Cintex;
Expand Down Expand Up @@ -187,6 +189,7 @@ namespace ROOT {
}

void Callback::operator () ( const Type& t ) {
R__LOCKGUARD2(gCINTMutex);
ArtificialSourceFile asf;
int autoload = G__set_class_autoloading(0); // To avoid recursive loads
if ( t.IsClass() || t.IsStruct() ) {
Expand All @@ -205,6 +208,7 @@ namespace ROOT {
}

void Callback::operator () ( const Member& m ) {
R__LOCKGUARD2(gCINTMutex);
ArtificialSourceFile asf;
int autoload = G__set_class_autoloading(0); // To avoid recursive loads
if ( m.IsFunctionMember() ) {
Expand Down
68 changes: 45 additions & 23 deletions cint/cintex/src/ROOTClassEnhancer.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "TClassStreamer.h"
#include "TCollectionProxyInfo.h"
#include "TVirtualCollectionProxy.h"
#include "TVirtualMutex.h"
#include "TMemberInspector.h"
#include "RVersion.h"
#include "Reflex/Reflex.h"
Expand All @@ -35,6 +36,9 @@
#include <sstream>
#include <memory>
#include <string>
#if __cplusplus > 199711L
#include <atomic>
#endif

#if ROOT_VERSION_CODE >= ROOT_VERSION(5,1,1)
#include "TVirtualIsAProxy.h"
Expand All @@ -44,6 +48,8 @@ using namespace ROOT::Reflex;
using namespace ROOT::Cintex;
using namespace std;

static TVirtualMutex* gCintexMutex = 0;

namespace ROOT { namespace Cintex {

class IsAProxy;
Expand All @@ -52,7 +58,11 @@ namespace ROOT { namespace Cintex {

Type fType;
string fName;
#if __cplusplus > 199711L
std::atomic<TClass*> fTclass;
#else
TClass* fTclass;
#endif
TClass* fLastClass;
std::map<const std::type_info*,TClass*> fSub_types;
const std::type_info* fLastType;
Expand Down Expand Up @@ -174,7 +184,10 @@ namespace ROOT { namespace Cintex {
// Constructor.
fType = CleanType(t);
fName = CintName(fType);
rootEnhancerInfos().push_back(this);
{
R__LOCKGUARD2(gCintexMutex);
rootEnhancerInfos().push_back(this);
}
fMyType = &t.TypeInfo();
fIsVirtual = TypeGet().IsVirtual();
fClassInfo = 0;
Expand Down Expand Up @@ -361,37 +374,46 @@ namespace ROOT { namespace Cintex {
if ( ! obj || ! fIsVirtual ) {
return Tclass();
}
else {
// Avoid the case that the first word is a virtual_base_offset_table instead of
// a virtual_function_table
long Offset = **(long**)obj;
if ( Offset == 0 ) return Tclass();

DynamicStruct_t* p = (DynamicStruct_t*)obj;
const std::type_info& typ = typeid(*p);
// Avoid the case that the first word is a virtual_base_offset_table instead of
// a virtual_function_table
long Offset = **(long**)obj;
if ( Offset == 0 ) return Tclass();

if ( &typ == fMyType ) {
return Tclass();
}
else if ( &typ == fLastType ) {
DynamicStruct_t* p = (DynamicStruct_t*)obj;
const std::type_info& typ = typeid(*p);

if ( &typ == fMyType ) {
return Tclass();
}
{
R__LOCKGUARD2(gCintexMutex);
if ( &typ == fLastType ) {
return fLastClass;
}

// Check if TypeNth is already in sub-class cache
else if ( 0 != (fLastClass=fSub_types[&typ]) ) {
TClass* findClass = fSub_types[&typ];
if ( 0 != findClass ) {
fLastClass = findClass;
fLastType = &typ;
return fLastClass;
}
// Last resort: lookup root class
else {
std::string nam;
Type t = Type::ByTypeInfo(typ);
if (t) nam = CintName(t);
else nam = CintName(Tools::Demangle(typ));
fLastClass = ROOT::GetROOT()->GetClass(nam.c_str());
fSub_types[fLastType=&typ] = fLastClass;
}
}
// Last resort: lookup root class
TClass* returnValue;
std::string nam;
Type t = Type::ByTypeInfo(typ);
if (t) nam = CintName(t);
else nam = CintName(Tools::Demangle(typ));
returnValue = ROOT::GetROOT()->GetClass(nam.c_str());
{
R__LOCKGUARD2(gCintexMutex);
fLastClass = returnValue;
fSub_types[fLastType=&typ] = fLastClass;
}
//std::cout << "Cintex: IsA:" << TypeNth.Name(SCOPED) << " dynamic:" << dtype.Name(SCOPED) << std::endl;
return fLastClass;
return returnValue;
}

TClass* ROOTClassEnhancerInfo::Default_CreateClass( Type typ, ROOT::TGenericClassInfo* info) {
Expand Down
11 changes: 8 additions & 3 deletions cint/reflex/python/genreflex/gendict.py
Original file line number Diff line number Diff line change
Expand Up @@ -2654,6 +2654,8 @@ def ClassDefImplementation(selclasses, self) :
returnValue += '#endif\n'
returnValue += '#include "TClass.h"\n'
returnValue += '#include "TMemberInspector.h"\n'
returnValue += '#include "TInterpreter.h"\n'
returnValue += '#include "TVirtualMutex.h"\n'
returnValue += '#include "RtypesImp.h"\n' # for GenericShowMembers etc
returnValue += '#include "TIsAProxy.h"\n'
haveClassDef = 0
Expand Down Expand Up @@ -2725,8 +2727,11 @@ def ClassDefImplementation(selclasses, self) :
specclname = clname

returnValue += template + 'TClass* ' + specclname + '::Class() {\n'
returnValue += ' if (!fgIsA)\n'
returnValue += ' fgIsA = TClass::GetClass("' + clname[2:] + '");\n'
returnValue += ' if (!fgIsA) {\n'
returnValue += ' R__LOCKGUARD2(gCINTMutex);'
returnValue += ' if (!fgIsA)\n'
returnValue += ' fgIsA = TClass::GetClass("' + clname[2:] + '");\n'
returnValue += ' }\n'
returnValue += ' return fgIsA;\n'
returnValue += '}\n'
returnValue += template + 'const char * ' + specclname + '::Class_Name() {return "' + clname[2:] + '";}\n'
Expand Down Expand Up @@ -2814,7 +2819,7 @@ def ClassDefImplementation(selclasses, self) :
returnValue += ' b.WriteClassBuffer(' + clname + '::Class(),this);\n'
returnValue += ' }\n'
returnValue += '}\n'
returnValue += template + 'TClass* ' + specclname + '::fgIsA = 0;\n'
returnValue += template + 'atomic_TClass_ptr ' + specclname + '::fgIsA(0);\n'
returnValue += namespacelevel * '}' + '\n'
elif derivesFromTObject :
# no fgIsA etc members but derives from TObject!
Expand Down
13 changes: 10 additions & 3 deletions core/base/inc/Rtypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
#include <string.h>
#include <snprintf.h> // part of stdio.h on systems that have it
#include <strlcpy.h> // part of string.h on systems that have it

#if __cplusplus > 199711L
#include <atomic>
#endif


//---- forward declared class types --------------------------------------------
Expand Down Expand Up @@ -268,12 +270,17 @@ namespace ROOT {
#include "TGenericClassInfo.h"
#endif

#if __cplusplus > 199711L
typedef std::atomic<TClass*> atomic_TClass_ptr;
#else
typedef TClass* atomic_TClass_ptr;
#endif
// Common part of ClassDef definition.
// DeclFileLine() is not part of it since CINT uses that as trigger for
// the class comment string.
#define _ClassDef_(name,id) \
private: \
static TClass *fgIsA; \
static atomic_TClass_ptr fgIsA; \
public: \
static TClass *Class(); \
static const char *Class_Name(); \
Expand All @@ -290,7 +297,7 @@ public: \
// Version without any virtual functions.
#define _ClassDefNV_(name,id) \
private: \
static TClass *fgIsA; \
static atomic_TClass_ptr fgIsA; \
public: \
static TClass *Class(); \
static const char *Class_Name(); \
Expand Down
4 changes: 2 additions & 2 deletions core/base/inc/TROOT.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ friend class TCintWithCling;
Long_t ProcessLine(const char *line, Int_t *error = 0);
Long_t ProcessLineSync(const char *line, Int_t *error = 0);
Long_t ProcessLineFast(const char *line, Int_t *error = 0);
Bool_t ReadingObject() const { /* Deprecated (will be removed in next release) */ return fReadingObject; }
Bool_t ReadingObject() const;
void RefreshBrowsers();
void RemoveClass(TClass *);
void Reset(Option_t *option="");
Expand All @@ -258,7 +258,7 @@ friend class TCintWithCling;
void SetEscape(Bool_t flag = kTRUE) { fEscape = flag; }
void SetLineIsProcessing() { fLineIsProcessing++; }
void SetLineHasBeenProcessed() { if (fLineIsProcessing) fLineIsProcessing--; }
void SetReadingObject(Bool_t flag = kTRUE) { fReadingObject = flag; }
void SetReadingObject(Bool_t flag = kTRUE);
void SetMustClean(Bool_t flag = kTRUE) { fMustClean=flag; }
void SetSelectedPrimitive(const TObject *obj) { fPrimitive = obj; }
void SetSelectedPad(TVirtualPad *pad) { fSelectPad = pad; }
Expand Down
13 changes: 5 additions & 8 deletions core/base/inc/TStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ typedef char *(*ReAllocCharFun_t)(char*, size_t, size_t);
class TStorage {

private:
static ULong_t fgHeapBegin; // begin address of heap
static ULong_t fgHeapEnd; // end address of heap
static size_t fgMaxBlockSize; // largest block allocated
static FreeHookFun_t fgFreeHook; // function called on free
static void *fgFreeHookData; // data used by this function
static ReAllocFun_t fgReAllocHook; // custom ReAlloc
static ReAllocCFun_t fgReAllocCHook; // custom ReAlloc with length check
static Bool_t fgHasCustomNewDelete; // true if using ROOT's new/delete
static const UInt_t kObjectAllocMemValue = 0x99999999;
// magic number for ObjectAlloc

public:
virtual ~TStorage() { }
Expand Down Expand Up @@ -77,16 +77,13 @@ class TStorage {
static void AddToHeap(ULong_t begin, ULong_t end);
static Bool_t IsOnHeap(void *p);

static Bool_t FilledByObjectAlloc(UInt_t* member);

ClassDef(TStorage,0) //Storage manager class
};

#ifndef WIN32
inline void TStorage::AddToHeap(ULong_t begin, ULong_t end)
{ if (begin < fgHeapBegin) fgHeapBegin = begin;
if (end > fgHeapEnd) fgHeapEnd = end; }

inline Bool_t TStorage::IsOnHeap(void *p)
{ return (ULong_t)p >= fgHeapBegin && (ULong_t)p < fgHeapEnd; }
inline Bool_t TStorage::FilledByObjectAlloc(UInt_t *member) { return *member == kObjectAllocMemValue; }

inline size_t TStorage::GetMaxBlockSize() { return fgMaxBlockSize; }

Expand Down
8 changes: 8 additions & 0 deletions core/base/inc/TSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,10 @@ class TSystem : public TNamed {
TSeqCollection *fCompiled; //List of shared libs from compiled macros to be deleted
TSeqCollection *fHelpers; //List of helper classes for alternative file/directory access

#if __cplusplus > 199711L
static thread_local TString fgLastErrorString; //Last system error message
#endif

TSystem *FindHelper(const char *path, void *dirptr = 0);
virtual Bool_t ConsistentWith(const char *path, void *dirptr = 0);
virtual const char *ExpandFileName(const char *fname);
Expand All @@ -340,7 +344,11 @@ class TSystem : public TNamed {
virtual void SetProgname(const char *name);
virtual void SetDisplay();
void SetErrorStr(const char *errstr);
#if __cplusplus > 199711L
const char *GetErrorStr() const { return fgLastErrorString; }
#else
const char *GetErrorStr() const { return fLastErrorString; }
#endif
virtual const char *GetError();
void RemoveOnExit(TObject *obj);
virtual const char *HostName();
Expand Down
18 changes: 9 additions & 9 deletions core/base/src/TError.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,8 @@ static void DebugPrint(const char *fmt, ...)
{
// Print debugging message to stderr and, on Windows, to the system debugger.

static Int_t buf_size = 2048;
static char *buf = 0;

R__LOCKGUARD2(gErrorMutex);
static thread_local Int_t buf_size = 2048;
static thread_local char *buf = 0;

va_list ap;
va_start(ap, fmt);
Expand All @@ -90,6 +88,8 @@ static void DebugPrint(const char *fmt, ...)
}
va_end(ap);

R__LOCKGUARD2(gErrorMutex);

fprintf(stderr, "%s", buf);

#ifdef WIN32
Expand Down Expand Up @@ -197,10 +197,9 @@ void ErrorHandler(Int_t level, const char *location, const char *fmt, va_list ap
{
// General error handler function. It calls the user set error handler.

R__LOCKGUARD2(gErrorMutex);

static Int_t buf_size = 2048;
static char *buf = 0;
static thread_local Int_t buf_size = 2048;
static thread_local char *buf = 0;

int vc = 0;
va_list sap;
Expand Down Expand Up @@ -233,9 +232,10 @@ void ErrorHandler(Int_t level, const char *location, const char *fmt, va_list ap
va_end(ap);

char *bp;
if (level >= kSysError && level < kFatal)
if (level >= kSysError && level < kFatal) {
R__LOCKGUARD2(gErrorMutex);
bp = Form("%s (%s)", buf, gSystem->GetError());
else
} else
bp = buf;

if (level != kFatal)
Expand Down
14 changes: 9 additions & 5 deletions core/base/src/TObject.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Bool_t TObject::fgObjectStat = kTRUE;
ClassImp(TObject)

//______________________________________________________________________________
TObject::TObject() : fUniqueID(0), fBits(kNotDeleted)
TObject::TObject() : fBits(kNotDeleted) //Need to leave FUniqueID unset
{
// TObject constructor. It sets the two data words of TObject to their
// initial values. The unique ID is set to 0 and the status word is
Expand All @@ -71,8 +71,10 @@ TObject::TObject() : fUniqueID(0), fBits(kNotDeleted)
// (see TEnv) the object is added to the global TObjectTable for
// bookkeeping.

if (TStorage::IsOnHeap(this))
if (TStorage::FilledByObjectAlloc(&fUniqueID))
fBits |= kIsOnHeap;

fUniqueID = 0;

if (fgObjectStat) TObjectTable::AddObj(this);
}
Expand All @@ -82,17 +84,19 @@ TObject::TObject(const TObject &obj)
{
// TObject copy ctor.

fUniqueID = obj.fUniqueID; // when really unique don't copy
fBits = obj.fBits;
fBits = obj.fBits;

if (TStorage::IsOnHeap(this))
if (TStorage::FilledByObjectAlloc(&fUniqueID))
fBits |= kIsOnHeap;
else
fBits &= ~kIsOnHeap;

fBits &= ~kIsReferenced;
fBits &= ~kCanDelete;

//Set only after used in above call
fUniqueID = obj.fUniqueID; // when really unique don't copy

if (fgObjectStat) TObjectTable::AddObj(this);
}

Expand Down
Loading