/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- mutex_finalize
- mutex_allocate
- mutex_print
- mutex_state_get
- mutex_name_get
- mutex_name_set
- mutex_specific_get
- mutex_specific_set
- Scm_MakeMutex
- Scm_MutexLock
- Scm_MutexUnlock
- cv_finalize
- cv_allocate
- cv_print
- cv_name_get
- cv_name_set
- cv_specific_get
- cv_specific_set
- Scm_MakeConditionVariable
- Scm_ConditionVariableSignal
- Scm_ConditionVariableBroadcast
- Scm_Init_mutex
1 /*
2 * mutex.c - Scheme-level synchronization devices
3 *
4 * Copyright (c) 2000-2003 Shiro Kawai, All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the authors nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * $Id: mutex.c,v 1.8 2005/07/22 09:26:55 shirok Exp $
34 */
35
36 #include <math.h>
37 #include <gauche.h>
38 #include <gauche/class.h>
39 #include <gauche/exception.h>
40 #include "threads.h"
41
42 /*=====================================================
43 * Mutex
44 */
45
46 static ScmObj mutex_allocate(ScmClass *klass, ScmObj initargs);
47 static void mutex_print(ScmObj mutex, ScmPort *port, ScmWriteContext *ctx);
48
49 static ScmClass *default_cpl[] = {
50 SCM_CLASS_STATIC_PTR(Scm_TopClass), NULL
51 };
52
53 SCM_DEFINE_BASE_CLASS(Scm_MutexClass, ScmMutex,
54 mutex_print, NULL, NULL, mutex_allocate,
55 default_cpl);
56
57 #ifdef GAUCHE_USE_PTHREADS
58 static void mutex_finalize(ScmObj obj, void* data)
59 {
60 ScmMutex *mutex = SCM_MUTEX(obj);
61 pthread_mutex_destroy(&(mutex->mutex));
62 pthread_cond_destroy(&(mutex->cv));
63 }
64 #endif /* GAUCHE_USE_PTHREADS */
65
66 static ScmObj mutex_allocate(ScmClass *klass, ScmObj initargs)
67 {
68 ScmMutex *mutex = SCM_ALLOCATE(ScmMutex, klass);
69 SCM_SET_CLASS(mutex, klass);
70 #ifdef GAUCHE_USE_PTHREADS
71 {
72 pthread_mutex_init(&(mutex->mutex), NULL);
73 pthread_cond_init(&(mutex->cv), NULL);
74 Scm_RegisterFinalizer(SCM_OBJ(mutex), mutex_finalize, NULL);
75 }
76 #else /*!GAUCHE_USE_PTHREADS*/
77 (void)SCM_INTERNAL_MUTEX_INIT(mutex->mutex);
78 #endif /*!GAUCHE_USE_PTHREADS*/
79 mutex->name = SCM_FALSE;
80 mutex->specific = SCM_UNDEFINED;
81 mutex->locked = FALSE;
82 mutex->owner = NULL;
83 return SCM_OBJ(mutex);
84 }
85
86 static void mutex_print(ScmObj obj, ScmPort *port, ScmWriteContext *ctx)
87 {
88 ScmMutex *mutex = SCM_MUTEX(obj);
89 ScmVM *vm;
90 ScmObj name;
91 int locked;
92
93 (void)SCM_INTERNAL_MUTEX_LOCK(mutex->mutex);
94 locked = mutex->locked;
95 vm = mutex->owner;
96 name = mutex->name;
97 (void)SCM_INTERNAL_MUTEX_UNLOCK(mutex->mutex);
98
99 if (SCM_FALSEP(name)) Scm_Printf(port, "#<mutex %p ", mutex);
100 else Scm_Printf(port, "#<mutex %S ", name);
101 if (locked) {
102 if (vm) {
103 if (vm->state == SCM_VM_TERMINATED) {
104 Scm_Printf(port, "unlocked/abandoned>");
105 } else {
106 Scm_Printf(port, "locked/owned by %S>", vm);
107 }
108 } else {
109 Scm_Printf(port, "locked/not-owned>");
110 }
111 } else {
112 Scm_Printf(port, "unlocked/not-abandoned>");
113 }
114 }
115
116 /*
117 * Accessors
118 */
119 static ScmObj sym_not_owned;
120 static ScmObj sym_abandoned;
121 static ScmObj sym_not_abandoned;
122
123 static ScmObj mutex_state_get(ScmMutex *mutex)
124 {
125 ScmObj r;
126 (void)SCM_INTERNAL_MUTEX_LOCK(mutex->mutex);
127 if (mutex->locked) {
128 if (mutex->owner) {
129 if (mutex->owner->state == SCM_VM_TERMINATED) r = sym_abandoned;
130 else r = SCM_OBJ(mutex->owner);
131 } else {
132 r = sym_not_owned;
133 }
134 } else {
135 r = sym_not_abandoned;
136 }
137 (void)SCM_INTERNAL_MUTEX_UNLOCK(mutex->mutex);
138 return r;
139 }
140
141 static ScmObj mutex_name_get(ScmMutex *mutex)
142 {
143 return mutex->name;
144 }
145
146 static void mutex_name_set(ScmMutex *mutex, ScmObj name)
147 {
148 mutex->name = name;
149 }
150
151 static ScmObj mutex_specific_get(ScmMutex *mutex)
152 {
153 return mutex->specific;
154 }
155
156 static void mutex_specific_set(ScmMutex *mutex, ScmObj value)
157 {
158 mutex->specific = value;
159 }
160
161 static ScmClassStaticSlotSpec mutex_slots[] = {
162 SCM_CLASS_SLOT_SPEC("name", mutex_name_get, mutex_name_set),
163 SCM_CLASS_SLOT_SPEC("state", mutex_state_get, NULL),
164 SCM_CLASS_SLOT_SPEC("specific", mutex_specific_get, mutex_specific_set),
165 { NULL }
166 };
167
168 /*
169 * Make mutex
170 */
171 ScmObj Scm_MakeMutex(ScmObj name)
172 {
173 ScmObj m = mutex_allocate(SCM_CLASS_MUTEX, SCM_NIL);
174 SCM_MUTEX(m)->name = name;
175 return m;
176 }
177
178 /*
179 * Lock and unlock mutex
180 */
181
182 ScmObj Scm_MutexLock(ScmMutex *mutex, ScmObj timeout, ScmVM *owner)
183 {
184 #ifdef GAUCHE_USE_PTHREADS
185 struct timespec ts, *pts;
186 ScmObj r = SCM_TRUE;
187 ScmVM *abandoned = NULL;
188 int intr = FALSE;
189
190 pts = Scm_GetTimeSpec(timeout, &ts);
191 pthread_cleanup_push((void (*)(void*))pthread_mutex_unlock,
192 (void *)&(mutex->mutex));
193 if (SCM_INTERNAL_MUTEX_LOCK(mutex->mutex) != 0) {
194 Scm_Error("mutex-lock!: failed to lock");
195 }
196 while (mutex->locked) {
197 if (mutex->owner && mutex->owner->state == SCM_VM_TERMINATED) {
198 abandoned = mutex->owner;
199 mutex->locked = FALSE;
200 break;
201 }
202 if (pts) {
203 int tr = pthread_cond_timedwait(&(mutex->cv), &(mutex->mutex), pts);
204 if (tr == ETIMEDOUT) { r = SCM_FALSE; break; }
205 else if (tr == EINTR) { intr = TRUE; break; }
206 } else {
207 pthread_cond_wait(&(mutex->cv), &(mutex->mutex));
208 }
209 }
210 if (SCM_TRUEP(r)) {
211 mutex->locked = TRUE;
212 mutex->owner = owner;
213 }
214 (void)SCM_INTERNAL_MUTEX_UNLOCK(mutex->mutex);
215 pthread_cleanup_pop(0);
216 if (intr) Scm_SigCheck(Scm_VM());
217 if (abandoned) {
218 ScmObj exc = Scm_MakeThreadException(SCM_CLASS_ABANDONED_MUTEX_EXCEPTION, abandoned);
219 SCM_THREAD_EXCEPTION(exc)->data = SCM_OBJ(mutex);
220 r = Scm_Raise(exc);
221 }
222 return r;
223 #else /* !GAUCHE_USE_PTHREADS */
224 return SCM_TRUE; /* dummy */
225 #endif /* !GAUCHE_USE_PTHREADS */
226 }
227
228 ScmObj Scm_MutexUnlock(ScmMutex *mutex, ScmConditionVariable *cv, ScmObj timeout)
229 {
230 ScmObj r = SCM_TRUE;
231 #ifdef GAUCHE_USE_PTHREADS
232 struct timespec ts, *pts;
233 int intr = FALSE;
234
235 pts = Scm_GetTimeSpec(timeout, &ts);
236 pthread_cleanup_push((void (*)(void*))pthread_mutex_unlock,
237 (void *)&(mutex->mutex));
238 if (SCM_INTERNAL_MUTEX_LOCK(mutex->mutex) != 0) {
239 Scm_Error("mutex-unlock!: failed to lock");
240 }
241 mutex->locked = FALSE;
242 mutex->owner = NULL;
243 pthread_cond_signal(&(mutex->cv));
244 if (cv) {
245 if (pts) {
246 int tr = pthread_cond_timedwait(&(cv->cv), &(mutex->mutex), pts);
247 if (tr == ETIMEDOUT) { r = SCM_FALSE; }
248 else if (tr == EINTR) { intr = TRUE; }
249 } else {
250 pthread_cond_wait(&(cv->cv), &(mutex->mutex));
251 }
252 }
253 (void)SCM_INTERNAL_MUTEX_UNLOCK(mutex->mutex);
254 pthread_cleanup_pop(0);
255 if (intr) Scm_SigCheck(Scm_VM());
256 #endif /* GAUCHE_USE_PTHREADS */
257 return r;
258 }
259
260 /*=====================================================
261 * Condition variable
262 */
263
264 static ScmObj cv_allocate(ScmClass *klass, ScmObj initargs);
265 static void cv_print(ScmObj cv, ScmPort *port, ScmWriteContext *ctx);
266
267 SCM_DEFINE_BASE_CLASS(Scm_ConditionVariableClass, ScmConditionVariable,
268 cv_print, NULL, NULL, cv_allocate,
269 default_cpl);
270
271 #ifdef GAUCHE_USE_PTHREADS
272 static void cv_finalize(ScmObj obj, void *data)
273 {
274 ScmConditionVariable *cv = SCM_CONDITION_VARIABLE(obj);
275 pthread_cond_destroy(&(cv->cv));
276 }
277 #endif /* GAUCHE_USE_PTHREADS */
278
279 static ScmObj cv_allocate(ScmClass *klass, ScmObj initargs)
280 {
281 ScmConditionVariable *cv = SCM_ALLOCATE(ScmConditionVariable, klass);
282 SCM_SET_CLASS(cv, klass);
283 #ifdef GAUCHE_USE_PTHREADS
284 {
285 pthread_cond_init(&(cv->cv), NULL);
286 Scm_RegisterFinalizer(SCM_OBJ(cv), cv_finalize, NULL);
287 }
288 #else /*!GAUCHE_USE_PTHREADS*/
289 (void)SCM_INTERNAL_COND_INIT(cv->cv);
290 #endif /*!GAUCHE_USE_PTHREADS*/
291 cv->name = SCM_FALSE;
292 cv->specific = SCM_UNDEFINED;
293 return SCM_OBJ(cv);
294 }
295
296 static void cv_print(ScmObj obj, ScmPort *port, ScmWriteContext *ctx)
297 {
298 ScmConditionVariable *cv = SCM_CONDITION_VARIABLE(obj);
299 ScmObj name = cv->name;
300 if (SCM_FALSEP(name)) Scm_Printf(port, "#<condition-variable %p>", cv);
301 else Scm_Printf(port, "#<condition-variable %S>", name);
302 }
303
304 /*
305 * Accessors
306 */
307
308 static ScmObj cv_name_get(ScmConditionVariable *cv)
309 {
310 return cv->name;
311 }
312
313 static void cv_name_set(ScmConditionVariable *cv, ScmObj name)
314 {
315 cv->name = name;
316 }
317
318 static ScmObj cv_specific_get(ScmConditionVariable *cv)
319 {
320 return cv->specific;
321 }
322
323 static void cv_specific_set(ScmConditionVariable *cv, ScmObj val)
324 {
325 cv->specific = val;
326 }
327
328 static ScmClassStaticSlotSpec cv_slots[] = {
329 SCM_CLASS_SLOT_SPEC("name", cv_name_get, cv_name_set),
330 SCM_CLASS_SLOT_SPEC("specific", cv_specific_get, cv_specific_set),
331 { NULL }
332 };
333
334 /*
335 * Make condition variable
336 */
337 ScmObj Scm_MakeConditionVariable(ScmObj name)
338 {
339 ScmObj cv = cv_allocate(SCM_CLASS_CONDITION_VARIABLE, SCM_NIL);
340 SCM_CONDITION_VARIABLE(cv)->name = name;
341 return cv;
342 }
343
344 ScmObj Scm_ConditionVariableSignal(ScmConditionVariable *cond)
345 {
346 #ifdef GAUCHE_USE_PTHREADS
347 pthread_cond_signal(&(cond->cv));
348 #endif
349 return SCM_UNDEFINED;
350 }
351
352 ScmObj Scm_ConditionVariableBroadcast(ScmConditionVariable *cond)
353 {
354 #ifdef GAUCHE_USE_PTHREADS
355 pthread_cond_broadcast(&(cond->cv));
356 #endif
357 return SCM_UNDEFINED;
358 }
359
360 /*
361 * Initialization
362 */
363
364 void Scm_Init_mutex(ScmModule *mod)
365 {
366 sym_not_owned = SCM_INTERN("not-owned");
367 sym_abandoned = SCM_INTERN("abandoned");
368 sym_not_abandoned = SCM_INTERN("not-abandoned");
369 Scm_InitStaticClass(&Scm_MutexClass, "<mutex>", mod, mutex_slots, 0);
370 Scm_InitStaticClass(&Scm_ConditionVariableClass, "<condition-variable>", mod, cv_slots, 0);
371 }