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