1 // Copyright (c) 2008-2014 Travis Geiselbrecht
2 // Copyright (c) 2012 Shantanu Gupta
3 //
4 // Use of this source code is governed by a MIT-style
5 // license that can be found in the LICENSE file or at
6 // https://opensource.org/licenses/MIT
7 #pragma once
8
9 #include <kernel/thread.h>
10 #include <lk/compiler.h>
11 #include <lk/debug.h>
12 #include <stdint.h>
13
14 __BEGIN_CDECLS
15
16 // Rules for Mutexes:
17 // - Mutexes are only safe to use from thread context.
18 // - Mutexes are non-recursive.
19 // - Mutexes must be released in the same thread that acquired them.
20
21 #define MUTEX_MAGIC (0x6D757478) // 'mutx'
22
23 typedef struct mutex {
24 uint32_t magic;
25 int count;
26 thread_t *holder;
27 wait_queue_t wait;
28 } mutex_t;
29
30 // Initializer for mutexes. May be statically initialized with the following
31 // value, or dynamically initialized with mutex_init().
32 #define MUTEX_INITIAL_VALUE(m) \
33 { \
34 .magic = MUTEX_MAGIC, \
35 .count = 0, \
36 .holder = NULL, \
37 .wait = WAIT_QUEUE_INITIAL_VALUE((m).wait), \
38 }
39
40 void mutex_init(mutex_t *);
41
42 // Destroy a mutex. This will release all threads waiting on the mutex
43 // and set the mutex to an invalid state. The caller must ensure that no
44 // other threads are using the mutex after this call.
45 void mutex_destroy(mutex_t *);
46
47 // Acquire the mutex, blocking until it is available.
48 // If passed a timeout value of INFINITE_TIME, it will block indefinitely.
49 // If passed a timeout value of 0, it will return immediately with ERR_TIMED_OUT.
50 status_t mutex_acquire_timeout(mutex_t *, lk_time_t);
mutex_acquire(mutex_t * m)51 static inline status_t mutex_acquire(mutex_t *m) {
52 return mutex_acquire_timeout(m, INFINITE_TIME);
53 }
54
55 // Release the mutex. This will wake up one thread waiting on the mutex, if any.
56 status_t mutex_release(mutex_t *);
57
58 // Is the mutex currently held by the current thread?
is_mutex_held(const mutex_t * m)59 static bool is_mutex_held(const mutex_t *m) {
60 return m->holder == get_current_thread();
61 }
62
63 __END_CDECLS
64
65 #ifdef __cplusplus
66
67 #include <lk/cpp.h>
68
69 // C++ wrapper object
70 class Mutex {
71 public:
72 constexpr Mutex() = default;
~Mutex()73 ~Mutex() { mutex_destroy(&lock_); }
74
75 status_t acquire(lk_time_t timeout = INFINITE_TIME) { return mutex_acquire_timeout(&lock_, timeout); }
release()76 status_t release() { return mutex_release(&lock_); }
is_held()77 bool is_held() { return is_mutex_held(&lock_); }
78
79 // suppress default constructors
80 DISALLOW_COPY_ASSIGN_AND_MOVE(Mutex);
81
82 private:
83 mutex_t lock_ = MUTEX_INITIAL_VALUE(lock_);
84
85 // AutoLock has access to the inner lock
86 friend class AutoLock;
87 };
88
89 // RAII wrapper around the mutex
90 class AutoLock {
91 public:
AutoLock(mutex_t * mutex)92 explicit AutoLock(mutex_t *mutex) : mutex_(mutex) { mutex_acquire(mutex_); }
AutoLock(mutex_t & mutex)93 AutoLock(mutex_t &mutex) : AutoLock(&mutex) {}
94
AutoLock(Mutex * mutex)95 explicit AutoLock(Mutex *mutex) : AutoLock(&mutex->lock_) {}
AutoLock(Mutex & mutex)96 AutoLock(Mutex &mutex) : AutoLock(&mutex) {}
97
~AutoLock()98 ~AutoLock() { release(); }
99
100 // early release the mutex before the object goes out of scope
release()101 void release() {
102 if (likely(mutex_)) {
103 mutex_release(mutex_);
104 mutex_ = nullptr;
105 }
106 }
107
108 // suppress default constructors
109 DISALLOW_COPY_ASSIGN_AND_MOVE(AutoLock);
110
111 private:
112 mutex_t *mutex_ = nullptr;
113 };
114
115 #endif // __cplusplus
116
117