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 }
|