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