xiuos3/kernel/thread/workqueue.c

183 lines
5.0 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: workqueue.c
* @brief: workqueue file
* @version: 1.0
* @author: AIIT XUOS Lab
* @date: 2020/3/15
*
*/
#include <xiuos.h>
#include <device.h>
queue *sys_workq = NONE;
static void WorkQueueKTaskEntry(void *parameter)
{
x_base lock = 0;
WorkqueueType *p_queue = (WorkqueueType *)parameter;
while (1)
{
lock = CriticalAreaLock();
if (p_queue->front != p_queue->rear) { // The team is not empty
struct Work *work = &(p_queue->base[p_queue->front]);
p_queue->front = (p_queue->front + 1) % MAX_WORK_QUEUE;
CriticalAreaUnLock(lock);
if (work->cb_func) {
work->cb_func(work, work->cb_param);
}
} else {
CriticalAreaUnLock(lock);
SuspendKTask(p_queue->task);
DO_KTASK_ASSIGN;
}
}
}
void DeInitWorkQueue(WorkqueueType *p_queue)
{
if (NONE != p_queue->task) {
KTaskDelete(p_queue->task);
}
if (NONE != p_queue) {
if (NONE != p_queue->base) {
x_free(p_queue->base);
}
x_free(p_queue);
}
}
WorkqueueType *CreateWorkQueue(const char *name, uint16 stack_size, uint8 priority)
{
WorkqueueType *p_queue = NONE;
do {
p_queue = x_malloc(sizeof(WorkqueueType));
if (NONE == p_queue) {
KPrintf("CreateWorkQueue x_malloc(sizeof(WorkqueueType) failed\n");
break;
}
memset(p_queue, 0, sizeof(WorkqueueType));
p_queue->base = x_malloc(MAX_WORK_QUEUE * sizeof(struct Work));
if (NONE == p_queue->base) {
KPrintf("CreateWorkQueue x_malloc(MAX_WORK_QUEUE * sizeof(struct Work) failed\n");
break;
}
memset(p_queue->base, 0, MAX_WORK_QUEUE * sizeof(struct Work));
p_queue->front = p_queue->rear = 0;
// start work thread
p_queue->task = KTaskCreate(name, WorkQueueKTaskEntry, p_queue, 2048, priority);
if (NONE == p_queue->task) {
KPrintf("CreateWorkQueue KTaskCreate failed\n");
break;
}
StartupKTask(p_queue->task);
return p_queue;
} while (0);
DeInitWorkQueue(p_queue);
return NONE;
}
void WorkInit(struct Work *work, void (*work_func)(struct Work *work, void *work_data), void *work_data)
{
work->cb_func = work_func;
work->cb_param = work_data;
}
void work_elem_timeout(void *in_param)
{
WorkTimerParamType *param = (WorkTimerParamType *)in_param;
WorkSubmit_immediate(param->p_queue, param->p_work);
}
x_err_t WorkSubmit_immediate(WorkqueueType *sys_workq, struct Work *work)
{
x_base lock = CriticalAreaLock();
NULL_PARAM_CHECK(sys_workq);
NULL_PARAM_CHECK(work);
// check queue is full
if ((sys_workq->rear + 1 + MAX_WORK_QUEUE) % MAX_WORK_QUEUE == sys_workq->front) {
CriticalAreaUnLock(lock);
return -ERROR;
}
// add to queue
sys_workq->base[sys_workq->rear].cb_func = work->cb_func;
sys_workq->base[sys_workq->rear].cb_param = work->cb_param;
sys_workq->rear = (sys_workq->rear + 1) % MAX_WORK_QUEUE;
// awake work thread
KTaskWakeup(sys_workq->task);
CriticalAreaUnLock(lock);
DO_KTASK_ASSIGN;
}
x_err_t WorkSubmit(WorkqueueType *sys_workq, struct Work *work, x_ticks_t time)
{
NULL_PARAM_CHECK(sys_workq);
NULL_PARAM_CHECK(work);
if (0 == time) {
WorkSubmit_immediate(sys_workq, work);
} else {
// init a timer, add to queue later
WorkTimerParamType *param = x_malloc(sizeof(WorkTimerParamType));
param->p_queue = sys_workq;
param->p_work = work;
param->p_timer = KCreateTimer("wk_timer", work_elem_timeout, param, time, TIMER_TRIGGER_ONCE);
if (NONE == param->p_timer) {
KPrintf("WorkSubmit KCreateTimer failed \n");
x_free(param);
return -ERROR;
}
KTimerStartRun(param->p_timer);
}
return EOK;
}
/**
* This function will create a workqueue.
*
* @return EOK on success;ERROR on failure
*/
int WorkSysWorkQueueInit(void)
{
if (sys_workq != NONE)
return 0;
sys_workq = x_malloc(sizeof(queue));
sys_workq->done = g_queue_done[WORK_QUEUE];
sys_workq->property = ((WorkQueueDoneType*)sys_workq->done)->CreateWorkQueue("sys_work", WORKQUEUE_KTASK_STACKSIZE,
WORKQUEUE_KTASK_PRIORITY);
if (sys_workq->property == NONE) {
KPrintf("log sys_workq create failed\n");
} else {
KPrintf("log sys_workq create success\n");
}
return sys_workq->property == NONE ? ERROR : EOK;
}