summaryrefslogtreecommitdiff
path: root/components/drivers
diff options
context:
space:
mode:
authorBernard Xiong <[email protected]>2017-02-27 00:58:11 +0800
committerBernard Xiong <[email protected]>2017-02-27 00:58:11 +0800
commitc442db81fcdb01ec6404958a90f5e5da8270aaa0 (patch)
tree65fb5dfcdb18884ef1f0d21093a0eb91809de4c5 /components/drivers
parent5e20f41638058f399d2462647abb48341f94cf96 (diff)
[DeviceDrivers] Fix the re-work issue in workqueue.
Diffstat (limited to 'components/drivers')
-rw-r--r--components/drivers/include/rtdevice.h5
-rw-r--r--components/drivers/src/workqueue.c226
2 files changed, 143 insertions, 88 deletions
diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h
index 3b22248c97..74406e82f9 100644
--- a/components/drivers/include/rtdevice.h
+++ b/components/drivers/include/rtdevice.h
@@ -150,8 +150,9 @@ struct rt_data_queue
/* workqueue implementation */
struct rt_workqueue
{
- rt_list_t work_list;
- rt_thread_t work_thread;
+ rt_list_t work_list;
+ struct rt_work *work_current; /* current work */
+ rt_thread_t work_thread;
};
struct rt_work
diff --git a/components/drivers/src/workqueue.c b/components/drivers/src/workqueue.c
index f1aa8c3319..767c991d94 100644
--- a/components/drivers/src/workqueue.c
+++ b/components/drivers/src/workqueue.c
@@ -1,139 +1,193 @@
+/*
+ * File : workqueue.c
+ * This file is part of RT-Thread RTOS
+ * COPYRIGHT (C) 2006 - 2017, RT-Thread Development Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2017-02-27 bernard fix the re-work issue.
+ */
+
+#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#ifdef RT_USING_HEAP
static void _workqueue_thread_entry(void* parameter)
{
- struct rt_work* work;
- struct rt_workqueue* queue;
-
- queue = (struct rt_workqueue*) parameter;
- RT_ASSERT(queue != RT_NULL);
+ rt_base_t level;
+ struct rt_work* work;
+ struct rt_workqueue* queue;
+
+ queue = (struct rt_workqueue*) parameter;
+ RT_ASSERT(queue != RT_NULL);
- while (1)
- {
- if (rt_list_isempty(&(queue->work_list)))
- {
- /* no software timer exist, suspend self. */
- rt_thread_suspend(rt_thread_self());
- rt_schedule();
- }
+ while (1)
+ {
+ if (rt_list_isempty(&(queue->work_list)))
+ {
+ /* no software timer exist, suspend self. */
+ rt_thread_suspend(rt_thread_self());
+ rt_schedule();
+ }
- /* we have work to do with. */
- rt_enter_critical();
- work = rt_list_entry(queue->work_list.next, struct rt_work, list);
- rt_list_remove(&(work->list));
- rt_exit_critical();
+ /* we have work to do with. */
+ level = rt_hw_interrupt_disable();
+ work = rt_list_entry(queue->work_list.next, struct rt_work, list);
+ rt_list_remove(&(work->list));
+ queue->work_current = work;
+ rt_hw_interrupt_enable(level);
- /* do work */
- work->work_func(work, work->work_data);
- }
+ /* do work */
+ work->work_func(work, work->work_data);
+ level = rt_hw_interrupt_disable();
+ /* clean current work */
+ queue->work_current = RT_NULL;
+ rt_hw_interrupt_enable(level);
+ }
}
struct rt_workqueue *rt_workqueue_create(const char* name, rt_uint16_t stack_size, rt_uint8_t priority)
{
- struct rt_workqueue *queue = RT_NULL;
+ struct rt_workqueue *queue = RT_NULL;
- queue = (struct rt_workqueue*)RT_KERNEL_MALLOC(sizeof(struct rt_workqueue));
- if (queue != RT_NULL)
- {
+ queue = (struct rt_workqueue*)RT_KERNEL_MALLOC(sizeof(struct rt_workqueue));
+ if (queue != RT_NULL)
+ {
/* initialize work list */
rt_list_init(&(queue->work_list));
-
- /* create the work thread */
- queue->work_thread = rt_thread_create(name, _workqueue_thread_entry, queue, stack_size, priority, 10);
- if (queue->work_thread == RT_NULL)
- {
- RT_KERNEL_FREE(queue);
- return RT_NULL;
- }
+ queue->work_current = RT_NULL;
+
+ /* create the work thread */
+ queue->work_thread = rt_thread_create(name, _workqueue_thread_entry, queue, stack_size, priority, 10);
+ if (queue->work_thread == RT_NULL)
+ {
+ RT_KERNEL_FREE(queue);
+ return RT_NULL;
+ }
- rt_thread_startup(queue->work_thread);
- }
+ rt_thread_startup(queue->work_thread);
+ }
- return queue;
+ return queue;
}
rt_err_t rt_workqueue_destroy(struct rt_workqueue* queue)
{
- RT_ASSERT(queue != RT_NULL);
+ RT_ASSERT(queue != RT_NULL);
- rt_thread_delete(queue->work_thread);
- RT_KERNEL_FREE(queue);
+ rt_thread_delete(queue->work_thread);
+ RT_KERNEL_FREE(queue);
- return RT_EOK;
+ return RT_EOK;
}
rt_err_t rt_workqueue_dowork(struct rt_workqueue* queue, struct rt_work* work)
{
- RT_ASSERT(queue != RT_NULL);
- RT_ASSERT(work != RT_NULL);
+ rt_base_t level;
+ RT_ASSERT(queue != RT_NULL);
+ RT_ASSERT(work != RT_NULL);
- rt_enter_critical();
- /* NOTE: the work MUST be initialized firstly */
- rt_list_remove(&(work->list));
+ level = rt_hw_interrupt_disable();
+ if (queue->work_current == work)
+ {
+ rt_hw_interrupt_enable(level);
+ return -RT_EBUSY;
+ }
- rt_list_insert_after(queue->work_list.prev, &(work->list));
- if (queue->work_thread->stat != RT_THREAD_READY)
- {
- rt_exit_critical();
- /* resume work thread */
- rt_thread_resume(queue->work_thread);
- rt_schedule();
- }
- else rt_exit_critical();
+ /* NOTE: the work MUST be initialized firstly */
+ rt_list_remove(&(work->list));
- return RT_EOK;
+ rt_list_insert_after(queue->work_list.prev, &(work->list));
+ /* whether the workqueue is doing work */
+ if (queue->work_current == RT_NULL)
+ {
+ rt_hw_interrupt_enable(level);
+ /* resume work thread */
+ rt_thread_resume(queue->work_thread);
+ rt_schedule();
+ }
+ else rt_hw_interrupt_enable(level);
+
+ return RT_EOK;
}
rt_err_t rt_workqueue_critical_work(struct rt_workqueue* queue, struct rt_work* work)
{
- RT_ASSERT(queue != RT_NULL);
- RT_ASSERT(work != RT_NULL);
+ rt_base_t level;
+ RT_ASSERT(queue != RT_NULL);
+ RT_ASSERT(work != RT_NULL);
+
+ level = rt_hw_interrupt_disable();
+ if (queue->work_current == work)
+ {
+ rt_hw_interrupt_enable(level);
+ return -RT_EBUSY;
+ }
- rt_enter_critical();
- /* NOTE: the work MUST be initialized firstly */
- rt_list_remove(&(work->list));
+ /* NOTE: the work MUST be initialized firstly */
+ rt_list_remove(&(work->list));
- rt_list_insert_after(queue->work_list.prev, &(work->list));
- if (queue->work_thread->stat != RT_THREAD_READY)
- {
- rt_exit_critical();
- /* resume work thread */
- rt_thread_resume(queue->work_thread);
- rt_schedule();
- }
- else rt_exit_critical();
+ rt_list_insert_after(queue->work_list.prev, &(work->list));
+ if (queue->work_current == RT_NULL)
+ {
+ rt_hw_interrupt_enable(level);
+ /* resume work thread */
+ rt_thread_resume(queue->work_thread);
+ rt_schedule();
+ }
+ else rt_hw_interrupt_enable(level);
- return RT_EOK;
+ return RT_EOK;
}
rt_err_t rt_workqueue_cancel_work(struct rt_workqueue* queue, struct rt_work* work)
{
- RT_ASSERT(queue != RT_NULL);
- RT_ASSERT(work != RT_NULL);
+ rt_base_t level;
+
+ RT_ASSERT(queue != RT_NULL);
+ RT_ASSERT(work != RT_NULL);
- rt_enter_critical();
- rt_list_remove(&(work->list));
- rt_exit_critical();
+ level = rt_hw_interrupt_disable();
+ if (queue->work_current == work)
+ {
+ rt_hw_interrupt_enable(level);
+ return -RT_EBUSY;
+ }
+ rt_list_remove(&(work->list));
+ rt_hw_interrupt_enable(level);
- return RT_EOK;
+ return RT_EOK;
}
rt_err_t rt_workqueue_cancel_all_work(struct rt_workqueue* queue)
{
- struct rt_list_node *node, *next;
- RT_ASSERT(queue != RT_NULL);
+ struct rt_list_node *node, *next;
+ RT_ASSERT(queue != RT_NULL);
- rt_enter_critical();
- for (node = queue->work_list.next; node != &(queue->work_list); node = next)
- {
- next = node->next;
- rt_list_remove(node);
- }
- rt_exit_critical();
+ rt_enter_critical();
+ for (node = queue->work_list.next; node != &(queue->work_list); node = next)
+ {
+ next = node->next;
+ rt_list_remove(node);
+ }
+ rt_exit_critical();
- return RT_EOK;
+ return RT_EOK;
}
#endif