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