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();
diff --git a/aos/ipc_lib/aos_sync.h b/aos/ipc_lib/aos_sync.h
index 2824516..8290faa 100644
--- a/aos/ipc_lib/aos_sync.h
+++ b/aos/ipc_lib/aos_sync.h
@@ -26,7 +26,7 @@
// No initialization is necessary.
typedef aos_futex aos_condition;
-// For use with the mutex_ functions.
+// For use with the mutex_ or death_notification_ functions.
// futex must be initialized to 0.
// No initialization is necessary for next and previous.
// Under ThreadSanitizer, pthread_mutex_init must be initialized to false.
@@ -88,6 +88,25 @@
// checking mutexes as they are destroyed to catch problems with that early and
// stack-based recursive mutex locking.
bool mutex_islocked(const aos_mutex *m);
+
+// The death_notification_ functions are designed for one thread to wait for
+// another thread (possibly in a different process) to end. This can mean
+// exiting gracefully or dying at any point. They use a standard aos_mutex, but
+// this mutex may not be used with any of the mutex_ functions.
+
+// Initializes a variable which can be used to wait for this thread to die.
+// This can only be called once after initializing *m.
+void death_notification_init(aos_mutex *m);
+
+// Manually triggers a death notification for this thread.
+// This thread must have previously called death_notification_init(m).
+void death_notification_release(aos_mutex *m);
+
+// Returns whether or not m is held by the current thread.
+// This is mainly useful as a debug assertion.
+inline bool death_notification_is_held(aos_mutex *m) {
+ return mutex_islocked(m);
+}
#endif
// The futex_ functions are similar to the mutex_ ones but different.