[cfe-commits] r139307 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.td lib/Sema/AnalysisBasedWarnings.cpp test/SemaCXX/warn-thread-safety-analysis.cpp

Caitlin Sadowski supertri at google.com
Thu Sep 8 11:19:38 PDT 2011


Author: supertri
Date: Thu Sep  8 13:19:38 2011
New Revision: 139307

URL: http://llvm.org/viewvc/llvm-project?rev=139307&view=rev
Log:
Thread safety: shared vs. exclusive locks

Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
    cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=139307&r1=139306&r2=139307&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu Sep  8 13:19:38 2011
@@ -1404,16 +1404,25 @@
   "lock '%0' is not released at the end of its scope">,
   InGroup<ThreadSafety>, DefaultIgnore;
 def warn_expecting_lock_held_on_loop : Warning<
-  "expecting lock '%0' to be held at start of each loop">,
+  "expecting lock '%0' to be %select{held|held exclusively}1 at start of each "
+  "loop">,
+  InGroup<ThreadSafety>, DefaultIgnore;
+def warn_lock_exclusive_and_shared : Warning<
+  "lock '%0' is held exclusively and shared in the same scope">,
+  InGroup<ThreadSafety>, DefaultIgnore;
+def note_lock_exclusive_and_shared : Note<
+  "the other acquire of lock '%0' is here">,
   InGroup<ThreadSafety>, DefaultIgnore;
 def warn_variable_requires_lock : Warning<
-  "accessing variable '%0' requires lock '%1'">,
+  "%select{reading|writing}2 variable '%0' requires lock '%1' to be "
+  "%select{held|held exclusively}2">,
   InGroup<ThreadSafety>, DefaultIgnore;
 def warn_variable_requires_any_lock : Warning<
   "accessing variable '%0' requires some lock">,
   InGroup<ThreadSafety>, DefaultIgnore;
 def warn_var_deref_requires_lock : Warning<
-  "accessing the value pointed to by '%0' requires lock '%1'">,
+  "%select{reading|writing}2 the value pointed to by '%0' requires lock '%1' to"
+  " be %select{held|held exclusively}2">,
   InGroup<ThreadSafety>, DefaultIgnore;
 def warn_var_deref_requires_any_lock : Warning<
   "accessing the value pointed to by '%0' requires some lock">,

Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=139307&r1=139306&r2=139307&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Thu Sep  8 13:19:38 2011
@@ -782,6 +782,16 @@
   }
 };
 
+enum LockKind {
+  LK_Shared,
+  LK_Exclusive
+};
+
+enum AccessKind {
+  AK_Read,
+  AK_Written
+};
+
 /// \brief This is a helper class that stores info about the most recent
 /// accquire of a Lock.
 ///
@@ -789,10 +799,19 @@
 struct LockData {
   SourceLocation AcquireLoc;
 
-  LockData(SourceLocation Loc) : AcquireLoc(Loc) {}
+  /// \brief LKind stores whether a lock is held shared or exclusively.
+  /// Note that this analysis does not currently support either re-entrant
+  /// locking or lock "upgrading" and "downgrading" between exclusive and
+  /// shared.
+  ///
+  /// FIXME: add support for re-entrant locking and lock up/downgrading
+  LockKind LKind;
+
+  LockData(SourceLocation AcquireLoc, LockKind LKind)
+    : AcquireLoc(AcquireLoc), LKind(LKind) {}
 
   bool operator==(const LockData &other) const {
-    return AcquireLoc == other.AcquireLoc;
+    return AcquireLoc == other.AcquireLoc && LKind == other.LKind;
   }
 
   bool operator!=(const LockData &other) const {
@@ -800,8 +819,9 @@
   }
 
   void Profile(llvm::FoldingSetNodeID &ID) const {
-    ID.AddInteger(AcquireLoc.getRawEncoding());
-  }
+      ID.AddInteger(AcquireLoc.getRawEncoding());
+      ID.AddInteger(LKind);
+    }
 };
 
 /// A Lockset maps each LockID (defined above) to information about how it has
@@ -820,10 +840,28 @@
 
   // Helper functions
   void removeLock(SourceLocation UnlockLoc, Expr *LockExp);
-  void addLock(SourceLocation LockLoc, Expr *LockExp);
+  void addLock(SourceLocation LockLoc, Expr *LockExp, LockKind LK);
   const ValueDecl *getValueDecl(Expr *Exp);
-  void checkAccess(Expr *Exp);
-  void checkDereference(Expr *Exp);
+  void warnIfLockNotHeld (const NamedDecl *D, Expr *Exp, AccessKind AK,
+                          LockID &Lock, unsigned DiagID);
+  void checkAccess(Expr *Exp, AccessKind AK);
+  void checkDereference(Expr *Exp, AccessKind AK);
+
+  template <class AttrType>
+  void addLocksToSet(LockKind LK, Attr *Attr, CXXMemberCallExpr *Exp);
+
+  /// \brief Returns true if the lockset contains a lock, regardless of whether
+  /// the lock is held exclusively or shared.
+  bool locksetContains(LockID Lock) {
+    return LSet.lookup(Lock);
+  }
+
+  /// \brief Returns true if the lockset contains a lock with the passed in
+  /// locktype.
+  bool locksetContains(LockID Lock, LockKind KindRequested) const {
+    const LockData *LockHeld = LSet.lookup(Lock);
+    return (LockHeld && KindRequested == LockHeld->LKind);
+  }
 
 public:
   BuildLockset(Sema &S, Lockset LS, Lockset::Factory &F)
@@ -843,13 +881,15 @@
 /// \brief Add a new lock to the lockset, warning if the lock is already there.
 /// \param LockLoc The source location of the acquire
 /// \param LockExp The lock expression corresponding to the lock to be added
-void BuildLockset::addLock(SourceLocation LockLoc, Expr *LockExp) {
+void BuildLockset::addLock(SourceLocation LockLoc, Expr *LockExp,
+                           LockKind LK) {
+  // FIXME: deal with acquired before/after annotations
   LockID Lock(LockExp);
-  LockData NewLockData(LockLoc);
+  LockData NewLockData(LockLoc, LK);
 
-  if (LSet.contains(Lock))
+  // FIXME: Don't always warn when we have support for reentrant locks.
+  if (locksetContains(Lock))
     S.Diag(LockLoc, diag::warn_double_lock) << Lock.getName();
-
   LSet = LocksetFactory.add(LSet, Lock, NewLockData);
 }
 
@@ -877,13 +917,33 @@
   return 0;
 }
 
+/// \brief Warn if the LSet does not contain a lock sufficient to protect access
+/// of at least the passed in AccessType.
+void BuildLockset::warnIfLockNotHeld(const NamedDecl *D, Expr *Exp,
+                                     AccessKind AK, LockID &Lock,
+                                     unsigned DiagID) {
+  switch (AK) {
+    case AK_Read:
+      if (!locksetContains(Lock))
+        S.Diag(Exp->getExprLoc(), DiagID)
+          << D->getName() << Lock.getName() << LK_Shared;
+      break;
+    case AK_Written :
+      if (!locksetContains(Lock, LK_Exclusive))
+        S.Diag(Exp->getExprLoc(), DiagID)
+          << D->getName() << Lock.getName() << LK_Exclusive;
+      break;
+  }
+}
+
+
 /// \brief This method identifies variable dereferences and checks pt_guarded_by
 /// and pt_guarded_var annotations. Note that we only check these annotations
 /// at the time a pointer is dereferenced.
 /// FIXME: We need to check for other types of pointer dereferences
 /// (e.g. [], ->) and deal with them here.
 /// \param Exp An expression that has been read or written.
-void BuildLockset::checkDereference(Expr *Exp) {
+void BuildLockset::checkDereference(Expr *Exp, AccessKind AK) {
   UnaryOperator *UO = dyn_cast<UnaryOperator>(Exp);
   if (!UO || UO->getOpcode() != clang::UO_Deref)
     return;
@@ -901,11 +961,9 @@
   for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i) {
     if (ArgAttrs[i]->getKind() != attr::PtGuardedBy)
       continue;
-    PtGuardedByAttr *PGBAttr = cast<PtGuardedByAttr>(ArgAttrs[i]);
+    const PtGuardedByAttr *PGBAttr = cast<PtGuardedByAttr>(ArgAttrs[i]);
     LockID Lock(PGBAttr->getArg());
-    if (!LSet.contains(Lock))
-      S.Diag(Exp->getExprLoc(), diag::warn_var_deref_requires_lock)
-        << D->getName() << Lock.getName();
+    warnIfLockNotHeld(D, Exp, AK, Lock, diag::warn_var_deref_requires_lock);
   }
 }
 
@@ -913,7 +971,7 @@
 /// Whenever we identify an access (read or write) of a DeclRefExpr or
 /// MemberExpr, we need to check whether there are any guarded_by or
 /// guarded_var attributes, and make sure we hold the appropriate locks.
-void BuildLockset::checkAccess(Expr *Exp) {
+void BuildLockset::checkAccess(Expr *Exp, AccessKind AK) {
   const ValueDecl *D = getValueDecl(Exp);
   if(!D || !D->hasAttrs())
     return;
@@ -926,11 +984,9 @@
   for(unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i) {
     if (ArgAttrs[i]->getKind() != attr::GuardedBy)
       continue;
-    GuardedByAttr *GBAttr = cast<GuardedByAttr>(ArgAttrs[i]);
+    const GuardedByAttr *GBAttr = cast<GuardedByAttr>(ArgAttrs[i]);
     LockID Lock(GBAttr->getArg());
-    if (!LSet.contains(Lock))
-      S.Diag(Exp->getExprLoc(), diag::warn_variable_requires_lock)
-        << D->getName() << Lock.getName();
+    warnIfLockNotHeld(D, Exp, AK, Lock, diag::warn_variable_requires_lock);
   }
 }
 
@@ -944,8 +1000,8 @@
     case clang::UO_PreDec:
     case clang::UO_PreInc: {
       Expr *SubExp = UO->getSubExpr()->IgnoreParenCasts();
-      checkAccess(SubExp);
-      checkDereference(SubExp);
+      checkAccess(SubExp, AK_Written);
+      checkDereference(SubExp, AK_Written);
       break;
     }
     default:
@@ -960,8 +1016,8 @@
   if (!BO->isAssignmentOp())
     return;
   Expr *LHSExp = BO->getLHS()->IgnoreParenCasts();
-  checkAccess(LHSExp);
-  checkDereference(LHSExp);
+  checkAccess(LHSExp, AK_Written);
+  checkDereference(LHSExp, AK_Written);
 }
 
 /// Whenever we do an LValue to Rvalue cast, we are reading a variable and
@@ -971,10 +1027,30 @@
   if (CE->getCastKind() != CK_LValueToRValue)
     return;
   Expr *SubExp = CE->getSubExpr()->IgnoreParenCasts();
-  checkAccess(SubExp);
-  checkDereference(SubExp);
+  checkAccess(SubExp, AK_Read);
+  checkDereference(SubExp, AK_Read);
 }
 
+/// \brief This function, parameterized by an attribute type, is used to add a
+/// set of locks specified as attribute arguments to the lockset.
+template <typename AttrType>
+void BuildLockset::addLocksToSet(LockKind LK, Attr *Attr,
+                                 CXXMemberCallExpr *Exp) {
+  typedef typename AttrType::args_iterator iterator_type;
+  SourceLocation ExpLocation = Exp->getExprLoc();
+  Expr *Parent = Exp->getImplicitObjectArgument();
+  AttrType *SpecificAttr = cast<AttrType>(Attr);
+
+  if (SpecificAttr->args_size() == 0) {
+    // The lock held is the "this" object.
+    addLock(ExpLocation, Parent, LK);
+    return;
+  }
+
+  for (iterator_type I = SpecificAttr->args_begin(),
+       E = SpecificAttr->args_end(); I != E; ++I)
+    addLock(ExpLocation, *I, LK);
+}
 
 /// \brief When visiting CXXMemberCallExprs we need to examine the attributes on
 /// the method that is being called and add, remove or check locks in the
@@ -998,22 +1074,16 @@
     Attr *Attr = ArgAttrs[i];
     switch (Attr->getKind()) {
       // When we encounter an exclusive lock function, we need to add the lock
-      // to our lockset.
-      case attr::ExclusiveLockFunction: {
-        ExclusiveLockFunctionAttr *ELFAttr =
-          cast<ExclusiveLockFunctionAttr>(Attr);
-
-        if (ELFAttr->args_size() == 0) {// The lock held is the "this" object.
-          addLock(ExpLocation, Parent);
-          break;
-        }
+      // to our lockset, marked as exclusive.
+      case attr::ExclusiveLockFunction:
+        addLocksToSet<ExclusiveLockFunctionAttr>(LK_Exclusive, Attr, Exp);
+        break;
 
-        for (ExclusiveLockFunctionAttr::args_iterator I = ELFAttr->args_begin(),
-             E = ELFAttr->args_end(); I != E; ++I)
-          addLock(ExpLocation, *I);
-        // FIXME: acquired_after/acquired_before annotations
+      // When we encounter a shared lock function, we need to add the lock
+      // to our lockset, marked as not exclusive
+      case attr::SharedLockFunction:
+        addLocksToSet<SharedLockFunctionAttr>(LK_Shared, Attr, Exp);
         break;
-      }
 
       // When we encounter an unlock function, we need to remove unlocked locks
       // from the lockset, and flag a warning if they are not there.
@@ -1066,6 +1136,35 @@
     S.Diag(I->first, I->second);
 }
 
+static Lockset warnIfNotInFirstSetOrNotSameKind(Sema &S, const Lockset LSet1,
+                                                const Lockset LSet2,
+                                                DiagList &Warnings,
+                                                Lockset Intersection,
+                                                Lockset::Factory &Fact) {
+  for (Lockset::iterator I = LSet2.begin(), E = LSet2.end(); I != E; ++I) {
+    const LockID &LSet2Lock = I.getKey();
+    const LockData &LSet2LockData = I.getData();
+    if (const LockData *LD = LSet1.lookup(LSet2Lock)) {
+      if (LD->LKind != LSet2LockData.LKind) {
+        PartialDiagnostic Warning =
+          S.PDiag(diag::warn_lock_exclusive_and_shared) << LSet2Lock.getName();
+        PartialDiagnostic Note =
+          S.PDiag(diag::note_lock_exclusive_and_shared) << LSet2Lock.getName();
+        Warnings.push_back(DelayedDiag(LSet2LockData.AcquireLoc, Warning));
+        Warnings.push_back(DelayedDiag(LD->AcquireLoc, Note));
+        if (LD->LKind != LK_Exclusive)
+          Intersection = Fact.add(Intersection, LSet2Lock, LSet2LockData);
+      }
+    } else {
+      PartialDiagnostic Warning =
+        S.PDiag(diag::warn_lock_not_released_in_scope) << LSet2Lock.getName();
+      Warnings.push_back(DelayedDiag(LSet2LockData.AcquireLoc, Warning));
+    }
+  }
+  return Intersection;
+}
+
+
 /// \brief Compute the intersection of two locksets and issue warnings for any
 /// locks in the symmetric difference.
 ///
@@ -1074,20 +1173,14 @@
 /// A; if () then B; else C; D; we need to check that the lockset after B and C
 /// are the same. In the event of a difference, we use the intersection of these
 /// two locksets at the start of D.
-static Lockset intersectAndWarn(Sema &S, Lockset LSet1, Lockset LSet2,
+static Lockset intersectAndWarn(Sema &S, const Lockset LSet1,
+                                const Lockset LSet2,
                                 Lockset::Factory &Fact) {
   Lockset Intersection = LSet1;
   DiagList Warnings;
 
-  for (Lockset::iterator I = LSet2.begin(), E = LSet2.end(); I != E; ++I) {
-    if (!LSet1.contains(I.getKey())) {
-      const LockID &MissingLock = I.getKey();
-      const LockData &MissingLockData = I.getData();
-      PartialDiagnostic Warning =
-        S.PDiag(diag::warn_lock_not_released_in_scope) << MissingLock.getName();
-      Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
-    }
-  }
+  Intersection = warnIfNotInFirstSetOrNotSameKind(S, LSet1, LSet2, Warnings,
+                                                  Intersection, Fact);
 
   for (Lockset::iterator I = LSet1.begin(), E = LSet1.end(); I != E; ++I) {
     if (!LSet2.contains(I.getKey())) {
@@ -1130,7 +1223,8 @@
 /// \param LoopReentrySet Locks held in the last CFG block of the loop
 static void warnBackEdgeUnequalLocksets(Sema &S, const Lockset LoopReentrySet,
                                         const Lockset LoopEntrySet,
-                                        SourceLocation FirstLocInLoop) {
+                                        SourceLocation FirstLocInLoop,
+                                        Lockset::Factory &Fact) {
   assert(FirstLocInLoop.isValid());
   DiagList Warnings;
 
@@ -1142,22 +1236,14 @@
       // We report this error at the location of the first statement in a loop
       PartialDiagnostic Warning =
         S.PDiag(diag::warn_expecting_lock_held_on_loop)
-          << MissingLock.getName();
+          << MissingLock.getName() << LK_Shared;
       Warnings.push_back(DelayedDiag(FirstLocInLoop, Warning));
     }
   }
 
   // Warn for locks held at the end of the loop, but not at the start.
-  for (Lockset::iterator I = LoopReentrySet.begin(), E = LoopReentrySet.end();
-       I != E; ++I) {
-    if (!LoopEntrySet.contains(I.getKey())) {
-      const LockID &MissingLock = I.getKey();
-      const LockData &MissingLockData = I.getData();
-      PartialDiagnostic Warning =
-        S.PDiag(diag::warn_lock_not_released_in_scope) << MissingLock.getName();
-      Warnings.push_back(DelayedDiag(MissingLockData.AcquireLoc, Warning));
-    }
-  }
+  warnIfNotInFirstSetOrNotSameKind(S, LoopEntrySet, LoopReentrySet, Warnings,
+                                   LoopReentrySet, Fact);
 
   EmitDiagnostics(S, Warnings);
 }
@@ -1250,13 +1336,15 @@
       SourceLocation FirstLoopLocation = getFirstStmtLocation(FirstLoopBlock);
 
       assert(FirstLoopLocation.isValid());
+
       // Fail gracefully in release code.
       if (!FirstLoopLocation.isValid())
         continue;
 
       Lockset PreLoop = EntryLocksets[FirstLoopBlock->getBlockID()];
       Lockset LoopEnd = ExitLocksets[CurrBlockID];
-      warnBackEdgeUnequalLocksets(S, LoopEnd, PreLoop, FirstLoopLocation);
+      warnBackEdgeUnequalLocksets(S, LoopEnd, PreLoop, FirstLoopLocation,
+                                  LocksetFactory);
     }
   }
 

Modified: cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp?rev=139307&r1=139306&r2=139307&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-thread-safety-analysis.cpp Thu Sep  8 13:19:38 2011
@@ -219,7 +219,6 @@
     // expected-warning{{unlocking 'sls_mu' that was not acquired}}
 }
 
-
 //-----------------------------------------//
 // Handling lock expressions in attribute args
 // -------------------------------------------//
@@ -306,13 +305,13 @@
                  __attribute__((pt_guarded_by(sls_mu)));
   void testFoo() {
     pgb_field = &x; // \
-      // expected-warning{{accessing variable 'pgb_field' requires lock 'sls_mu2'}}
-    *pgb_field = x; // expected-warning {{accessing variable 'pgb_field' requires lock 'sls_mu2'}} \
-      // expected-warning {{accessing the value pointed to by 'pgb_field' requires lock 'sls_mu'}}
-    x = *pgb_field; // expected-warning {{accessing variable 'pgb_field' requires lock 'sls_mu2'}} \
-      // expected-warning {{accessing the value pointed to by 'pgb_field' requires lock 'sls_mu'}}
-    (*pgb_field)++; // expected-warning {{accessing variable 'pgb_field' requires lock 'sls_mu2'}} \
-      // expected-warning {{accessing the value pointed to by 'pgb_field' requires lock 'sls_mu'}}
+      // expected-warning {{writing variable 'pgb_field' requires lock 'sls_mu2' to be held exclusively}}
+    *pgb_field = x; // expected-warning {{reading variable 'pgb_field' requires lock 'sls_mu2' to be held}} \
+      // expected-warning {{writing the value pointed to by 'pgb_field' requires lock 'sls_mu' to be held exclusively}}
+    x = *pgb_field; // expected-warning {{reading variable 'pgb_field' requires lock 'sls_mu2' to be held}} \
+      // expected-warning {{reading the value pointed to by 'pgb_field' requires lock 'sls_mu' to be held}}
+    (*pgb_field)++; // expected-warning {{reading variable 'pgb_field' requires lock 'sls_mu2' to be held}} \
+      // expected-warning {{writing the value pointed to by 'pgb_field' requires lock 'sls_mu' to be held exclusively}}
   }
 };
 
@@ -322,7 +321,7 @@
 
   void testFoo() {
     gb_field = 0; // \
-      // expected-warning{{accessing variable 'gb_field' requires lock 'sls_mu'}}
+      // expected-warning {{writing variable 'gb_field' requires lock 'sls_mu' to be held exclusively}}
   }
 };
 
@@ -361,12 +360,12 @@
 
 void gb_bad_2() {
   sls_guardby_var = 1; // \
-    // expected-warning{{accessing variable 'sls_guardby_var' requires lock 'sls_mu'}}
+    // expected-warning {{writing variable 'sls_guardby_var' requires lock 'sls_mu' to be held exclusively}}
 }
 
 void gb_bad_3() {
   int x = sls_guardby_var; // \
-    // expected-warning{{accessing variable 'sls_guardby_var' requires lock 'sls_mu'}}
+    // expected-warning {{reading variable 'sls_guardby_var' requires lock 'sls_mu' to be held}}
 }
 
 void gb_bad_4() {
@@ -381,18 +380,18 @@
 
 void gb_bad_6() {
   *pgb_var = 1; // \
-    // expected-warning {{accessing the value pointed to by 'pgb_var' requires lock 'sls_mu'}}
+    // expected-warning {{writing the value pointed to by 'pgb_var' requires lock 'sls_mu' to be held exclusively}}
 }
 
 void gb_bad_7() {
   int x = *pgb_var; // \
-    // expected-warning {{accessing the value pointed to by 'pgb_var' requires lock 'sls_mu'}}
+    // expected-warning {{reading the value pointed to by 'pgb_var' requires lock 'sls_mu' to be held}}
 }
 
 void gb_bad_8() {
   GBFoo G;
   G.gb_field = 0; // \
-    // expected-warning{{accessing variable 'gb_field' requires lock 'sls_mu'}}
+    // expected-warning {{writing variable 'gb_field' requires lock 'sls_mu'}}
 }
 
 void gb_bad_9() {
@@ -419,11 +418,11 @@
 
   void test() {
     a = 0; // \
-      // expected-warning{{accessing variable 'a' requires lock 'mu'}}
+      // expected-warning{{writing variable 'a' requires lock 'mu' to be held exclusively}}
     b = a; // \
-      // expected-warning {{accessing variable 'a' requires lock 'mu'}}
+      // expected-warning {{reading variable 'a' requires lock 'mu' to be held}}
     c = 0; // \
-      // expected-warning {{accessing variable 'c' requires lock 'mu'}}
+      // expected-warning {{writing variable 'c' requires lock 'mu' to be held exclusively}}
   }
 
   int c __attribute__((guarded_by(mu)));
@@ -431,3 +430,85 @@
   Mutex mu;
 };
 
+//-----------------------------------------------//
+// Extra warnings for shared vs. exclusive locks
+// ----------------------------------------------//
+
+void shared_fun_0() {
+  sls_mu.Lock();
+  do {
+    sls_mu.Unlock();
+    sls_mu.Lock();
+  } while (getBool());
+  sls_mu.Unlock();
+}
+
+void shared_fun_1() {
+  sls_mu.ReaderLock();
+  do {
+    sls_mu.Unlock();
+    sls_mu.Lock(); // \
+      // expected-warning {{lock 'sls_mu' is held exclusively and shared in the same scope}}
+  } while (getBool());
+  sls_mu.Unlock();
+}
+
+void shared_fun_3() {
+  if (getBool())
+    sls_mu.Lock();
+  else
+    sls_mu.Lock();
+  *pgb_var = 1;
+  sls_mu.Unlock();
+}
+
+void shared_fun_4() {
+  if (getBool())
+    sls_mu.ReaderLock();
+  else
+    sls_mu.ReaderLock();
+  int x = sls_guardby_var;
+  sls_mu.Unlock();
+}
+
+void shared_fun_8() {
+  if (getBool())
+    sls_mu.Lock(); // \
+      // expected-warning {{lock 'sls_mu' is held exclusively and shared in the same scope}}
+  else
+    sls_mu.ReaderLock(); // \
+      // expected-note {{the other acquire of lock 'sls_mu' is here}}
+  sls_mu.Unlock();
+}
+
+void shared_bad_0() {
+  sls_mu.Lock();
+  do {
+    sls_mu.Unlock();
+    sls_mu.ReaderLock(); // \
+      // expected-warning {{lock 'sls_mu' is held exclusively and shared in the same scope}}
+  } while (getBool());
+  sls_mu.Unlock();
+}
+
+void shared_bad_1() {
+  if (getBool())
+    sls_mu.Lock(); // \
+      // expected-warning {{lock 'sls_mu' is held exclusively and shared in the same scope}}
+  else
+    sls_mu.ReaderLock(); // \
+      // expected-note {{the other acquire of lock 'sls_mu' is here}}
+  *pgb_var = 1;
+  sls_mu.Unlock();
+}
+
+void shared_bad_2() {
+  if (getBool())
+    sls_mu.ReaderLock(); // \
+      // expected-warning {{lock 'sls_mu' is held exclusively and shared in the same scope}}
+  else
+    sls_mu.Lock(); // \
+      // expected-note {{the other acquire of lock 'sls_mu' is here}}
+  *pgb_var = 1;
+  sls_mu.Unlock();
+}





More information about the cfe-commits mailing list