create an stl-compatible wrapper around aos_sync mutexes and use it

Change-Id: I1f7ec0c5366b0ef985563225365db18dec8b70bd
diff --git a/aos/build/aos_all.gyp b/aos/build/aos_all.gyp
index d67a11d..638185d 100644
--- a/aos/build/aos_all.gyp
+++ b/aos/build/aos_all.gyp
@@ -18,6 +18,7 @@
         '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:ipc_stress_test',
         '<(AOS)/linux_code/starter/starter.gyp:starter_exe',
         '<(AOS)/linux_code/linux_code.gyp:complex_thread_local_test',
+        '<(AOS)/common/common.gyp:stl_mutex_test',
         '<(AOS)/linux_code/linux_code.gyp:dump_rtprio',
       ],
     },
diff --git a/aos/common/common.gyp b/aos/common/common.gyp
index 5a16c08..5264dab 100644
--- a/aos/common/common.gyp
+++ b/aos/common/common.gyp
@@ -300,5 +300,34 @@
         'die',
       ],
     },
+    {
+      'target_name': 'stl_mutex',
+      'type': 'static_library',
+      'sources': [
+        #'stl_mutex.h'
+      ],
+      'dependencies': [
+        '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
+        '<(AOS)/build/aos.gyp:logging',
+      ],
+      'export_dependent_settings': [
+        '<(AOS)/linux_code/ipc_lib/ipc_lib.gyp:aos_sync',
+        '<(AOS)/build/aos.gyp:logging',
+      ],
+    },
+    {
+      'target_name': 'stl_mutex_test',
+      'type': 'executable',
+      'sources': [
+        'stl_mutex_test.cc',
+      ],
+      'dependencies': [
+        'stl_mutex',
+        '<(EXTERNALS):gtest',
+        'queue_testutils',
+        '<(AOS)/common/util/util.gyp:thread',
+        'die',
+      ],
+    },
   ],
 }
diff --git a/aos/common/stl_mutex.h b/aos/common/stl_mutex.h
new file mode 100644
index 0000000..9242312
--- /dev/null
+++ b/aos/common/stl_mutex.h
@@ -0,0 +1,144 @@
+#ifndef AOS_COMMON_STL_MUTEX_H_
+#define AOS_COMMON_STL_MUTEX_H_
+
+#include <mutex>
+
+#include "aos/linux_code/ipc_lib/aos_sync.h"
+#include "aos/common/logging/logging.h"
+#include "aos/common/type_traits.h"
+#include "aos/common/macros.h"
+
+namespace aos {
+
+// A mutex with the same API and semantics as ::std::mutex, with the addition of
+// methods for checking if the previous owner died and a constexpr default
+// constructor.
+// Definitely safe to put in SHM.
+// This uses the pthread_mutex semantics for owner-died: once somebody dies with
+// the lock held, anybody else who takes it will see true for owner_died() until
+// one of them calls consistent(). It is an error to call unlock() when
+// owner_died() returns true.
+class stl_mutex {
+ public:
+  constexpr stl_mutex() : native_handle_() {}
+
+  void lock() {
+    const int ret = mutex_grab(&native_handle_);
+    switch (ret) {
+      case 0:
+        break;
+      case 1:
+        owner_died_ = true;
+        break;
+      default:
+        LOG(FATAL, "mutex_grab(%p) failed with %d\n", &native_handle_, ret);
+    }
+  }
+
+  bool try_lock() {
+    const int ret = mutex_trylock(&native_handle_);
+    switch (ret) {
+      case 0:
+        return true;
+      case 1:
+        owner_died_ = true;
+        return true;
+      case 4:
+        return false;
+      default:
+        LOG(FATAL, "mutex_trylock(%p) failed with %d\n", &native_handle_, ret);
+    }
+  }
+
+  void unlock() {
+    CHECK(!owner_died_);
+    mutex_unlock(&native_handle_);
+  }
+
+  typedef aos_mutex *native_handle_type;
+  native_handle_type native_handle() { return &native_handle_; }
+
+  bool owner_died() const { return owner_died_; }
+  void consistent() { owner_died_ = false; }
+
+ private:
+  aos_mutex native_handle_;
+
+  bool owner_died_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(stl_mutex);
+};
+
+// A mutex with the same API and semantics as ::std::recursive_mutex, with the
+// addition of methods for checking if the previous owner died and a constexpr
+// default constructor.
+// Definitely safe to put in SHM.
+// This uses the pthread_mutex semantics for owner-died: once somebody dies with
+// the lock held, anybody else who takes it will see true for owner_died() until
+// one of them calls consistent(). It is an error to call unlock() or lock()
+// again when owner_died() returns true.
+class stl_recursive_mutex {
+ public:
+  constexpr stl_recursive_mutex() {}
+
+  void lock() {
+    if (mutex_islocked(mutex_.native_handle())) {
+      CHECK(!owner_died());
+      ++recursive_locks_;
+    } else {
+      mutex_.lock();
+      if (mutex_.owner_died()) {
+        recursive_locks_ = 0;
+      } else {
+        CHECK_EQ(0, recursive_locks_);
+      }
+    }
+  }
+  bool try_lock() {
+    if (mutex_islocked(mutex_.native_handle())) {
+      CHECK(!owner_died());
+      ++recursive_locks_;
+      return true;
+    } else {
+      if (mutex_.try_lock()) {
+        if (mutex_.owner_died()) {
+          recursive_locks_ = 0;
+        } else {
+          CHECK_EQ(0, recursive_locks_);
+        }
+        return true;
+      } else {
+        return false;
+      }
+    }
+  }
+  void unlock() {
+    if (recursive_locks_ == 0) {
+      mutex_.unlock();
+    } else {
+      --recursive_locks_;
+    }
+  }
+
+  typedef stl_mutex::native_handle_type native_handle_type;
+  native_handle_type native_handle() { return mutex_.native_handle(); }
+
+  bool owner_died() const { return mutex_.owner_died(); }
+  void consistent() { mutex_.consistent(); }
+
+ private:
+  stl_mutex mutex_;
+  int recursive_locks_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(stl_recursive_mutex);
+};
+
+// Convenient typedefs for various types of locking objects.
+typedef ::std::lock_guard<stl_mutex> mutex_lock_guard;
+typedef ::std::lock_guard<stl_recursive_mutex> recursive_lock_guard;
+typedef ::std::unique_lock<stl_mutex> mutex_unique_lock;
+typedef ::std::unique_lock<stl_recursive_mutex> recursive_unique_lock;
+
+}  // namespace aos
+
+#endif  // AOS_COMMON_STL_MUTEX_H_
diff --git a/aos/common/stl_mutex_test.cc b/aos/common/stl_mutex_test.cc
new file mode 100644
index 0000000..7d84416
--- /dev/null
+++ b/aos/common/stl_mutex_test.cc
@@ -0,0 +1,74 @@
+#include "aos/common/stl_mutex.h"
+
+#include "gtest/gtest.h"
+
+#include "aos/common/queue_testutils.h"
+#include "aos/common/util/thread.h"
+#include "aos/common/die.h"
+
+namespace aos {
+namespace testing {
+
+class StlMutexDeathTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ::aos::common::testing::EnableTestLogging();
+    SetDieTestMode(true);
+  }
+};
+
+typedef StlMutexDeathTest StlRecursiveMutexDeathTest;
+
+// Tests that locking/unlocking without any blocking works.
+TEST(StlMutexTest, Basic) {
+  stl_mutex mutex;
+
+  mutex.lock();
+  mutex.unlock();
+
+  ASSERT_TRUE(mutex.try_lock());
+  ASSERT_FALSE(mutex.try_lock());
+  mutex.unlock();
+
+  mutex.lock();
+  ASSERT_FALSE(mutex.try_lock());
+  mutex.unlock();
+}
+
+// Tests that unlocking an unlocked mutex fails.
+TEST_F(StlMutexDeathTest, MultipleUnlock) {
+  stl_mutex mutex;
+  mutex.lock();
+  mutex.unlock();
+  EXPECT_DEATH(mutex.unlock(), ".*multiple unlock.*");
+}
+
+// Tests that locking/unlocking (including recursively) without any blocking
+// works.
+TEST(StlRecursiveMutexTest, Basic) {
+  stl_recursive_mutex mutex;
+
+  mutex.lock();
+  mutex.unlock();
+
+  ASSERT_TRUE(mutex.try_lock());
+  ASSERT_TRUE(mutex.try_lock());
+  mutex.unlock();
+  mutex.unlock();
+
+  mutex.lock();
+  ASSERT_TRUE(mutex.try_lock());
+  mutex.unlock();
+	mutex.unlock();
+}
+
+// Tests that unlocking an unlocked recursive mutex fails.
+TEST_F(StlRecursiveMutexDeathTest, MultipleUnlock) {
+  stl_recursive_mutex mutex;
+  mutex.lock();
+  mutex.unlock();
+  EXPECT_DEATH(mutex.unlock(), ".*multiple unlock.*");
+}
+
+}  // namespace testing
+}  // namespace aos
diff --git a/frc971/wpilib/wpilib.gyp b/frc971/wpilib/wpilib.gyp
index 170a4c5..39a5036 100644
--- a/frc971/wpilib/wpilib.gyp
+++ b/frc971/wpilib/wpilib.gyp
@@ -8,6 +8,7 @@
       ],
       'dependencies': [
         '<(AOS)/linux_code/linux_code.gyp:init',
+        '<(AOS)/common/common.gyp:stl_mutex',
         '<(AOS)/build/aos.gyp:logging',
         '<(EXTERNALS):WPILib',
         '<(DEPTH)/frc971/control_loops/drivetrain/drivetrain.gyp:drivetrain_loop',
diff --git a/frc971/wpilib/wpilib_interface.cc b/frc971/wpilib/wpilib_interface.cc
index 86a456b..b7c49c8 100644
--- a/frc971/wpilib/wpilib_interface.cc
+++ b/frc971/wpilib/wpilib_interface.cc
@@ -16,6 +16,7 @@
 #include "aos/common/util/log_interval.h"
 #include "aos/common/util/phased_loop.h"
 #include "aos/common/util/wrapping_counter.h"
+#include "aos/common/stl_mutex.h"
 #include "aos/linux_code/init.h"
 
 #include "frc971/control_loops/drivetrain/drivetrain.q.h"
@@ -47,54 +48,11 @@
 namespace frc971 {
 namespace wpilib {
 
-class priority_mutex {
- public:
-  typedef pthread_mutex_t *native_handle_type;
-
-  // TODO(austin): Write a test case for the mutex, and make the constructor
-  // constexpr.
-  priority_mutex() {
-    pthread_mutexattr_t attr;
-#ifdef NDEBUG
-#error "Won't let assert_perror be no-op ed"
-#endif
-    // Turn on priority inheritance.
-    assert_perror(pthread_mutexattr_init(&attr));
-    assert_perror(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL));
-    assert_perror(pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT));
-
-    assert_perror(pthread_mutex_init(native_handle(), &attr));
-
-    assert_perror(pthread_mutexattr_destroy(&attr));
-  }
-
-  ~priority_mutex() { pthread_mutex_destroy(&handle_); }
-
-  void lock() { assert_perror(pthread_mutex_lock(&handle_)); }
-  bool try_lock() {
-    int ret = pthread_mutex_trylock(&handle_);
-    if (ret == 0) {
-      return true;
-    } else if (ret == EBUSY) {
-      return false;
-    } else {
-      assert_perror(ret);
-    }
-  }
-  void unlock() { assert_perror(pthread_mutex_unlock(&handle_)); }
-
-  native_handle_type native_handle() { return &handle_; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(priority_mutex);
-  pthread_mutex_t handle_;
-};
-
 // TODO(brian): Split this out into a separate file once DMA is in.
 class EdgeCounter {
  public:
   EdgeCounter(int priority, Encoder *encoder, HallEffect *input,
-              priority_mutex *mutex)
+              ::aos::stl_mutex *mutex)
       : priority_(priority),
         encoder_(encoder),
         input_(input),
@@ -115,7 +73,7 @@
     input_->SetUpSourceEdge(true, true);
 
     {
-      ::std::unique_lock<priority_mutex> mutex_guard(*mutex_);
+      ::std::unique_lock<::aos::stl_mutex> mutex_guard(*mutex_);
       current_value_ = input_->GetHall();
     }
 
@@ -129,7 +87,7 @@
       }
       ++any_interrupt_count_;
 
-      ::std::unique_lock<priority_mutex> mutex_guard(*mutex_);
+      ::std::unique_lock<::aos::stl_mutex> mutex_guard(*mutex_);
       int32_t encoder_value = encoder_->GetRaw();
       bool hall_value = input_->GetHall();
       if (current_value_ != hall_value) {
@@ -188,7 +146,7 @@
   int priority_;
   Encoder *encoder_;
   HallEffect *input_;
-  priority_mutex *mutex_;
+  ::aos::stl_mutex *mutex_;
   ::std::atomic<bool> run_;
 
   ::std::atomic<int> any_interrupt_count_;
@@ -247,7 +205,7 @@
 
     {
       // Now, update the encoder and sensor values.
-      ::std::unique_lock<priority_mutex> mutex_guard(mutex_);
+      ::std::unique_lock<::aos::stl_mutex> mutex_guard(mutex_);
       encoder_value_ = encoder_->GetRaw();
       for (int i = 0; i < num_sensors; ++i) {
         edge_counters_[i]->set_polled_value(sensors_[i]->GetHall());
@@ -260,7 +218,7 @@
   bool TryFinishingIteration() {
     // Make sure no interrupts have occurred while we were waiting.  If they
     // have, we are in an inconsistent state and need to try again.
-    ::std::unique_lock<priority_mutex> mutex_guard(mutex_);
+    ::std::unique_lock<::aos::stl_mutex> mutex_guard(mutex_);
     bool retry = false;
     for (int i = 0; i < num_sensors; ++i) {
       retry = retry || (interrupt_counts_[i] !=
@@ -325,7 +283,7 @@
   // A list of all the digital inputs.
   ::std::array<::std::unique_ptr<HallEffect>, num_sensors> sensors_;
   // The mutex used to synchronize all the state.
-  priority_mutex mutex_;
+  ::aos::stl_mutex mutex_;
   ::std::atomic<bool> run_;
 
   // The state.