SDL-1.2.14
[sdl_omap.git] / src / thread / dc / SDL_syscond.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2009 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 SDL_cond * 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 void 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 int 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 int 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 int 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 int SDL_CondWait(SDL_cond *cond, SDL_mutex *mutex)
213 {
214         return SDL_CondWaitTimeout(cond, mutex, SDL_MUTEX_MAXWAIT);
215 }