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, ¤t) < 0) {
174 return -1;
175 }
176
177 abstime_ms = (int64_t)_ts_to_ms(abstime);
178 current_ms = (int64_t)_ts_to_ms(¤t);
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