Generalize futex observers for the compare_exchanges in lockless_queue

This is necessary to get lockless_queue_death_test running on aarch64.

Change-Id: I621fe55fac4d43a8e40d053d4f069d618e738f7d
Signed-off-by: Austin Schuh <austin.schuh@bluerivertech.com>
diff --git a/aos/ipc_lib/shm_observers.h b/aos/ipc_lib/shm_observers.h
new file mode 100644
index 0000000..5780699
--- /dev/null
+++ b/aos/ipc_lib/shm_observers.h
@@ -0,0 +1,52 @@
+#ifndef AOS_IPC_LIB_SHM_OBSERVERS_H_
+#define AOS_IPC_LIB_SHM_OBSERVERS_H_
+
+#include <type_traits>
+
+namespace aos {
+namespace linux_code {
+namespace ipc_lib {
+
+typedef void (*ShmAccessorObserver)(void *address, bool write);
+
+extern ShmAccessorObserver before_observer, after_observer;
+
+// Sets functions to run before and after SHM write operations which may
+// involved multiple instructions. This is important when doing robustness
+// testing because the memory has to be made writable for the whole operation,
+// otherwise it never succeeds.
+void SetShmAccessorObservers(ShmAccessorObserver before,
+                             ShmAccessorObserver after);
+
+// RAII class which runs before_observer during construction and after_observer
+// during destruction.
+class RunShmObservers {
+ public:
+  template <class T>
+  RunShmObservers(T *address, bool write)
+      : address_(static_cast<void *>(
+            const_cast<typename ::std::remove_cv<T>::type *>(address))),
+        write_(write) {
+    if (__builtin_expect(before_observer != nullptr, false)) {
+      before_observer(address_, write_);
+    }
+  }
+  ~RunShmObservers() {
+    if (__builtin_expect(after_observer != nullptr, false)) {
+      after_observer(address_, write_);
+    }
+  }
+
+  RunShmObservers(const RunShmObservers &) = delete;
+  RunShmObservers &operator=(const RunShmObservers &) = delete;
+
+ private:
+  void *const address_;
+  const bool write_;
+};
+
+}  // namespace ipc_lib
+}  // namespace linux_code
+}  // namespace aos
+
+#endif  // AOS_IPC_LIB_SHM_OBSERVERS_H_