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 */ 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 */ 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