DragonOS/kernel/sched/completion.c
guanjinquan 09f8d6f577
添加completion模块+wait_queue_head模块+schedule_timeout (#70)
* 添加completion模块+wait_queue_head模块+schedule_timeout

* 修复一些bug

* 实现设置pcb名字和vsnprintf (#72)

* 实现pcb设置名字

* 实现设置pcb名字,实现vsnprintf

* 修改set_pcb_name和va_end

* bugfix: 修正一些小问题

Co-authored-by: longjin <longjin@RinGoTek.cn>

* new: FAT32删除文件的功能 (#73)

* new: 将sys_rmdir更改为sys_unlink,.且完成删除文件操作的vfs部分

* new: fat32删除文件

*bugfix: 解决创建文件时的bug

* new: 将可执行文件移动到bin目录下

* 完善completion和wait_queue_head文档,并确保测试ok。

Co-authored-by: longjin <longjin@RinGoTek.cn>
Co-authored-by: houmkh <100781004+houmkh@users.noreply.github.com>
2022-11-03 21:54:59 +08:00

328 lines
9.6 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "common/completion.h"
#include "common/kthread.h"
/**
* @brief 初始化一个completion变量
*
* @param x completion
*/
void completion_init(struct completion *x)
{
x->done = 0;
wait_queue_head_init(&x->wait_queue);
}
/**
* @brief 唤醒一个wait_queue中的节点
*
* @param x completion
*/
void complete(struct completion *x)
{
spin_lock(&x->wait_queue.lock);
if (x->done != COMPLETE_ALL)
++(x->done);
wait_queue_wakeup_on_stack(&x->wait_queue, -1UL); // -1UL代表所有节点都满足条件,暂时这么写
spin_unlock(&x->wait_queue.lock);
}
/**
* @brief 永久标记done为Complete_All, 并从wait_queue中删除所有节点
*
* @param x completion
*/
void complete_all(struct completion *x)
{
spin_lock(&x->wait_queue.lock);
x->done = COMPLETE_ALL; // 永久赋值
while (!list_empty(&x->wait_queue.wait_list))
wait_queue_wakeup_on_stack(&x->wait_queue, -1UL); // -1UL代表所有节点都满足条件,暂时这么写
spin_unlock(&x->wait_queue.lock);
}
/**
* @brief 辅助函数通用的处理wait命令的函数(即所有wait_for_completion函数最核心部分在这里)
*
* @param x completion
* @param action 函数指针
* @param timeout 一个非负整数
* @param state 你要设置进程的状态为state
* @return long - 返回剩余的timeout
*/
static long __wait_for_common(struct completion *x, long (*action)(long), long timeout, int state)
{
if (!x->done)
{
DECLARE_WAIT_ON_STACK_SELF(wait);
while (!x->done && timeout > 0)
{
// 加入等待队列, 但是不会调度走
if (list_empty(&wait.wait_list))
list_append(&x->wait_queue.wait_list, &wait.wait_list);
wait.pcb->state = state; // 清除运行位, 并设置为interuptible/uninteruptible
spin_unlock(&x->wait_queue.lock);
timeout = action(timeout);
spin_lock(&x->wait_queue.lock);
}
if (!x->done)
return timeout; // 仍然没有complete, 但是被其他进程唤醒
wait.pcb->state = PROC_RUNNING; // 设置为运行, 并清空state 所以使用等号赋值
if (!list_empty(&wait.wait_list))
list_del_init(&wait.wait_list); // 必须使用del_init
}
if (x->done != COMPLETE_ALL)
--(x->done);
return timeout ? timeout : 1; // 这里linux返回1不知道为啥
}
/**
* @brief 等待completion命令唤醒进程, 同时设置pcb->state为uninteruptible.
*
* @param x completion
*/
void wait_for_completion(struct completion *x)
{
spin_lock(&x->wait_queue.lock);
__wait_for_common(x, &schedule_timeout_ms, MAX_TIMEOUT, PROC_UNINTERRUPTIBLE);
spin_unlock(&x->wait_queue.lock);
}
/**
* @brief 等待指定时间,超时后就返回, 同时设置pcb->state为uninteruptible.
*
* @param x completion
* @param timeout 非负整数,等待指定时间,超时后就返回/ 或者提前done则返回剩余timeout时间
* @return long - 返回剩余的timeout
*/
long wait_for_completion_timeout(struct completion *x, long timeout)
{
BUG_ON(timeout < 0);
spin_lock(&x->wait_queue.lock);
timeout = __wait_for_common(x, &schedule_timeout_ms, timeout, PROC_UNINTERRUPTIBLE);
spin_unlock(&x->wait_queue.lock);
return timeout;
}
/**
* @brief 等待completion的完成但是可以被中断我也不太懂可以被中断是什么意思就是pcb->state=interuptible
*
* @param x completion
*/
void wait_for_completion_interruptible(struct completion *x)
{
spin_lock(&x->wait_queue.lock);
__wait_for_common(x, &schedule_timeout_ms, MAX_TIMEOUT, PROC_INTERRUPTIBLE);
spin_unlock(&x->wait_queue.lock);
}
/**
* @brief 等待指定时间,超时后就返回, 等待completion的完成但是可以被中断.
*
* @param x completion
* @param timeout 非负整数,等待指定时间,超时后就返回/ 或者提前done则返回剩余timeout时间
* @return long - 返回剩余的timeout
*/
long wait_for_completion_interruptible_timeout(struct completion *x, long timeout)
{
BUG_ON(timeout < 0);
spin_lock(&x->wait_queue.lock);
timeout = __wait_for_common(x, &schedule_timeout_ms, timeout, PROC_INTERRUPTIBLE);
spin_unlock(&x->wait_queue.lock);
return timeout;
}
/**
* @brief 尝试获取completion的一个done如果您在wait之前加上这个函数作为判断说不定会加快运行速度。
*
* @param x completion
* @return true - 表示不需要wait_for_completion并且已经获取到了一个completion(即返回true意味着done已经被 减1 ) \
* @return false - 表示当前done=0您需要进入等待即wait_for_completion
*/
bool try_wait_for_completion(struct completion *x)
{
if (!READ_ONCE(x->done))
return false;
bool ret = true;
spin_lock(&x->wait_queue.lock);
if (!x->done)
ret = false;
else if (x->done != COMPLETE_ALL)
--(x->done);
spin_unlock(&x->wait_queue.lock);
return ret;
}
/**
* @brief 测试一个completion是否有waiter。(即done是不是等于0)
*
* @param x completion
* @return true
* @return false
*/
bool completion_done(struct completion *x)
{
if (!READ_ONCE(x->done))
return false;
// 这里的意义是: 如果是多线程的情况下您有可能需要等待另一个进程的complete操作, 才算真正意义上的completed!
spin_lock(&x->wait_queue.lock);
if (!READ_ONCE(x->done))
{
spin_unlock(&x->wait_queue.lock);
return false;
}
spin_unlock(&x->wait_queue.lock);
return true;
}
/**
* @brief 对completion数组进行wait操作
*
* @param x completion array
* @param n len of the array
*/
void wait_for_multicompletion(struct completion x[], int n)
{
for (int i = 0; i < n; i++) // 对每一个completion都等一遍
{
if (!completion_done(&x[i])) // 如果没有done直接wait
{
wait_for_completion(&x[i]);
}
else if (!try_wait_for_completion(&x[i])) //上面测试过done>0那么这里尝试去获取一个done如果失败了就继续wait
{
wait_for_completion(&x[i]);
}
}
}
/**
* @brief 等待者, 等待wait_for_completion
*
* @param one_to_one
* @param one_to_many
* @param many_to_one
*/
int __test_completion_waiter(void *input_data)
{
struct __test_data *data = (struct __test_data *)input_data;
// kdebug("THE %d WAITER BEGIN", -data->id);
// 测试一对多能不能实现等待 - 由外部统一放闸一起跑
if (!try_wait_for_completion(data->one_to_many))
{
wait_for_completion(data->one_to_many);
}
// 测试一对一能不能实现等待
if (!try_wait_for_completion(data->one_to_many))
{
wait_for_completion(data->one_to_many);
}
// 完成上面两个等待, 执行complete声明自己已经完成
complete(data->many_to_one);
// kdebug("THE %d WAITER SOLVED", -data->id);
return true;
}
/**
* @brief 执行者执行complete
*
* @param one_to_one
* @param one_to_many
* @param many_to_one
*/
int __test_completion_worker(void *input_data)
{
struct __test_data *data = (struct __test_data *)input_data;
// kdebug("THE %d WORKER BEGIN", data->id);
// 测试一对多能不能实现等待 - 由外部统一放闸一起跑
if (!try_wait_for_completion(data->one_to_many))
{
wait_for_completion(data->one_to_many);
}
schedule_timeout_ms(50);
// for(uint64_t i=0;i<1e7;++i)
// pause();
complete(data->one_to_one);
// 完成上面两个等待, 执行complete声明自己已经完成
complete(data->many_to_one);
// kdebug("THE %d WORKER SOLVED", data->id);
return true;
}
/**
* @brief 测试函数
*
*/
void __test_completion()
{
// kdebug("BEGIN COMPLETION TEST");
const int N = 100;
struct completion *one_to_one = kzalloc(sizeof(struct completion) * N, 0);
struct completion *one_to_many = kzalloc(sizeof(struct completion), 0);
struct completion *waiter_many_to_one = kzalloc(sizeof(struct completion) * N, 0);
struct completion *worker_many_to_one = kzalloc(sizeof(struct completion) * N, 0);
struct __test_data *waiter_data = kzalloc(sizeof(struct __test_data) * N, 0);
struct __test_data *worker_data = kzalloc(sizeof(struct __test_data) * N, 0);
completion_init(one_to_many);
for (int i = 0; i < N; i++)
{
completion_init(&one_to_one[i]);
completion_init(&waiter_many_to_one[i]);
completion_init(&worker_many_to_one[i]);
}
for (int i = 0; i < N; i++)
{
waiter_data[i].id = -i; // waiter
waiter_data[i].many_to_one = &waiter_many_to_one[i];
waiter_data[i].one_to_one = &one_to_one[i];
waiter_data[i].one_to_many = one_to_many;
kthread_run(__test_completion_waiter, &waiter_data[i], "the %dth waiter", i);
}
for (int i = 0; i < N; i++)
{
worker_data[i].id = i; // worker
worker_data[i].many_to_one = &worker_many_to_one[i];
worker_data[i].one_to_one = &one_to_one[i];
worker_data[i].one_to_many = one_to_many;
kthread_run(__test_completion_worker, &worker_data[i], "the %dth worker", i);
}
complete_all(one_to_many);
// kdebug("all of the waiters and workers begin running");
// kdebug("BEGIN COUNTING");
wait_for_multicompletion(waiter_many_to_one, N);
wait_for_multicompletion(worker_many_to_one, N);
// kdebug("all of the waiters and workers complete");
kfree(one_to_one);
kfree(one_to_many);
kfree(waiter_many_to_one);
kfree(worker_many_to_one);
kfree(waiter_data);
kfree(worker_data);
// kdebug("completion test done.");
}