1use esp_hal::{system::Cpu, time::Instant};
6use esp_sync::NonReentrantMutex;
7
8use crate::{
9 SCHEDULER,
10 run_queue::Priority,
11 task::{TaskExt, TaskPtr},
12 wait_queue::WaitQueue,
13};
14
15enum SemaphoreInner {
16 Counting {
17 current: u32,
18 max: u32,
19 waiting: WaitQueue,
20 },
21 Mutex {
22 recursive: bool,
23 owner: Option<TaskPtr>,
24 original_priority: Priority,
25 lock_counter: u32,
26 waiting: WaitQueue,
27 },
28}
29
30impl SemaphoreInner {
31 fn try_take(&mut self) -> bool {
32 match self {
33 SemaphoreInner::Counting { current, .. } => {
34 if *current > 0 {
35 *current -= 1;
36 true
37 } else {
38 false
39 }
40 }
41 SemaphoreInner::Mutex {
42 recursive,
43 owner,
44 lock_counter,
45 original_priority,
46 ..
47 } => {
48 SCHEDULER.with(|scheduler| {
49 let current = scheduler.current_task(Cpu::current());
50 if let Some(owner) = owner {
51 if *owner == current && *recursive {
52 *lock_counter += 1;
53 true
54 } else {
55 let current_priority = current.priority(&mut scheduler.run_queue);
58 if owner.priority(&mut scheduler.run_queue) < current_priority {
59 owner.set_priority(&mut scheduler.run_queue, current_priority);
60 scheduler.resume_task(*owner);
61 }
62 false
63 }
64 } else {
65 *owner = Some(current);
66 *lock_counter += 1;
67 *original_priority = current.priority(&mut scheduler.run_queue);
68 true
69 }
70 })
71 }
72 }
73 }
74
75 fn try_give(&mut self) -> bool {
76 match self {
77 SemaphoreInner::Counting { current, max, .. } => {
78 if *current < *max {
79 *current += 1;
80 true
81 } else {
82 false
83 }
84 }
85 SemaphoreInner::Mutex {
86 owner,
87 lock_counter,
88 original_priority,
89 ..
90 } => SCHEDULER.with(|scheduler| {
91 let current_cpu = Cpu::current() as usize;
92 let current = unwrap!(scheduler.per_cpu[current_cpu].current_task);
93
94 if *owner == Some(current) && *lock_counter > 0 {
95 *lock_counter -= 1;
96 if *lock_counter == 0
97 && let Some(owner) = owner.take()
98 {
99 owner.set_priority(&mut scheduler.run_queue, *original_priority);
100 }
101 true
102 } else {
103 false
104 }
105 }),
106 }
107 }
108
109 fn current_count(&mut self) -> u32 {
110 match self {
111 SemaphoreInner::Counting { current, .. } => *current,
112 SemaphoreInner::Mutex { .. } => {
113 panic!("RecursiveMutex does not support current_count")
114 }
115 }
116 }
117
118 fn wait_with_deadline(&mut self, deadline: Instant) {
119 trace!("Semaphore wait_with_deadline - {:?}", deadline);
120 match self {
121 SemaphoreInner::Counting { waiting, .. } => waiting.wait_with_deadline(deadline),
122 SemaphoreInner::Mutex { waiting, .. } => waiting.wait_with_deadline(deadline),
123 }
124 }
125
126 fn notify(&mut self) {
127 trace!("Semaphore notify");
128 match self {
129 SemaphoreInner::Counting { waiting, .. } => waiting.notify(),
130 SemaphoreInner::Mutex { waiting, .. } => waiting.notify(),
131 }
132 }
133}
134
135pub struct Semaphore {
137 inner: NonReentrantMutex<SemaphoreInner>,
138}
139
140impl Semaphore {
141 pub const fn new_counting(initial: u32, max: u32) -> Self {
143 Semaphore {
144 inner: NonReentrantMutex::new(SemaphoreInner::Counting {
145 current: initial,
146 max,
147 waiting: WaitQueue::new(),
148 }),
149 }
150 }
151
152 pub const fn new_mutex(recursive: bool) -> Self {
156 Semaphore {
157 inner: NonReentrantMutex::new(SemaphoreInner::Mutex {
158 recursive,
159 owner: None,
160 lock_counter: 0,
161 original_priority: Priority::new(0),
162 waiting: WaitQueue::new(),
163 }),
164 }
165 }
166
167 pub fn try_take(&self) -> bool {
171 self.inner.with(|sem| sem.try_take())
172 }
173
174 pub fn take(&self, timeout_us: Option<u32>) -> bool {
181 if crate::with_deadline(timeout_us, |deadline| {
182 self.inner.with(|sem| {
183 if sem.try_take() {
184 true
185 } else {
186 sem.wait_with_deadline(deadline);
188 false
189 }
190 })
191 }) {
192 debug!("Semaphore - take - success");
193 true
194 } else {
195 debug!("Semaphore - take - timed out");
196 false
197 }
198 }
199
200 pub fn current_count(&self) -> u32 {
202 self.inner.with(|sem| sem.current_count())
203 }
204
205 pub fn give(&self) -> bool {
207 self.inner.with(|sem| {
208 if sem.try_give() {
209 sem.notify();
210 true
211 } else {
212 false
213 }
214 })
215 }
216}