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