xref: /haiku/src/system/libroot/posix/semaphore.cpp (revision a5a3b2d9a3d95cbae71eaf371708c73a1780ac0d)
1  /*
2   * Copyright 2015, Hamish Morrison, hamishm53@gmail.com.
3   * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
4   * Distributed under the terms of the MIT License.
5   */
6  
7  #include <semaphore.h>
8  
9  #include <errno.h>
10  #include <fcntl.h>
11  #include <stdarg.h>
12  #include <stdlib.h>
13  
14  #include <OS.h>
15  
16  #include <AutoDeleter.h>
17  #include <errno_private.h>
18  #include <posix/realtime_sem_defs.h>
19  #include <syscall_utils.h>
20  #include <syscalls.h>
21  #include <user_mutex_defs.h>
22  
23  
24  #define SEM_TYPE_NAMED		1
25  #define SEM_TYPE_UNNAMED	2
26  
27  
28  static int32
29  atomic_add_if_greater(int32* value, int32 amount, int32 testValue)
30  {
31  	int32 current = atomic_get(value);
32  	while (current > testValue) {
33  		int32 old = atomic_test_and_set(value, current + amount, current);
34  		if (old == current)
35  			return old;
36  		current = old;
37  	}
38  	return current;
39  }
40  
41  
42  sem_t*
43  sem_open(const char* name, int openFlags,...)
44  {
45  	if (name == NULL) {
46  		__set_errno(B_BAD_VALUE);
47  		return SEM_FAILED;
48  	}
49  
50  	// get the mode and semaphore count parameters, if O_CREAT is specified
51  	mode_t mode = 0;
52  	unsigned semCount = 0;
53  
54  	if ((openFlags & O_CREAT) != 0) {
55  		va_list args;
56  		va_start(args, openFlags);
57  		mode = va_arg(args, mode_t);
58  		semCount = va_arg(args, unsigned);
59  		va_end(args);
60  	} else {
61  		// clear O_EXCL, if O_CREAT is not given
62  		openFlags &= ~O_EXCL;
63  	}
64  
65  	// Allocate a sem_t structure -- we don't know, whether this is the first
66  	// call of this process to open the semaphore. If it is, we will keep the
67  	// structure, otherwise we will delete it later.
68  	sem_t* sem = (sem_t*)malloc(sizeof(sem_t));
69  	if (sem == NULL) {
70  		__set_errno(B_NO_MEMORY);
71  		return SEM_FAILED;
72  	}
73  
74  	sem->type = SEM_TYPE_NAMED;
75  	MemoryDeleter semDeleter(sem);
76  
77  	// ask the kernel to open the semaphore
78  	sem_t* usedSem;
79  	status_t error = _kern_realtime_sem_open(name, openFlags, mode, semCount,
80  		sem, &usedSem);
81  	if (error != B_OK) {
82  		__set_errno(error);
83  		return SEM_FAILED;
84  	}
85  
86  	if (usedSem == sem)
87  		semDeleter.Detach();
88  
89  	return usedSem;
90  }
91  
92  
93  int
94  sem_close(sem_t* semaphore)
95  {
96  	sem_t* deleteSem = NULL;
97  	status_t error = _kern_realtime_sem_close(semaphore->u.named_sem_id,
98  		&deleteSem);
99  	if (error == B_OK)
100  		free(deleteSem);
101  
102  	RETURN_AND_SET_ERRNO(error);
103  }
104  
105  
106  int
107  sem_unlink(const char* name)
108  {
109  	RETURN_AND_SET_ERRNO(_kern_realtime_sem_unlink(name));
110  }
111  
112  
113  int
114  sem_init(sem_t* semaphore, int shared, unsigned value)
115  {
116  	semaphore->type = SEM_TYPE_UNNAMED;
117  	semaphore->u.unnamed_sem = value;
118  	return 0;
119  }
120  
121  
122  int
123  sem_destroy(sem_t* semaphore)
124  {
125  	if (semaphore->type != SEM_TYPE_UNNAMED)
126  		RETURN_AND_SET_ERRNO(EINVAL);
127  
128  	return 0;
129  }
130  
131  
132  static int
133  unnamed_sem_post(sem_t* semaphore) {
134  	int32* sem = (int32*)&semaphore->u.unnamed_sem;
135  	int32 oldValue = atomic_add_if_greater(sem, 1, -1);
136  	if (oldValue > -1)
137  		return 0;
138  
139  	return _kern_mutex_sem_release(sem);
140  }
141  
142  
143  static int
144  unnamed_sem_trywait(sem_t* semaphore) {
145  	int32* sem = (int32*)&semaphore->u.unnamed_sem;
146  	int32 oldValue = atomic_add_if_greater(sem, -1, 0);
147  	if (oldValue > 0)
148  		return 0;
149  
150  	return EAGAIN;
151  }
152  
153  
154  static int
155  unnamed_sem_timedwait(sem_t* semaphore, const struct timespec* timeout) {
156  	int32* sem = (int32*)&semaphore->u.unnamed_sem;
157  
158  	bigtime_t timeoutMicros = B_INFINITE_TIMEOUT;
159  	if (timeout != NULL) {
160  		timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000
161  			+ timeout->tv_nsec / 1000;
162  	}
163  
164  	int result = unnamed_sem_trywait(semaphore);
165  	if (result == 0)
166  		return 0;
167  
168  	return _kern_mutex_sem_acquire(sem,	NULL,
169  		timeoutMicros == B_INFINITE_TIMEOUT ? 0 : B_ABSOLUTE_REAL_TIME_TIMEOUT,
170  		timeoutMicros);
171  }
172  
173  
174  int
175  sem_post(sem_t* semaphore)
176  {
177  	status_t error;
178  	if (semaphore->type == SEM_TYPE_NAMED)
179  		error = _kern_realtime_sem_post(semaphore->u.named_sem_id);
180  	else
181  		error = unnamed_sem_post(semaphore);
182  
183  	RETURN_AND_SET_ERRNO(error);
184  }
185  
186  
187  static int
188  named_sem_timedwait(sem_t* semaphore, const struct timespec* timeout)
189  {
190  	if (timeout != NULL
191  		&& (timeout->tv_nsec < 0 || timeout->tv_nsec >= 1000000000)) {
192  		status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id, 0);
193  		if (err == B_WOULD_BLOCK)
194  			err = EINVAL;
195  		// do nothing, return err as it is.
196  		return err;
197  	}
198  
199  	bigtime_t timeoutMicros = B_INFINITE_TIMEOUT;
200  	if (timeout != NULL) {
201  		timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000
202  			+ timeout->tv_nsec / 1000;
203  	}
204  	status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id,
205  		timeoutMicros);
206  	if (err == B_WOULD_BLOCK)
207  		err = ETIMEDOUT;
208  
209  	return err;
210  }
211  
212  
213  int
214  sem_trywait(sem_t* semaphore)
215  {
216  	status_t error;
217  	if (semaphore->type == SEM_TYPE_NAMED)
218  		error = _kern_realtime_sem_wait(semaphore->u.named_sem_id, 0);
219  	else
220  		error = unnamed_sem_trywait(semaphore);
221  
222  	RETURN_AND_SET_ERRNO(error);
223  }
224  
225  
226  int
227  sem_wait(sem_t* semaphore)
228  {
229  	status_t error;
230  	if (semaphore->type == SEM_TYPE_NAMED)
231  		error = named_sem_timedwait(semaphore, NULL);
232  	else
233  		error = unnamed_sem_timedwait(semaphore, NULL);
234  
235  	RETURN_AND_SET_ERRNO_TEST_CANCEL(error);
236  }
237  
238  
239  int
240  sem_timedwait(sem_t* semaphore, const struct timespec* timeout)
241  {
242  	status_t error;
243  	if (semaphore->type == SEM_TYPE_NAMED)
244  		error = named_sem_timedwait(semaphore, timeout);
245  	else
246  		error = unnamed_sem_timedwait(semaphore, timeout);
247  
248  	RETURN_AND_SET_ERRNO_TEST_CANCEL(error);
249  }
250  
251  
252  int
253  sem_getvalue(sem_t* semaphore, int* value)
254  {
255  	if (semaphore->type == SEM_TYPE_NAMED) {
256  		RETURN_AND_SET_ERRNO(_kern_realtime_sem_get_value(
257  			semaphore->u.named_sem_id, value));
258  	} else {
259  		*value = semaphore->u.unnamed_sem < 0 ? 0 : semaphore->u.unnamed_sem;
260  		return 0;
261  	}
262  }
263