Skip to content

Commit 6c291c6

Browse files
committed
Do not reset the mutex to 0 readers, 0 writers upon interp invocation.
But reset it to wherever the mutex was before entering a lock frame related to user code interpretation (which might in turn trigger a call to the interpreter). (PR-1564)
2 parents 2428c15 + 9a65c2a commit 6c291c6

20 files changed

+513
-496
lines changed

core/base/inc/TVirtualMutex.h

Lines changed: 0 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,6 @@ R__EXTERN TVirtualMutex *gGlobalMutex;
3434
class TVirtualMutex {
3535

3636
public:
37-
struct State {
38-
virtual ~State();
39-
};
40-
4137
TVirtualMutex(Bool_t /* recursive */ = kFALSE) { }
4238
virtual ~TVirtualMutex() { }
4339

@@ -49,8 +45,6 @@ class TVirtualMutex {
4945
Int_t Release() { return UnLock(); }
5046

5147
virtual TVirtualMutex *Factory(Bool_t /*recursive*/ = kFALSE) = 0;
52-
virtual std::unique_ptr<TVirtualMutex::State> Reset() = 0;
53-
virtual void Restore(std::unique_ptr<State> &&state) = 0;
5448

5549
ClassDef(TVirtualMutex, 0) // Virtual mutex lock class
5650
};
@@ -94,53 +88,6 @@ class TLockGuard {
9488
ClassDefNV(TLockGuard,0) // Exception safe locking/unlocking of mutex
9589
};
9690

97-
namespace ROOT {
98-
99-
//////////////////////////////////////////////////////////////////////////
100-
// //
101-
// TLockSuspend //
102-
// //
103-
// This class provides mutex supension management in a guaranteed and //
104-
// exception safe way. Use like this: //
105-
// { //
106-
// TLockSuspend guard(mutex); //
107-
// ... // do something //
108-
// } //
109-
// when guard is created, 'suspend' the lock by calling Reset on the //
110-
// on the mutex and when the guard goes out of scope the mutex is //
111-
// restored in the TLockSuspend destructor. //
112-
// The exception mechanism takes care of calling the dtors //
113-
// of local objects so it is exception safe. //
114-
// //
115-
//////////////////////////////////////////////////////////////////////////
116-
117-
class TLockSuspend {
118-
private:
119-
TVirtualMutex *const fMutex;
120-
121-
std::unique_ptr<TVirtualMutex::State> fState;
122-
123-
TLockSuspend(const TLockSuspend &) = delete;
124-
TLockSuspend &operator=(const TLockSuspend &) = delete;
125-
126-
public:
127-
TLockSuspend(TVirtualMutex *mutex) : fMutex(mutex)
128-
{
129-
if (fMutex)
130-
fState = mutex->Reset();
131-
}
132-
133-
~TLockSuspend()
134-
{
135-
if (fMutex)
136-
fMutex->Restore(std::move(fState));
137-
}
138-
139-
ClassDefNV(TLockSuspend, 0) // Exception safe Reset/Restore of mutex
140-
};
141-
142-
} // Namespace ROOT.
143-
14491
// Zero overhead macros in case not compiled with thread support
14592
#if defined (_REENTRANT) || defined (WIN32)
14693

@@ -155,13 +102,11 @@ class TLockSuspend {
155102
R__LOCKGUARD(mutex)
156103
#define R__LOCKGUARD_NAMED(name,mutex) TLockGuard _NAME2_(R__guard,name)(mutex)
157104
#define R__LOCKGUARD_UNLOCK(name) _NAME2_(R__guard,name).UnLock()
158-
#define R__LOCK_SUSPEND(mutex) TLockSuspend _R__UNIQUE_(R__guard)(mutex)
159105
#else
160106
#define R__LOCKGUARD(mutex) (void)mutex; { }
161107
#define R__LOCKGUARD_NAMED(name,mutex) (void)mutex; { }
162108
#define R__LOCKGUARD2(mutex) (void)mutex; { }
163109
#define R__LOCKGUARD_UNLOCK(name) { }
164-
#define R__LOCK_SUSPEND(mutex) { }
165110
#endif
166111

167112
#ifdef R__USE_IMT

core/base/inc/TVirtualRWMutex.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,20 @@ class TVirtualRWMutex : public TVirtualMutex {
4444
// distinct from these)
4545
class Hint_t;
4646

47+
/// \class State
48+
/// Earlier lock state as returned by `GetState()` that can be passed to
49+
/// `Restore()`
50+
struct State {
51+
virtual ~State(); // implemented in TVirtualMutex.cxx
52+
};
53+
54+
/// \class StateDelta
55+
/// State as returned by `GetStateDelta()` that can be passed to
56+
/// `Restore()`
57+
struct StateDelta {
58+
virtual ~StateDelta(); // implemented in TVirtualMutex.cxx
59+
};
60+
4761
virtual Hint_t *ReadLock() = 0;
4862
virtual void ReadUnLock(Hint_t *) = 0;
4963
virtual Hint_t *WriteLock() = 0;
@@ -54,6 +68,10 @@ class TVirtualRWMutex : public TVirtualMutex {
5468
Int_t UnLock() override { WriteUnLock(nullptr); return 1; }
5569
Int_t CleanUp() override { WriteUnLock(nullptr); return 1; }
5670

71+
virtual std::unique_ptr<State> GetStateBefore() = 0;
72+
virtual std::unique_ptr<StateDelta> Rewind(const State& earlierState) = 0;
73+
virtual void Apply(std::unique_ptr<StateDelta> &&delta) = 0;
74+
5775
TVirtualRWMutex *Factory(Bool_t /*recursive*/ = kFALSE) override = 0;
5876

5977
ClassDefOverride(TVirtualRWMutex, 0) // Virtual mutex lock class

core/base/src/TVirtualMutex.cxx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ of local objects so it is exception safe.
3333
*/
3434

3535
#include "TVirtualMutex.h"
36+
#include "TVirtualRWMutex.h"
3637

3738
ClassImp(TVirtualMutex);
3839
ClassImp(TLockGuard);
@@ -43,5 +44,6 @@ ClassImp(TLockGuard);
4344
// lockup of the system (see TMutex::Factory)
4445
TVirtualMutex *gGlobalMutex = 0;
4546

46-
// Pin that vtable.
47-
TVirtualMutex::State::~State() = default;
47+
// From TVirtualRWMutex.h:
48+
ROOT::TVirtualRWMutex::State::~State() = default;
49+
ROOT::TVirtualRWMutex::StateDelta::~StateDelta() = default;

core/meta/inc/TInterpreter.h

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
#include "TDictionary.h"
2626

27-
#include "TVirtualMutex.h"
27+
#include "TVirtualRWMutex.h"
2828

2929
#include <map>
3030
#include <typeinfo>
@@ -41,6 +41,22 @@ class TListOfEnums;
4141

4242
R__EXTERN TVirtualMutex *gInterpreterMutex;
4343

44+
#if defined (_REENTRANT) || defined (WIN32)
45+
# define R__LOCKGUARD_CLING(mutex) ::ROOT::Internal::InterpreterMutexRegistrationRAII _R__UNIQUE_(R__guard)(mutex); { }
46+
#else
47+
# define R__LOCKGUARD_CLING(mutex) (void)mutex; { }
48+
#endif
49+
50+
namespace ROOT {
51+
namespace Internal {
52+
struct InterpreterMutexRegistrationRAII {
53+
TLockGuard fLockGuard;
54+
InterpreterMutexRegistrationRAII(TVirtualMutex* mutex);
55+
~InterpreterMutexRegistrationRAII();
56+
};
57+
}
58+
}
59+
4460
class TInterpreter : public TNamed {
4561

4662
protected:
@@ -205,6 +221,9 @@ class TInterpreter : public TNamed {
205221
virtual void SetProcessLineLock(Bool_t lock = kTRUE) = 0;
206222
virtual const char *TypeName(const char *s) = 0;
207223

224+
virtual void SnapshotMutexState(ROOT::TVirtualRWMutex* mtx) = 0;
225+
virtual void ForgetMutexState() = 0;
226+
208227
// All the functions below must be virtual with a dummy implementation
209228
// These functions are redefined in TCling.
210229
//The dummy implementation avoids an implementation in TGWin32InterpreterProxy
@@ -510,4 +529,16 @@ R__EXTERN TInterpreter* (*gPtr2Interpreter)();
510529
R__EXTERN TInterpreter* gCling;
511530
#endif
512531

532+
inline ROOT::Internal::InterpreterMutexRegistrationRAII::InterpreterMutexRegistrationRAII(TVirtualMutex* mutex):
533+
fLockGuard(mutex)
534+
{
535+
if (gCoreMutex)
536+
::gCling->SnapshotMutexState(gCoreMutex);
537+
}
538+
inline ROOT::Internal::InterpreterMutexRegistrationRAII::~InterpreterMutexRegistrationRAII()
539+
{
540+
if (gCoreMutex)
541+
::gCling->ForgetMutexState();
542+
}
543+
513544
#endif

core/metacling/src/TCling.cxx

Lines changed: 78 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -354,27 +354,20 @@ void TCling__PrintStackTrace() {
354354
}
355355

356356
////////////////////////////////////////////////////////////////////////////////
357-
/// Restore the interpreter lock.
357+
/// Re-apply the lock count delta that TCling__ResetInterpreterMutex() caused.
358358

359-
extern "C" void TCling__RestoreInterpreterMutex(void *state)
359+
extern "C" void TCling__RestoreInterpreterMutex(void *delta)
360360
{
361-
if (gInterpreterMutex && state) {
362-
auto typedState = static_cast<TVirtualMutex::State *>(state);
363-
std::unique_ptr<TVirtualMutex::State> uniqueP{typedState};
364-
gInterpreterMutex->Restore(std::move(uniqueP));
365-
}
361+
((TCling*)gCling)->ApplyToInterpreterMutex(delta);
366362
}
367363

368364
////////////////////////////////////////////////////////////////////////////////
369-
/// Completely reset the interpreter lock.
365+
/// Reset the interpreter lock to the state it had before interpreter-related
366+
/// calls happened.
370367

371368
extern "C" void *TCling__ResetInterpreterMutex()
372369
{
373-
if (gInterpreterMutex) {
374-
auto uniqueP = gInterpreterMutex->Reset();
375-
return uniqueP.release();
376-
}
377-
return nullptr;
370+
return ((TCling*)gCling)->RewindInterpreterMutex();
378371
}
379372

380373
////////////////////////////////////////////////////////////////////////////////
@@ -2120,7 +2113,7 @@ Long_t TCling::ProcessLine(const char* line, EErrorCode* error/*=0*/)
21202113
gInterpreterMutex = gGlobalMutex->Factory(kTRUE);
21212114
gGlobalMutex->UnLock();
21222115
}
2123-
R__LOCKGUARD(fLockProcessLine ? gInterpreterMutex : 0);
2116+
R__LOCKGUARD_CLING(fLockProcessLine ? gInterpreterMutex : 0);
21242117
gROOT->SetLineIsProcessing();
21252118

21262119
struct InterpreterFlagsRAII {
@@ -2683,7 +2676,7 @@ void TCling::ClearStack()
26832676

26842677
bool TCling::Declare(const char* code)
26852678
{
2686-
R__LOCKGUARD(gInterpreterMutex);
2679+
R__LOCKGUARD_CLING(gInterpreterMutex);
26872680

26882681
int oldload = SetClassAutoloading(0);
26892682
SuspendAutoParsing autoParseRaii(this);
@@ -3000,7 +2993,7 @@ Int_t TCling::Load(const char* filename, Bool_t system)
30002993
}
30012994

30022995
// Used to return 0 on success, 1 on duplicate, -1 on failure, -2 on "fatal".
3003-
R__LOCKGUARD(gInterpreterMutex);
2996+
R__LOCKGUARD_CLING(gInterpreterMutex);
30042997
cling::DynamicLibraryManager* DLM = fInterpreter->getDynamicLibraryManager();
30052998
std::string canonLib = DLM->lookupLibrary(filename);
30062999
cling::DynamicLibraryManager::LoadLibResult res
@@ -3051,7 +3044,7 @@ Long_t TCling::ProcessLineAsynch(const char* line, EErrorCode* error)
30513044

30523045
Long_t TCling::ProcessLineSynch(const char* line, EErrorCode* error)
30533046
{
3054-
R__LOCKGUARD(fLockProcessLine ? gInterpreterMutex : 0);
3047+
R__LOCKGUARD_CLING(fLockProcessLine ? gInterpreterMutex : 0);
30553048
if (gApplication) {
30563049
if (gApplication->IsCmdThread()) {
30573050
return ProcessLine(line, error);
@@ -3079,7 +3072,7 @@ Long_t TCling::Calc(const char* line, EErrorCode* error)
30793072
gROOT->SetLineIsProcessing();
30803073
}
30813074
#endif // R__WIN32
3082-
R__LOCKGUARD(gInterpreterMutex);
3075+
R__LOCKGUARD_CLING(gInterpreterMutex);
30833076
if (error) {
30843077
*error = TInterpreter::kNoError;
30853078
}
@@ -4565,7 +4558,7 @@ void TCling::GetInterpreterTypeName(const char* name, std::string &output, Bool_
45654558

45664559
void TCling::Execute(const char* function, const char* params, int* error)
45674560
{
4568-
R__LOCKGUARD(gInterpreterMutex);
4561+
R__LOCKGUARD_CLING(gInterpreterMutex);
45694562
if (error) {
45704563
*error = TInterpreter::kNoError;
45714564
}
@@ -4590,7 +4583,7 @@ void TCling::Execute(const char* function, const char* params, int* error)
45904583
void TCling::Execute(TObject* obj, TClass* cl, const char* method,
45914584
const char* params, Bool_t objectIsConst, int* error)
45924585
{
4593-
R__LOCKGUARD(gInterpreterMutex);
4586+
R__LOCKGUARD_CLING(gInterpreterMutex);
45944587
if (error) {
45954588
*error = TInterpreter::kNoError;
45964589
}
@@ -4693,7 +4686,7 @@ void TCling::Execute(TObject* obj, TClass* cl, TMethod* method,
46934686
}
46944687

46954688
// And now execute it.
4696-
R__LOCKGUARD(gInterpreterMutex);
4689+
R__LOCKGUARD_CLING(gInterpreterMutex);
46974690
if (error) {
46984691
*error = TInterpreter::kNoError;
46994692
}
@@ -4735,7 +4728,7 @@ void TCling::ExecuteWithArgsAndReturn(TMethod* method, void* address,
47354728

47364729
Long_t TCling::ExecuteMacro(const char* filename, EErrorCode* error)
47374730
{
4738-
R__LOCKGUARD(fLockProcessLine ? gInterpreterMutex : 0);
4731+
R__LOCKGUARD_CLING(fLockProcessLine ? gInterpreterMutex : 0);
47394732
fCurExecutingMacros.push_back(filename);
47404733
Long_t result = TApplication::ExecuteFile(filename, (int*)error);
47414734
fCurExecutingMacros.pop_back();
@@ -8453,3 +8446,66 @@ const char* TCling::TypedefInfo_Title(TypedefInfo_t* tinfo) const
84538446
TClingTypedefInfo* TClinginfo = (TClingTypedefInfo*) tinfo;
84548447
return TClinginfo->Title();
84558448
}
8449+
8450+
////////////////////////////////////////////////////////////////////////////////
8451+
8452+
void TCling::SnapshotMutexState(ROOT::TVirtualRWMutex* mtx)
8453+
{
8454+
if (!fInitialMutex.back()) {
8455+
if (fInitialMutex.back().fRecurseCount) {
8456+
Error("SnapshotMutexState", "fRecurseCount != 0 even though initial mutex state is unset!");
8457+
}
8458+
fInitialMutex.back().fState = mtx->GetStateBefore();
8459+
}
8460+
// We will "forget" this lock once we backed out of all interpreter frames.
8461+
// Here we are entering one, so ++.
8462+
++fInitialMutex.back().fRecurseCount;
8463+
}
8464+
8465+
////////////////////////////////////////////////////////////////////////////////
8466+
8467+
void TCling::ForgetMutexState()
8468+
{
8469+
if (!fInitialMutex.back())
8470+
return;
8471+
if (fInitialMutex.back().fRecurseCount == 0) {
8472+
Error("ForgetMutexState", "mutex state's recurse count already 0!");
8473+
}
8474+
else if (--fInitialMutex.back().fRecurseCount == 0) {
8475+
// We have returned from all interpreter frames. Reset the initial lock state.
8476+
fInitialMutex.back().fState.reset();
8477+
}
8478+
}
8479+
8480+
////////////////////////////////////////////////////////////////////////////////
8481+
/// Re-apply the lock count delta that TCling__ResetInterpreterMutex() caused.
8482+
8483+
void TCling::ApplyToInterpreterMutex(void *delta)
8484+
{
8485+
R__ASSERT(!fInitialMutex.empty() && "Inconsistent state of fInitialMutex!");
8486+
if (gInterpreterMutex) {
8487+
if (delta) {
8488+
auto typedDelta = static_cast<TVirtualRWMutex::StateDelta *>(delta);
8489+
std::unique_ptr<TVirtualRWMutex::StateDelta> uniqueP{typedDelta};
8490+
gCoreMutex->Apply(std::move(uniqueP));
8491+
}
8492+
}
8493+
fInitialMutex.pop_back();
8494+
}
8495+
8496+
////////////////////////////////////////////////////////////////////////////////
8497+
/// Reset the interpreter lock to the state it had before interpreter-related
8498+
/// calls happened.
8499+
8500+
void *TCling::RewindInterpreterMutex()
8501+
{
8502+
if (fInitialMutex.back()) {
8503+
std::unique_ptr<TVirtualRWMutex::StateDelta> uniqueP = gCoreMutex->Rewind(*fInitialMutex.back().fState);
8504+
// Need to start a new recurse count.
8505+
fInitialMutex.emplace_back();
8506+
return uniqueP.release();
8507+
}
8508+
// Need to start a new recurse count.
8509+
fInitialMutex.emplace_back();
8510+
return nullptr;
8511+
}

0 commit comments

Comments
 (0)