Use dedicated functions for using futexes to wait for a task to die

This avoids hard-to-debug failure modes where processes hang in places
they should never block in the new queue code.

Change-Id: I1f1749a608b4f81990685ff718908f8b68a2262a
diff --git a/aos/ipc_lib/aos_sync.cc b/aos/ipc_lib/aos_sync.cc
index efb1fb1..d5c3d4e 100644
--- a/aos/ipc_lib/aos_sync.cc
+++ b/aos/ipc_lib/aos_sync.cc
@@ -851,6 +851,44 @@
   return (value & FUTEX_TID_MASK) == tid;
 }
 
+void death_notification_init(aos_mutex *m) {
+  const uint32_t tid = get_tid();
+  if (kPrintOperations) {
+    printf("%" PRId32 ": %p death_notification start\n", tid, m);
+  }
+  my_robust_list::Adder adder(m);
+  {
+    RunObservers run_observers(m, true);
+    CHECK(compare_and_swap(&m->futex, 0, tid));
+  }
+  adder.Add();
+}
+
+void death_notification_release(aos_mutex *m) {
+  RunObservers run_observers(m, true);
+
+#ifndef NDEBUG
+  // Verify it's "locked", like it should be.
+  {
+    const uint32_t tid = get_tid();
+    if (kPrintOperations) {
+      printf("%" PRId32 ": %p death_notification release\n", tid, m);
+    }
+    const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_SEQ_CST);
+    assert((value & ~FUTEX_WAITERS) == tid);
+  }
+#endif
+
+  my_robust_list::Remover remover(m);
+  ANNOTATE_HAPPENS_BEFORE(m);
+  const int ret = sys_futex_unlock_pi(&m->futex);
+  if (ret != 0) {
+    my_robust_list::robust_head.pending_next = 0;
+    errno = -ret;
+    PLOG(FATAL)  << "FUTEX_UNLOCK_PI(" << &m->futex << ") failed";
+  }
+}
+
 int condition_wait(aos_condition *c, aos_mutex *m, struct timespec *end_time) {
   RunObservers run_observers(c, false);
   const uint32_t tid = get_tid();