blob: 968c2f764340a5caea3d381f553592993d828c48 [file] [log] [blame]
Philipp Schraderab2f8432023-09-17 18:58:06 -07001#ifndef AOS_IPC_LIB_ROBUST_OWNERSHIP_TRACKER_H_
2#define AOS_IPC_LIB_ROBUST_OWNERSHIP_TRACKER_H_
3
4#include <linux/futex.h>
5
6#include <string>
7
8#include "aos/ipc_lib/aos_sync.h"
9
10namespace aos::ipc_lib {
11
12// Results of atomically loading the ownership state via RobustOwnershipTracker
13// below. This allows the state to be compared and queried later.
14class ThreadOwnerStatusSnapshot {
15 public:
16 ThreadOwnerStatusSnapshot() : futex_(0) {}
17 ThreadOwnerStatusSnapshot(aos_futex futex) : futex_(futex) {}
18 ThreadOwnerStatusSnapshot(const ThreadOwnerStatusSnapshot &) = default;
19 ThreadOwnerStatusSnapshot &operator=(const ThreadOwnerStatusSnapshot &) =
20 default;
21 ThreadOwnerStatusSnapshot(ThreadOwnerStatusSnapshot &&) = default;
22 ThreadOwnerStatusSnapshot &operator=(ThreadOwnerStatusSnapshot &&) = default;
23
24 // Returns if the owner died as noticed by the robust futex using Acquire
25 // memory ordering.
26 bool OwnerIsDead() const { return (futex_ & FUTEX_OWNER_DIED) != 0; }
27
28 // Returns true if no one has claimed ownership.
29 bool IsUnclaimed() const { return futex_ == 0; }
30
31 // Returns true if either ownership hasn't been acquired or the owner died.
32 bool IsUnclaimedOrOwnerIsDead() const {
33 return IsUnclaimed() || OwnerIsDead();
34 }
35
36 // Returns the thread ID (a.k.a. "tid") of the owning thread. Use this when
37 // trying to access the /proc entry that corresponds to the owning thread for
38 // example. Do not use the futex value directly.
39 pid_t tid() const { return futex_ & FUTEX_TID_MASK; }
40
41 bool operator==(const ThreadOwnerStatusSnapshot &other) const {
42 return other.futex_ == futex_;
43 }
44
45 private:
46 aos_futex futex_;
47};
48
49// This object reliably tracks a thread owning a resource. A single thread may
50// possess multiple resources like senders and receivers. Each resource can have
51// its own instance of this class. These instances are responsible for
52// monitoring the thread that owns them. Each resource can use its instance of
53// this class to reliably check whether the owning thread is no longer alive.
54//
55// All methods other than Load* must be accessed under a mutex.
56class RobustOwnershipTracker {
57 public:
58 // Loads all the contents of the ownership tracker with Acquire memory
59 // ordering.
60 ThreadOwnerStatusSnapshot LoadAcquire() const {
61 return ThreadOwnerStatusSnapshot(
62 __atomic_load_n(&(mutex_.futex), __ATOMIC_ACQUIRE));
63 }
64
65 // Loads all the contents of the ownership tracker with Relaxed memory order.
66 ThreadOwnerStatusSnapshot LoadRelaxed() const {
67 return ThreadOwnerStatusSnapshot(
68 __atomic_load_n(&(mutex_.futex), __ATOMIC_RELAXED));
69 }
70
71 // Clears all ownership state.
72 //
73 // This should only really be called if you are 100% certain that the owner is
74 // dead. Use `LoadAquire().OwnerIsDead()` to determine this.
75 void ForceClear() {
76 // Must be opposite order of Acquire.
77 // We only deal with the futex here because we don't want to change anything
78 // about the linked list. We just want to release ownership here. We still
79 // want the kernel to know about this element via the linked list the next
80 // time someone takes ownership.
81 __atomic_store_n(&(mutex_.futex), 0, __ATOMIC_RELEASE);
82 }
83
84 // Returns true if this thread holds ownership.
85 bool IsHeldBySelf() { return death_notification_is_held(&mutex_); }
86
87 // Acquires ownership. Other threads will know that this thread holds the
88 // ownership or be notified if this thread dies.
89 void Acquire() { death_notification_init(&mutex_); }
90
91 // Releases ownership.
92 //
93 // This should only be called from the owning thread.
94 void Release() {
95 // Must be opposite order of Acquire.
96 death_notification_release(&mutex_);
97 }
98
99 // Marks the owner as dead if the specified tid is the current owner. In other
100 // words, after this call, a call to `LoadAcquire().OwnerIsDead()` may start
101 // returning true.
102 //
103 // The motivation here is for use in testing. DO NOT USE in production code.
104 // The logic here is only good enough for testing.
105 bool PretendThatOwnerIsDeadForTesting(pid_t tid);
106
107 // Returns a string representing this object.
108 std::string DebugString() const;
109
110 private:
111 // Robust futex to track ownership the normal way. The futex is inside the
112 // mutex here. We use the wrapper mutex because the death_notification_*
113 // functions operate on that instead of the futex directly.
114 //
115 // We use a futex here because:
116 // - futexes are fast.
117 // - The kernel can atomically clean up a dead thread and mark the futex
118 // appropriately.
119 // - Owners can clean up after dead threads.
120 aos_mutex mutex_;
121};
122
123} // namespace aos::ipc_lib
124
125#endif // AOS_IPC_LIB_ROBUST_OWNERSHIP_TRACKER_H_