1 /*
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 <kernel/thread.h>
12 #include <lk/compiler.h>
13 #include <lk/debug.h>
14 #include <stdint.h>
15 
16 __BEGIN_CDECLS
17 
18 #define MUTEX_MAGIC (0x6D757478)  // 'mutx'
19 
20 typedef struct mutex {
21     uint32_t magic;
22     thread_t *holder;
23     int count;
24     wait_queue_t wait;
25 } mutex_t;
26 
27 #define MUTEX_INITIAL_VALUE(m) \
28 { \
29     .magic = MUTEX_MAGIC, \
30     .holder = NULL, \
31     .count = 0, \
32     .wait = WAIT_QUEUE_INITIAL_VALUE((m).wait), \
33 }
34 
35 /* Rules for Mutexes:
36  * - Mutexes are only safe to use from thread context.
37  * - Mutexes are non-recursive.
38 */
39 
40 void mutex_init(mutex_t *);
41 void mutex_destroy(mutex_t *);
42 status_t mutex_acquire_timeout(mutex_t *, lk_time_t); /* try to acquire the mutex with a timeout value */
43 status_t mutex_release(mutex_t *);
44 
mutex_acquire(mutex_t * m)45 static inline status_t mutex_acquire(mutex_t *m) {
46     return mutex_acquire_timeout(m, INFINITE_TIME);
47 }
48 
49 /* does the current thread hold the mutex? */
is_mutex_held(const mutex_t * m)50 static bool is_mutex_held(const mutex_t *m) {
51     return m->holder == get_current_thread();
52 }
53 
54 __END_CDECLS
55 
56 #ifdef __cplusplus
57 
58 #include <lk/cpp.h>
59 
60 // C++ wrapper object
61 class Mutex {
62 public:
63     constexpr Mutex() = default;
~Mutex()64     ~Mutex() { mutex_destroy(&lock_); }
65 
acquire(lk_time_t timeout)66     status_t acquire(lk_time_t timeout) { return mutex_acquire_timeout(&lock_, timeout); }
release()67     status_t release() { return mutex_release(&lock_); }
is_held()68     bool is_held() { return is_mutex_held(&lock_); }
69 
70     // suppress default constructors
71     DISALLOW_COPY_ASSIGN_AND_MOVE(Mutex);
72 
73 private:
74     mutex_t lock_ = MUTEX_INITIAL_VALUE(lock_);
75 
76     // AutoLock has access to the inner lock
77     friend class AutoLock;
78 };
79 
80 // RAII wrapper around the mutex
81 class AutoLock {
82 public:
AutoLock(mutex_t * mutex)83     explicit AutoLock(mutex_t *mutex) : mutex_(mutex) { mutex_acquire(mutex_); }
AutoLock(mutex_t & mutex)84     AutoLock(mutex_t &mutex) : AutoLock(&mutex) {}
85 
AutoLock(Mutex * mutex)86     explicit AutoLock(Mutex *mutex) : AutoLock(&mutex->lock_) {}
AutoLock(Mutex & mutex)87     AutoLock(Mutex &mutex) : AutoLock(&mutex) {}
88 
~AutoLock()89     ~AutoLock() { release(); }
90 
91     // early release the mutex before the object goes out of scope
release()92     void release() {
93         if (likely(mutex_)) {
94             mutex_release(mutex_);
95             mutex_ = nullptr;
96         }
97     }
98 
99     // suppress default constructors
100     DISALLOW_COPY_ASSIGN_AND_MOVE(AutoLock);
101 
102 private:
103     mutex_t *mutex_ = nullptr;
104 };
105 
106 #endif // __cplusplus
107 
108