1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
4 
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8 
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12 
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22 
23 /* An implementation of semaphores using mutexes and condition variables */
24 
25 #include "SDL_timer.h"
26 #include "SDL_thread.h"
27 #include "SDL_systhread_c.h"
28 
29 
30 #if SDL_THREADS_DISABLED
31 
32 SDL_sem *
SDL_CreateSemaphore(Uint32 initial_value)33 SDL_CreateSemaphore(Uint32 initial_value)
34 {
35     SDL_SetError("SDL not built with thread support");
36     return (SDL_sem *) 0;
37 }
38 
39 void
SDL_DestroySemaphore(SDL_sem * sem)40 SDL_DestroySemaphore(SDL_sem * sem)
41 {
42 }
43 
44 int
SDL_SemTryWait(SDL_sem * sem)45 SDL_SemTryWait(SDL_sem * sem)
46 {
47     return SDL_SetError("SDL not built with thread support");
48 }
49 
50 int
SDL_SemWaitTimeout(SDL_sem * sem,Uint32 timeout)51 SDL_SemWaitTimeout(SDL_sem * sem, Uint32 timeout)
52 {
53     return SDL_SetError("SDL not built with thread support");
54 }
55 
56 int
SDL_SemWait(SDL_sem * sem)57 SDL_SemWait(SDL_sem * sem)
58 {
59     return SDL_SetError("SDL not built with thread support");
60 }
61 
62 Uint32
SDL_SemValue(SDL_sem * sem)63 SDL_SemValue(SDL_sem * sem)
64 {
65     return 0;
66 }
67 
68 int
SDL_SemPost(SDL_sem * sem)69 SDL_SemPost(SDL_sem * sem)
70 {
71     return SDL_SetError("SDL not built with thread support");
72 }
73 
74 #else
75 
76 struct SDL_semaphore
77 {
78     Uint32 count;
79     Uint32 waiters_count;
80     SDL_mutex *count_lock;
81     SDL_cond *count_nonzero;
82 };
83 
84 SDL_sem *
SDL_CreateSemaphore(Uint32 initial_value)85 SDL_CreateSemaphore(Uint32 initial_value)
86 {
87     SDL_sem *sem;
88 
89     sem = (SDL_sem *) SDL_malloc(sizeof(*sem));
90     if (!sem) {
91         SDL_OutOfMemory();
92         return NULL;
93     }
94     sem->count = initial_value;
95     sem->waiters_count = 0;
96 
97     sem->count_lock = SDL_CreateMutex();
98     sem->count_nonzero = SDL_CreateCond();
99     if (!sem->count_lock || !sem->count_nonzero) {
100         SDL_DestroySemaphore(sem);
101         return NULL;
102     }
103 
104     return sem;
105 }
106 
107 /* WARNING:
108    You cannot call this function when another thread is using the semaphore.
109 */
110 void
SDL_DestroySemaphore(SDL_sem * sem)111 SDL_DestroySemaphore(SDL_sem * sem)
112 {
113     if (sem) {
114         sem->count = 0xFFFFFFFF;
115         while (sem->waiters_count > 0) {
116             SDL_CondSignal(sem->count_nonzero);
117             SDL_Delay(10);
118         }
119         SDL_DestroyCond(sem->count_nonzero);
120         if (sem->count_lock) {
121             SDL_LockMutex(sem->count_lock);
122             SDL_UnlockMutex(sem->count_lock);
123             SDL_DestroyMutex(sem->count_lock);
124         }
125         SDL_free(sem);
126     }
127 }
128 
129 int
SDL_SemTryWait(SDL_sem * sem)130 SDL_SemTryWait(SDL_sem * sem)
131 {
132     int retval;
133 
134     if (!sem) {
135         return SDL_SetError("Passed a NULL semaphore");
136     }
137 
138     retval = SDL_MUTEX_TIMEDOUT;
139     SDL_LockMutex(sem->count_lock);
140     if (sem->count > 0) {
141         --sem->count;
142         retval = 0;
143     }
144     SDL_UnlockMutex(sem->count_lock);
145 
146     return retval;
147 }
148 
149 int
SDL_SemWaitTimeout(SDL_sem * sem,Uint32 timeout)150 SDL_SemWaitTimeout(SDL_sem * sem, Uint32 timeout)
151 {
152     int retval;
153 
154     if (!sem) {
155         return SDL_SetError("Passed a NULL semaphore");
156     }
157 
158     /* A timeout of 0 is an easy case */
159     if (timeout == 0) {
160         return SDL_SemTryWait(sem);
161     }
162 
163     SDL_LockMutex(sem->count_lock);
164     ++sem->waiters_count;
165     retval = 0;
166     while ((sem->count == 0) && (retval != SDL_MUTEX_TIMEDOUT)) {
167         retval = SDL_CondWaitTimeout(sem->count_nonzero,
168                                      sem->count_lock, timeout);
169     }
170     --sem->waiters_count;
171     if (retval == 0) {
172         --sem->count;
173     }
174     SDL_UnlockMutex(sem->count_lock);
175 
176     return retval;
177 }
178 
179 int
SDL_SemWait(SDL_sem * sem)180 SDL_SemWait(SDL_sem * sem)
181 {
182     return SDL_SemWaitTimeout(sem, SDL_MUTEX_MAXWAIT);
183 }
184 
185 Uint32
SDL_SemValue(SDL_sem * sem)186 SDL_SemValue(SDL_sem * sem)
187 {
188     Uint32 value;
189 
190     value = 0;
191     if (sem) {
192         SDL_LockMutex(sem->count_lock);
193         value = sem->count;
194         SDL_UnlockMutex(sem->count_lock);
195     }
196     return value;
197 }
198 
199 int
SDL_SemPost(SDL_sem * sem)200 SDL_SemPost(SDL_sem * sem)
201 {
202     if (!sem) {
203         return SDL_SetError("Passed a NULL semaphore");
204     }
205 
206     SDL_LockMutex(sem->count_lock);
207     if (sem->waiters_count > 0) {
208         SDL_CondSignal(sem->count_nonzero);
209     }
210     ++sem->count;
211     SDL_UnlockMutex(sem->count_lock);
212 
213     return 0;
214 }
215 
216 #endif /* SDL_THREADS_DISABLED */
217 /* vi: set ts=4 sw=4 expandtab: */
218