166 lines
4.6 KiB
C
166 lines
4.6 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 implementation
|
|
* @version 3.0
|
|
* @author AIIT XUOS Lab
|
|
* @date 2024.05.28
|
|
*/
|
|
|
|
#include "assert.h"
|
|
#include "ksemaphore.h"
|
|
#include "task.h"
|
|
|
|
void semaphore_pool_init(struct XiziSemaphorePool* sem_pool)
|
|
{
|
|
assert(sem_pool != NULL);
|
|
sem_pool->next_sem_id = INVALID_SEM_ID + 1;
|
|
slab_init(&sem_pool->allocator, sizeof(struct ksemaphore), "SemAllocator");
|
|
doubleListNodeInit(&sem_pool->sem_list_guard);
|
|
rbtree_init(&sem_pool->sem_pool_map);
|
|
sem_pool->nr_sem = 0;
|
|
}
|
|
|
|
static inline struct ksemaphore* ksemaphore_get_by_id(struct XiziSemaphorePool* sem_pool, sem_id_t sem_id)
|
|
{
|
|
RbtNode* target_sem_node = rbt_search(&sem_pool->sem_pool_map, sem_id);
|
|
if (target_sem_node == NULL) {
|
|
return NULL;
|
|
}
|
|
return (struct ksemaphore*)target_sem_node->data;
|
|
}
|
|
|
|
sem_id_t ksemaphore_alloc(struct XiziSemaphorePool* sem_pool, sem_val_t val)
|
|
{
|
|
struct ksemaphore* sem = (struct ksemaphore*)slab_alloc(&sem_pool->allocator);
|
|
if (sem == NULL) {
|
|
ERROR("No memeory to alloc new semaphore.\n");
|
|
return INVALID_SEM_ID;
|
|
}
|
|
|
|
/* No error down here */
|
|
/* init ksemaphore since here */
|
|
/// @warning sem->id could overflow
|
|
sem->id = sem_pool->next_sem_id++;
|
|
if (UNLIKELY(sem->id == 0)) {
|
|
slab_free(&sem_pool->allocator, sem);
|
|
return INVALID_SEM_ID;
|
|
}
|
|
sem->val = val;
|
|
doubleListNodeInit(&sem->sem_list_node);
|
|
rbtree_init(&sem->wait_thd_tree);
|
|
|
|
if (0 != rbt_insert(&sem_pool->sem_pool_map, sem->id, sem)) {
|
|
slab_free(&sem_pool->allocator, sem);
|
|
return INVALID_SEM_ID;
|
|
}
|
|
|
|
doubleListAddOnHead(&sem->sem_list_node, &sem_pool->sem_list_guard);
|
|
sem_pool->nr_sem++;
|
|
|
|
return sem->id;
|
|
}
|
|
|
|
bool ksemaphore_consume(struct XiziSemaphorePool* sem_pool, sem_id_t sem_id, sem_val_t decre)
|
|
{
|
|
struct ksemaphore* sem = ksemaphore_get_by_id(sem_pool, sem_id);
|
|
// invalid sem id
|
|
if (sem == NULL) {
|
|
return false;
|
|
}
|
|
|
|
// if (decre >= 0) {
|
|
sem->val -= decre;
|
|
// }
|
|
return true;
|
|
}
|
|
|
|
bool ksemaphore_wait(struct XiziSemaphorePool* sem_pool, struct Thread* thd, sem_id_t sem_id)
|
|
{
|
|
assert(thd != NULL);
|
|
assert(thd->snode.state == RUNNING);
|
|
/* find sem */
|
|
struct ksemaphore* sem = ksemaphore_get_by_id(sem_pool, sem_id);
|
|
// invalid sem id
|
|
if (sem == NULL) {
|
|
return false;
|
|
}
|
|
// DEBUG("%s waiting sem %lu(%d), nr_sem: %d I\n", thd->name, sem_id, sem->val, sem_pool->nr_sem);
|
|
|
|
// no need to wait
|
|
if (sem->val > 0) {
|
|
sem->val--;
|
|
return true;
|
|
}
|
|
|
|
// waiting at the sem
|
|
sem->val--;
|
|
THREAD_TRANS_STATE(thd, BLOCKED);
|
|
int rbt_insert_res = rbt_insert(&sem->wait_thd_tree, thd->tid, thd);
|
|
assert(RBTTREE_INSERT_SECC == rbt_insert_res || RBTTREE_INSERT_EXISTED == rbt_insert_res);
|
|
return true;
|
|
}
|
|
|
|
bool ksemaphore_signal(struct XiziSemaphorePool* sem_pool, sem_id_t sem_id)
|
|
{
|
|
/* find sem */
|
|
struct ksemaphore* sem = ksemaphore_get_by_id(sem_pool, sem_id);
|
|
// invalid sem id
|
|
if (sem == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (sem->val < 0 && !rbt_is_empty(&sem->wait_thd_tree)) {
|
|
assert(!rbt_is_empty(&sem->wait_thd_tree));
|
|
RbtNode* root = sem->wait_thd_tree.root;
|
|
struct Thread* thd = (struct Thread*)root->data;
|
|
rbt_delete(&sem->wait_thd_tree, root->key);
|
|
THREAD_TRANS_STATE(thd, TRANS_WAKING);
|
|
}
|
|
|
|
sem->val++;
|
|
return true;
|
|
}
|
|
|
|
bool ksemaphore_signal_no_wake(struct XiziSemaphorePool* sem_pool, sem_id_t sem_id)
|
|
{
|
|
/* find sem */
|
|
struct ksemaphore* sem = ksemaphore_get_by_id(sem_pool, sem_id);
|
|
// invalid sem id
|
|
if (sem == NULL) {
|
|
return false;
|
|
}
|
|
|
|
sem->val++;
|
|
return true;
|
|
}
|
|
|
|
bool ksemaphore_free(struct XiziSemaphorePool* sem_pool, sem_id_t sem_id)
|
|
{
|
|
/* find sem */
|
|
struct ksemaphore* sem = ksemaphore_get_by_id(sem_pool, sem_id);
|
|
// invalid sem id
|
|
if (sem == NULL) {
|
|
return false;
|
|
}
|
|
|
|
// by design: no waking any waiting threads
|
|
|
|
rbt_delete(&sem_pool->sem_pool_map, sem_id);
|
|
doubleListDel(&sem->sem_list_node);
|
|
slab_free(&sem_pool->allocator, sem);
|
|
sem_pool->nr_sem--;
|
|
|
|
return true;
|
|
}
|