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