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
39struct SDL_semaphore
40{
41 sem_t sem;
42};
43
44/* Create a semaphore, initialized with value */
45SDL_sem *
46SDL_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
61void
62SDL_DestroySemaphore(SDL_sem * sem)
63{
64 if (sem) {
65 sem_destroy(&sem->sem);
66 SDL_free(sem);
67 }
68}
69
70int
71SDL_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
85int
86SDL_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
104int
105SDL_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
179Uint32
180SDL_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
192int
193SDL_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