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