root/ext/threads/mutex.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mutex_finalize
  2. mutex_allocate
  3. mutex_print
  4. mutex_state_get
  5. mutex_name_get
  6. mutex_name_set
  7. mutex_specific_get
  8. mutex_specific_set
  9. Scm_MakeMutex
  10. Scm_MutexLock
  11. Scm_MutexUnlock
  12. cv_finalize
  13. cv_allocate
  14. cv_print
  15. cv_name_get
  16. cv_name_set
  17. cv_specific_get
  18. cv_specific_set
  19. Scm_MakeConditionVariable
  20. Scm_ConditionVariableSignal
  21. Scm_ConditionVariableBroadcast
  22. 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 }

/* [<][>][^][v][top][bottom][index][help] */