src/thread/os2/SDL_syscond.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 16 Feb 2006 10:11:48 +0000
changeset 1361 19418e4422cb
parent 1358 c71e05b4dc2e
child 1402 d910939febfa
permissions -rw-r--r--
New configure-based build system. Still work in progress, but much improved
     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_thread.h"
    30 
    31 struct SDL_cond
    32 {
    33 	SDL_mutex *lock;
    34 	int waiting;
    35 	int signals;
    36 	SDL_sem *wait_sem;
    37 	SDL_sem *wait_done;
    38 };
    39 
    40 /* Create a condition variable */
    41 DECLSPEC SDL_cond * SDLCALL SDL_CreateCond(void)
    42 {
    43 	SDL_cond *cond;
    44 
    45 	cond = (SDL_cond *) SDL_malloc(sizeof(SDL_cond));
    46 	if ( cond ) {
    47 		cond->lock = SDL_CreateMutex();
    48 		cond->wait_sem = SDL_CreateSemaphore(0);
    49 		cond->wait_done = SDL_CreateSemaphore(0);
    50 		cond->waiting = cond->signals = 0;
    51 		if ( ! cond->lock || ! cond->wait_sem || ! cond->wait_done ) {
    52 			SDL_DestroyCond(cond);
    53 			cond = NULL;
    54 		}
    55 	} else {
    56 		SDL_OutOfMemory();
    57 	}
    58 	return(cond);
    59 }
    60 
    61 /* Destroy a condition variable */
    62 DECLSPEC void SDLCALL SDL_DestroyCond(SDL_cond *cond)
    63 {
    64 	if ( cond ) {
    65 		if ( cond->wait_sem ) {
    66 			SDL_DestroySemaphore(cond->wait_sem);
    67 		}
    68 		if ( cond->wait_done ) {
    69 			SDL_DestroySemaphore(cond->wait_done);
    70 		}
    71 		if ( cond->lock ) {
    72 			SDL_DestroyMutex(cond->lock);
    73 		}
    74 		SDL_free(cond);
    75 	}
    76 }
    77 
    78 /* Restart one of the threads that are waiting on the condition variable */
    79 DECLSPEC int SDLCALL SDL_CondSignal(SDL_cond *cond)
    80 {
    81 	if ( ! cond ) {
    82 		SDL_SetError("Passed a NULL condition variable");
    83 		return -1;
    84 	}
    85 
    86 	/* If there are waiting threads not already signalled, then
    87 	   signal the condition and wait for the thread to respond.
    88 	*/
    89 	SDL_LockMutex(cond->lock);
    90 	if ( cond->waiting > cond->signals ) {
    91 		++cond->signals;
    92 		SDL_SemPost(cond->wait_sem);
    93 		SDL_UnlockMutex(cond->lock);
    94 		SDL_SemWait(cond->wait_done);
    95 	} else {
    96 		SDL_UnlockMutex(cond->lock);
    97 	}
    98 
    99 	return 0;
   100 }
   101 
   102 /* Restart all threads that are waiting on the condition variable */
   103 DECLSPEC int SDLCALL SDL_CondBroadcast(SDL_cond *cond)
   104 {
   105 	if ( ! cond ) {
   106 		SDL_SetError("Passed a NULL condition variable");
   107 		return -1;
   108 	}
   109 
   110 	/* If there are waiting threads not already signalled, then
   111 	   signal the condition and wait for the thread to respond.
   112 	*/
   113 	SDL_LockMutex(cond->lock);
   114 	if ( cond->waiting > cond->signals ) {
   115 		int i, num_waiting;
   116 
   117 		num_waiting = (cond->waiting - cond->signals);
   118 		cond->signals = cond->waiting;
   119 		for ( i=0; i<num_waiting; ++i ) {
   120 			SDL_SemPost(cond->wait_sem);
   121 		}
   122 		/* Now all released threads are blocked here, waiting for us.
   123 		   Collect them all (and win fabulous prizes!) :-)
   124 		 */
   125 		SDL_UnlockMutex(cond->lock);
   126 		for ( i=0; i<num_waiting; ++i ) {
   127 			SDL_SemWait(cond->wait_done);
   128 		}
   129 	} else {
   130 		SDL_UnlockMutex(cond->lock);
   131 	}
   132 
   133 	return 0;
   134 }
   135 
   136 /* Wait on the condition variable for at most 'ms' milliseconds.
   137    The mutex must be locked before entering this function!
   138    The mutex is unlocked during the wait, and locked again after the wait.
   139 
   140 Typical use:
   141 
   142 Thread A:
   143 	SDL_LockMutex(lock);
   144 	while ( ! condition ) {
   145 		SDL_CondWait(cond);
   146 	}
   147 	SDL_UnlockMutex(lock);
   148 
   149 Thread B:
   150 	SDL_LockMutex(lock);
   151 	...
   152 	condition = true;
   153 	...
   154 	SDL_UnlockMutex(lock);
   155  */
   156 DECLSPEC int SDLCALL SDL_CondWaitTimeout(SDL_cond *cond, SDL_mutex *mutex, Uint32 ms)
   157 {
   158 	int retval;
   159 
   160 	if ( ! cond ) {
   161 		SDL_SetError("Passed a NULL condition variable");
   162 		return -1;
   163 	}
   164 
   165 	/* Obtain the protection mutex, and increment the number of waiters.
   166 	   This allows the signal mechanism to only perform a signal if there
   167 	   are waiting threads.
   168 	 */
   169 	SDL_LockMutex(cond->lock);
   170 	++cond->waiting;
   171 	SDL_UnlockMutex(cond->lock);
   172 
   173 	/* Unlock the mutex, as is required by condition variable semantics */
   174 	SDL_UnlockMutex(mutex);
   175 
   176 	/* Wait for a signal */
   177 	if ( ms == SDL_MUTEX_MAXWAIT ) {
   178 		retval = SDL_SemWait(cond->wait_sem);
   179 	} else {
   180 		retval = SDL_SemWaitTimeout(cond->wait_sem, ms);
   181 	}
   182 
   183 	/* Let the signaler know we have completed the wait, otherwise
   184            the signaler can race ahead and get the condition semaphore
   185            if we are stopped between the mutex unlock and semaphore wait,
   186            giving a deadlock.  See the following URL for details:
   187         http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html
   188 	*/
   189 	SDL_LockMutex(cond->lock);
   190 	if ( cond->signals > 0 ) {
   191 		/* If we timed out, we need to eat a condition signal */
   192 		if ( retval > 0 ) {
   193 			SDL_SemWait(cond->wait_sem);
   194 		}
   195 		/* We always notify the signal thread that we are done */
   196 		SDL_SemPost(cond->wait_done);
   197 
   198 		/* Signal handshake complete */
   199 		--cond->signals;
   200 	}
   201 	--cond->waiting;
   202 	SDL_UnlockMutex(cond->lock);
   203 
   204 	/* Lock the mutex, as is required by condition variable semantics */
   205 	SDL_LockMutex(mutex);
   206 
   207 	return retval;
   208 }
   209 
   210 /* Wait on the condition variable forever */
   211 DECLSPEC int SDLCALL SDL_CondWait(SDL_cond *cond, SDL_mutex *mutex)
   212 {
   213 	return SDL_CondWaitTimeout(cond, mutex, SDL_MUTEX_MAXWAIT);
   214 }