RT-Thread / RT-Thread/rt-thread

[Bug] 关于workqueue的一些问题

Open
#11,144 0 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C
Stars
12.2k
Forks
5.4k
Avg merge
4d 12h
Merged PRs (30d)
40

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);

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the workqueue implementation and the entry points named in the report: rt_workqueue_cancel_work_sync, rt_workqueue_destroy, rt_workqueue_cancel_all_work, and queue->work_current. Run the two provided MSH test commands to reproduce cancellation and destruction during a yielding work item; done requires an agreed, synchronized behavior for these cases on single- and multicore systems.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
embedded-iot, operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.