xref: /nrf52832-nimble/rt-thread/components/vbus/watermark_queue.h (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2  * COPYRIGHT (C) 2018, Real-Thread Information Technology Ltd
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2014-04-16     Grissiom     first version
9  */
10 
11 struct rt_watermark_queue
12 {
13     /* Current water level. */
14     unsigned int level;
15     unsigned int high_mark;
16     unsigned int low_mark;
17     rt_list_t suspended_threads;
18 };
19 
20 /** Init the struct rt_watermark_queue.
21  */
22 void rt_wm_que_init(struct rt_watermark_queue *wg,
23                     unsigned int low, unsigned int high);
24 void rt_wm_que_set_mark(struct rt_watermark_queue *wg,
25                         unsigned int low, unsigned int high);
26 void rt_wm_que_dump(struct rt_watermark_queue *wg);
27 
28 /* Water marks are often used in performance critical places. Benchmark shows
29  * inlining functions will have 10% performance gain in some situation(for
30  * example, VBus). So keep the inc/dec compact and inline. */
31 
32 /** Increase the water level.
33  *
34  * It should be called in the thread that want to raise the water level. If the
35  * current level is above the high mark, the thread will be suspended up to
36  * @timeout ticks.
37  *
38  * @return RT_EOK if water level increased successfully. -RT_EFULL on @timeout
39  * is zero and the level is above water mark. -RT_ETIMEOUT if timeout occurred.
40  */
rt_wm_que_inc(struct rt_watermark_queue * wg,int timeout)41 rt_inline rt_err_t rt_wm_que_inc(struct rt_watermark_queue *wg,
42                                  int timeout)
43 {
44     rt_base_t ilvl;
45 
46     /* Assert as early as possible. */
47     if (timeout != 0)
48     {
49         RT_DEBUG_IN_THREAD_CONTEXT;
50     }
51 
52     ilvl = rt_hw_interrupt_disable();
53 
54     while (wg->level > wg->high_mark)
55     {
56         rt_thread_t thread;
57 
58         if (timeout == 0)
59         {
60             rt_hw_interrupt_enable(ilvl);
61             return -RT_EFULL;
62         }
63 
64         thread = rt_thread_self();
65         thread->error = RT_EOK;
66         rt_thread_suspend(thread);
67         rt_list_insert_after(&wg->suspended_threads, &thread->tlist);
68         if (timeout > 0)
69         {
70             rt_timer_control(&(thread->thread_timer),
71                              RT_TIMER_CTRL_SET_TIME,
72                              &timeout);
73             rt_timer_start(&(thread->thread_timer));
74         }
75         rt_hw_interrupt_enable(ilvl);
76         rt_schedule();
77         if (thread->error != RT_EOK)
78             return thread->error;
79 
80         ilvl = rt_hw_interrupt_disable();
81     }
82 
83     wg->level++;
84 
85     if (wg->level == 0)
86     {
87         wg->level = ~0;
88     }
89 
90     rt_hw_interrupt_enable(ilvl);
91 
92     return RT_EOK;
93 }
94 
95 /** Decrease the water level.
96  *
97  * It should be called by the consumer that drain the water out. If the water
98  * level reached low mark, all the thread suspended in this queue will be waken
99  * up. It's safe to call this function in interrupt context.
100  */
rt_wm_que_dec(struct rt_watermark_queue * wg)101 rt_inline void rt_wm_que_dec(struct rt_watermark_queue *wg)
102 {
103     int need_sched = 0;
104     rt_base_t ilvl;
105 
106     if (wg->level == 0)
107         return;
108 
109     ilvl = rt_hw_interrupt_disable();
110     wg->level--;
111     if (wg->level == wg->low_mark)
112     {
113         /* There should be spaces between the low mark and high mark, so it's
114          * safe to resume all the threads. */
115         while (!rt_list_isempty(&wg->suspended_threads))
116         {
117             rt_thread_t thread;
118 
119             thread = rt_list_entry(wg->suspended_threads.next,
120                                    struct rt_thread,
121                                    tlist);
122             rt_thread_resume(thread);
123             need_sched = 1;
124         }
125     }
126     rt_hw_interrupt_enable(ilvl);
127 
128     if (need_sched)
129         rt_schedule();
130 }
131