Imported Upstream version 2.0.14
[platform/upstream/SDL.git] / src / thread / pthread / SDL_syssem.c
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 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: */