@@ -99,8 +99,6 @@ class FactSet;
99
99
// / particular point in program execution. Currently, a fact is a capability,
100
100
// / along with additional information, such as where it was acquired, whether
101
101
// / it is exclusive or shared, etc.
102
- // /
103
- // / FIXME: this analysis does not currently support re-entrant locking.
104
102
class FactEntry : public CapabilityExpr {
105
103
public:
106
104
enum FactEntryKind { Lockable, ScopedLockable };
@@ -114,31 +112,39 @@ class FactEntry : public CapabilityExpr {
114
112
};
115
113
116
114
private:
117
- const FactEntryKind Kind : 8 ;
115
+ const FactEntryKind Kind : 4 ;
118
116
119
117
// / Exclusive or shared.
120
- LockKind LKind : 8 ;
118
+ const LockKind LKind : 4 ;
119
+
120
+ // / How it was acquired.
121
+ const SourceKind Source : 4 ;
121
122
122
- // How it was acquired.
123
- SourceKind Source : 8 ;
123
+ // / Reentrancy depth; 16 bits should be enough given that FactID is a short,
124
+ // / and thus we can't store more than 65536 facts anyway.
125
+ unsigned int ReentrancyDepth : 16 ;
124
126
125
127
// / Where it was acquired.
126
- SourceLocation AcquireLoc;
128
+ const SourceLocation AcquireLoc;
127
129
128
130
public:
129
131
FactEntry (FactEntryKind FK, const CapabilityExpr &CE, LockKind LK,
130
132
SourceLocation Loc, SourceKind Src)
131
- : CapabilityExpr(CE), Kind(FK), LKind(LK), Source(Src), AcquireLoc(Loc) {}
133
+ : CapabilityExpr(CE), Kind(FK), LKind(LK), Source(Src),
134
+ ReentrancyDepth (0 ), AcquireLoc(Loc) {}
132
135
virtual ~FactEntry () = default ;
133
136
134
137
LockKind kind () const { return LKind; }
135
138
SourceLocation loc () const { return AcquireLoc; }
136
139
FactEntryKind getFactEntryKind () const { return Kind; }
140
+ unsigned int getReentrancyDepth () const { return ReentrancyDepth; }
137
141
138
142
bool asserted () const { return Source == Asserted; }
139
143
bool declared () const { return Source == Declared; }
140
144
bool managed () const { return Source == Managed; }
141
145
146
+ virtual std::unique_ptr<FactEntry> clone () const = 0;
147
+
142
148
virtual void
143
149
handleRemovalFromIntersection (const FactSet &FSet, FactManager &FactMan,
144
150
SourceLocation JoinLoc, LockErrorKind LEK,
@@ -155,6 +161,29 @@ class FactEntry : public CapabilityExpr {
155
161
bool isAtLeast (LockKind LK) const {
156
162
return (LKind == LK_Exclusive) || (LK == LK_Shared);
157
163
}
164
+
165
+ // Return an updated FactEntry if we can acquire this capability reentrant,
166
+ // nullptr otherwise. Returns nullopt if reentrancy depth limit was reached.
167
+ [[nodiscard]] std::unique_ptr<FactEntry>
168
+ tryReenter (LockKind ReenterKind) const {
169
+ if (!reentrant ())
170
+ return nullptr ;
171
+ if (kind () != ReenterKind)
172
+ return nullptr ;
173
+ auto NewFact = clone ();
174
+ NewFact->ReentrancyDepth ++;
175
+ return NewFact;
176
+ }
177
+
178
+ // Return an updated FactEntry if we are releasing a capability previously
179
+ // acquired reentrant, nullptr otherwise.
180
+ [[nodiscard]] std::unique_ptr<FactEntry> leaveReentrant () const {
181
+ if (!ReentrancyDepth)
182
+ return nullptr ;
183
+ auto NewFact = clone ();
184
+ NewFact->ReentrancyDepth --;
185
+ return NewFact;
186
+ }
158
187
};
159
188
160
189
using FactID = unsigned short ;
@@ -168,6 +197,8 @@ class FactManager {
168
197
public:
169
198
FactID newFact (std::unique_ptr<FactEntry> Entry) {
170
199
Facts.push_back (std::move (Entry));
200
+ assert (Facts.size () - 1 <= std::numeric_limits<unsigned short >::max () &&
201
+ " FactID space exhausted" );
171
202
return static_cast <unsigned short >(Facts.size () - 1 );
172
203
}
173
204
@@ -235,6 +266,20 @@ class FactSet {
235
266
return false ;
236
267
}
237
268
269
+ std::optional<FactID> replaceLock (FactManager &FM, iterator It,
270
+ std::unique_ptr<FactEntry> Entry) {
271
+ if (It == end ())
272
+ return std::nullopt;
273
+ FactID F = FM.newFact (std::move (Entry));
274
+ *It = F;
275
+ return F;
276
+ }
277
+
278
+ std::optional<FactID> replaceLock (FactManager &FM, const CapabilityExpr &CapE,
279
+ std::unique_ptr<FactEntry> Entry) {
280
+ return replaceLock (FM, findLockIter (FM, CapE), std::move (Entry));
281
+ }
282
+
238
283
iterator findLockIter (FactManager &FM, const CapabilityExpr &CapE) {
239
284
return std::find_if (begin (), end (), [&](FactID ID) {
240
285
return FM[ID].matches (CapE);
@@ -864,6 +909,10 @@ class LockableFactEntry : public FactEntry {
864
909
SourceKind Src = Acquired)
865
910
: FactEntry(Lockable, CE, LK, Loc, Src) {}
866
911
912
+ std::unique_ptr<FactEntry> clone () const override {
913
+ return std::make_unique<LockableFactEntry>(*this );
914
+ }
915
+
867
916
void
868
917
handleRemovalFromIntersection (const FactSet &FSet, FactManager &FactMan,
869
918
SourceLocation JoinLoc, LockErrorKind LEK,
@@ -876,14 +925,28 @@ class LockableFactEntry : public FactEntry {
876
925
877
926
void handleLock (FactSet &FSet, FactManager &FactMan, const FactEntry &entry,
878
927
ThreadSafetyHandler &Handler) const override {
879
- Handler.handleDoubleLock (entry.getKind (), entry.toString (), loc (),
880
- entry.loc ());
928
+ if (std::unique_ptr<FactEntry> ReentrantFact = tryReenter (entry.kind ())) {
929
+ if (ReentrantFact->getReentrancyDepth () == 0 )
930
+ Handler.handleReentrancyDepthLimit (entry.getKind (), entry.toString (),
931
+ entry.loc ());
932
+ // This capability has been reentrantly acquired.
933
+ FSet.replaceLock (FactMan, entry, std::move (ReentrantFact));
934
+ } else {
935
+ Handler.handleDoubleLock (entry.getKind (), entry.toString (), loc (),
936
+ entry.loc ());
937
+ }
881
938
}
882
939
883
940
void handleUnlock (FactSet &FSet, FactManager &FactMan,
884
941
const CapabilityExpr &Cp, SourceLocation UnlockLoc,
885
942
bool FullyRemove,
886
943
ThreadSafetyHandler &Handler) const override {
944
+ if (std::unique_ptr<FactEntry> ReentrantFact = leaveReentrant ()) {
945
+ // This capability remains reentrantly acquired.
946
+ FSet.replaceLock (FactMan, Cp, std::move (ReentrantFact));
947
+ return ;
948
+ }
949
+
887
950
FSet.removeLock (FactMan, Cp);
888
951
if (!Cp.negative ()) {
889
952
FSet.addLock (FactMan, std::make_unique<LockableFactEntry>(
@@ -916,6 +979,10 @@ class ScopedLockableFactEntry : public FactEntry {
916
979
SourceKind Src)
917
980
: FactEntry(ScopedLockable, CE, LK_Exclusive, Loc, Src) {}
918
981
982
+ std::unique_ptr<FactEntry> clone () const override {
983
+ return std::make_unique<ScopedLockableFactEntry>(*this );
984
+ }
985
+
919
986
CapExprSet getUnderlyingMutexes () const {
920
987
CapExprSet UnderlyingMutexesSet;
921
988
for (const UnderlyingCapability &UnderlyingMutex : UnderlyingMutexes)
@@ -996,10 +1063,16 @@ class ScopedLockableFactEntry : public FactEntry {
996
1063
void lock (FactSet &FSet, FactManager &FactMan, const CapabilityExpr &Cp,
997
1064
LockKind kind, SourceLocation loc,
998
1065
ThreadSafetyHandler *Handler) const {
999
- if (const FactEntry *Fact = FSet.findLock (FactMan, Cp)) {
1000
- if (Handler)
1001
- Handler->handleDoubleLock (Cp.getKind (), Cp.toString (), Fact->loc (),
1002
- loc);
1066
+ if (const auto It = FSet.findLockIter (FactMan, Cp); It != FSet.end ()) {
1067
+ const FactEntry &Fact = FactMan[*It];
1068
+ if (std::unique_ptr<FactEntry> ReentrantFact = Fact.tryReenter (kind)) {
1069
+ if (ReentrantFact->getReentrancyDepth () == 0 && Handler)
1070
+ Handler->handleReentrancyDepthLimit (Cp.getKind (), Cp.toString (), loc);
1071
+ // This capability has been reentrantly acquired.
1072
+ FSet.replaceLock (FactMan, It, std::move (ReentrantFact));
1073
+ } else if (Handler) {
1074
+ Handler->handleDoubleLock (Cp.getKind (), Cp.toString (), Fact.loc (), loc);
1075
+ }
1003
1076
} else {
1004
1077
FSet.removeLock (FactMan, !Cp);
1005
1078
FSet.addLock (FactMan,
@@ -1009,10 +1082,17 @@ class ScopedLockableFactEntry : public FactEntry {
1009
1082
1010
1083
void unlock (FactSet &FSet, FactManager &FactMan, const CapabilityExpr &Cp,
1011
1084
SourceLocation loc, ThreadSafetyHandler *Handler) const {
1012
- if (FSet.findLock (FactMan, Cp)) {
1085
+ if (const auto It = FSet.findLockIter (FactMan, Cp); It != FSet.end ()) {
1086
+ const FactEntry &Fact = FactMan[*It];
1087
+ if (std::unique_ptr<FactEntry> ReentrantFact = Fact.leaveReentrant ()) {
1088
+ // This capability remains reentrantly acquired.
1089
+ FSet.replaceLock (FactMan, It, std::move (ReentrantFact));
1090
+ return ;
1091
+ }
1092
+
1013
1093
FSet.removeLock (FactMan, Cp);
1014
- FSet.addLock (FactMan, std::make_unique<LockableFactEntry>(
1015
- !Cp, LK_Exclusive, loc));
1094
+ FSet.addLock (FactMan,
1095
+ std::make_unique<LockableFactEntry>( !Cp, LK_Exclusive, loc));
1016
1096
} else if (Handler) {
1017
1097
SourceLocation PrevLoc;
1018
1098
if (const FactEntry *Neg = FSet.findLock (FactMan, !Cp))
@@ -1306,7 +1386,6 @@ void ThreadSafetyAnalyzer::addLock(FactSet &FSet,
1306
1386
Entry->loc (), Entry->getKind ());
1307
1387
}
1308
1388
1309
- // FIXME: Don't always warn when we have support for reentrant locks.
1310
1389
if (const FactEntry *Cp = FSet.findLock (FactMan, *Entry)) {
1311
1390
if (!Entry->asserted ())
1312
1391
Cp->handleLock (FSet, FactMan, *Entry, Handler);
@@ -1831,15 +1910,15 @@ void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
1831
1910
assert (!Self);
1832
1911
const auto *TagT = Exp->getType ()->getAs <TagType>();
1833
1912
if (D->hasAttrs () && TagT && Exp->isPRValue ()) {
1834
- std::pair<til::LiteralPtr *, StringRef > Placeholder =
1913
+ std::pair<til::LiteralPtr *, CapabilityExpr > Placeholder =
1835
1914
Analyzer->SxBuilder .createThisPlaceholder (Exp);
1836
1915
[[maybe_unused]] auto inserted =
1837
1916
Analyzer->ConstructedObjects .insert ({Exp, Placeholder.first });
1838
1917
assert (inserted.second && " Are we visiting the same expression again?" );
1839
1918
if (isa<CXXConstructExpr>(Exp))
1840
1919
Self = Placeholder.first ;
1841
1920
if (TagT->getDecl ()->hasAttr <ScopedLockableAttr>())
1842
- Scp = CapabilityExpr ( Placeholder.first , Placeholder. second , false ) ;
1921
+ Scp = Placeholder.second ;
1843
1922
}
1844
1923
1845
1924
assert (Loc.isInvalid ());
@@ -1977,12 +2056,13 @@ void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
1977
2056
if (DeclaredLocks.empty ())
1978
2057
continue ;
1979
2058
CapabilityExpr Cp (Analyzer->SxBuilder .translate (Arg, nullptr ),
1980
- StringRef (" mutex" ), false );
2059
+ StringRef (" mutex" ), /* Neg= */ false , /* Reentrant= */ false );
1981
2060
if (const auto *CBTE = dyn_cast<CXXBindTemporaryExpr>(Arg->IgnoreCasts ());
1982
2061
Cp.isInvalid () && CBTE) {
1983
2062
if (auto Object = Analyzer->ConstructedObjects .find (CBTE->getSubExpr ());
1984
2063
Object != Analyzer->ConstructedObjects .end ())
1985
- Cp = CapabilityExpr (Object->second , StringRef (" mutex" ), false );
2064
+ Cp = CapabilityExpr (Object->second , StringRef (" mutex" ), /* Neg=*/ false ,
2065
+ /* Reentrant=*/ false );
1986
2066
}
1987
2067
const FactEntry *Fact = FSet.findLock (Analyzer->FactMan , Cp);
1988
2068
if (!Fact) {
@@ -2491,7 +2571,8 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
2491
2571
}
2492
2572
if (UnderlyingLocks.empty ())
2493
2573
continue ;
2494
- CapabilityExpr Cp (SxBuilder.createVariable (Param), StringRef (), false );
2574
+ CapabilityExpr Cp (SxBuilder.createVariable (Param), StringRef (),
2575
+ /* Neg=*/ false , /* Reentrant=*/ false );
2495
2576
auto ScopedEntry = std::make_unique<ScopedLockableFactEntry>(
2496
2577
Cp, Param->getLocation (), FactEntry::Declared);
2497
2578
for (const CapabilityExpr &M : UnderlyingLocks)
0 commit comments