os_sem.c
2026/7/19大约 5 分钟附录源码附录
os_sem.c
路径: kernel\source\os_sem.c
/**
***********************************************************************************************************************
* Copyright (c) 2020, China Mobile Communications Group Co.,Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* @file os_sem.c
*
* @brief This file implements the semaphore functions.
*
* @revision
* Date Author Notes
* 2020-02-11 OneOS team First Version
***********************************************************************************************************************
*/
#include <os_sem.h>
#include <os_errno.h>
#include <arch_interrupt.h>
#include <string.h>
#include <stdlib.h>
#include <os_spinlock.h>
#include <os_ipc_hook.h>
#include <os_sem_trace.h>
#ifdef OS_USING_IPC_TRACE
#include <os_ipc_trace.h>
#endif
#include "os_kernel_internal.h"
#ifdef OS_USING_SEMAPHORE
#define SEMAPHORE_TAG "SEMAPHORE"
static os_list_node_t gs_os_semaphore_resource_list_head = OS_LIST_INIT(gs_os_semaphore_resource_list_head);
static OS_DEFINE_SPINLOCK(gs_os_semaphore_resource_list_lock);
OS_INLINE void
_k_semaphore_init(os_semaphore_t *semaphore, uint32_t value, uint32_t max_value, const char *name, uint8_t alloc_type)
{
os_list_init(&semaphore->task_list_head);
semaphore->count = value;
semaphore->max_count = max_value;
semaphore->wake_type = OS_SEM_WAKE_TYPE_PRIO;
semaphore->object_alloc_type = alloc_type;
semaphore->object_inited = OS_KOBJ_INITED;
#if defined(OS_USING_SMP) && !defined(OS_IPC_MONOLOCK_MODE)
os_spin_lock_init(&semaphore->lock);
#endif
if (OS_NULL != name)
{
(void)strncpy(&semaphore->name[0], name, OS_NAME_MAX);
semaphore->name[OS_NAME_MAX] = '\0';
}
else
{
semaphore->name[0] = '\0';
}
return;
}
/**
***********************************************************************************************************************
* @brief This function will create a semaphore object from heap.
*
* @param[in] semaphore_cb The static semaphore cb to be create, NULL will dynamic create.
* @param[in] name The name of semaphore.
* @param[in] value The init value of semaphore.
* @param[in] max_value The maximum value of semaphore.
*
* @return The id of semaphore.
* @retval os_semaphore_id The id of semaphore.
* @retval 0 Error occurred.
***********************************************************************************************************************
*/
os_semaphore_id
os_semaphore_create(os_semaphore_dummy_t *semaphore_cb, const char *name, uint32_t value, uint32_t max_value)
{
uint8_t alloc_type;
const os_semaphore_t *iter_sem;
os_semaphore_t *sem;
os_bool_t flag;
const os_list_node_t *pos;
OS_ASSERT(max_value >= value);
OS_ASSERT(max_value > 0U);
#ifndef OS_USING_HEAP
OS_ASSERT(OS_NULL != semaphore_cb);
#endif
flag = OS_TRUE;
if (OS_NULL != semaphore_cb)
{
sem = OS_TYPE_CONVERT(os_semaphore_t *, semaphore_cb);
alloc_type = OS_ALLOC_TYPE_STATIC;
os_spin_lock(&gs_os_semaphore_resource_list_lock);
os_list_for_each(pos, &gs_os_semaphore_resource_list_head)
{
iter_sem = os_list_entry(pos, os_semaphore_t, resource_node);
if (iter_sem == sem)
{
sem = OS_NULL;
os_spin_unlock(&gs_os_semaphore_resource_list_lock);
OS_KERN_LOG(KERN_ERROR, SEMAPHORE_TAG, "%s", (OS_KERNEL_ERR_INFO));
flag = OS_FALSE;
break;
}
}
}
#ifdef OS_USING_HEAP
else
{
alloc_type = OS_KOBJ_ALLOC_TYPE_DYNAMIC;
sem = (os_semaphore_t *)OS_KERNEL_MALLOC(sizeof(os_semaphore_t));
if (OS_NULL == sem)
{
OS_KERN_LOG(KERN_ERROR, SEMAPHORE_TAG, "Semaphore memory malloc fail");
flag = OS_FALSE;
}
}
#endif
if (OS_TRUE == flag)
{
_k_semaphore_init(sem, value, max_value, name, alloc_type);
if (OS_KOBJ_ALLOC_TYPE_DYNAMIC == (alloc_type & OS_KOBJ_ALLOC_TYPE_DYNAMIC))
{
os_spin_lock(&gs_os_semaphore_resource_list_lock);
}
os_list_add_tail(&gs_os_semaphore_resource_list_head, &sem->resource_node);
os_spin_unlock(&gs_os_semaphore_resource_list_lock);
}
return OS_TYPE_CONVERT(os_semaphore_id, sem);
}
/**
***********************************************************************************************************************
* @brief Destory the specific semaphore object.
*
* @param[in] semaphore_id The semaphore id to destroy.
*
* @return The operation result.
* @retval OS_SUCCESS If the operation successful.
* @retval else Error code.
***********************************************************************************************************************
*/
os_err_t os_semaphore_destroy(os_semaphore_id semaphore_id)
{
os_bool_t need_sched;
os_semaphore_t *semaphore;
OS_KERNEL_INIT();
semaphore = OS_TYPE_CONVERT(os_semaphore_t *, semaphore_id);
OS_ASSERT(OS_FALSE == os_is_irq_active());
OS_ASSERT(OS_NULL != semaphore);
OS_ASSERT(OS_KOBJ_INITED == semaphore->object_inited);
#ifndef OS_USING_HEAP
OS_ASSERT(OS_ALLOC_TYPE_STATIC == semaphore->object_alloc_type);
#endif
#if defined(OS_USING_SMP) && !defined(OS_IPC_MONOLOCK_MODE)
os_spin_lock_irq(&semaphore->lock, &irq_save);
#else
OS_KERNEL_ENTER();
#endif
semaphore->object_inited = OS_KOBJ_DEINITED;
need_sched = k_cancel_all_blocked_task(&semaphore->task_list_head);
#if defined(OS_USING_SMP) && !defined(OS_IPC_MONOLOCK_MODE)
os_spin_unlock_irq(&semaphore->lock, irq_save);
if (OS_TRUE == need_sched)
{
OS_KERNEL_ENTER();
OS_KERNEL_EXIT_SCHED();
}
#else
if (OS_TRUE == need_sched)
{
OS_KERNEL_EXIT_SCHED();
}
else
{
OS_KERNEL_EXIT();
}
#endif
os_spin_lock(&gs_os_semaphore_resource_list_lock);
os_list_del(&semaphore->resource_node);
os_spin_unlock(&gs_os_semaphore_resource_list_lock);
if (OS_KOBJ_ALLOC_TYPE_DYNAMIC == semaphore->object_alloc_type)
{
#ifdef OS_USING_HEAP
OS_KERNEL_FREE(semaphore);
semaphore = OS_NULL;
#endif
}
return OS_SUCCESS;
}
/**
***********************************************************************************************************************
* @brief This function decrements the semaphore's count. If the semaphore's count greater than 0, the
* function decrements semaphore's count and returns immediately. Otherwise, the calling task blocks
* until either the semaphore's count greater than 0 or waiting time expires.
*
* @param[in] semaphore_id The semaphore id to destroy.
* @param[in] timeout Wait time (in clock ticks).
*
* @return The operation result.
* @retval OS_SUCCESS If the operation successful.
* @retval else Error code.
***********************************************************************************************************************
*/
os_err_t os_semaphore_wait(os_semaphore_id semaphore_id, os_tick_t timeout)
{
os_err_t ret;
os_semaphore_t *semaphore;
OS_KERNEL_INIT();
semaphore = OS_TYPE_CONVERT(os_semaphore_t *, semaphore_id);
OS_ASSERT(OS_NULL != semaphore);
OS_ASSERT(OS_KOBJ_INITED == semaphore->object_inited);
OS_ASSERT((OS_NO_WAIT == timeout) || (OS_NULL == k_task_self()) || (OS_FALSE == os_is_irq_active()));
OS_ASSERT((OS_NO_WAIT == timeout) || (OS_NULL == k_task_self()) || (OS_FALSE == os_is_irq_disabled()));
OS_ASSERT((OS_NO_WAIT == timeout) || (OS_NULL == k_task_self()) || (OS_FALSE == os_is_schedule_locked()));
OS_ASSERT((timeout < (OS_TICK_MAX / 2)) || (OS_WAIT_FOREVER == timeout));
ret = OS_SUCCESS;
#if defined(OS_USING_SMP) && !defined(OS_IPC_MONOLOCK_MODE)
os_spin_lock_irq(&semaphore->lock, &irq_save);
#else
OS_KERNEL_ENTER();
#endif
if (semaphore->count > 0)
{
semaphore->count--;
OS_SEM_HOOK_CALL(sem, wait, success, semaphore_id);
}
else
{
if (OS_NO_WAIT == timeout)
{
OS_SEM_HOOK_CALL(sem, wait, fail, semaphore_id);
ret = OS_BUSY;
}
else
{
OS_ASSERT((OS_NULL != _k_task_self()));
OS_SEM_HOOK_CALL(sem, wait, block, semaphore_id);
if (OS_SEM_WAKE_TYPE_PRIO == semaphore->wake_type)
{
ret = k_block_task(&semaphore->lock, irq_save, &semaphore->task_list_head, timeout, OS_TRUE);
}
else
{
ret = k_block_task(&semaphore->lock, irq_save, &semaphore->task_list_head, timeout, OS_FALSE);
}
if (OS_SUCCESS == ret)
{
OS_SEM_HOOK_CALL(sem, wait, success, semaphore_id);
}
else // timeout
{
OS_SEM_HOOK_CALL(sem, wait, timeout, semaphore_id);
}
return ret;
}
}
#if defined(OS_USING_SMP) && !defined(OS_IPC_MONOLOCK_MODE)
os_spin_unlock_irq(&semaphore->lock, irq_save);
#else
OS_KERNEL_EXIT();
#endif
return ret;
}
/**
***********************************************************************************************************************
* @brief This function increments the semaphore's count. If the semaphore's count becomes greater than 0. A
* task blocked on it will be woken up.
*
* @param[in] semaphore_id The semaphore id to destroy.
*
* @return The operation result.
* @retval OS_SUCCESS If the operation successful.
* @retval else Error code.
***********************************************************************************************************************
*/
os_err_t os_semaphore_post(os_semaphore_id semaphore_id)
{
os_err_t ret;
os_task_t *task;
os_semaphore_t *semaphore;
OS_KERNEL_INIT();
semaphore = OS_TYPE_CONVERT(os_semaphore_t *, semaphore_id);
OS_ASSERT(OS_NULL != semaphore);
OS_ASSERT(OS_KOBJ_INITED == semaphore->object_inited);
ret = OS_SUCCESS;
do
{
#if defined(OS_USING_SMP) && !defined(OS_IPC_MONOLOCK_MODE)
os_spin_lock_irq(&semaphore->lock, &irq_save);
k_kernel_spin_lock();
#else
OS_KERNEL_ENTER();
#endif
if (os_list_empty(&semaphore->task_list_head))
{
if (semaphore->count < semaphore->max_count)
{
semaphore->count++;
OS_SEM_HOOK_CALL(sem, post, success, semaphore_id);
}
else
{
OS_SEM_HOOK_CALL(sem, post, full, semaphore_id);
ret = OS_FULL;
}
}
else
{
task = os_list_first_entry(&semaphore->task_list_head, os_task_t, task_node);
OS_SEM_HOOK_CALL(sem, post, wake, semaphore_id);
if (k_unblock_task(task) == OS_TRUE)
{
#if defined(OS_USING_SMP) && !defined(OS_IPC_MONOLOCK_MODE)
k_kernel_spin_unlock();
os_spin_unlock_irq(&semaphore->lock, irq_save);
OS_KERNEL_ENTER();
OS_KERNEL_EXIT_SCHED();
#else
OS_KERNEL_EXIT_SCHED();
#endif
break;
}
}
#if defined(OS_USING_SMP) && !defined(OS_IPC_MONOLOCK_MODE)
k_kernel_spin_unlock();
os_spin_unlock_irq(&semaphore->lock, irq_save);
#else
OS_KERNEL_EXIT();
#endif
} while (0);
return ret;
}
/**
***********************************************************************************************************************
* @brief This function to set the semaphore wake type(by prio or fifo)
*
* @param[in] semaphore_id The semaphore id to set.
* @param[in] wake_type The semaphore wake type to set.
*
* @return The operation result.
* @retval OS_SUCCESS If the operation successful.
* @retval else Error code.
***********************************************************************************************************************
*/
os_err_t os_semaphore_set_wake_type(os_semaphore_id semaphore_id, uint8_t wake_type)
{
os_err_t ret;
os_semaphore_t *semaphore;
OS_KERNEL_INIT();
semaphore = OS_TYPE_CONVERT(os_semaphore_t *, semaphore_id);
OS_ASSERT(OS_NULL != semaphore);
OS_ASSERT(OS_KOBJ_INITED == semaphore->object_inited);
OS_ASSERT((OS_SEM_WAKE_TYPE_PRIO == wake_type) || (OS_SEM_WAKE_TYPE_FIFO == wake_type));
#if defined(OS_USING_SMP) && !defined(OS_IPC_MONOLOCK_MODE)
os_spin_lock_irq(&semaphore->lock, &irq_save);
k_kernel_spin_lock();
#else
OS_KERNEL_ENTER();
#endif
if (os_list_empty(&semaphore->task_list_head))
{
semaphore->wake_type = wake_type;
ret = OS_SUCCESS;
}
else
{
ret = OS_BUSY;
}
#if defined(OS_USING_SMP) && !defined(OS_IPC_MONOLOCK_MODE)
k_kernel_spin_unlock();
os_spin_unlock_irq(&semaphore->lock, irq_save);
#else
OS_KERNEL_EXIT();
#endif
return ret;
}
/**
***********************************************************************************************************************
* @brief This function to get the semaphore effectivity value
*
* @param[in] semaphore_id The semaphore id.
* @param[out] count A pointer to return the effectivity value of semaphore.
*
* @return The operation result.
* @retval OS_SUCCESS Get success.
* @retval else Error code.
***********************************************************************************************************************
*/
os_err_t os_semaphore_get_value(os_semaphore_id semaphore_id, uint32_t *count)
{
os_semaphore_t *semaphore;
semaphore = OS_TYPE_CONVERT(os_semaphore_t *, semaphore_id);
OS_ASSERT(OS_NULL != semaphore);
OS_ASSERT(OS_KOBJ_INITED == semaphore->object_inited);
OS_ASSERT(OS_NULL != count);
*count = semaphore->count;
return OS_SUCCESS;
}
/**
***********************************************************************************************************************
* @brief This function to get the semaphore name
*
* @param[in] semaphore_id The semaphore id.
*
* @return The name.
***********************************************************************************************************************
*/
const char *os_semaphore_get_name(os_semaphore_id semaphore_id)
{
os_semaphore_t *semaphore;
semaphore = OS_TYPE_CONVERT(os_semaphore_t *, semaphore_id);
OS_ASSERT(OS_NULL != semaphore);
OS_ASSERT(OS_KOBJ_INITED == semaphore->object_inited);
return semaphore->name;
}
/**
***********************************************************************************************************************
* @brief This function to get the semaphore maximum value
*
* @param[in] semaphore_id The semaphore id.
* @param[out] max_count A pointer to return the maximum value of semaphore.
*
* @return The operation result.
* @retval OS_SUCCESS Get success.
* @retval else Error code.
***********************************************************************************************************************
*/
os_err_t os_semaphore_get_max_value(os_semaphore_id semaphore_id, uint32_t *max_count)
{
os_semaphore_t *semaphore;
semaphore = OS_TYPE_CONVERT(os_semaphore_t *, semaphore_id);
OS_ASSERT(OS_NULL != semaphore);
OS_ASSERT(OS_KOBJ_INITED == semaphore->object_inited);
OS_ASSERT(OS_NULL != max_count);
*max_count = semaphore->max_count;
return OS_SUCCESS;
}
/**
***********************************************************************************************************************
* @brief This function checks whether the semaphore exists
*
* @param[in] semaphore_id The semaphore id.
*
* @return OS_TRUE: exsit
* OS_FALSE: not
***********************************************************************************************************************
*/
os_bool_t os_semaphore_is_exist(os_semaphore_id semaphore_id)
{
os_semaphore_t *iter_sem;
os_bool_t valid_sem;
os_semaphore_t *semaphore;
semaphore = OS_TYPE_CONVERT(os_semaphore_t *, semaphore_id);
OS_ASSERT(OS_NULL != semaphore);
valid_sem = OS_FALSE;
os_spin_lock(&gs_os_semaphore_resource_list_lock);
os_list_for_each_entry(iter_sem, &gs_os_semaphore_resource_list_head, os_semaphore_t, resource_node)
{
if (iter_sem == semaphore)
{
valid_sem = OS_TRUE;
break;
}
}
os_spin_unlock(&gs_os_semaphore_resource_list_lock);
return valid_sem;
}
#if defined(OS_USING_SHELL)
#include <shell.h>
#define SH_SHOW_TASK_CNT_MAX 10
typedef struct
{
os_semaphore_t *sem;
uint32_t count;
uint32_t max_count;
uint16_t block_task_count;
block_task_info_t block_info[SH_SHOW_TASK_CNT_MAX];
} sh_semaphore_info_t;
static os_err_t os_sem_show(os_semaphore_t *sem)
{
sh_semaphore_info_t sem_info;
os_task_t *iter_task;
uint16_t task_index;
OS_KERNEL_INIT();
if ((OS_NULL == sem) || (OS_KOBJ_INITED != sem->object_inited))
{
os_kprintf("The input parameter is an illegal semaphore object.\r\n");
return OS_FAILURE;
}
#if defined(OS_USING_SMP) && !defined(OS_IPC_MONOLOCK_MODE)
os_spin_lock_irq(&sem->lock, &irq_save);
k_kernel_spin_lock();
#else
OS_KERNEL_ENTER();
#endif
sem_info.sem = sem;
sem_info.count = sem->count;
sem_info.max_count = sem->max_count;
sem_info.block_task_count = os_list_len(&sem->task_list_head);
task_index = 0;
os_list_for_each_entry(iter_task, &sem->task_list_head, os_task_t, task_node)
{
sem_info.block_info[task_index].current_priority = iter_task->current_priority;
sem_info.block_info[task_index].name = iter_task->name;
task_index++;
if (task_index >= SH_SHOW_TASK_CNT_MAX)
{
break;
}
}
#if defined(OS_USING_SMP) && !defined(OS_IPC_MONOLOCK_MODE)
k_kernel_spin_unlock();
os_spin_unlock_irq(&sem->lock, irq_save);
#else
OS_KERNEL_EXIT();
#endif
os_kprintf("%-*s 0x%x %-10u %-10u ",
OS_NAME_MAX,
(sem->name[0] != '\0') ? sem->name : "-",
sem,
sem_info.count,
sem_info.max_count);
if (sem_info.block_task_count > 0)
{
os_kprintf(" %-6u:", sem_info.block_task_count);
k_show_blocked_task(sem_info.block_info, task_index);
if (sem_info.block_task_count > task_index)
{
os_kprintf("/...\r\n");
}
else
{
os_kprintf("\r\n");
}
}
else
{
os_kprintf(" %-6u\r\n", 0);
}
return OS_SUCCESS;
}
/**
***********************************************************************************************************************
* @brief This function prints information about all the semaphore and it's corresponding blokced tasks.
*
* @param[in] argc Argment count.
* @param[in] argv Argment list.
*
* @return The operation result.
* @retval OS_SUCCESS If the operation successful.
* @retval else Error code.
***********************************************************************************************************************
*/
os_err_t sh_show_semaphore_info(int32_t argc, char **argv)
{
os_semaphore_t *iter_sem;
uint16_t len;
os_semaphore_t *sem_tmp;
OS_UNREFERENCE(argc);
OS_UNREFERENCE(argv);
os_kprintf("%-*s %-10s %-10s %-10s %-10s\r\n",
OS_NAME_MAX,
"Semaphore",
"Sem ID",
"Count",
"Max Count",
"Block Task");
len = OS_NAME_MAX;
while (len > 0)
{
os_kprintf("-");
len--;
}
os_kprintf(" ---------- ---------- ---------- ----------\r\n");
if (argc >= 2)
{
if ((OS_NULL != argv[1]))
{
sem_tmp = (os_semaphore_t *)strtoul(argv[1], OS_NULL, 0);
os_spin_lock(&gs_os_semaphore_resource_list_lock);
os_list_for_each_entry(iter_sem, &gs_os_semaphore_resource_list_head, os_semaphore_t, resource_node)
{
if (sem_tmp == iter_sem)
{
os_sem_show(sem_tmp);
os_spin_unlock(&gs_os_semaphore_resource_list_lock);
return OS_SUCCESS;
}
}
os_spin_unlock(&gs_os_semaphore_resource_list_lock);
}
os_kprintf("Invalid Semaphore Object.\r\n");
return OS_FAILURE;
}
os_spin_lock(&gs_os_semaphore_resource_list_lock);
os_list_for_each_entry(iter_sem, &gs_os_semaphore_resource_list_head, os_semaphore_t, resource_node)
{
os_sem_show(iter_sem);
}
os_spin_unlock(&gs_os_semaphore_resource_list_lock);
return OS_SUCCESS;
}
SH_CMD_EXPORT(show_sem, sh_show_semaphore_info, "show semaphore information");
#endif /* defined(OS_USING_SHELL) */
#endif /* OS_USING_SEMAPHORE */