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