@@ -99,8 +99,6 @@ class FactSet;
9999// / particular point in program execution. Currently, a fact is a capability,
100100// / along with additional information, such as where it was acquired, whether
101101// / it is exclusive or shared, etc.
102- // /
103- // / FIXME: this analysis does not currently support re-entrant locking.
104102class FactEntry : public CapabilityExpr {
105103public:
106104 enum FactEntryKind { Lockable, ScopedLockable };
@@ -114,31 +112,39 @@ class FactEntry : public CapabilityExpr {
114112 };
115113
116114private:
117- const FactEntryKind Kind : 8 ;
115+ const FactEntryKind Kind : 4 ;
118116
119117 // / Exclusive or shared.
120- LockKind LKind : 8 ;
118+ const LockKind LKind : 4 ;
119+
120+ // / How it was acquired.
121+ const SourceKind Source : 4 ;
121122
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 ;
124126
125127 // / Where it was acquired.
126- SourceLocation AcquireLoc;
128+ const SourceLocation AcquireLoc;
127129
128130public:
129131 FactEntry (FactEntryKind FK, const CapabilityExpr &CE, LockKind LK,
130132 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) {}
132135 virtual ~FactEntry () = default ;
133136
134137 LockKind kind () const { return LKind; }
135138 SourceLocation loc () const { return AcquireLoc; }
136139 FactEntryKind getFactEntryKind () const { return Kind; }
140+ unsigned int getReentrancyDepth () const { return ReentrancyDepth; }
137141
138142 bool asserted () const { return Source == Asserted; }
139143 bool declared () const { return Source == Declared; }
140144 bool managed () const { return Source == Managed; }
141145
146+ virtual std::unique_ptr<FactEntry> clone () const = 0;
147+
142148 virtual void
143149 handleRemovalFromIntersection (const FactSet &FSet, FactManager &FactMan,
144150 SourceLocation JoinLoc, LockErrorKind LEK,
@@ -155,6 +161,29 @@ class FactEntry : public CapabilityExpr {
155161 bool isAtLeast (LockKind LK) const {
156162 return (LKind == LK_Exclusive) || (LK == LK_Shared);
157163 }
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+ }
158187};
159188
160189using FactID = unsigned short ;
@@ -168,6 +197,8 @@ class FactManager {
168197public:
169198 FactID newFact (std::unique_ptr<FactEntry> Entry) {
170199 Facts.push_back (std::move (Entry));
200+ assert (Facts.size () - 1 <= std::numeric_limits<unsigned short >::max () &&
201+ " FactID space exhausted" );
171202 return static_cast <unsigned short >(Facts.size () - 1 );
172203 }
173204
@@ -235,6 +266,20 @@ class FactSet {
235266 return false ;
236267 }
237268
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+
238283 iterator findLockIter (FactManager &FM, const CapabilityExpr &CapE) {
239284 return std::find_if (begin (), end (), [&](FactID ID) {
240285 return FM[ID].matches (CapE);
@@ -864,6 +909,10 @@ class LockableFactEntry : public FactEntry {
864909 SourceKind Src = Acquired)
865910 : FactEntry(Lockable, CE, LK, Loc, Src) {}
866911
912+ std::unique_ptr<FactEntry> clone () const override {
913+ return std::make_unique<LockableFactEntry>(*this );
914+ }
915+
867916 void
868917 handleRemovalFromIntersection (const FactSet &FSet, FactManager &FactMan,
869918 SourceLocation JoinLoc, LockErrorKind LEK,
@@ -876,14 +925,28 @@ class LockableFactEntry : public FactEntry {
876925
877926 void handleLock (FactSet &FSet, FactManager &FactMan, const FactEntry &entry,
878927 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+ }
881938 }
882939
883940 void handleUnlock (FactSet &FSet, FactManager &FactMan,
884941 const CapabilityExpr &Cp, SourceLocation UnlockLoc,
885942 bool FullyRemove,
886943 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+
887950 FSet.removeLock (FactMan, Cp);
888951 if (!Cp.negative ()) {
889952 FSet.addLock (FactMan, std::make_unique<LockableFactEntry>(
@@ -916,6 +979,10 @@ class ScopedLockableFactEntry : public FactEntry {
916979 SourceKind Src)
917980 : FactEntry(ScopedLockable, CE, LK_Exclusive, Loc, Src) {}
918981
982+ std::unique_ptr<FactEntry> clone () const override {
983+ return std::make_unique<ScopedLockableFactEntry>(*this );
984+ }
985+
919986 CapExprSet getUnderlyingMutexes () const {
920987 CapExprSet UnderlyingMutexesSet;
921988 for (const UnderlyingCapability &UnderlyingMutex : UnderlyingMutexes)
@@ -996,10 +1063,16 @@ class ScopedLockableFactEntry : public FactEntry {
9961063 void lock (FactSet &FSet, FactManager &FactMan, const CapabilityExpr &Cp,
9971064 LockKind kind, SourceLocation loc,
9981065 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+ }
10031076 } else {
10041077 FSet.removeLock (FactMan, !Cp);
10051078 FSet.addLock (FactMan,
@@ -1009,10 +1082,17 @@ class ScopedLockableFactEntry : public FactEntry {
10091082
10101083 void unlock (FactSet &FSet, FactManager &FactMan, const CapabilityExpr &Cp,
10111084 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+
10131093 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));
10161096 } else if (Handler) {
10171097 SourceLocation PrevLoc;
10181098 if (const FactEntry *Neg = FSet.findLock (FactMan, !Cp))
@@ -1306,7 +1386,6 @@ void ThreadSafetyAnalyzer::addLock(FactSet &FSet,
13061386 Entry->loc (), Entry->getKind ());
13071387 }
13081388
1309- // FIXME: Don't always warn when we have support for reentrant locks.
13101389 if (const FactEntry *Cp = FSet.findLock (FactMan, *Entry)) {
13111390 if (!Entry->asserted ())
13121391 Cp->handleLock (FSet, FactMan, *Entry, Handler);
@@ -1831,15 +1910,15 @@ void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
18311910 assert (!Self);
18321911 const auto *TagT = Exp->getType ()->getAs <TagType>();
18331912 if (D->hasAttrs () && TagT && Exp->isPRValue ()) {
1834- std::pair<til::LiteralPtr *, StringRef > Placeholder =
1913+ std::pair<til::LiteralPtr *, CapabilityExpr > Placeholder =
18351914 Analyzer->SxBuilder .createThisPlaceholder (Exp);
18361915 [[maybe_unused]] auto inserted =
18371916 Analyzer->ConstructedObjects .insert ({Exp, Placeholder.first });
18381917 assert (inserted.second && " Are we visiting the same expression again?" );
18391918 if (isa<CXXConstructExpr>(Exp))
18401919 Self = Placeholder.first ;
18411920 if (TagT->getDecl ()->hasAttr <ScopedLockableAttr>())
1842- Scp = CapabilityExpr ( Placeholder.first , Placeholder. second , false ) ;
1921+ Scp = Placeholder.second ;
18431922 }
18441923
18451924 assert (Loc.isInvalid ());
@@ -1977,12 +2056,13 @@ void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
19772056 if (DeclaredLocks.empty ())
19782057 continue ;
19792058 CapabilityExpr Cp (Analyzer->SxBuilder .translate (Arg, nullptr ),
1980- StringRef (" mutex" ), false );
2059+ StringRef (" mutex" ), /* Neg= */ false , /* Reentrant= */ false );
19812060 if (const auto *CBTE = dyn_cast<CXXBindTemporaryExpr>(Arg->IgnoreCasts ());
19822061 Cp.isInvalid () && CBTE) {
19832062 if (auto Object = Analyzer->ConstructedObjects .find (CBTE->getSubExpr ());
19842063 Object != Analyzer->ConstructedObjects .end ())
1985- Cp = CapabilityExpr (Object->second , StringRef (" mutex" ), false );
2064+ Cp = CapabilityExpr (Object->second , StringRef (" mutex" ), /* Neg=*/ false ,
2065+ /* Reentrant=*/ false );
19862066 }
19872067 const FactEntry *Fact = FSet.findLock (Analyzer->FactMan , Cp);
19882068 if (!Fact) {
@@ -2491,7 +2571,8 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
24912571 }
24922572 if (UnderlyingLocks.empty ())
24932573 continue ;
2494- CapabilityExpr Cp (SxBuilder.createVariable (Param), StringRef (), false );
2574+ CapabilityExpr Cp (SxBuilder.createVariable (Param), StringRef (),
2575+ /* Neg=*/ false , /* Reentrant=*/ false );
24952576 auto ScopedEntry = std::make_unique<ScopedLockableFactEntry>(
24962577 Cp, Param->getLocation (), FactEntry::Declared);
24972578 for (const CapabilityExpr &M : UnderlyingLocks)
0 commit comments