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