1 /*
2  * Copyright (c) 2008-2014 Travis Geiselbrecht
3  * Copyright (c) 2012-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 
10 /**
11  * @file
12  * @brief  Mutex functions
13  *
14  * @defgroup mutex Mutex
15  * @{
16  */
17 
18 #include <kernel/mutex.h>
19 
20 #include <assert.h>
21 #include <kernel/thread.h>
22 #include <lk/debug.h>
23 #include <lk/err.h>
24 
25 /**
26  * @brief  Initialize a mutex_t
27  */
mutex_init(mutex_t * m)28 void mutex_init(mutex_t *m) {
29     *m = (mutex_t)MUTEX_INITIAL_VALUE(*m);
30 }
31 
32 /**
33  * @brief  Destroy a mutex_t
34  *
35  * This function frees any resources that were allocated
36  * in mutex_init().  The mutex_t object itself is not freed.
37  */
mutex_destroy(mutex_t * m)38 void mutex_destroy(mutex_t *m) {
39     DEBUG_ASSERT(m->magic == MUTEX_MAGIC);
40 
41 #if LK_DEBUGLEVEL > 0
42     if (unlikely(m->holder != 0 && get_current_thread() != m->holder))
43         panic("mutex_destroy: thread %p (%s) tried to release mutex %p it doesn't own. owned by %p (%s)\n",
44               get_current_thread(), get_current_thread()->name, m, m->holder, m->holder->name);
45 #endif
46 
47     THREAD_LOCK(state);
48     m->magic = 0;
49     m->count = 0;
50     wait_queue_destroy(&m->wait, true);
51     THREAD_UNLOCK(state);
52 }
53 
54 /**
55  * @brief  Mutex wait with timeout
56  *
57  * This function waits up to \a timeout ms for the mutex to become available.
58  * Timeout may be zero, in which case this function returns immediately if
59  * the mutex is not free.
60  *
61  * @return  NO_ERROR on success, ERR_TIMED_OUT on timeout,
62  * other values on error
63  */
mutex_acquire_timeout(mutex_t * m,lk_time_t timeout)64 status_t mutex_acquire_timeout(mutex_t *m, lk_time_t timeout) {
65     DEBUG_ASSERT(m->magic == MUTEX_MAGIC);
66 
67 #if LK_DEBUGLEVEL > 0
68     if (unlikely(get_current_thread() == m->holder))
69         panic("mutex_acquire_timeout: thread %p (%s) tried to acquire mutex %p it already owns.\n",
70               get_current_thread(), get_current_thread()->name, m);
71 #endif
72 
73     THREAD_LOCK(state);
74 
75     status_t ret = NO_ERROR;
76     if (unlikely(++m->count > 1)) {
77         ret = wait_queue_block(&m->wait, timeout);
78         if (unlikely(ret < NO_ERROR)) {
79             /* if the acquisition timed out, back out the acquire and exit */
80             if (likely(ret == ERR_TIMED_OUT)) {
81                 /*
82                  * race: the mutex may have been destroyed after the timeout,
83                  * but before we got scheduled again which makes messing with the
84                  * count variable dangerous.
85                  */
86                 m->count--;
87             }
88             /* if there was a general error, it may have been destroyed out from
89              * underneath us, so just exit (which is really an invalid state anyway)
90              */
91             goto err;
92         }
93     }
94 
95     m->holder = get_current_thread();
96 
97 err:
98     THREAD_UNLOCK(state);
99     return ret;
100 }
101 
102 /**
103  * @brief  Release mutex
104  */
mutex_release(mutex_t * m)105 status_t mutex_release(mutex_t *m) {
106     DEBUG_ASSERT(m->magic == MUTEX_MAGIC);
107 
108 #if LK_DEBUGLEVEL > 0
109     if (unlikely(get_current_thread() != m->holder)) {
110         panic("mutex_release: thread %p (%s) tried to release mutex %p it doesn't own. owned by %p (%s)\n",
111               get_current_thread(), get_current_thread()->name, m, m->holder, m->holder ? m->holder->name : "none");
112     }
113 #endif
114 
115     THREAD_LOCK(state);
116 
117     m->holder = 0;
118 
119     if (unlikely(--m->count >= 1)) {
120         /* release a thread */
121         wait_queue_wake_one(&m->wait, true, NO_ERROR);
122     }
123 
124     THREAD_UNLOCK(state);
125     return NO_ERROR;
126 }
127 
128