1*10465441SEvalZero /*
2*10465441SEvalZero * Copyright (c) 2006-2018, RT-Thread Development Team
3*10465441SEvalZero *
4*10465441SEvalZero * SPDX-License-Identifier: Apache-2.0
5*10465441SEvalZero *
6*10465441SEvalZero * Change Logs:
7*10465441SEvalZero * Date Author Notes
8*10465441SEvalZero * 2017-02-27 bernard fix the re-work issue.
9*10465441SEvalZero */
10*10465441SEvalZero
11*10465441SEvalZero #include <rthw.h>
12*10465441SEvalZero #include <rtthread.h>
13*10465441SEvalZero #include <rtdevice.h>
14*10465441SEvalZero
15*10465441SEvalZero #ifdef RT_USING_HEAP
_workqueue_work_completion(struct rt_workqueue * queue)16*10465441SEvalZero rt_inline rt_err_t _workqueue_work_completion(struct rt_workqueue *queue)
17*10465441SEvalZero {
18*10465441SEvalZero rt_err_t result;
19*10465441SEvalZero
20*10465441SEvalZero rt_enter_critical();
21*10465441SEvalZero while (1)
22*10465441SEvalZero {
23*10465441SEvalZero /* try to take condition semaphore */
24*10465441SEvalZero result = rt_sem_trytake(&(queue->sem));
25*10465441SEvalZero if (result == -RT_ETIMEOUT)
26*10465441SEvalZero {
27*10465441SEvalZero /* it's timeout, release this semaphore */
28*10465441SEvalZero rt_sem_release(&(queue->sem));
29*10465441SEvalZero }
30*10465441SEvalZero else if (result == RT_EOK)
31*10465441SEvalZero {
32*10465441SEvalZero /* keep the sem value = 0 */
33*10465441SEvalZero result = RT_EOK;
34*10465441SEvalZero break;
35*10465441SEvalZero }
36*10465441SEvalZero else
37*10465441SEvalZero {
38*10465441SEvalZero result = -RT_ERROR;
39*10465441SEvalZero break;
40*10465441SEvalZero }
41*10465441SEvalZero }
42*10465441SEvalZero rt_exit_critical();
43*10465441SEvalZero
44*10465441SEvalZero return result;
45*10465441SEvalZero }
46*10465441SEvalZero
_workqueue_thread_entry(void * parameter)47*10465441SEvalZero static void _workqueue_thread_entry(void* parameter)
48*10465441SEvalZero {
49*10465441SEvalZero rt_base_t level;
50*10465441SEvalZero struct rt_work* work;
51*10465441SEvalZero struct rt_workqueue* queue;
52*10465441SEvalZero
53*10465441SEvalZero queue = (struct rt_workqueue*) parameter;
54*10465441SEvalZero RT_ASSERT(queue != RT_NULL);
55*10465441SEvalZero
56*10465441SEvalZero while (1)
57*10465441SEvalZero {
58*10465441SEvalZero if (rt_list_isempty(&(queue->work_list)))
59*10465441SEvalZero {
60*10465441SEvalZero /* no software timer exist, suspend self. */
61*10465441SEvalZero rt_thread_suspend(rt_thread_self());
62*10465441SEvalZero rt_schedule();
63*10465441SEvalZero }
64*10465441SEvalZero
65*10465441SEvalZero /* we have work to do with. */
66*10465441SEvalZero level = rt_hw_interrupt_disable();
67*10465441SEvalZero work = rt_list_entry(queue->work_list.next, struct rt_work, list);
68*10465441SEvalZero rt_list_remove(&(work->list));
69*10465441SEvalZero queue->work_current = work;
70*10465441SEvalZero rt_hw_interrupt_enable(level);
71*10465441SEvalZero
72*10465441SEvalZero /* do work */
73*10465441SEvalZero work->work_func(work, work->work_data);
74*10465441SEvalZero level = rt_hw_interrupt_disable();
75*10465441SEvalZero /* clean current work */
76*10465441SEvalZero queue->work_current = RT_NULL;
77*10465441SEvalZero rt_hw_interrupt_enable(level);
78*10465441SEvalZero
79*10465441SEvalZero /* ack work completion */
80*10465441SEvalZero _workqueue_work_completion(queue);
81*10465441SEvalZero }
82*10465441SEvalZero }
83*10465441SEvalZero
rt_workqueue_create(const char * name,rt_uint16_t stack_size,rt_uint8_t priority)84*10465441SEvalZero struct rt_workqueue *rt_workqueue_create(const char* name, rt_uint16_t stack_size, rt_uint8_t priority)
85*10465441SEvalZero {
86*10465441SEvalZero struct rt_workqueue *queue = RT_NULL;
87*10465441SEvalZero
88*10465441SEvalZero queue = (struct rt_workqueue*)RT_KERNEL_MALLOC(sizeof(struct rt_workqueue));
89*10465441SEvalZero if (queue != RT_NULL)
90*10465441SEvalZero {
91*10465441SEvalZero /* initialize work list */
92*10465441SEvalZero rt_list_init(&(queue->work_list));
93*10465441SEvalZero queue->work_current = RT_NULL;
94*10465441SEvalZero rt_sem_init(&(queue->sem), "wqueue", 0, RT_IPC_FLAG_FIFO);
95*10465441SEvalZero
96*10465441SEvalZero /* create the work thread */
97*10465441SEvalZero queue->work_thread = rt_thread_create(name, _workqueue_thread_entry, queue, stack_size, priority, 10);
98*10465441SEvalZero if (queue->work_thread == RT_NULL)
99*10465441SEvalZero {
100*10465441SEvalZero RT_KERNEL_FREE(queue);
101*10465441SEvalZero return RT_NULL;
102*10465441SEvalZero }
103*10465441SEvalZero
104*10465441SEvalZero rt_thread_startup(queue->work_thread);
105*10465441SEvalZero }
106*10465441SEvalZero
107*10465441SEvalZero return queue;
108*10465441SEvalZero }
109*10465441SEvalZero
rt_workqueue_destroy(struct rt_workqueue * queue)110*10465441SEvalZero rt_err_t rt_workqueue_destroy(struct rt_workqueue* queue)
111*10465441SEvalZero {
112*10465441SEvalZero RT_ASSERT(queue != RT_NULL);
113*10465441SEvalZero
114*10465441SEvalZero rt_thread_delete(queue->work_thread);
115*10465441SEvalZero RT_KERNEL_FREE(queue);
116*10465441SEvalZero
117*10465441SEvalZero return RT_EOK;
118*10465441SEvalZero }
119*10465441SEvalZero
rt_workqueue_dowork(struct rt_workqueue * queue,struct rt_work * work)120*10465441SEvalZero rt_err_t rt_workqueue_dowork(struct rt_workqueue* queue, struct rt_work* work)
121*10465441SEvalZero {
122*10465441SEvalZero rt_base_t level;
123*10465441SEvalZero RT_ASSERT(queue != RT_NULL);
124*10465441SEvalZero RT_ASSERT(work != RT_NULL);
125*10465441SEvalZero
126*10465441SEvalZero level = rt_hw_interrupt_disable();
127*10465441SEvalZero if (queue->work_current == work)
128*10465441SEvalZero {
129*10465441SEvalZero rt_hw_interrupt_enable(level);
130*10465441SEvalZero return -RT_EBUSY;
131*10465441SEvalZero }
132*10465441SEvalZero
133*10465441SEvalZero /* NOTE: the work MUST be initialized firstly */
134*10465441SEvalZero rt_list_remove(&(work->list));
135*10465441SEvalZero
136*10465441SEvalZero rt_list_insert_after(queue->work_list.prev, &(work->list));
137*10465441SEvalZero /* whether the workqueue is doing work */
138*10465441SEvalZero if (queue->work_current == RT_NULL)
139*10465441SEvalZero {
140*10465441SEvalZero rt_hw_interrupt_enable(level);
141*10465441SEvalZero /* resume work thread */
142*10465441SEvalZero rt_thread_resume(queue->work_thread);
143*10465441SEvalZero rt_schedule();
144*10465441SEvalZero }
145*10465441SEvalZero else rt_hw_interrupt_enable(level);
146*10465441SEvalZero
147*10465441SEvalZero return RT_EOK;
148*10465441SEvalZero }
149*10465441SEvalZero
rt_workqueue_critical_work(struct rt_workqueue * queue,struct rt_work * work)150*10465441SEvalZero rt_err_t rt_workqueue_critical_work(struct rt_workqueue* queue, struct rt_work* work)
151*10465441SEvalZero {
152*10465441SEvalZero rt_base_t level;
153*10465441SEvalZero RT_ASSERT(queue != RT_NULL);
154*10465441SEvalZero RT_ASSERT(work != RT_NULL);
155*10465441SEvalZero
156*10465441SEvalZero level = rt_hw_interrupt_disable();
157*10465441SEvalZero if (queue->work_current == work)
158*10465441SEvalZero {
159*10465441SEvalZero rt_hw_interrupt_enable(level);
160*10465441SEvalZero return -RT_EBUSY;
161*10465441SEvalZero }
162*10465441SEvalZero
163*10465441SEvalZero /* NOTE: the work MUST be initialized firstly */
164*10465441SEvalZero rt_list_remove(&(work->list));
165*10465441SEvalZero
166*10465441SEvalZero rt_list_insert_after(queue->work_list.prev, &(work->list));
167*10465441SEvalZero if (queue->work_current == RT_NULL)
168*10465441SEvalZero {
169*10465441SEvalZero rt_hw_interrupt_enable(level);
170*10465441SEvalZero /* resume work thread */
171*10465441SEvalZero rt_thread_resume(queue->work_thread);
172*10465441SEvalZero rt_schedule();
173*10465441SEvalZero }
174*10465441SEvalZero else rt_hw_interrupt_enable(level);
175*10465441SEvalZero
176*10465441SEvalZero return RT_EOK;
177*10465441SEvalZero }
178*10465441SEvalZero
rt_workqueue_cancel_work(struct rt_workqueue * queue,struct rt_work * work)179*10465441SEvalZero rt_err_t rt_workqueue_cancel_work(struct rt_workqueue* queue, struct rt_work* work)
180*10465441SEvalZero {
181*10465441SEvalZero rt_base_t level;
182*10465441SEvalZero
183*10465441SEvalZero RT_ASSERT(queue != RT_NULL);
184*10465441SEvalZero RT_ASSERT(work != RT_NULL);
185*10465441SEvalZero
186*10465441SEvalZero level = rt_hw_interrupt_disable();
187*10465441SEvalZero if (queue->work_current == work)
188*10465441SEvalZero {
189*10465441SEvalZero rt_hw_interrupt_enable(level);
190*10465441SEvalZero return -RT_EBUSY;
191*10465441SEvalZero }
192*10465441SEvalZero rt_list_remove(&(work->list));
193*10465441SEvalZero rt_hw_interrupt_enable(level);
194*10465441SEvalZero
195*10465441SEvalZero return RT_EOK;
196*10465441SEvalZero }
197*10465441SEvalZero
rt_workqueue_cancel_work_sync(struct rt_workqueue * queue,struct rt_work * work)198*10465441SEvalZero rt_err_t rt_workqueue_cancel_work_sync(struct rt_workqueue* queue, struct rt_work* work)
199*10465441SEvalZero {
200*10465441SEvalZero rt_base_t level;
201*10465441SEvalZero
202*10465441SEvalZero RT_ASSERT(queue != RT_NULL);
203*10465441SEvalZero RT_ASSERT(work != RT_NULL);
204*10465441SEvalZero
205*10465441SEvalZero level = rt_hw_interrupt_disable();
206*10465441SEvalZero if (queue->work_current == work) /* it's current work in the queue */
207*10465441SEvalZero {
208*10465441SEvalZero /* wait for work completion */
209*10465441SEvalZero rt_sem_take(&(queue->sem), RT_WAITING_FOREVER);
210*10465441SEvalZero }
211*10465441SEvalZero else
212*10465441SEvalZero {
213*10465441SEvalZero rt_list_remove(&(work->list));
214*10465441SEvalZero }
215*10465441SEvalZero rt_hw_interrupt_enable(level);
216*10465441SEvalZero
217*10465441SEvalZero return RT_EOK;
218*10465441SEvalZero }
219*10465441SEvalZero
rt_workqueue_cancel_all_work(struct rt_workqueue * queue)220*10465441SEvalZero rt_err_t rt_workqueue_cancel_all_work(struct rt_workqueue* queue)
221*10465441SEvalZero {
222*10465441SEvalZero struct rt_list_node *node, *next;
223*10465441SEvalZero RT_ASSERT(queue != RT_NULL);
224*10465441SEvalZero
225*10465441SEvalZero rt_enter_critical();
226*10465441SEvalZero for (node = queue->work_list.next; node != &(queue->work_list); node = next)
227*10465441SEvalZero {
228*10465441SEvalZero next = node->next;
229*10465441SEvalZero rt_list_remove(node);
230*10465441SEvalZero }
231*10465441SEvalZero rt_exit_critical();
232*10465441SEvalZero
233*10465441SEvalZero return RT_EOK;
234*10465441SEvalZero }
235*10465441SEvalZero
236*10465441SEvalZero #endif
237*10465441SEvalZero
238