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