src/thread/generic/SDL_syscond.c
author Sam Lantinga
Mon, 06 Feb 2006 08:28:51 +0000
changeset 1330 450721ad5436
parent 1312 c9b51268668f
child 1336 3692456e7b0f
permissions -rw-r--r--
It's now possible to build SDL without any C runtime at all on Windows,
using Visual C++ 2005
     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 
    23 /* An implementation of condition variables using semaphores and mutexes */
    24 /*
    25    This implementation borrows heavily from the BeOS condition variable
    26    implementation, written by Christopher Tate and Owen Smith.  Thanks!
    27  */
    28 
    29 #include "SDL_error.h"
    30 #include "SDL_thread.h"
    31 #include "SDL_stdlib.h"
    32 
    33 struct SDL_cond
    34 {
    35 	SDL_mutex *lock;
    36 	int waiting;
    37 	int signals;
    38 	SDL_sem *wait_sem;
    39 	SDL_sem *wait_done;
    40 };
    41 
    42 /* Create a condition variable */
    43 SDL_cond * SDL_CreateCond(void)
    44 {
    45 	SDL_cond *cond;
    46 
    47 	cond = (SDL_cond *) 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 SDL_DestroyCond(SDL_cond *cond)
    65 {
    66 	if ( cond ) {
    67 		if ( cond->wait_sem ) {
    68 			SDL_DestroySemaphore(cond->wait_sem);
    69 		}
    70 		if ( cond->wait_done ) {
    71 			SDL_DestroySemaphore(cond->wait_done);
    72 		}
    73 		if ( cond->lock ) {
    74 			SDL_DestroyMutex(cond->lock);
    75 		}
    76 		free(cond);
    77 	}
    78 }
    79 
    80 /* Restart one of the threads that are waiting on the condition variable */
    81 int SDL_CondSignal(SDL_cond *cond)
    82 {
    83 	if ( ! cond ) {
    84 		SDL_SetError("Passed a NULL condition variable");
    85 		return -1;
    86 	}
    87 
    88 	/* If there are waiting threads not already signalled, then
    89 	   signal the condition and wait for the thread to respond.
    90 	*/
    91 	SDL_LockMutex(cond->lock);
    92 	if ( cond->waiting > cond->signals ) {
    93 		++cond->signals;
    94 		SDL_SemPost(cond->wait_sem);
    95 		SDL_UnlockMutex(cond->lock);
    96 		SDL_SemWait(cond->wait_done);
    97 	} else {
    98 		SDL_UnlockMutex(cond->lock);
    99 	}
   100 
   101 	return 0;
   102 }
   103 
   104 /* Restart all threads that are waiting on the condition variable */
   105 int SDL_CondBroadcast(SDL_cond *cond)
   106 {
   107 	if ( ! cond ) {
   108 		SDL_SetError("Passed a NULL condition variable");
   109 		return -1;
   110 	}
   111 
   112 	/* If there are waiting threads not already signalled, then
   113 	   signal the condition and wait for the thread to respond.
   114 	*/
   115 	SDL_LockMutex(cond->lock);
   116 	if ( cond->waiting > cond->signals ) {
   117 		int i, num_waiting;
   118 
   119 		num_waiting = (cond->waiting - cond->signals);
   120 		cond->signals = cond->waiting;
   121 		for ( i=0; i<num_waiting; ++i ) {
   122 			SDL_SemPost(cond->wait_sem);
   123 		}
   124 		/* Now all released threads are blocked here, waiting for us.
   125 		   Collect them all (and win fabulous prizes!) :-)
   126 		 */
   127 		SDL_UnlockMutex(cond->lock);
   128 		for ( i=0; i<num_waiting; ++i ) {
   129 			SDL_SemWait(cond->wait_done);
   130 		}
   131 	} else {
   132 		SDL_UnlockMutex(cond->lock);
   133 	}
   134 
   135 	return 0;
   136 }
   137 
   138 /* Wait on the condition variable for at most 'ms' milliseconds.
   139    The mutex must be locked before entering this function!
   140    The mutex is unlocked during the wait, and locked again after the wait.
   141 
   142 Typical use:
   143 
   144 Thread A:
   145 	SDL_LockMutex(lock);
   146 	while ( ! condition ) {
   147 		SDL_CondWait(cond);
   148 	}
   149 	SDL_UnlockMutex(lock);
   150 
   151 Thread B:
   152 	SDL_LockMutex(lock);
   153 	...
   154 	condition = true;
   155 	...
   156 	SDL_UnlockMutex(lock);
   157  */
   158 int SDL_CondWaitTimeout(SDL_cond *cond, SDL_mutex *mutex, Uint32 ms)
   159 {
   160 	int retval;
   161 
   162 	if ( ! cond ) {
   163 		SDL_SetError("Passed a NULL condition variable");
   164 		return -1;
   165 	}
   166 
   167 	/* Obtain the protection mutex, and increment the number of waiters.
   168 	   This allows the signal mechanism to only perform a signal if there
   169 	   are waiting threads.
   170 	 */
   171 	SDL_LockMutex(cond->lock);
   172 	++cond->waiting;
   173 	SDL_UnlockMutex(cond->lock);
   174 
   175 	/* Unlock the mutex, as is required by condition variable semantics */
   176 	SDL_UnlockMutex(mutex);
   177 
   178 	/* Wait for a signal */
   179 	if ( ms == SDL_MUTEX_MAXWAIT ) {
   180 		retval = SDL_SemWait(cond->wait_sem);
   181 	} else {
   182 		retval = SDL_SemWaitTimeout(cond->wait_sem, ms);
   183 	}
   184 
   185 	/* Let the signaler know we have completed the wait, otherwise
   186            the signaler can race ahead and get the condition semaphore
   187            if we are stopped between the mutex unlock and semaphore wait,
   188            giving a deadlock.  See the following URL for details:
   189         http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html
   190 	*/
   191 	SDL_LockMutex(cond->lock);
   192 	if ( cond->signals > 0 ) {
   193 		/* If we timed out, we need to eat a condition signal */
   194 		if ( retval > 0 ) {
   195 			SDL_SemWait(cond->wait_sem);
   196 		}
   197 		/* We always notify the signal thread that we are done */
   198 		SDL_SemPost(cond->wait_done);
   199 
   200 		/* Signal handshake complete */
   201 		--cond->signals;
   202 	}
   203 	--cond->waiting;
   204 	SDL_UnlockMutex(cond->lock);
   205 
   206 	/* Lock the mutex, as is required by condition variable semantics */
   207 	SDL_LockMutex(mutex);
   208 
   209 	return retval;
   210 }
   211 
   212 /* Wait on the condition variable forever */
   213 int SDL_CondWait(SDL_cond *cond, SDL_mutex *mutex)
   214 {
   215 	return SDL_CondWaitTimeout(cond, mutex, SDL_MUTEX_MAXWAIT);
   216 }