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