First version
[3rdparty/ote_partner/tlk.git] / kernel / mutex.c
1 /*
2  * Copyright (c) 2008-2009 Travis Geiselbrecht
3  * Copyright (c) 2012-2012 Shantanu Gupta
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files
7  * (the "Software"), to deal in the Software without restriction,
8  * including without limitation the rights to use, copy, modify, merge,
9  * publish, distribute, sublicense, and/or sell copies of the Software,
10  * and to permit persons to whom the Software is furnished to do so,
11  * subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24
25 /**
26  * @file
27  * @brief  Mutex functions
28  *
29  * @defgroup mutex Mutex
30  * @{
31  */
32
33 #include <debug.h>
34 #include <assert.h>
35 #include <err.h>
36 #include <kernel/mutex.h>
37 #include <kernel/thread.h>
38
39 /**
40  * @brief  Initialize a mutex_t
41  */
42 void mutex_init(mutex_t *m)
43 {
44 #if MUTEX_CHECK
45         m->magic = MUTEX_MAGIC;
46         m->holder = 0; // In good code, release is only called if acquire was successful        
47 #endif
48
49         m->count = 0;
50         wait_queue_init(&m->wait);
51 }
52
53 /**
54  * @brief  Destroy a mutex_t
55  *
56  * This function frees any resources that were allocated
57  * in mutex_init().  The mutex_t object itself is not freed.
58  */
59 void mutex_destroy(mutex_t *m)
60 {
61         enter_critical_section();
62
63 #if MUTEX_CHECK
64         ASSERT(m->magic == MUTEX_MAGIC);
65         m->magic = 0;
66
67         if (m->holder != 0 && current_thread != m->holder)
68                 panic("mutex_destroy: thread %p (%s) tried to release mutex %p it doesn't own. owned by %p (%s)\n", 
69                                 current_thread, current_thread->name, m, m->holder, m->holder->name);
70 #endif
71
72         m->count = 0;
73         wait_queue_destroy(&m->wait, true);
74         exit_critical_section();
75 }
76
77 /**
78  * @brief  Acquire a mutex; wait if needed.
79  *
80  * This function waits for a mutex to become available.  It
81  * may wait forever if the mutex never becomes free.
82  *
83  * @return  NO_ERROR on success, other values on error
84  */
85 status_t mutex_acquire(mutex_t *m)
86 {
87         status_t ret = NO_ERROR;
88
89 #if MUTEX_CHECK
90         ASSERT(m->magic == MUTEX_MAGIC);
91
92         if (current_thread == m->holder)
93                 panic("mutex_acquire: thread %p (%s) tried to acquire mutex %p it already owns.\n",
94                                 current_thread, current_thread->name, m);
95 #endif
96
97         enter_critical_section();
98
99         if (unlikely(++m->count > 1)) {
100                 /* 
101                  * block on the wait queue. If it returns an error, it was likely destroyed
102                  * out from underneath us, so make sure we dont scribble thread ownership 
103                  * on the mutex.
104                  */
105                 ret = wait_queue_block(&m->wait, INFINITE_TIME);
106                 if (ret < 0)
107                         goto err;
108         }
109
110 #if MUTEX_CHECK
111         m->holder = current_thread;
112 #endif
113
114 err:
115         exit_critical_section();
116         return ret;
117 }
118
119 /**
120  * @brief  Mutex wait with timeout
121  *
122  * This function waits up to \a timeout ms for the mutex to become available.
123  * Timeout may be zero, in which case this function returns immediately if
124  * the mutex is not free.
125  *
126  * @return  NO_ERROR on success, ERR_TIMED_OUT on timeout,
127  * other values on error
128  */
129 status_t mutex_acquire_timeout(mutex_t *m, lk_time_t timeout)
130 {
131         status_t ret = NO_ERROR;
132
133 #if MUTEX_CHECK
134         if (timeout == INFINITE_TIME)
135                 return mutex_acquire(m); // Unecessary overhead for correct calls, this function can handle this anyway
136
137         ASSERT(m->magic == MUTEX_MAGIC);
138
139         if (current_thread == m->holder)
140                 panic("mutex_acquire_timeout: thread %p (%s) tried to acquire mutex %p it already owns.\n",
141                                 current_thread, current_thread->name, m);
142 #endif
143
144
145         enter_critical_section();
146
147         if (unlikely(++m->count > 1)) {
148                 ret = wait_queue_block(&m->wait, timeout);
149                 if (ret < NO_ERROR) {
150                         /* if the acquisition timed out, back out the acquire and exit */
151                         if (ret == ERR_TIMED_OUT) {
152                                 /* 
153                                  * XXX race: the mutex may have been destroyed after the timeout,
154                                  * but before we got scheduled again which makes messing with the
155                                  * count variable dangerous.
156                                  */
157                                 m->count--;
158                                 goto err;
159                         }
160                         /* if there was a general error, it may have been destroyed out from 
161                          * underneath us, so just exit (which is really an invalid state anyway)
162                          */
163                 }       
164         }
165
166 #if MUTEX_CHECK
167         m->holder = current_thread;
168 #endif
169
170 err:
171         exit_critical_section();
172         return ret;
173 }
174
175 /**
176  * @brief  Release mutex
177  */
178 status_t mutex_release(mutex_t *m)
179 {
180 #if MUTEX_CHECK
181         ASSERT(m->magic == MUTEX_MAGIC);
182
183         if (current_thread != m->holder)
184                 panic("mutex_release: thread %p (%s) tried to release mutex %p it doesn't own. owned by %p (%s)\n", 
185                                 current_thread, current_thread->name, m, m->holder, m->holder ? m->holder->name : "none");
186
187 #endif
188
189         enter_critical_section();
190
191 #if MUTEX_CHECK
192         m->holder = 0;
193 #endif
194
195         if (unlikely(--m->count >= 1)) {
196                 /* release a thread */
197                 wait_queue_wake_one(&m->wait, true, NO_ERROR);
198         }
199
200         exit_critical_section();
201         return NO_ERROR;
202 }
203