xiuos3/kernel/thread/semaphore.c

328 lines
7.5 KiB
C

/*
* Copyright (c) 2020 AIIT XUOS Lab
* XiUOS is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
/**
* @file: semaphore.c
* @brief: semaphore file
* @version: 1.0
* @author: AIIT XUOS Lab
* @date: 2020/3/15
*
*/
#include <xiuos.h>
#ifdef KDEBUG_NOT_IN_INTERRUPT
#undef KDEBUG_NOT_IN_INTERRUPT
#endif
#define KDEBUG_NOT_IN_INTERRUPT
DECLARE_ID_MANAGER(k_sem_id_manager, ID_NUM_MAX);
DoubleLinklistType k_sem_list = {&k_sem_list, &k_sem_list};
static int32 _SemaphoreCreate(uint16 val)
{
int32 id = 0;
x_base lock = 0;
struct Semaphore *sem = NONE;
sem = (struct Semaphore *)x_malloc(sizeof(struct Semaphore));
if (sem == NONE)
return -ENOMEMORY;
memset(sem, 0, sizeof(struct Semaphore));
lock = CriticalAreaLock();
id = IdInsertObj(&k_sem_id_manager, &sem->id);
if (id < 0) {
x_free(sem);
CriticalAreaUnLock(lock);
return -ENOMEMORY;
}
sem->value = val;
InitDoubleLinkList(&sem->pend_list);
DoubleLinkListInsertNodeAfter(&k_sem_list, &sem->link);
CriticalAreaUnLock(lock);
SYS_KDEBUG_LOG(KDBG_IPC, ("created semaphore: id %d, value %d\n", id, (int)val));
return id;
}
static void _SemaphoreDelete(struct Semaphore *sem)
{
int resched = 0;
x_base lock = 0;
NULL_PARAM_CHECK(sem);
SYS_KDEBUG_LOG(KDBG_IPC, ("deleted semaphore: id %d\n", (int)sem->id.id));
lock = CriticalAreaLock();
if (!IsDoubleLinkListEmpty(&sem->pend_list)) {
resched = 1;
LinklistResumeAll(&sem->pend_list);
}
IdRemoveObj(&k_sem_id_manager, sem->id.id);
DoubleLinkListRmNode(&sem->link);
CriticalAreaUnLock(lock);
x_free(sem);
if (resched){
DO_KTASK_ASSIGN;
}
}
static int32 _SemaphoreObtain(struct Semaphore *sem, int32 msec)
{
int lock = 0;
int32 wait_time = 0;
struct TaskDescriptor *task = NONE;
NULL_PARAM_CHECK(sem);
wait_time = CalculteTickFromTimeMs(msec);
lock = CriticalAreaLock();
SYS_KDEBUG_LOG(KDBG_IPC, ("obtain semaphore: id %d, value %d, by task %s\n",
(int)sem->id.id, (int)sem->value, GetKTaskDescriptor()->task_base_info.name));
if (sem->value > 0) {
sem->value--;
CriticalAreaUnLock(lock);
return EOK;
}
if (wait_time == 0) {
CriticalAreaUnLock(lock);
return -ETIMEOUT;
}
task = GetKTaskDescriptor();
task->exstatus = EOK;
SYS_KDEBUG_LOG(KDBG_IPC, ("obtain semaphore: suspending task %s\n",
GetKTaskDescriptor()->task_base_info.name));
LinklistSuspend(&sem->pend_list, task, LINKLIST_FLAG_PRIO);
if (wait_time > 0) {
KTaskSetDelay(task, wait_time);
}
CriticalAreaUnLock(lock);
DO_KTASK_ASSIGN;
return task->exstatus;
}
static int32 _SemaphoreAbandon(struct Semaphore *sem)
{
int lock = 0;
int resched = 0;
NULL_PARAM_CHECK(sem);
lock = CriticalAreaLock();
SYS_KDEBUG_LOG(KDBG_IPC, ("abandon semaphore: id %d, value %d, by task %s\n",
(int)sem->id.id, (int)sem->value, GetKTaskDescriptor()->task_base_info.name));
if (!IsDoubleLinkListEmpty(&sem->pend_list)) {
resched = 1;
LinklistResume(&sem->pend_list);
} else {
sem->value++;
}
CriticalAreaUnLock(lock);
if (resched){
DO_KTASK_ASSIGN;
}
return EOK;
}
static int32 _SemaphoreSetValue(struct Semaphore *sem, uint16 val)
{
int lock = 0;
int resched = 0;
NULL_PARAM_CHECK(sem);
lock = CriticalAreaLock();
SYS_KDEBUG_LOG(KDBG_IPC, ("set semaphore value: id %d, old value %d, new value %d, by task %s\n",
(int)sem->id.id, (int)sem->value, (int)val, GetKTaskDescriptor()->task_base_info.name));
if (sem->value == val) {
CriticalAreaUnLock(lock);
return EOK;
}
if (!IsDoubleLinkListEmpty(&sem->pend_list)) {
resched = 1;
LinklistResumeAll(&sem->pend_list);
}
sem->value = val;
CriticalAreaUnLock(lock);
if (resched)
DO_KTASK_ASSIGN;
return EOK;
}
static SemaphoreDoneType done = {
.SemaphoreCreate = _SemaphoreCreate,
.SemaphoreDelete = _SemaphoreDelete,
.SemaphoreObtain = _SemaphoreObtain,
.SemaphoreAbandon = _SemaphoreAbandon,
.SemaphoreSetValue = _SemaphoreSetValue,
};
/**
* Create a new semaphore with specified initial value.
*
* @param val initial value
* @return id of the semaphore
*/
int32 KSemaphoreCreate(uint16 val)
{
KDEBUG_NOT_IN_INTERRUPT;
return done.SemaphoreCreate(val);
}
/**
* Delete a semaphore and wakeup all pending tasks on it.
*
* @param id id of the semaphore to be deleted
*/
void KSemaphoreDelete(int32 id)
{
x_base lock = 0;
struct Semaphore *sem = NONE;
struct IdNode *idnode = NONE;
KDEBUG_NOT_IN_INTERRUPT;
if (id < 0)
return;
lock = CriticalAreaLock();
idnode = IdGetObj(&k_sem_id_manager, id);
if (idnode == NONE){
CriticalAreaUnLock(lock);
return;
}
CriticalAreaUnLock(lock);
sem = CONTAINER_OF(idnode, struct Semaphore, id);
done.SemaphoreDelete(sem);
}
/**
* Obtain a semaphore when its value is greater than 0; pend on it otherwise.
*
* @param id id of the semaphore to be obtained
* @param msec wait time in millisecond
* @return EOK on success, error code on failure
*/
int32 KSemaphoreObtain(int32 id, int32 msec)
{
KDEBUG_NOT_IN_INTERRUPT;
x_base lock = 0;
struct Semaphore *sem = NONE;
struct IdNode *idnode = NONE;
if (id < 0)
return -ERROR;
lock = CriticalAreaLock();
idnode = IdGetObj(&k_sem_id_manager, id);
if (idnode == NONE){
CriticalAreaUnLock(lock);
return -ERROR;
}
sem =CONTAINER_OF(idnode, struct Semaphore, id);
CriticalAreaUnLock(lock);
return done.SemaphoreObtain(sem, msec);
}
/**
* Abandon a semaphore and wakeup a pending task if any.
*
* @param id id of the semaphore to be abandoned
* @return EOK on success, error code on failure
*/
int32 KSemaphoreAbandon(int32 id)
{
KDEBUG_NOT_IN_INTERRUPT;
x_base lock = 0;
struct Semaphore *sem = NONE;
struct IdNode *idnode = NONE;
if (id < 0)
return -ERROR;
lock = CriticalAreaLock();
idnode = IdGetObj(&k_sem_id_manager, id);
if (idnode == NONE) {
CriticalAreaUnLock(lock);
return -ERROR;
}
sem =CONTAINER_OF(idnode, struct Semaphore, id);
CriticalAreaUnLock(lock);
return done.SemaphoreAbandon(sem);
}
/**
* Set the value of a semaphore, wakeup all pending tasks if new value is positive.
*
* @param id id of the semaphore for which to set value
* @param val new value
* @return EOK on success, error code on failure
*/
int32 KSemaphoreSetValue(int32 id, uint16 val)
{
KDEBUG_NOT_IN_INTERRUPT;
x_base lock = 0;
struct Semaphore *sem = NONE;
struct IdNode *idnode = NONE;
if (id < 0)
return -ERROR;
lock = CriticalAreaLock();
idnode = IdGetObj(&k_sem_id_manager, id);
if (idnode == NONE) {
CriticalAreaUnLock(lock);
return -ERROR;
}
sem = CONTAINER_OF(idnode, struct Semaphore, id);
CriticalAreaUnLock(lock);
return done.SemaphoreSetValue(sem, val);
}