1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2021 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 | #include <errno.h> |
24 | #include <pthread.h> |
25 | #include <semaphore.h> |
26 | #include <sys/time.h> |
27 | #include <time.h> |
28 | |
29 | #include "SDL_thread.h" |
30 | #include "SDL_timer.h" |
31 | |
32 | /* Wrapper around POSIX 1003.1b semaphores */ |
33 | |
34 | #if defined(__MACOSX__) || defined(__IPHONEOS__) |
35 | /* Mac OS X doesn't support sem_getvalue() as of version 10.4 */ |
36 | #include "../generic/SDL_syssem.c" |
37 | #else |
38 | |
39 | struct SDL_semaphore |
40 | { |
41 | sem_t sem; |
42 | }; |
43 | |
44 | /* Create a semaphore, initialized with value */ |
45 | SDL_sem * |
46 | SDL_CreateSemaphore(Uint32 initial_value) |
47 | { |
48 | SDL_sem *sem = (SDL_sem *) SDL_malloc(sizeof(SDL_sem)); |
49 | if (sem) { |
50 | if (sem_init(&sem->sem, 0, initial_value) < 0) { |
51 | SDL_SetError("sem_init() failed" ); |
52 | SDL_free(sem); |
53 | sem = NULL; |
54 | } |
55 | } else { |
56 | SDL_OutOfMemory(); |
57 | } |
58 | return sem; |
59 | } |
60 | |
61 | void |
62 | SDL_DestroySemaphore(SDL_sem * sem) |
63 | { |
64 | if (sem) { |
65 | sem_destroy(&sem->sem); |
66 | SDL_free(sem); |
67 | } |
68 | } |
69 | |
70 | int |
71 | SDL_SemTryWait(SDL_sem * sem) |
72 | { |
73 | int retval; |
74 | |
75 | if (!sem) { |
76 | return SDL_SetError("Passed a NULL semaphore" ); |
77 | } |
78 | retval = SDL_MUTEX_TIMEDOUT; |
79 | if (sem_trywait(&sem->sem) == 0) { |
80 | retval = 0; |
81 | } |
82 | return retval; |
83 | } |
84 | |
85 | int |
86 | SDL_SemWait(SDL_sem * sem) |
87 | { |
88 | int retval; |
89 | |
90 | if (!sem) { |
91 | return SDL_SetError("Passed a NULL semaphore" ); |
92 | } |
93 | |
94 | do { |
95 | retval = sem_wait(&sem->sem); |
96 | } while (retval < 0 && errno == EINTR); |
97 | |
98 | if (retval < 0) { |
99 | retval = SDL_SetError("sem_wait() failed" ); |
100 | } |
101 | return retval; |
102 | } |
103 | |
104 | int |
105 | SDL_SemWaitTimeout(SDL_sem * sem, Uint32 timeout) |
106 | { |
107 | int retval; |
108 | #ifdef HAVE_SEM_TIMEDWAIT |
109 | #ifndef HAVE_CLOCK_GETTIME |
110 | struct timeval now; |
111 | #endif |
112 | struct timespec ts_timeout; |
113 | #else |
114 | Uint32 end; |
115 | #endif |
116 | |
117 | if (!sem) { |
118 | return SDL_SetError("Passed a NULL semaphore" ); |
119 | } |
120 | |
121 | /* Try the easy cases first */ |
122 | if (timeout == 0) { |
123 | return SDL_SemTryWait(sem); |
124 | } |
125 | if (timeout == SDL_MUTEX_MAXWAIT) { |
126 | return SDL_SemWait(sem); |
127 | } |
128 | |
129 | #ifdef HAVE_SEM_TIMEDWAIT |
130 | /* Setup the timeout. sem_timedwait doesn't wait for |
131 | * a lapse of time, but until we reach a certain time. |
132 | * This time is now plus the timeout. |
133 | */ |
134 | #ifdef HAVE_CLOCK_GETTIME |
135 | clock_gettime(CLOCK_REALTIME, &ts_timeout); |
136 | |
137 | /* Add our timeout to current time */ |
138 | ts_timeout.tv_nsec += (timeout % 1000) * 1000000; |
139 | ts_timeout.tv_sec += timeout / 1000; |
140 | #else |
141 | gettimeofday(&now, NULL); |
142 | |
143 | /* Add our timeout to current time */ |
144 | ts_timeout.tv_sec = now.tv_sec + (timeout / 1000); |
145 | ts_timeout.tv_nsec = (now.tv_usec + (timeout % 1000) * 1000) * 1000; |
146 | #endif |
147 | |
148 | /* Wrap the second if needed */ |
149 | if (ts_timeout.tv_nsec > 1000000000) { |
150 | ts_timeout.tv_sec += 1; |
151 | ts_timeout.tv_nsec -= 1000000000; |
152 | } |
153 | |
154 | /* Wait. */ |
155 | do { |
156 | retval = sem_timedwait(&sem->sem, &ts_timeout); |
157 | } while (retval < 0 && errno == EINTR); |
158 | |
159 | if (retval < 0) { |
160 | if (errno == ETIMEDOUT) { |
161 | retval = SDL_MUTEX_TIMEDOUT; |
162 | } else { |
163 | SDL_SetError("sem_timedwait returned an error: %s" , strerror(errno)); |
164 | } |
165 | } |
166 | #else |
167 | end = SDL_GetTicks() + timeout; |
168 | while ((retval = SDL_SemTryWait(sem)) == SDL_MUTEX_TIMEDOUT) { |
169 | if (SDL_TICKS_PASSED(SDL_GetTicks(), end)) { |
170 | break; |
171 | } |
172 | SDL_Delay(1); |
173 | } |
174 | #endif /* HAVE_SEM_TIMEDWAIT */ |
175 | |
176 | return retval; |
177 | } |
178 | |
179 | Uint32 |
180 | SDL_SemValue(SDL_sem * sem) |
181 | { |
182 | int ret = 0; |
183 | if (sem) { |
184 | sem_getvalue(&sem->sem, &ret); |
185 | if (ret < 0) { |
186 | ret = 0; |
187 | } |
188 | } |
189 | return (Uint32) ret; |
190 | } |
191 | |
192 | int |
193 | SDL_SemPost(SDL_sem * sem) |
194 | { |
195 | int retval; |
196 | |
197 | if (!sem) { |
198 | return SDL_SetError("Passed a NULL semaphore" ); |
199 | } |
200 | |
201 | retval = sem_post(&sem->sem); |
202 | if (retval < 0) { |
203 | SDL_SetError("sem_post() failed" ); |
204 | } |
205 | return retval; |
206 | } |
207 | |
208 | #endif /* __MACOSX__ */ |
209 | /* vi: set ts=4 sw=4 expandtab: */ |
210 | |