-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Open
Description
RT-Thread Version
5.2.2-master
Hardware Type/Architectures
All
Develop Toolchain
Other
Describe the bug
rt_workqueue_cancel_work_sync 在某些情况下是无法彻底取消任务的,某些任务会在执行完成后重新将自身提交到工作队列中,但是工作队列的删除是在执行任务前执行的,导致等待到信号量后,任务对象仍然在工作队列中,这会导致很多潜在的问题。例如调用者在同步取消任务后,可能会释放一些共享资源,结果任务重新执行访问野指针导致系统崩溃
rt_workqueue_destroy 没有同步取消任务,就盲目将工作线程关闭,如果工作线程执行的任务申请了一些资源,还没有释放,被强行关闭后,这部分资源就得不到释放
rt_workqueue_cancel_all_work 使用了rt_enter_critical来保护临界资源,但是在多核系统上,这是不安全的,其他接口使用的也是自旋锁的方式实现的临界保护
另外queue->work_current这个成员我认为需要保证原子性,有些地方并没有做到这一点
下面是一些测试代码,这些写法正常人不会这么写,这里只是为了模拟一些极端情况下可能出现的抢占问题,当然可能是我的理解有问题,如果各位认同我的这些看法的话,我可以提交一个PR
#include <rtthread.h>
#include <ipc/workqueue.h>
static void work_func(struct rt_work *work, void *data)
{
rt_kprintf("%d\n", rt_tick_get());
char *buf = rt_malloc(128);
// 一些其他工作
rt_thread_delay(RT_TICK_PER_SECOND / 100); // 模拟被抢占
rt_free(buf);
rt_workqueue_submit_work((struct rt_workqueue *)data, work, RT_TICK_PER_SECOND / 100); // 重新提交任务
}
static int wq_cancel_work_sync_test(int argc, char *argv[])
{
// 保证工作队列线程优先级高于shell优先级,主要是为了模拟工作队列执行过程中被抢占
struct rt_workqueue *wq = rt_workqueue_create("test", 512, 1);
struct rt_work work;
rt_work_init(&work, work_func, wq);
rt_workqueue_submit_work(wq, &work, 0);
rt_workqueue_cancel_work_sync(wq, &work);
return 0; // work是栈上分配的,这里退出后,work实际还在工作队列中,会导致问题,这里这样做是为了直观体现rt_workqueue_cancel_work_sync并不可靠
}
MSH_CMD_EXPORT(wq_cancel_work_sync_test, workqueue cancel work sync test);
static int wq_destroy_test(int argc, char *argv[])
{
// 保证工作队列线程优先级高于shell优先级,主要是为了模拟工作队列执行过程中被抢占
struct rt_workqueue *wq = rt_workqueue_create("test", 512, 1);
struct rt_work work;
rt_work_init(&work, work_func, wq);
rt_workqueue_submit_work(wq, &work, 0); // 这里提交任务后,任务会立即执行,但是任务中途又让出了CPU
rt_workqueue_destroy(wq); // 任务让出CPU后,顺利执行到这里,销毁工作队列,但是工作队列中分配的内存将不会被释放
return 0;
}
MSH_CMD_EXPORT(wq_destroy_test, workqueue destroy test);Ryan-CW-Code and illustriousness
Metadata
Metadata
Assignees
Labels
No labels