1 /*
2 * Copyright (c) 2006-2021, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2010-10-26 Bernard the first version
9 */
10
11 #include <rtthread.h>
12 #include <string.h>
13 #include <fcntl.h>
14 #include <sys/errno.h>
15 #include "semaphore.h"
16
17 static sem_t *posix_sem_list = RT_NULL;
18 static struct rt_semaphore posix_sem_lock;
19
20 /* initialize posix semaphore */
posix_sem_system_init(void)21 static int posix_sem_system_init(void)
22 {
23 rt_sem_init(&posix_sem_lock, "psem", 1, RT_IPC_FLAG_FIFO);
24 return 0;
25 }
26 INIT_COMPONENT_EXPORT(posix_sem_system_init);
27
28 /**
29 * @brief Inserts a semaphore into the linked list of semaphores.
30 * @param psem Pointer to the semaphore structure to be inserted.
31 *
32 * @note This function inserts the specified semaphore into a linked list of semaphores.
33 * The newly inserted semaphore becomes the head of the list.
34 * It updates the 'next' pointer of the semaphore structure to link it to the
35 * current head of the list and then sets the head of the list to point to the
36 * newly inserted semaphore.
37 */
posix_sem_insert(sem_t * psem)38 rt_inline void posix_sem_insert(sem_t *psem)
39 {
40 psem->next = posix_sem_list;
41 posix_sem_list = psem;
42 }
43
44 /**
45 * @brief Deletes a semaphore from the linked list of semaphores.
46 * @param psem Pointer to the semaphore structure to be deleted.
47 *
48 * @note This function deletes the specified semaphore from a linked list of semaphores.
49 * If the semaphore to be deleted is the head of the list, it updates the head of the list
50 * to point to the next semaphore. Otherwise, it traverses the list to find the semaphore
51 * to be deleted and updates the 'next' pointer of the preceding semaphore to skip over
52 * the semaphore to be deleted.
53 * After deleting the semaphore, it also deletes the underlying RT-Thread semaphore if it exists
54 * and frees the memory associated with the semaphore structure if it's not unnamed.
55 */
posix_sem_delete(sem_t * psem)56 static void posix_sem_delete(sem_t *psem)
57 {
58 sem_t *iter;
59 if (posix_sem_list == psem)
60 {
61 posix_sem_list = psem->next;
62
63 rt_sem_delete(psem->sem);
64
65 if(psem->unamed == 0)
66 rt_free(psem);
67
68 return;
69 }
70 for (iter = posix_sem_list; iter->next != RT_NULL; iter = iter->next)
71 {
72 if (iter->next == psem)
73 {
74 /* delete this mq */
75 if (psem->next != RT_NULL)
76 iter->next = psem->next;
77 else
78 iter->next = RT_NULL;
79
80 /* delete RT-Thread mqueue */
81 rt_sem_delete(psem->sem);
82
83 if(psem->unamed == 0)
84 rt_free(psem);
85
86 return ;
87 }
88 }
89 }
90
91 /**
92 * @brief Finds a semaphore by name in the linked list of semaphores.
93 * @param name Pointer to the name of the semaphore to be found.
94 * @return Pointer to the semaphore structure if found; otherwise, RT_NULL.
95 *
96 * @note This function searches for a semaphore with the specified name in the linked list of semaphores.
97 * It iterates through the list and compares the name of each semaphore with the given name.
98 * If a semaphore with a matching name is found, a pointer to its structure is returned.
99 * Otherwise, RT_NULL is returned to indicate that no semaphore with the given name was found.
100 */
posix_sem_find(const char * name)101 static sem_t *posix_sem_find(const char* name)
102 {
103 sem_t *iter;
104 rt_object_t object;
105
106 for (iter = posix_sem_list; iter != RT_NULL; iter = iter->next)
107 {
108 object = (rt_object_t)iter->sem;
109
110 if (strncmp(object->name, name, RT_NAME_MAX) == 0)
111 {
112 return iter;
113 }
114 }
115
116 return RT_NULL;
117 }
118
119 /**
120 * @brief Closes a POSIX semaphore.
121 * @param sem Pointer to the semaphore to be closed.
122 * @return Upon successful completion, returns 0; otherwise, returns -1 and sets errno to indicate the error.
123 *
124 * @note This function decreases the reference count of the specified semaphore.
125 * If the reference count reaches zero, the semaphore is removed from the list
126 * of POSIX semaphores if it was unlinked. The memory associated with the semaphore
127 * is not immediately freed; instead, it will be freed when the last reference to
128 * the semaphore is released.
129 */
sem_close(sem_t * sem)130 int sem_close(sem_t *sem)
131 {
132 if (sem == RT_NULL)
133 {
134 rt_set_errno(EINVAL);
135
136 return -1;
137 }
138
139 /* lock posix semaphore list */
140 rt_sem_take(&posix_sem_lock, RT_WAITING_FOREVER);
141 sem->refcount --;
142 if (sem->refcount == 0)
143 {
144 /* delete from posix semaphore list */
145 if (sem->unlinked)
146 posix_sem_delete(sem);
147 sem = RT_NULL;
148 }
149 rt_sem_release(&posix_sem_lock);
150
151 return 0;
152 }
153 RTM_EXPORT(sem_close);
154
155 /**
156 * @brief Destroys a POSIX semaphore.
157 * @param sem Pointer to the semaphore to be destroyed.
158 * @return Upon successful completion, returns 0; otherwise, returns -1 and sets errno to indicate the error.
159 *
160 * @note This function destroys an unnamed POSIX semaphore.
161 * It first checks if the semaphore pointer is valid and if the semaphore is unnamed.
162 * If the semaphore is still in use (i.e., there are threads waiting on it),
163 * the function returns with an error code (EBUSY) without destroying the semaphore.
164 * Otherwise, it removes the semaphore from the list of POSIX semaphores and frees
165 * the memory associated with the semaphore structure.
166 */
sem_destroy(sem_t * sem)167 int sem_destroy(sem_t *sem)
168 {
169 if ((!sem) || !(sem->unamed))
170 {
171 rt_set_errno(EINVAL);
172
173 return -1;
174 }
175
176 /* lock posix semaphore list */
177 rt_sem_take(&posix_sem_lock, RT_WAITING_FOREVER);
178 if(rt_list_len(&sem->sem->parent.suspend_thread) != 0)
179 {
180 rt_sem_release(&posix_sem_lock);
181 rt_set_errno(EBUSY);
182
183 return -1;
184 }
185
186 /* destroy an unamed posix semaphore */
187 posix_sem_delete(sem);
188 rt_sem_release(&posix_sem_lock);
189
190 return 0;
191 }
192 RTM_EXPORT(sem_destroy);
193
194 /**
195 * @brief Unlinks a named POSIX semaphore.
196 * @param name Pointer to the name of the semaphore to be unlinked.
197 * @return Upon successful completion, returns 0; otherwise, returns -1 and sets errno to indicate the error.
198 *
199 * @note This function unlinks a named POSIX semaphore identified by the given name.
200 * It first searches for the semaphore with the specified name in the list of
201 * POSIX semaphores. If the semaphore is found, it marks the semaphore as unlinked.
202 * If the reference count of the semaphore is zero, indicating that no threads are
203 * currently using the semaphore, it removes the semaphore from the list and frees
204 * the associated memory. Otherwise, the semaphore is not immediately removed; it
205 * will be removed when its reference count reaches zero.
206 */
sem_unlink(const char * name)207 int sem_unlink(const char *name)
208 {
209 sem_t *psem;
210
211 /* lock posix semaphore list */
212 rt_sem_take(&posix_sem_lock, RT_WAITING_FOREVER);
213 psem = posix_sem_find(name);
214 if (psem != RT_NULL)
215 {
216 psem->unlinked = 1;
217 if (psem->refcount == 0)
218 {
219 /* remove this semaphore */
220 posix_sem_delete(psem);
221 }
222 rt_sem_release(&posix_sem_lock);
223
224 return 0;
225 }
226 rt_sem_release(&posix_sem_lock);
227
228 /* no this entry */
229 rt_set_errno(ENOENT);
230
231 return -1;
232 }
233 RTM_EXPORT(sem_unlink);
234
235 /**
236 * @brief Retrieves the value of a POSIX semaphore.
237 * @param sem Pointer to the semaphore.
238 * @param sval Pointer to an integer where the semaphore value will be stored.
239 * @return Upon successful completion, returns 0; otherwise, returns -1 and sets errno to indicate the error.
240 *
241 * @note This function retrieves the current value of the specified POSIX semaphore.
242 * It copies the semaphore value into the memory location pointed to by sval.
243 * If either sem or sval is a null pointer, the function sets errno to EINVAL
244 * to indicate an invalid argument and returns -1.
245 */
sem_getvalue(sem_t * sem,int * sval)246 int sem_getvalue(sem_t *sem, int *sval)
247 {
248 if (!sem || !sval)
249 {
250 rt_set_errno(EINVAL);
251
252 return -1;
253 }
254 *sval = sem->sem->value;
255
256 return 0;
257 }
258 RTM_EXPORT(sem_getvalue);
259
260 /**
261 * @brief Initializes a POSIX semaphore.
262 * @param sem Pointer to the semaphore structure to be initialized.
263 * @param pshared Flag indicating whether the semaphore is to be shared between processes.
264 * @param value Initial value of the semaphore.
265 * @return Upon successful completion, returns 0; otherwise, returns -1 and sets errno to indicate the error.
266 *
267 * @note This function initializes a POSIX semaphore with the specified initial value.
268 * If sem is a null pointer, the function sets errno to EINVAL to indicate an invalid argument and returns -1.
269 * The pshared parameter is not used in this implementation, as all semaphores are created as local (unshared).
270 * The value parameter specifies the initial value of the semaphore.
271 * The semaphore is given a unique name using a static counter, and it is created using the RT-Thread semaphore
272 * creation function rt_sem_create(). If memory allocation fails during semaphore creation, errno is set to ENOMEM.
273 * After successful initialization, the semaphore structure is inserted into the linked list of POSIX semaphores.
274 */
sem_init(sem_t * sem,int pshared,unsigned int value)275 int sem_init(sem_t *sem, int pshared, unsigned int value)
276 {
277 char name[RT_NAME_MAX];
278 static rt_uint16_t psem_number = 0;
279
280 if (sem == RT_NULL)
281 {
282 rt_set_errno(EINVAL);
283
284 return -1;
285 }
286
287 rt_snprintf(name, sizeof(name), "psem%02d", psem_number++);
288 sem->sem = rt_sem_create(name, value, RT_IPC_FLAG_FIFO);
289 if (sem->sem == RT_NULL)
290 {
291 rt_set_errno(ENOMEM);
292
293 return -1;
294 }
295
296 /* initialize posix semaphore */
297 sem->refcount = 1;
298 sem->unlinked = 0;
299 sem->unamed = 1;
300 /* lock posix semaphore list */
301 rt_sem_take(&posix_sem_lock, RT_WAITING_FOREVER);
302 posix_sem_insert(sem);
303 rt_sem_release(&posix_sem_lock);
304
305 return 0;
306 }
307 RTM_EXPORT(sem_init);
308
309 /**
310 * @brief Opens or creates a POSIX semaphore.
311 * @param name Pointer to the name of the semaphore.
312 * @param oflag Bitwise OR of flags indicating the operation mode.
313 * @param ... Additional arguments (optional).
314 * @return Upon successful completion, returns a pointer to the semaphore;
315 * otherwise, returns RT_NULL and sets errno to indicate the error.
316 *
317 * @note This function opens or creates a POSIX semaphore specified by the name argument.
318 * If the oflag argument includes O_CREAT, the semaphore is created if it does not already exist.
319 * Additional arguments may include the mode (not used in this implementation) and the initial value of the semaphore.
320 * If the oflag argument includes O_EXCL along with O_CREAT, the function fails if the semaphore already exists.
321 * If memory allocation fails during semaphore creation, errno is set to ENFILE.
322 * After successful creation or opening, the semaphore's reference count is incremented, and it is inserted into
323 * the linked list of POSIX semaphores. If the semaphore already exists and is successfully opened, its reference
324 * count is incremented. If the semaphore cannot be opened or created for any reason, errno is set to indicate the error,
325 * and the function returns RT_NULL.
326 */
sem_open(const char * name,int oflag,...)327 sem_t *sem_open(const char *name, int oflag, ...)
328 {
329 sem_t* sem;
330 va_list arg;
331 mode_t mode;
332 unsigned int value;
333
334 sem = RT_NULL;
335
336 /* lock posix semaphore list */
337 rt_sem_take(&posix_sem_lock, RT_WAITING_FOREVER);
338 if (oflag & O_CREAT)
339 {
340 va_start(arg, oflag);
341 mode = (mode_t) va_arg( arg, unsigned int); mode = mode;
342 value = va_arg( arg, unsigned int);
343 va_end(arg);
344
345 if (oflag & O_EXCL)
346 {
347 if (posix_sem_find(name) != RT_NULL)
348 {
349 rt_set_errno(EEXIST);
350 goto __return;
351 }
352 }
353 sem = (sem_t*) rt_malloc (sizeof(struct posix_sem));
354 if (sem == RT_NULL)
355 {
356 rt_set_errno(ENFILE);
357 goto __return;
358 }
359
360 /* create RT-Thread semaphore */
361 sem->sem = rt_sem_create(name, value, RT_IPC_FLAG_FIFO);
362 if (sem->sem == RT_NULL) /* create failed */
363 {
364 rt_set_errno(ENFILE);
365 goto __return;
366 }
367 /* initialize reference count */
368 sem->refcount = 1;
369 sem->unlinked = 0;
370 sem->unamed = 0;
371
372 /* insert semaphore to posix semaphore list */
373 posix_sem_insert(sem);
374 }
375 else
376 {
377 /* find semaphore */
378 sem = posix_sem_find(name);
379 if (sem != RT_NULL)
380 {
381 sem->refcount ++; /* increase reference count */
382 }
383 else
384 {
385 rt_set_errno(ENOENT);
386 goto __return;
387 }
388 }
389 rt_sem_release(&posix_sem_lock);
390
391 return sem;
392
393 __return:
394 /* release lock */
395 rt_sem_release(&posix_sem_lock);
396
397 /* release allocated memory */
398 if (sem != RT_NULL)
399 {
400 /* delete RT-Thread semaphore */
401 if (sem->sem != RT_NULL)
402 rt_sem_delete(sem->sem);
403 rt_free(sem);
404 }
405
406 return RT_NULL;
407 }
408 RTM_EXPORT(sem_open);
409
410 /**
411 * @brief Posts (increments) a POSIX semaphore.
412 * @param sem Pointer to the semaphore.
413 * @return Upon successful completion, returns 0; otherwise, returns -1 and sets errno to indicate the error.
414 *
415 * @note This function increments the value of the specified POSIX semaphore by one.
416 * If sem is a null pointer, errno is set to EINVAL to indicate an invalid argument, and the function returns -1.
417 * The semaphore is released using the RT-Thread semaphore release function rt_sem_release().
418 * If the semaphore release operation succeeds, the function returns 0; otherwise, errno is set to EINVAL,
419 * indicating an error, and the function returns -1.
420 */
sem_post(sem_t * sem)421 int sem_post(sem_t *sem)
422 {
423 rt_err_t result;
424
425 if (!sem)
426 {
427 rt_set_errno(EINVAL);
428
429 return -1;
430 }
431
432 result = rt_sem_release(sem->sem);
433 if (result == RT_EOK)
434 return 0;
435
436 rt_set_errno(EINVAL);
437
438 return -1;
439 }
440 RTM_EXPORT(sem_post);
441
442 /**
443 * @brief Waits for a POSIX semaphore with a timeout.
444 * @param sem Pointer to the semaphore.
445 * @param abs_timeout Pointer to the absolute timeout value.
446 * @return Upon successful completion, returns 0; otherwise, returns -1 and sets errno to indicate the error.
447 *
448 * @note This function waits for the specified POSIX semaphore to become available within the specified timeout.
449 * If either sem or abs_timeout is a null pointer, the function returns EINVAL, indicating an invalid argument.
450 * The abs_timeout parameter specifies an absolute timeout value based on the CLOCK_REALTIME clock.
451 * The timeout is converted to RT-Thread ticks using the rt_timespec_to_tick() function.
452 * The semaphore is waited upon using the RT-Thread semaphore take function rt_sem_take().
453 * If the semaphore is successfully acquired within the specified timeout, the function returns 0.
454 * If the timeout expires before the semaphore becomes available, errno is set to ETIMEDOUT,
455 * and the function returns -1. If the semaphore wait operation is interrupted by a signal,
456 * errno is set to EINTR, and the function returns -1.
457 */
sem_timedwait(sem_t * sem,const struct timespec * abs_timeout)458 int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout)
459 {
460 rt_err_t result;
461 rt_int32_t tick;
462
463 if (!sem || !abs_timeout)
464 return EINVAL;
465
466 /* calculate os tick */
467 tick = rt_timespec_to_tick(abs_timeout);
468
469 result = rt_sem_take(sem->sem, tick);
470 if (result == -RT_ETIMEOUT)
471 {
472 rt_set_errno(ETIMEDOUT);
473
474 return -1;
475 }
476 if (result == RT_EOK)
477 return 0;
478
479 rt_set_errno(EINTR);
480
481 return -1;
482 }
483 RTM_EXPORT(sem_timedwait);
484
485 /**
486 * @brief Attempts to wait for a POSIX semaphore without blocking.
487 * @param sem Pointer to the semaphore.
488 * @return Upon successful completion, returns 0 if the semaphore was acquired;
489 * otherwise, returns -1 and sets errno to indicate the error.
490 *
491 * @note This function attempts to acquire the specified POSIX semaphore without blocking.
492 * If sem is a null pointer, errno is set to EINVAL to indicate an invalid argument, and the function returns -1.
493 * The semaphore is waited upon using the RT-Thread semaphore take function rt_sem_take() with a timeout of 0,
494 * meaning that the function does not block if the semaphore is not available.
495 * If the semaphore is successfully acquired, the function returns 0. If the semaphore is not available,
496 * errno is set to EAGAIN to indicate that the operation would result in blocking, and the function returns -1.
497 * If the semaphore wait operation is interrupted by a signal, errno is set to EINTR, and the function returns -1.
498 */
sem_trywait(sem_t * sem)499 int sem_trywait(sem_t *sem)
500 {
501 rt_err_t result;
502
503 if (!sem)
504 {
505 rt_set_errno(EINVAL);
506
507 return -1;
508 }
509
510 result = rt_sem_take(sem->sem, 0);
511 if (result == -RT_ETIMEOUT)
512 {
513 rt_set_errno(EAGAIN);
514
515 return -1;
516 }
517 if (result == RT_EOK)
518 return 0;
519
520 rt_set_errno(EINTR);
521
522 return -1;
523 }
524 RTM_EXPORT(sem_trywait);
525
526 /**
527 * @brief Waits indefinitely for a POSIX semaphore to become available.
528 * @param sem Pointer to the semaphore.
529 * @return Upon successful completion, returns 0; otherwise, returns -1 and sets errno to indicate the error.
530 *
531 * @note This function waits indefinitely for the specified POSIX semaphore to become available.
532 * If sem is a null pointer, errno is set to EINVAL to indicate an invalid argument, and the function returns -1.
533 * The semaphore is waited upon using the RT-Thread semaphore take function rt_sem_take() with a timeout
534 * value of RT_WAITING_FOREVER, indicating an infinite wait time.
535 * If the semaphore is successfully acquired, the function returns 0. If the semaphore wait operation is interrupted
536 * by a signal, errno is set to EINTR, and the function returns -1.
537 */
sem_wait(sem_t * sem)538 int sem_wait(sem_t *sem)
539 {
540 rt_err_t result;
541
542 if (!sem)
543 {
544 rt_set_errno(EINVAL);
545
546 return -1;
547 }
548
549 result = rt_sem_take(sem->sem, RT_WAITING_FOREVER);
550 if (result == RT_EOK)
551 return 0;
552
553 rt_set_errno(EINTR);
554
555 return -1;
556 }
557 RTM_EXPORT(sem_wait);
558
559