pw_sync: add lock safety annotations for C++ clang usage

Adds clang thread safety annotations to the interrupt spin lock for
C++ usage. Note that this is only checked when compiling with clang.

C APIs explicitly escape the thread safety annotations.

Change-Id: Ic829afd41b3978c507ebe294d34e1e0dc03865b4
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/40241
Reviewed-by: Keir Mierle <keir@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Pigweed-Auto-Submit: Ewout van Bekkum <ewout@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
diff --git a/pw_sync/docs.rst b/pw_sync/docs.rst
index de5a125..f2959c8 100644
--- a/pw_sync/docs.rst
+++ b/pw_sync/docs.rst
@@ -611,6 +611,345 @@
     pw_sync_InterruptSpinLock_Unlock(&interrupt_spin_lock);
   }
 
+Thread Safety Lock Annotations
+==============================
+Pigweed's critical section lock primitives support Clang's thread safety
+analysis extension for C++. The analysis is completely static at compile-time.
+This is only supported when building with Clang. The annotations are no-ops when
+using different compilers.
+
+Pigweed provides the ``pw_sync/lock_annotations.h`` header file with macro
+definitions to allow developers to document the locking policies of
+multi-threaded code. The annotations can also help program analysis tools to
+identify potential thread safety issues.
+
+More information on Clang's thread safety analysis system can be found
+`here <https://clang.llvm.org/docs/ThreadSafetyAnalysis.html>`_.
+
+Enabling Clang's Analysis
+-------------------------
+In order to enable the analysis, Clang requires that the ``-Wthread-safety``
+compilation flag be used. In addition, if any STL components like
+``std::lock_guard`` are used, the STL's built in annotations have to be manually
+enabled, typically by setting the ``_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS``
+macro.
+
+If using GN, the ``pw_build:clang_thread_safety_warnings`` config is provided
+to do this for you, when added to your clang toolchain definition's default
+configs.
+
+Why use lock annotations?
+-------------------------
+Lock annotations can help warn you about potential race conditions in your code
+when using locks: you have to remember to grab lock(s) before entering a
+critical section, yuou have to remember to unlock it when you leave, and you
+have to avoid deadlocks.
+
+Clang's lock annotations let you inform the compiler and anyone reading your
+code which variables are guarded by which locks, which locks should or cannot be
+held when calling which function, which order locks should be acquired in, etc.
+
+Using Lock Annotations
+----------------------
+When referring to locks in the arguments of the attributes, you should
+use variable names or more complex expressions (e.g. ``my_object->lock_``)
+that evaluate to a concrete lock object whenever possible. If the lock
+you want to refer to is not in scope, you may use a member pointer
+(e.g. ``&MyClass::lock_``) to refer to a lock in some (unknown) object.
+
+Annotating Lock Usage
+^^^^^^^^^^^^^^^^^^^^^
+.. cpp:function:: PW_GUARDED_BY(x)
+
+  Documents if a shared field or global variable needs to be protected by a
+  lock. ``PW_GUARDED_BY()`` allows the user to specify a particular lock that
+  should be held when accessing the annotated variable.
+
+  Although this annotation (and ``PW_PT_GUARDED_BY``, below) cannot be applied
+  to local variables, a local variable and its associated lock can often be
+  combined into a small class or struct, thereby allowing the annotation.
+
+  Example:
+
+  .. code-block:: cpp
+
+    class Foo {
+      Mutex mu_;
+      int p1_ PW_GUARDED_BY(mu_);
+      ...
+    };
+
+.. cpp:function:: PW_PT_GUARDED_BY(x)
+
+  Documents if the memory location pointed to by a pointer should be guarded
+  by a lock when dereferencing the pointer.
+
+  Example:
+
+  .. code-block:: cpp
+
+    class Foo {
+      Mutex mu_;
+      int *p1_ PW_PT_GUARDED_BY(mu_);
+      ...
+    };
+
+  Note that a pointer variable to a shared memory location could itself be a
+  shared variable.
+
+  Example:
+
+  .. code-block:: cpp
+
+    // `q_`, guarded by `mu1_`, points to a shared memory location that is
+    // guarded by `mu2_`:
+    int *q_ PW_GUARDED_BY(mu1_) PW_PT_GUARDED_BY(mu2_);
+
+.. cpp:function:: PW_ACQUIRED_AFTER(...)
+.. cpp:function:: PW_ACQUIRED_BEFORE(...)
+
+  Documents the acquisition order between locks that can be held
+  simultaneously by a thread. For any two locks that need to be annotated
+  to establish an acquisition order, only one of them needs the annotation.
+  (i.e. You don't have to annotate both locks with both ``PW_ACQUIRED_AFTER``
+  and ``PW_ACQUIRED_BEFORE``.)
+
+  As with ``PW_GUARDED_BY``, this is only applicable to locks that are shared
+  fields or global variables.
+
+  Example:
+
+  .. code-block:: cpp
+
+    Mutex m1_;
+    Mutex m2_ PW_ACQUIRED_AFTER(m1_);
+
+.. cpp:function:: PW_EXCLUSIVE_LOCKS_REQUIRED(...)
+.. cpp:function:: PW_SHARED_LOCKS_REQUIRED(...)
+
+  Documents a function that expects a lock to be held prior to entry.
+  The lock is expected to be held both on entry to, and exit from, the
+  function.
+
+  An exclusive lock allows read-write access to the guarded data member(s), and
+  only one thread can acquire a lock exclusively at any one time. A shared lock
+  allows read-only access, and any number of threads can acquire a shared lock
+  concurrently.
+
+  Generally, non-const methods should be annotated with
+  ``PW_EXCLUSIVE_LOCKS_REQUIRED``, while const methods should be annotated with
+  ``PW_SHARED_LOCKS_REQUIRED``.
+
+  Example:
+
+  .. code-block:: cpp
+
+    Mutex mu1, mu2;
+    int a PW_GUARDED_BY(mu1);
+    int b PW_GUARDED_BY(mu2);
+
+    void foo() PW_EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... }
+    void bar() const PW_SHARED_LOCKS_REQUIRED(mu1, mu2) { ... }
+
+.. cpp:function:: PW_LOCKS_EXCLUDED(...)
+
+  Documents the locks acquired in the body of the function. These locks
+  cannot be held when calling this function (as Pigweed's default locks are
+  non-reentrant).
+
+  Example:
+
+  .. code-block:: cpp
+
+    Mutex mu;
+    int a PW_GUARDED_BY(mu);
+
+    void foo() PW_LOCKS_EXCLUDED(mu) {
+      mu.lock();
+      ...
+      mu.unlock();
+    }
+
+.. cpp:function:: PW_LOCK_RETURNED(...)
+
+  Documents a function that returns a lock without acquiring it.  For example,
+  a public getter method that returns a pointer to a private lock should
+  be annotated with ``PW_LOCK_RETURNED``.
+
+  Example:
+
+  .. code-block:: cpp
+
+    class Foo {
+     public:
+      Mutex* mu() PW_LOCK_RETURNED(mu) { return &mu; }
+
+     private:
+      Mutex mu;
+    };
+
+.. cpp:function:: PW_NO_LOCK_SAFETY_ANALYSIS()
+
+   Turns off thread safety checking within the body of a particular function.
+   This annotation is used to mark functions that are known to be correct, but
+   the locking behavior is more complicated than the analyzer can handle.
+
+Annotating Lock Objects
+^^^^^^^^^^^^^^^^^^^^^^^
+In order of lock usage annotation to work, the lock objects themselves need to
+be annotated as well. In case you are providing your own lock or psuedo-lock
+object, you can use the macros in this section to annotate it.
+
+As an example we've annotated a Lock and a RAII ScopedLocker object for you, see
+the macro documentation after for more details:
+
+.. code-block:: cpp
+
+  class PW_LOCKABLE("Lock") Lock {
+   public:
+    void Lock() PW_EXCLUSIVE_LOCK_FUNCTION();
+
+    void ReaderLock() PW_SHARED_LOCK_FUNCTION();
+
+    void Unlock() PW_UNLOCK_FUNCTION();
+
+    void ReaderUnlock() PW_SHARED_TRYLOCK_FUNCTION();
+
+    bool TryLock() PW_EXCLUSIVE_TRYLOCK_FUNCTION(true);
+
+    bool ReaderTryLock() PW_SHARED_TRYLOCK_FUNCTION(true);
+
+    void AssertHeld() PW_ASSERT_EXCLUSIVE_LOCK();
+
+    void AssertReaderHeld() PW_ASSERT_SHARED_LOCK();
+  };
+
+
+  // Tag types for selecting a constructor.
+  struct adopt_lock_t {} inline constexpr adopt_lock = {};
+  struct defer_lock_t {} inline constexpr defer_lock = {};
+  struct shared_lock_t {} inline constexpr shared_lock = {};
+
+  class PW_SCOPED_LOCKABLE ScopedLocker {
+    // Acquire lock, implicitly acquire *this and associate it with lock.
+    ScopedLocker(Lock *lock) PW_EXCLUSIVE_LOCK_FUNCTION(lock)
+        : lock_(lock), locked(true) {
+      lock->Lock();
+    }
+
+    // Assume lock is held, implicitly acquire *this and associate it with lock.
+    ScopedLocker(Lock *lock, adopt_lock_t) PW_EXCLUSIVE_LOCKS_REQUIRED(lock)
+        : lock_(lock), locked(true) {}
+
+    // Acquire lock in shared mode, implicitly acquire *this and associate it
+    // with lock.
+    ScopedLocker(Lock *lock, shared_lock_t) PW_SHARED_LOCK_FUNCTION(lock)
+        : lock_(lock), locked(true) {
+      lock->ReaderLock();
+    }
+
+    // Assume lock is held in shared mode, implicitly acquire *this and associate
+    // it with lock.
+    ScopedLocker(Lock *lock, adopt_lock_t, shared_lock_t)
+        PW_SHARED_LOCKS_REQUIRED(lock) : lock_(lock), locked(true) {}
+
+    // Assume lock is not held, implicitly acquire *this and associate it with
+    // lock.
+    ScopedLocker(Lock *lock, defer_lock_t) PW_LOCKS_EXCLUDED(lock)
+        : lock_(lock), locked(false) {}
+
+    // Release *this and all associated locks, if they are still held.
+    // There is no warning if the scope was already unlocked before.
+    ~ScopedLocker() PW_UNLOCK_FUNCTION() {
+      if (locked)
+        lock_->GenericUnlock();
+    }
+
+    // Acquire all associated locks exclusively.
+    void Lock() PW_EXCLUSIVE_LOCK_FUNCTION() {
+      lock_->Lock();
+      locked = true;
+    }
+
+    // Try to acquire all associated locks exclusively.
+    bool TryLock() PW_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
+      return locked = lock_->TryLock();
+    }
+
+    // Acquire all associated locks in shared mode.
+    void ReaderLock() PW_SHARED_LOCK_FUNCTION() {
+      lock_->ReaderLock();
+      locked = true;
+    }
+
+    // Try to acquire all associated locks in shared mode.
+    bool ReaderTryLock() PW_SHARED_TRYLOCK_FUNCTION(true) {
+      return locked = lock_->ReaderTryLock();
+    }
+
+    // Release all associated locks. Warn on double unlock.
+    void Unlock() PW_UNLOCK_FUNCTION() {
+      lock_->Unlock();
+      locked = false;
+    }
+
+    // Release all associated locks. Warn on double unlock.
+    void ReaderUnlock() PW_UNLOCK_FUNCTION() {
+      lock_->ReaderUnlock();
+      locked = false;
+    }
+
+   private:
+    Lock* lock_;
+    bool locked_;
+  };
+
+.. cpp:function:: PW_LOCKABLE(name)
+
+  Documents if a class/type is a lockable type (such as the ``pw::sync::Mutex``
+  class). The name is used in the warning messages. This can also be useful on
+  classes which have locking like semantics but aren't actually locks.
+
+.. cpp:function:: PW_SCOPED_LOCKABLE()
+
+  Documents if a class does RAII locking. The name is used in the warning
+  messages.
+
+  The constructor should use ``LOCK_FUNCTION()`` to specify the lock that is
+  acquired, and the destructor should use ``UNLOCK_FUNCTION()`` with no
+  arguments; the analysis will assume that the destructor unlocks whatever the
+  constructor locked.
+
+.. cpp:function:: PW_EXCLUSIVE_LOCK_FUNCTION()
+
+  Documents functions that acquire a lock in the body of a function, and do
+  not release it.
+
+.. cpp:function:: PW_SHARED_LOCK_FUNCTION()
+
+   Documents functions that acquire a shared (reader) lock in the body of a
+   function, and do not release it.
+
+.. cpp:function:: PW_UNLOCK_FUNCTION()
+
+   Documents functions that expect a lock to be held on entry to the function,
+   and release it in the body of the function.
+
+.. cpp:function:: PW_EXCLUSIVE_TRYLOCK_FUNCTION(try_success)
+.. cpp:function:: PW_SHARED_TRYLOCK_FUNCTION(try_success)
+
+  Documents functions that try to acquire a lock, and return success or failure
+  (or a non-boolean value that can be interpreted as a boolean).
+  The first argument should be ``true`` for functions that return ``true`` on
+  success, or ``false`` for functions that return `false` on success. The second
+  argument specifies the lock that is locked on success. If unspecified, this
+  lock is assumed to be ``this``.
+
+.. cpp:function:: PW_ASSERT_EXCLUSIVE_LOCK()
+.. cpp:function:: PW_ASSERT_SHARED_LOCK()
+
+   Documents functions that dynamically check to see if a lock is held, and fail
+   if it is not held.
 
 --------------------
 Signaling Primitives