1 // Copyright 2016 The Fuchsia Authors
2 // Copyright (c) 2008-2014 Travis Geiselbrecht
3 // Copyright (c) 2012 Shantanu Gupta
4 //
5 // Use of this source code is governed by a MIT-style
6 // license that can be found in the LICENSE file or at
7 // https://opensource.org/licenses/MIT
8
9 #pragma once
10
11 #include <assert.h>
12 #include <debug.h>
13 #include <kernel/atomic.h>
14 #include <kernel/thread.h>
15 #include <stdint.h>
16 #include <zircon/compiler.h>
17 #include <zircon/thread_annotations.h>
18
19 __BEGIN_CDECLS
20
21 #define MUTEX_MAGIC (0x6D757478) // 'mutx'
22
23 // Body of the mutex.
24 // The val field holds either 0 or a pointer to the thread_t holding the mutex.
25 // If one or more threads are blocking and queued up, MUTEX_FLAG_QUEUED is ORed in as well.
26 // NOTE: MUTEX_FLAG_QUEUED is only manipulated under the THREAD_LOCK.
27 typedef struct TA_CAP("mutex") mutex {
28 uint32_t magic;
29 uintptr_t val;
30 wait_queue_t wait;
31 } mutex_t;
32
33 #define MUTEX_FLAG_QUEUED ((uintptr_t)1)
34
35 // accessors to extract the holder pointer from the val member
mutex_val(const mutex_t * m)36 static inline uintptr_t mutex_val(const mutex_t* m) {
37 static_assert(sizeof(uintptr_t) == sizeof(uint64_t), "");
38 return atomic_load_u64_relaxed((uint64_t*)&m->val);
39 }
40
mutex_holder(const mutex_t * m)41 static inline thread_t* mutex_holder(const mutex_t* m) {
42 static_assert(sizeof(uintptr_t) == sizeof(uint64_t), "");
43 return (thread_t*)(mutex_val(m) & ~MUTEX_FLAG_QUEUED);
44 }
45
46 #define MUTEX_INITIAL_VALUE(m) \
47 { \
48 .magic = MUTEX_MAGIC, \
49 .val = 0, \
50 .wait = WAIT_QUEUE_INITIAL_VALUE((m).wait), \
51 }
52
53 // Rules for Mutexes:
54 // - Mutexes are only safe to use from thread context.
55 // - Mutexes are non-recursive.
56 void mutex_init(mutex_t* m);
57 void mutex_destroy(mutex_t* m);
58 void mutex_acquire(mutex_t* m) TA_ACQ(m);
59 void mutex_release(mutex_t* m) TA_REL(m);
60
61 // special version of the above with the thread lock held
62 void mutex_release_thread_locked(mutex_t* m, bool reschedule) TA_REL(m);
63
64 // does the current thread hold the mutex?
is_mutex_held(const mutex_t * m)65 static inline bool is_mutex_held(const mutex_t* m) {
66 return (mutex_holder(m) == get_current_thread());
67 }
68
69 __END_CDECLS
70
71 #ifdef __cplusplus
72
73 #include <lockdep/lock_policy.h>
74 #include <lockdep/lock_traits.h>
75
76 // Declares a fbl::Mutex member of the struct or class |containing_type|.
77 //
78 // Example usage:
79 //
80 // struct MyType {
81 // DECLARE_MUTEX(MyType) lock;
82 // };
83 //
84 #define DECLARE_MUTEX(containing_type) \
85 LOCK_DEP_INSTRUMENT(containing_type, fbl::Mutex)
86
87 // Declares a |lock_type| member of the struct or class |containing_type|.
88 //
89 // Example usage:
90 //
91 // struct MyType {
92 // DECLARE_LOCK(MyType, LockType) lock;
93 // };
94 //
95 #define DECLARE_LOCK(containing_type, lock_type) \
96 LOCK_DEP_INSTRUMENT(containing_type, lock_type)
97
98 // Declares a singleton fbl::Mutex with the name |name|.
99 //
100 // Example usage:
101 //
102 // DECLARE_SINGLETON_MUTEX(MyGlobalLock [, LockFlags]);
103 //
104 #define DECLARE_SINGLETON_MUTEX(name, ...) \
105 LOCK_DEP_SINGLETON_LOCK(name, fbl::Mutex, ##__VA_ARGS__)
106
107 // Declares a singleton |lock_type| with the name |name|.
108 //
109 // Example usage:
110 //
111 // DECLARE_SINGLETON_LOCK(MyGlobalLock, LockType, [, LockFlags]);
112 //
113 #define DECLARE_SINGLETON_LOCK(name, lock_type, ...) \
114 LOCK_DEP_SINGLETON_LOCK(name, lock_type, ##__VA_ARGS__)
115
116 // Forward declaration.
117 struct MutexPolicy;
118
119 namespace fbl {
120
121 // Forward declaration. In the kernel this header is included by fbl/mutext.h.
122 class Mutex;
123
124 // Configure Guard<fbl::Mutex> to use the following policy. This must be done
125 // in the same namespace as the mutex type.
126 LOCK_DEP_POLICY(Mutex, MutexPolicy);
127
128 } // namespace fbl
129
130 // Lock policy for acquiring an fbl::Mutex.
131 struct MutexPolicy {
132 // No extra state required for mutexes.
133 struct State {};
134
135 // Basic acquire and release operations.
136 template <typename LockType>
AcquireMutexPolicy137 static bool Acquire(LockType* lock, State*) TA_ACQ(lock) {
138 lock->Acquire();
139 return true;
140 }
141 template <typename LockType>
ReleaseMutexPolicy142 static void Release(LockType* lock, State*) TA_REL(lock) {
143 lock->Release();
144 }
145
146 // A enum tag that can be passed to Guard<fbl::Mutex>::Release(...) to
147 // select the special-case release method below.
148 enum SelectThreadLockHeld { ThreadLockHeld };
149
150 // Specifies whether the special-case release method below should
151 // reschedule.
152 enum RescheduleOption : bool {
153 NoReschedule = false,
154 Reschedule = true,
155 };
156
157 // Releases the lock using the special mutex release operation. This
158 // is selected by calling:
159 //
160 // Guard<fbl::Mutex>::Release(ThreadLockHeld [, Reschedule | NoReschedule])
161 //
162 template <typename LockType>
163 static void Release(LockType* lock, State*, SelectThreadLockHeld,
164 RescheduleOption reschedule = Reschedule)
165 TA_NO_THREAD_SAFETY_ANALYSIS {
166 mutex_release_thread_locked(lock->GetInternal(), reschedule);
167 }
168 };
169
170 #endif // __cplusplus
171