src/thread/generic/SDL_syscond.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 29 May 2006 04:04:35 +0000
branchSDL-1.3
changeset 1668 4da1ee79c9af
parent 1662 782fd950bd46
permissions -rw-r--r--
more tweaking indent options
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2006 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 /* An implementation of condition variables using semaphores and mutexes */
    25 /*
    26    This implementation borrows heavily from the BeOS condition variable
    27    implementation, written by Christopher Tate and Owen Smith.  Thanks!
    28  */
    29 
    30 #include "SDL_thread.h"
    31 
    32 struct SDL_cond
    33 {
    34     SDL_mutex *lock;
    35     int waiting;
    36     int signals;
    37     SDL_sem *wait_sem;
    38     SDL_sem *wait_done;
    39 };
    40 
    41 /* Create a condition variable */
    42 SDL_cond *
    43 SDL_CreateCond(void)
    44 {
    45     SDL_cond *cond;
    46 
    47     cond = (SDL_cond *) SDL_malloc(sizeof(SDL_cond));
    48     if (cond) {
    49         cond->lock = SDL_CreateMutex();
    50         cond->wait_sem = SDL_CreateSemaphore(0);
    51         cond->wait_done = SDL_CreateSemaphore(0);
    52         cond->waiting = cond->signals = 0;
    53         if (!cond->lock || !cond->wait_sem || !cond->wait_done) {
    54             SDL_DestroyCond(cond);
    55             cond = NULL;
    56         }
    57     } else {
    58         SDL_OutOfMemory();
    59     }
    60     return (cond);
    61 }
    62 
    63 /* Destroy a condition variable */
    64 void
    65 SDL_DestroyCond(SDL_cond * cond)
    66 {
    67     if (cond) {
    68         if (cond->wait_sem) {
    69             SDL_DestroySemaphore(cond->wait_sem);
    70         }
    71         if (cond->wait_done) {
    72             SDL_DestroySemaphore(cond->wait_done);
    73         }
    74         if (cond->lock) {
    75             SDL_DestroyMutex(cond->lock);
    76         }
    77         SDL_free(cond);
    78     }
    79 }
    80 
    81 /* Restart one of the threads that are waiting on the condition variable */
    82 int
    83 SDL_CondSignal(SDL_cond * cond)
    84 {
    85     if (!cond) {
    86         SDL_SetError("Passed a NULL condition variable");
    87         return -1;
    88     }
    89 
    90     /* If there are waiting threads not already signalled, then
    91        signal the condition and wait for the thread to respond.
    92      */
    93     SDL_LockMutex(cond->lock);
    94     if (cond->waiting > cond->signals) {
    95         ++cond->signals;
    96         SDL_SemPost(cond->wait_sem);
    97         SDL_UnlockMutex(cond->lock);
    98         SDL_SemWait(cond->wait_done);
    99     } else {
   100         SDL_UnlockMutex(cond->lock);
   101     }
   102 
   103     return 0;
   104 }
   105 
   106 /* Restart all threads that are waiting on the condition variable */
   107 int
   108 SDL_CondBroadcast(SDL_cond * cond)
   109 {
   110     if (!cond) {
   111         SDL_SetError("Passed a NULL condition variable");
   112         return -1;
   113     }
   114 
   115     /* If there are waiting threads not already signalled, then
   116        signal the condition and wait for the thread to respond.
   117      */
   118     SDL_LockMutex(cond->lock);
   119     if (cond->waiting > cond->signals) {
   120         int i, num_waiting;
   121 
   122         num_waiting = (cond->waiting - cond->signals);
   123         cond->signals = cond->waiting;
   124         for (i = 0; i < num_waiting; ++i) {
   125             SDL_SemPost(cond->wait_sem);
   126         }
   127         /* Now all released threads are blocked here, waiting for us.
   128            Collect them all (and win fabulous prizes!) :-)
   129          */
   130         SDL_UnlockMutex(cond->lock);
   131         for (i = 0; i < num_waiting; ++i) {
   132             SDL_SemWait(cond->wait_done);
   133         }
   134     } else {
   135         SDL_UnlockMutex(cond->lock);
   136     }
   137 
   138     return 0;
   139 }
   140 
   141 /* Wait on the condition variable for at most 'ms' milliseconds.
   142    The mutex must be locked before entering this function!
   143    The mutex is unlocked during the wait, and locked again after the wait.
   144 
   145 Typical use:
   146 
   147 Thread A:
   148 	SDL_LockMutex(lock);
   149 	while ( ! condition ) {
   150 		SDL_CondWait(cond);
   151 	}
   152 	SDL_UnlockMutex(lock);
   153 
   154 Thread B:
   155 	SDL_LockMutex(lock);
   156 	...
   157 	condition = true;
   158 	...
   159 	SDL_UnlockMutex(lock);
   160  */
   161 int
   162 SDL_CondWaitTimeout(SDL_cond * cond, SDL_mutex * mutex, Uint32 ms)
   163 {
   164     int retval;
   165 
   166     if (!cond) {
   167         SDL_SetError("Passed a NULL condition variable");
   168         return -1;
   169     }
   170 
   171     /* Obtain the protection mutex, and increment the number of waiters.
   172        This allows the signal mechanism to only perform a signal if there
   173        are waiting threads.
   174      */
   175     SDL_LockMutex(cond->lock);
   176     ++cond->waiting;
   177     SDL_UnlockMutex(cond->lock);
   178 
   179     /* Unlock the mutex, as is required by condition variable semantics */
   180     SDL_UnlockMutex(mutex);
   181 
   182     /* Wait for a signal */
   183     if (ms == SDL_MUTEX_MAXWAIT) {
   184         retval = SDL_SemWait(cond->wait_sem);
   185     } else {
   186         retval = SDL_SemWaitTimeout(cond->wait_sem, ms);
   187     }
   188 
   189     /* Let the signaler know we have completed the wait, otherwise
   190        the signaler can race ahead and get the condition semaphore
   191        if we are stopped between the mutex unlock and semaphore wait,
   192        giving a deadlock.  See the following URL for details:
   193        http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html
   194      */
   195     SDL_LockMutex(cond->lock);
   196     if (cond->signals > 0) {
   197         /* If we timed out, we need to eat a condition signal */
   198         if (retval > 0) {
   199             SDL_SemWait(cond->wait_sem);
   200         }
   201         /* We always notify the signal thread that we are done */
   202         SDL_SemPost(cond->wait_done);
   203 
   204         /* Signal handshake complete */
   205         --cond->signals;
   206     }
   207     --cond->waiting;
   208     SDL_UnlockMutex(cond->lock);
   209 
   210     /* Lock the mutex, as is required by condition variable semantics */
   211     SDL_LockMutex(mutex);
   212 
   213     return retval;
   214 }
   215 
   216 /* Wait on the condition variable forever */
   217 int
   218 SDL_CondWait(SDL_cond * cond, SDL_mutex * mutex)
   219 {
   220     return SDL_CondWaitTimeout(cond, mutex, SDL_MUTEX_MAXWAIT);
   221 }
   222 
   223 /* vi: set ts=4 sw=4 expandtab: */