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 "SDL_thread.h"
25#include "SDL_timer.h"
26
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <sys/types.h>
31#include <sys/ipc.h>
32#include <sys/sem.h>
33#include <errno.h>
34
35#include "SDL_error.h"
36#include "SDL_thread.h"
37
38
39struct SDL_semaphore {
40	int id;
41};
42
43/* Not defined by many operating systems, use configure to detect */
44/*
45#if !defined(HAVE_SEMUN)
46union semun {
47	int val;
48	struct semid_ds *buf;
49	ushort *array;
50};
51#endif
52*/
53
54static struct sembuf op_trywait[2] = {
55	{ 0, -1, (IPC_NOWAIT|SEM_UNDO) } /* Decrement semaphore, no block */
56};
57static struct sembuf op_wait[2] = {
58	{ 0, -1, SEM_UNDO }		/* Decrement semaphore */
59};
60static struct sembuf op_post[1] = {
61	{ 0, 1, (IPC_NOWAIT|SEM_UNDO) }	/* Increment semaphore */
62};
63
64/* Create a blockable semaphore */
65SDL_sem *SDL_CreateSemaphore(Uint32 initial_value)
66{
67	extern int _creating_thread_lock;	/* SDL_threads.c */
68	SDL_sem *sem;
69	union semun init;
70
71	sem = (SDL_sem *)SDL_malloc(sizeof(*sem));
72	if ( sem == NULL ) {
73		SDL_OutOfMemory();
74		return(NULL);
75	}
76	sem->id = semget(IPC_PRIVATE, 1, (0600|IPC_CREAT));
77	if ( sem->id < 0 ) {
78		SDL_SetError("Couldn't create semaphore");
79		SDL_free(sem);
80		return(NULL);
81	}
82	init.val = initial_value;	/* Initialize semaphore */
83	semctl(sem->id, 0, SETVAL, init);
84	return(sem);
85}
86
87void SDL_DestroySemaphore(SDL_sem *sem)
88{
89	if ( sem ) {
90#ifdef __IRIX__
91		semctl(sem->id, 0, IPC_RMID);
92#else
93		union semun dummy;
94		dummy.val = 0;
95		semctl(sem->id, 0, IPC_RMID, dummy);
96#endif
97		SDL_free(sem);
98	}
99}
100
101int SDL_SemTryWait(SDL_sem *sem)
102{
103	int retval;
104
105	if ( ! sem ) {
106		SDL_SetError("Passed a NULL semaphore");
107		return -1;
108	}
109
110	retval = 0;
111  tryagain:
112	if ( semop(sem->id, op_trywait, 1) < 0 ) {
113		if ( errno == EINTR ) {
114			goto tryagain;
115		}
116		retval = SDL_MUTEX_TIMEDOUT;
117	}
118	return retval;
119}
120
121int SDL_SemWait(SDL_sem *sem)
122{
123	int retval;
124
125	if ( ! sem ) {
126		SDL_SetError("Passed a NULL semaphore");
127		return -1;
128	}
129
130	retval = 0;
131  tryagain:
132	if ( semop(sem->id, op_wait, 1) < 0 ) {
133		if ( errno == EINTR ) {
134			goto tryagain;
135		}
136		SDL_SetError("Semaphore operation error");
137		retval = -1;
138	}
139	return retval;
140}
141
142int SDL_SemWaitTimeout(SDL_sem *sem, Uint32 timeout)
143{
144	int retval;
145
146	if ( ! sem ) {
147		SDL_SetError("Passed a NULL semaphore");
148		return -1;
149	}
150
151	/* Try the easy cases first */
152	if ( timeout == 0 ) {
153		return SDL_SemTryWait(sem);
154	}
155	if ( timeout == SDL_MUTEX_MAXWAIT ) {
156		return SDL_SemWait(sem);
157	}
158
159	/* Ack!  We have to busy wait... */
160	timeout += SDL_GetTicks();
161	do {
162		retval = SDL_SemTryWait(sem);
163		if ( retval == 0 ) {
164			break;
165		}
166		SDL_Delay(1);
167	} while ( SDL_GetTicks() < timeout );
168
169	return retval;
170}
171
172Uint32 SDL_SemValue(SDL_sem *sem)
173{
174	int semval;
175	Uint32 value;
176
177	value = 0;
178	if ( sem ) {
179	  tryagain:
180#ifdef __IRIX__
181		semval = semctl(sem->id, 0, GETVAL);
182#else
183		{
184		union semun arg;
185		arg.val = 0;
186		semval = semctl(sem->id, 0, GETVAL, arg);
187		}
188#endif
189		if ( semval < 0 ) {
190			if ( errno == EINTR ) {
191				goto tryagain;
192			}
193		} else {
194			value = (Uint32)semval;
195		}
196	}
197	return value;
198}
199
200int SDL_SemPost(SDL_sem *sem)
201{
202	int retval;
203
204	if ( ! sem ) {
205		SDL_SetError("Passed a NULL semaphore");
206		return -1;
207	}
208
209	retval = 0;
210  tryagain:
211	if ( semop(sem->id, op_post, 1) < 0 ) {
212		if ( errno == EINTR ) {
213			goto tryagain;
214		}
215		SDL_SetError("Semaphore operation error");
216		retval = -1;
217	}
218	return retval;
219}
220