1 /*
2  * Copyright (c) 2018 Intel Corporation
3  * Copyright (c) 2023 Meta
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include "posix_clock.h"
9 
10 #include <errno.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/sys/atomic.h>
13 #include <zephyr/posix/fcntl.h>
14 #include <zephyr/posix/pthread.h>
15 #include <zephyr/posix/semaphore.h>
16 
17 struct nsem_obj {
18 	sys_snode_t snode;
19 	sem_t sem;
20 	int ref_count;
21 	char *name;
22 };
23 
24 /* Initialize the list */
25 static sys_slist_t nsem_list = SYS_SLIST_STATIC_INIT(&nsem_list);
26 
27 static K_MUTEX_DEFINE(nsem_mutex);
28 
nsem_list_lock(void)29 static inline void nsem_list_lock(void)
30 {
31 	__unused int ret = k_mutex_lock(&nsem_mutex, K_FOREVER);
32 
33 	__ASSERT(ret == 0, "nsem_list_lock() failed: %d", ret);
34 }
35 
nsem_list_unlock(void)36 static inline void nsem_list_unlock(void)
37 {
38 	(void)k_mutex_unlock(&nsem_mutex);
39 }
40 
nsem_find(const char * name)41 static struct nsem_obj *nsem_find(const char *name)
42 {
43 	struct nsem_obj *nsem;
44 
45 	SYS_SLIST_FOR_EACH_CONTAINER(&nsem_list, nsem, snode) {
46 		if ((nsem->name != NULL) && (strcmp(nsem->name, name) == 0)) {
47 			return nsem;
48 		}
49 	}
50 
51 	return NULL;
52 }
53 
54 /* Clean up a named semaphore object completely (incl its `name` buffer) */
nsem_cleanup(struct nsem_obj * nsem)55 static void nsem_cleanup(struct nsem_obj *nsem)
56 {
57 	if (nsem != NULL) {
58 		if (nsem->name != NULL) {
59 			k_free(nsem->name);
60 		}
61 		k_free(nsem);
62 	}
63 }
64 
65 /* Remove a named semaphore if it isn't used */
nsem_unref(struct nsem_obj * nsem)66 static void nsem_unref(struct nsem_obj *nsem)
67 {
68 	nsem->ref_count -= 1;
69 	__ASSERT(nsem->ref_count >= 0, "ref_count may not be negative");
70 
71 	if (nsem->ref_count == 0) {
72 		__ASSERT(nsem->name == NULL, "ref_count is 0 but sem is not unlinked");
73 
74 		sys_slist_find_and_remove(&nsem_list, (sys_snode_t *) nsem);
75 
76 		/* Free nsem */
77 		nsem_cleanup(nsem);
78 	}
79 }
80 
81 /**
82  * @brief Destroy semaphore.
83  *
84  * see IEEE 1003.1
85  */
sem_destroy(sem_t * semaphore)86 int sem_destroy(sem_t *semaphore)
87 {
88 	if (semaphore == NULL) {
89 		errno = EINVAL;
90 		return -1;
91 	}
92 
93 	if (k_sem_count_get(semaphore)) {
94 		errno = EBUSY;
95 		return -1;
96 	}
97 
98 	k_sem_reset(semaphore);
99 	return 0;
100 }
101 
102 /**
103  * @brief Get value of semaphore.
104  *
105  * See IEEE 1003.1
106  */
sem_getvalue(sem_t * semaphore,int * value)107 int sem_getvalue(sem_t *semaphore, int *value)
108 {
109 	if (semaphore == NULL) {
110 		errno = EINVAL;
111 		return -1;
112 	}
113 
114 	*value = (int) k_sem_count_get(semaphore);
115 
116 	return 0;
117 }
118 /**
119  * @brief Initialize semaphore.
120  *
121  * See IEEE 1003.1
122  */
sem_init(sem_t * semaphore,int pshared,unsigned int value)123 int sem_init(sem_t *semaphore, int pshared, unsigned int value)
124 {
125 	if (value > CONFIG_POSIX_SEM_VALUE_MAX) {
126 		errno = EINVAL;
127 		return -1;
128 	}
129 
130 	/*
131 	 * Zephyr has no concept of process, so only thread shared
132 	 * semaphore makes sense in here.
133 	 */
134 	__ASSERT(pshared == 0, "pshared should be 0");
135 
136 	k_sem_init(semaphore, value, CONFIG_POSIX_SEM_VALUE_MAX);
137 
138 	return 0;
139 }
140 
141 /**
142  * @brief Unlock a semaphore.
143  *
144  * See IEEE 1003.1
145  */
sem_post(sem_t * semaphore)146 int sem_post(sem_t *semaphore)
147 {
148 	if (semaphore == NULL) {
149 		errno = EINVAL;
150 		return -1;
151 	}
152 
153 	k_sem_give(semaphore);
154 	return 0;
155 }
156 
157 /**
158  * @brief Try time limited locking a semaphore.
159  *
160  * See IEEE 1003.1
161  */
sem_timedwait(sem_t * semaphore,struct timespec * abstime)162 int sem_timedwait(sem_t *semaphore, struct timespec *abstime)
163 {
164 	if ((abstime == NULL) || !timespec_is_valid(abstime)) {
165 		errno = EINVAL;
166 		return -1;
167 	}
168 
169 	if (k_sem_take(semaphore, K_MSEC(timespec_to_timeoutms(CLOCK_REALTIME, abstime)))) {
170 		errno = ETIMEDOUT;
171 		return -1;
172 	}
173 
174 	return 0;
175 }
176 
177 /**
178  * @brief Lock a semaphore if not taken.
179  *
180  * See IEEE 1003.1
181  */
sem_trywait(sem_t * semaphore)182 int sem_trywait(sem_t *semaphore)
183 {
184 	if (k_sem_take(semaphore, K_NO_WAIT) == -EBUSY) {
185 		errno = EAGAIN;
186 		return -1;
187 	} else {
188 		return 0;
189 	}
190 }
191 
192 /**
193  * @brief Lock a semaphore.
194  *
195  * See IEEE 1003.1
196  */
sem_wait(sem_t * semaphore)197 int sem_wait(sem_t *semaphore)
198 {
199 	/* With K_FOREVER, may return only success. */
200 	(void)k_sem_take(semaphore, K_FOREVER);
201 	return 0;
202 }
203 
sem_open(const char * name,int oflags,...)204 sem_t *sem_open(const char *name, int oflags, ...)
205 {
206 	va_list va;
207 	mode_t mode;
208 	unsigned int value;
209 	struct nsem_obj *nsem = NULL;
210 	size_t namelen;
211 
212 	va_start(va, oflags);
213 	BUILD_ASSERT(sizeof(mode_t) <= sizeof(int));
214 	mode = va_arg(va, int);
215 	value = va_arg(va, unsigned int);
216 	va_end(va);
217 
218 	if (value > CONFIG_POSIX_SEM_VALUE_MAX) {
219 		errno = EINVAL;
220 		return (sem_t *)SEM_FAILED;
221 	}
222 
223 	if (name == NULL) {
224 		errno = EINVAL;
225 		return (sem_t *)SEM_FAILED;
226 	}
227 
228 	namelen = strlen(name);
229 	if ((namelen + 1) > CONFIG_POSIX_SEM_NAMELEN_MAX) {
230 		errno = ENAMETOOLONG;
231 		return (sem_t *)SEM_FAILED;
232 	}
233 
234 	/* Lock before checking to make sure that the call is atomic */
235 	nsem_list_lock();
236 
237 	/* Check if the named semaphore exists */
238 	nsem = nsem_find(name);
239 
240 	if (nsem != NULL) { /* Named semaphore exists */
241 		if (((oflags & O_CREAT) != 0) && ((oflags & O_EXCL) != 0)) {
242 			errno = EEXIST;
243 			goto error_unlock;
244 		}
245 
246 		__ASSERT_NO_MSG(nsem->ref_count != INT_MAX);
247 		nsem->ref_count++;
248 		goto unlock;
249 	}
250 
251 	/* Named semaphore doesn't exist, try to create new one */
252 
253 	if ((oflags & O_CREAT) == 0) {
254 		errno = ENOENT;
255 		goto error_unlock;
256 	}
257 
258 	nsem = k_calloc(1, sizeof(struct nsem_obj));
259 	if (nsem == NULL) {
260 		errno = ENOSPC;
261 		goto error_unlock;
262 	}
263 
264 	/* goto `cleanup_error_unlock` past this point to avoid memory leak */
265 
266 	nsem->name = k_calloc(namelen + 1, sizeof(uint8_t));
267 	if (nsem->name == NULL) {
268 		errno = ENOSPC;
269 		goto cleanup_error_unlock;
270 	}
271 
272 	strcpy(nsem->name, name);
273 
274 	/* 1 for this open instance, +1 for the linked name */
275 	nsem->ref_count = 2;
276 
277 	(void)k_sem_init(&nsem->sem, value, CONFIG_POSIX_SEM_VALUE_MAX);
278 
279 	sys_slist_append(&nsem_list, (sys_snode_t *)&(nsem->snode));
280 
281 	goto unlock;
282 
283 cleanup_error_unlock:
284 	nsem_cleanup(nsem);
285 
286 error_unlock:
287 	nsem = NULL;
288 
289 unlock:
290 	nsem_list_unlock();
291 	return nsem == NULL ? SEM_FAILED : &nsem->sem;
292 }
293 
sem_unlink(const char * name)294 int sem_unlink(const char *name)
295 {
296 	int ret = 0;
297 	struct nsem_obj *nsem;
298 
299 	if (name == NULL) {
300 		errno = EINVAL;
301 		return -1;
302 	}
303 
304 	if ((strlen(name) + 1)  > CONFIG_POSIX_SEM_NAMELEN_MAX) {
305 		errno = ENAMETOOLONG;
306 		return -1;
307 	}
308 
309 	nsem_list_lock();
310 
311 	/* Check if queue already exists */
312 	nsem = nsem_find(name);
313 	if (nsem == NULL) {
314 		ret = -1;
315 		errno = ENOENT;
316 		goto unlock;
317 	}
318 
319 	k_free(nsem->name);
320 	nsem->name = NULL;
321 	nsem_unref(nsem);
322 
323 unlock:
324 	nsem_list_unlock();
325 	return ret;
326 }
327 
sem_close(sem_t * sem)328 int sem_close(sem_t *sem)
329 {
330 	struct nsem_obj *nsem = CONTAINER_OF(sem, struct nsem_obj, sem);
331 
332 	if (sem == NULL) {
333 		errno = EINVAL;
334 		return -1;
335 	}
336 
337 	nsem_list_lock();
338 	nsem_unref(nsem);
339 	nsem_list_unlock();
340 	return 0;
341 }
342 
343 #ifdef CONFIG_ZTEST
344 /* Used by ztest to get the ref count of a named semaphore */
nsem_get_ref_count(sem_t * sem)345 int nsem_get_ref_count(sem_t *sem)
346 {
347 	struct nsem_obj *nsem = CONTAINER_OF(sem, struct nsem_obj, sem);
348 	int ref_count;
349 
350 	__ASSERT_NO_MSG(sem != NULL);
351 	__ASSERT_NO_MSG(nsem != NULL);
352 
353 	nsem_list_lock();
354 	ref_count = nsem->ref_count;
355 	nsem_list_unlock();
356 
357 	return ref_count;
358 }
359 
360 /* Used by ztest to get the length of the named semaphore */
nsem_get_list_len(void)361 size_t nsem_get_list_len(void)
362 {
363 	size_t len;
364 
365 	nsem_list_lock();
366 	len = sys_slist_len(&nsem_list);
367 	nsem_list_unlock();
368 
369 	return len;
370 }
371 #endif
372