📄 sem.c
字号:
/** * @file * Semaphore functions. * @author Paolo Mantegazza * * @note Copyright (C) 1999-2003 Paolo Mantegazza * <mantegazza@aero.polimi.it> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * @ingroup sem *//** * @ingroup sched * @defgroup sem Semaphore functions * *@{*/#include <rtai_schedcore.h>#include <rtai_sem.h>#include <rtai_rwl.h>#include <rtai_spl.h>MODULE_LICENSE("GPL");/* +++++++++++++++++++++ ALL SEMAPHORES TYPES SUPPORT +++++++++++++++++++++++ *//** * @anchor rt_typed_sem_init * @brief Initialize a specifically typed (counting, binary, resource) * semaphore * * rt_typed_sem_init initializes a semaphore @e sem of type @e type. A * semaphore can be used for communication and synchronization among * real time tasks. Negative value of a semaphore shows how many tasks * are blocked on the semaphore queue, waiting to be awaken by calls * to rt_sem_signal. * * @param sem must point to an allocated SEM structure. * * @param value is the initial value of the semaphore, always set to 1 * for a resource semaphore. * * @param type is the semaphore type and queuing policy. It can be an OR * a semaphore kind: CNT_SEM for counting semaphores, BIN_SEM for binary * semaphores, RES_SEM for resource semaphores; and queuing policy: * FIFO_Q, PRIO_Q for a fifo and priority queueing respectively. * Resource semaphores will enforce a PRIO_Q policy anyhow. * * Counting semaphores can register up to 0xFFFE events. Binary * semaphores do not count signalled events, their count will never * exceed 1 whatever number of events is signaled to them. Resource * semaphores are special binary semaphores suitable for managing * resources. The task that acquires a resource semaphore becomes its * owner, also called resource owner, since it is the only one capable * of manipulating the resource the semaphore is protecting. The owner * has its priority increased to that of any task blocking on a wait * to the semaphore. Such a feature, called priority inheritance, * ensures that a high priority task is never slaved to a lower * priority one, thus allowing to avoid any deadlock due to priority * inversion. Resource semaphores can be recursed, i.e. their task * owner is not blocked by nested waits placed on an owned * resource. The owner must insure that it will signal the semaphore, * in reversed order, as many times as he waited on it. Note that that * full priority inheritance is supported both for resource semaphores * and inter task messages, for a singly owned resource. Instead it * becomes an adaptive priority ceiling when a task owns multiple * resources, including messages sent to him. In such a case in fact * its priority is returned to its base one only when all such * resources are released and no message is waiting for being * received. This is a compromise design choice aimed at avoiding * extensive searches for the new priority to be inherited across * multiply owned resources and blocked tasks sending messages to * him. Such a solution will be implemented only if it proves * necessary. Note also that, to avoid @e deadlocks, a task owning a * resource semaphore cannot be suspended. Any @ref rt_task_suspend() * posed on it is just registered. An owner task will go into suspend * state only when it releases all the owned resources. * * @note RTAI counting semaphores assume that their counter will never * exceed 0xFFFF, such a number being used to signal returns in * error. Thus also the initial count value cannot be greater * than 0xFFFF. To be used only with RTAI24.x.xx (FIXME). */void rt_typed_sem_init(SEM *sem, int value, int type){ sem->magic = RT_SEM_MAGIC; sem->count = value; sem->qtype = type != RES_SEM && (type & FIFO_Q) ? 1 : 0; type = (type & 3) - 2; if ((sem->type = type) < 0 && value > 1) { sem->count = 1; } else if (type > 0) { sem->type = sem->count = 1; } sem->queue.prev = &(sem->queue); sem->queue.next = &(sem->queue); sem->queue.task = sem->owndby = 0;}/** * @anchor rt_sem_init * @brief Initialize a counting semaphore. * * rt_sem_init initializes a counting fifo queueing semaphore @e sem. * * A semaphore can be used for communication and synchronization among * real time tasks. * * @param sem must point to an allocated @e SEM structure. * * @param value is the initial value of the semaphore. * * Positive values of the semaphore variable show how many tasks can * do a @ref rt_sem_wait() call without blocking. Negative value of a * semaphore shows how many tasks are blocked on the semaphore queue, * waiting to be awaken by calls to @ref rt_sem_signal(). * * @note RTAI counting semaphores assume that their counter will never * exceed 0xFFFF, such a number being used to signal returns in * error. Thus also the initial count value cannot be greater * than 0xFFFF. * This is an old legacy function. RTAI 24.1.xx has also * @ref rt_typed_sem_init(), allowing to * choose among counting, binary and resource * semaphores. Resource semaphores have priority inherithance. */void rt_sem_init(SEM *sem, int value){ rt_typed_sem_init(sem, value, CNT_SEM);}/** * @anchor rt_sem_delete * @brief Delete a semaphore * * rt_sem_delete deletes a semaphore previously created with * @ref rt_sem_init(). * * @param sem points to the structure used in the corresponding * call to rt_sem_init. * * Any tasks blocked on this semaphore is returned in error and * allowed to run when semaphore is destroyed. * * @return 0 is returned upon success. A negative value is returned on * failure as described below: * - @b 0xFFFF: @e sem does not refer to a valid semaphore. * * @note In principle 0xFFFF could theoretically be a usable * semaphores events count, so it could be returned also under * normal circumstances. It is unlikely you are going to count * up to such number of events, in any case avoid counting up * to 0xFFFF. */int rt_sem_delete(SEM *sem){ unsigned long flags; RT_TASK *task; unsigned long schedmap; QUEUE *q; if (sem->magic != RT_SEM_MAGIC) { return SEM_ERR; } schedmap = 0; q = &(sem->queue); flags = rt_global_save_flags_and_cli(); sem->magic = 0; while ((q = q->next) != &(sem->queue) && (task = q->task)) { rem_timed_task(task); if (task->state != RT_SCHED_READY && (task->state &= ~(RT_SCHED_SEMAPHORE | RT_SCHED_DELAYED)) == RT_SCHED_READY) { enq_ready_task(task); set_bit(task->runnable_on_cpus & 0x1F, &schedmap); } } clear_bit(hard_cpu_id(), &schedmap); if ((task = sem->owndby) && sem->type > 0) { int sched; if (task->owndres & SEMHLF) { --task->owndres; } if (!task->owndres) { sched = renq_ready_task(task, task->base_priority); } else if (!(task->owndres & SEMHLF)) { int priority; sched = renq_ready_task(task, task->base_priority > (priority = ((task->msg_queue.next)->task)->priority) ? priority : task->base_priority); } else { sched = 0; } if (task->suspdepth) { if (task->suspdepth > 0) { task->state |= RT_SCHED_SUSPENDED; rem_ready_task(task); sched = 1; } else { rt_task_delete(task); } } if (sched) { if (schedmap) { RT_SCHEDULE_MAP_BOTH(schedmap); } else { rt_schedule(); } } } else { RT_SCHEDULE_MAP(schedmap); } rt_global_restore_flags(flags); return 0;}int rt_sem_count(SEM *sem){ return sem->count;}/** * @anchor rt_sem_signal * @brief Signaling a semaphore. * * rt_sem_signal signals an event to a semaphore. It is typically * called when the task leaves a critical region. The semaphore value * is incremented and tested. If the value is not positive, the first * task in semaphore's waiting queue is allowed to run. rt_sem_signal * never blocks the caller task. * * @param sem points to the structure used in the call to @ref * rt_sem_init(). * * @return 0 is returned upon success. A negative value is returned on * failure as described below: * - @b 0xFFFF: @e sem does not refer to a valid semaphore. * * @note In principle 0xFFFF could theoretically be a usable * semaphores events count, so it could be returned also under * normal circumstances. It is unlikely you are going to count * up to such number of events, in any case avoid counting up to * 0xFFFF. * See @ref rt_sem_wait() notes for some curiosities. */int rt_sem_signal(SEM *sem){ unsigned long flags; RT_TASK *task; int tosched; if (sem->magic != RT_SEM_MAGIC) { return SEM_ERR; } flags = rt_global_save_flags_and_cli(); if (sem->type) { if (sem->type > 1) { sem->type--; rt_global_restore_flags(flags); return 0; } if (++sem->count > 1) { sem->count = 1; } } else { sem->count++; } if ((task = (sem->queue.next)->task)) { dequeue_blocked(task); rem_timed_task(task); if (task->state != RT_SCHED_READY && (task->state &= ~(RT_SCHED_SEMAPHORE | RT_SCHED_DELAYED)) == RT_SCHED_READY) { enq_ready_task(task); if (sem->type <= 0) { RT_SCHEDULE(task, hard_cpu_id()); rt_global_restore_flags(flags); return 0; } tosched = 1; goto res; } } tosched = 0;res: if (sem->type > 0) { DECLARE_RT_CURRENT; int sched; ASSIGN_RT_CURRENT; sem->owndby = 0; if (rt_current->owndres & SEMHLF) { --rt_current->owndres; } if (!rt_current->owndres) { sched = renq_current(rt_current, rt_current->base_priority); } else if (!(rt_current->owndres & SEMHLF)) { int priority; sched = renq_current(rt_current, rt_current->base_priority > (priority = ((rt_current->msg_queue.next)->task)->priority) ? priority : rt_current->base_priority); } else { sched = 0; } if (rt_current->suspdepth) { if (rt_current->suspdepth > 0) { rt_current->state |= RT_SCHED_SUSPENDED; rem_ready_current(rt_current); sched = 1; } else { rt_task_delete(rt_current); } } if (sched) { if (tosched) { RT_SCHEDULE_BOTH(task, cpuid); } else { rt_schedule(); } } else if (tosched) { RT_SCHEDULE(task, cpuid); } } rt_global_restore_flags(flags); return 0;}/** * @anchor rt_sem_broadcast * @brief Signaling a semaphore. * * rt_sem_broadcast signals an event to a semaphore that unblocks all tasks * waiting on it. It is used as a support for RTAI proper conditional * variables but can be of help in many other instances. After the broadcast * the semaphore counts is set to zero, thus all tasks waiting on it will * blocked. * * @param sem points to the structure used in the call to @ref * rt_sem_init(). * * @returns 0 always. */int rt_sem_broadcast(SEM *sem){ unsigned long flags, schedmap; RT_TASK *task; QUEUE *q; if (sem->magic != RT_SEM_MAGIC) { return SEM_ERR; } schedmap = 0; q = &(sem->queue); flags = rt_global_save_flags_and_cli(); while ((q = q->next) != &(sem->queue)) { dequeue_blocked(task = q->task); rem_timed_task(task); if (task->state != RT_SCHED_READY && (task->state &= ~(RT_SCHED_SEMAPHORE | RT_SCHED_DELAYED)) == RT_SCHED_READY) { enq_ready_task(task); set_bit(task->runnable_on_cpus & 0x1F, &schedmap); } flags = rt_global_save_flags_and_cli(); rt_global_restore_flags(flags); } sem->count = 0; if (schedmap) { if (test_and_clear_bit(hard_cpu_id(), &schedmap)) { RT_SCHEDULE_MAP_BOTH(schedmap); } else { RT_SCHEDULE_MAP(schedmap); } } rt_global_restore_flags(flags); return 0;}/** * @anchor rt_sem_wait * @brief Take a semaphore. * * rt_sem_wait waits for a event to be signaled to a semaphore. It is * typically called when a task enters a critical region. The * semaphore value is decremented and tested. If it is still * non-negative rt_sem_wait returns immediately. Otherwise the caller * task is blocked and queued up. Queuing may happen in priority order * or on FIFO base. This is determined by the compile time option @e * SEM_PRIORD. In this case rt_sem_wait returns if: * - The caller task is in the first place of the waiting * queue and another task issues a @ref rt_sem_signal() * call; * - An error occurs (e.g. the semaphore is destroyed); * * @param sem points to the structure used in the call to @ref * rt_sem_init(). * * @return the number of events already signaled upon success. * A special value" as described below in case of a failure : * - @b 0xFFFF: @e sem does not refer to a valid semaphore. * * @note In principle 0xFFFF could theoretically be a usable * semaphores events count, so it could be returned also under * normal circumstances. It is unlikely you are going to count * up to such number of events, in any case avoid counting up to * 0xFFFF.<br> * Just for curiosity: the original Dijkstra notation for * rt_sem_wait was a "P" operation, and rt_sem_signal was a "V" * operation. The name for P comes from the Dutch "prolagen", a * combination of "proberen" (to probe) and "verlagen" (to * decrement). Also from the word "passeren" (to pass).<br> * The name for V comes from the Dutch "verhogen" (to increase) * or "vrygeven" (to release). (Source: Daniel Tabak - * Multiprocessors, Prentice Hall, 1990).<br> * It should be also remarked that real time programming * practitioners were using semaphores a long time before * Dijkstra formalized P and V. "In Italian semaforo" means a * traffic light, so that semaphores have an intuitive appeal * and their use and meaning is easily understood. */int rt_sem_wait(SEM *sem){ RT_TASK *rt_current; unsigned long flags; int count; if (sem->magic != RT_SEM_MAGIC) { return SEM_ERR; } flags = rt_global_save_flags_and_cli(); rt_current = RT_CURRENT; if ((count = sem->count) <= 0) { unsigned long schedmap; if (sem->type > 0) { if (sem->owndby == rt_current) { sem->type++; rt_global_restore_flags(flags); return count; } schedmap = pass_prio(sem->owndby, rt_current); } else { schedmap = 0; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -