aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorivmarkov <[email protected]>2022-10-24 09:17:43 +0300
committerivmarkov <[email protected]>2022-10-24 09:17:43 +0300
commit4d5550070fe5e80ff2296a71239c568c774b9ceb (patch)
tree3248eb5c70b9dd5402c5edc049cafc31a5f66ed3
parent53608a87ac4b6c8c60b5508551d12f5ba76ca2f6 (diff)
Change time Driver contract to never fire the alarm synchronously
-rw-r--r--embassy-executor/src/raw/mod.rs72
-rw-r--r--embassy-nrf/src/time_driver.rs17
-rw-r--r--embassy-rp/src/timer.rs17
-rw-r--r--embassy-stm32/src/time_driver.rs18
-rw-r--r--embassy-time/src/driver.rs13
-rw-r--r--embassy-time/src/driver_std.rs4
-rw-r--r--embassy-time/src/driver_wasm.rs4
-rw-r--r--embassy-time/src/queue_generic.rs77
8 files changed, 109 insertions, 113 deletions
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs
index e1258ebb5..5bcb1e6e7 100644
--- a/embassy-executor/src/raw/mod.rs
+++ b/embassy-executor/src/raw/mod.rs
@@ -354,46 +354,54 @@ impl Executor {
354 /// somehow schedule for `poll()` to be called later, at a time you know for sure there's 354 /// somehow schedule for `poll()` to be called later, at a time you know for sure there's
355 /// no `poll()` already running. 355 /// no `poll()` already running.
356 pub unsafe fn poll(&'static self) { 356 pub unsafe fn poll(&'static self) {
357 #[cfg(feature = "integrated-timers")] 357 loop {
358 self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task)); 358 #[cfg(feature = "integrated-timers")]
359 self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task));
359 360
360 self.run_queue.dequeue_all(|p| { 361 self.run_queue.dequeue_all(|p| {
361 let task = p.as_ref(); 362 let task = p.as_ref();
362 363
363 #[cfg(feature = "integrated-timers")] 364 #[cfg(feature = "integrated-timers")]
364 task.expires_at.set(Instant::MAX); 365 task.expires_at.set(Instant::MAX);
365 366
366 let state = task.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel); 367 let state = task.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel);
367 if state & STATE_SPAWNED == 0 { 368 if state & STATE_SPAWNED == 0 {
368 // If task is not running, ignore it. This can happen in the following scenario: 369 // If task is not running, ignore it. This can happen in the following scenario:
369 // - Task gets dequeued, poll starts 370 // - Task gets dequeued, poll starts
370 // - While task is being polled, it gets woken. It gets placed in the queue. 371 // - While task is being polled, it gets woken. It gets placed in the queue.
371 // - Task poll finishes, returning done=true 372 // - Task poll finishes, returning done=true
372 // - RUNNING bit is cleared, but the task is already in the queue. 373 // - RUNNING bit is cleared, but the task is already in the queue.
373 return; 374 return;
374 } 375 }
375 376
376 #[cfg(feature = "rtos-trace")] 377 #[cfg(feature = "rtos-trace")]
377 trace::task_exec_begin(p.as_ptr() as u32); 378 trace::task_exec_begin(p.as_ptr() as u32);
378 379
379 // Run the task 380 // Run the task
380 task.poll_fn.read()(p as _); 381 task.poll_fn.read()(p as _);
381 382
382 #[cfg(feature = "rtos-trace")] 383 #[cfg(feature = "rtos-trace")]
383 trace::task_exec_end(); 384 trace::task_exec_end();
385
386 // Enqueue or update into timer_queue
387 #[cfg(feature = "integrated-timers")]
388 self.timer_queue.update(p);
389 });
384 390
385 // Enqueue or update into timer_queue
386 #[cfg(feature = "integrated-timers")] 391 #[cfg(feature = "integrated-timers")]
387 self.timer_queue.update(p); 392 {
388 }); 393 // If this is already in the past, set_alarm might return false
394 // In that case do another poll loop iteration.
395 let next_expiration = self.timer_queue.next_expiration();
396 if driver::set_alarm(self.alarm, next_expiration.as_ticks()) {
397 break;
398 }
399 }
389 400
390 #[cfg(feature = "integrated-timers")] 401 #[cfg(not(feature = "integrated-timers"))]
391 { 402 {
392 // If this is already in the past, set_alarm will immediately trigger the alarm. 403 break;
393 // This will cause `signal_fn` to be called, which will cause `poll()` to be called again, 404 }
394 // so we immediately do another poll loop iteration.
395 let next_expiration = self.timer_queue.next_expiration();
396 driver::set_alarm(self.alarm, next_expiration.as_ticks());
397 } 405 }
398 406
399 #[cfg(feature = "rtos-trace")] 407 #[cfg(feature = "rtos-trace")]
diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs
index c32a44637..0d03ad529 100644
--- a/embassy-nrf/src/time_driver.rs
+++ b/embassy-nrf/src/time_driver.rs
@@ -243,20 +243,19 @@ impl Driver for RtcDriver {
243 }) 243 })
244 } 244 }
245 245
246 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { 246 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
247 critical_section::with(|cs| { 247 critical_section::with(|cs| {
248 let n = alarm.id() as _;
249 let alarm = self.get_alarm(cs, alarm);
250 alarm.timestamp.set(timestamp);
251
252 let t = self.now(); 248 let t = self.now();
253 249
254 // If alarm timestamp has passed, trigger it instantly. 250 // If alarm timestamp has passed don't set the alarm and return `false` to indicate that.
255 if timestamp <= t { 251 if timestamp <= t {
256 self.trigger_alarm(n, cs); 252 return false;
257 return;
258 } 253 }
259 254
255 let n = alarm.id() as _;
256 let alarm = self.get_alarm(cs, alarm);
257 alarm.timestamp.set(timestamp);
258
260 let r = rtc(); 259 let r = rtc();
261 260
262 // If it hasn't triggered yet, setup it in the compare channel. 261 // If it hasn't triggered yet, setup it in the compare channel.
@@ -287,6 +286,8 @@ impl Driver for RtcDriver {
287 // It will be setup later by `next_period`. 286 // It will be setup later by `next_period`.
288 r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) }); 287 r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) });
289 } 288 }
289
290 true
290 }) 291 })
291 } 292 }
292} 293}
diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs
index 5215c0c0f..8f280f550 100644
--- a/embassy-rp/src/timer.rs
+++ b/embassy-rp/src/timer.rs
@@ -68,9 +68,16 @@ impl Driver for TimerDriver {
68 }) 68 })
69 } 69 }
70 70
71 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { 71 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
72 let n = alarm.id() as usize; 72 let n = alarm.id() as usize;
73 critical_section::with(|cs| { 73 critical_section::with(|cs| {
74 let now = self.now();
75
76 // If alarm timestamp has passed don't set the alarm and return `false` to indicate that.
77 if timestamp <= now {
78 return false;
79 }
80
74 let alarm = &self.alarms.borrow(cs)[n]; 81 let alarm = &self.alarms.borrow(cs)[n];
75 alarm.timestamp.set(timestamp); 82 alarm.timestamp.set(timestamp);
76 83
@@ -80,13 +87,7 @@ impl Driver for TimerDriver {
80 // it is checked if the alarm time has passed. 87 // it is checked if the alarm time has passed.
81 unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; 88 unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) };
82 89
83 let now = self.now(); 90 true
84
85 // If alarm timestamp has passed, trigger it instantly.
86 // This disarms it.
87 if timestamp <= now {
88 self.trigger_alarm(n, cs);
89 }
90 }) 91 })
91 } 92 }
92} 93}
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index ed3225c51..e4c266e7f 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -292,21 +292,21 @@ impl Driver for RtcDriver {
292 }) 292 })
293 } 293 }
294 294
295 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { 295 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
296 critical_section::with(|cs| { 296 critical_section::with(|cs| {
297 let t = self.now();
298
299 // If alarm timestamp has passed don't set the alarm and return `false` to indicate that.
300 if timestamp <= t {
301 return false;
302 }
303
297 let r = T::regs_gp16(); 304 let r = T::regs_gp16();
298 305
299 let n = alarm.id() as _; 306 let n = alarm.id() as _;
300 let alarm = self.get_alarm(cs, alarm); 307 let alarm = self.get_alarm(cs, alarm);
301 alarm.timestamp.set(timestamp); 308 alarm.timestamp.set(timestamp);
302 309
303 let t = self.now();
304 if timestamp <= t {
305 unsafe { r.dier().modify(|w| w.set_ccie(n + 1, false)) };
306 self.trigger_alarm(n, cs);
307 return;
308 }
309
310 let safe_timestamp = timestamp.max(t + 3); 310 let safe_timestamp = timestamp.max(t + 3);
311 311
312 // Write the CCR value regardless of whether we're going to enable it now or not. 312 // Write the CCR value regardless of whether we're going to enable it now or not.
@@ -317,6 +317,8 @@ impl Driver for RtcDriver {
317 let diff = timestamp - t; 317 let diff = timestamp - t;
318 // NOTE(unsafe) We're in a critical section 318 // NOTE(unsafe) We're in a critical section
319 unsafe { r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)) }; 319 unsafe { r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)) };
320
321 true
320 }) 322 })
321 } 323 }
322} 324}
diff --git a/embassy-time/src/driver.rs b/embassy-time/src/driver.rs
index 79ae14b91..5c2ad3b23 100644
--- a/embassy-time/src/driver.rs
+++ b/embassy-time/src/driver.rs
@@ -105,20 +105,21 @@ pub trait Driver: Send + Sync + 'static {
105 /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm 105 /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm
106 /// timestamp, the provided callback function will be called. 106 /// timestamp, the provided callback function will be called.
107 /// 107 ///
108 /// If `timestamp` is already in the past, the alarm callback must be immediately fired. 108 /// The `Driver` implementation should guarantee that the alarm callback is never called synchronously from `set_alarm`.
109 /// In this case, it is allowed (but not mandatory) to call the alarm callback synchronously from `set_alarm`. 109 /// Rather - if `timestamp` is already in the past - `false` should be returned and alarm should not be set,
110 /// or alternatively, the driver should return `true` and arrange to call the alarm callback as soon as possible, but not synchronously.
110 /// 111 ///
111 /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. 112 /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp.
112 /// 113 ///
113 /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any. 114 /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any.
114 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64); 115 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool;
115} 116}
116 117
117extern "Rust" { 118extern "Rust" {
118 fn _embassy_time_now() -> u64; 119 fn _embassy_time_now() -> u64;
119 fn _embassy_time_allocate_alarm() -> Option<AlarmHandle>; 120 fn _embassy_time_allocate_alarm() -> Option<AlarmHandle>;
120 fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); 121 fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
121 fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64); 122 fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool;
122} 123}
123 124
124/// See [`Driver::now`] 125/// See [`Driver::now`]
@@ -139,7 +140,7 @@ pub fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut (
139} 140}
140 141
141/// See [`Driver::set_alarm`] 142/// See [`Driver::set_alarm`]
142pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) { 143pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool {
143 unsafe { _embassy_time_set_alarm(alarm, timestamp) } 144 unsafe { _embassy_time_set_alarm(alarm, timestamp) }
144} 145}
145 146
@@ -167,7 +168,7 @@ macro_rules! time_driver_impl {
167 } 168 }
168 169
169 #[no_mangle] 170 #[no_mangle]
170 fn _embassy_time_set_alarm(alarm: $crate::driver::AlarmHandle, timestamp: u64) { 171 fn _embassy_time_set_alarm(alarm: $crate::driver::AlarmHandle, timestamp: u64) -> bool {
171 <$t as $crate::driver::Driver>::set_alarm(&$name, alarm, timestamp) 172 <$t as $crate::driver::Driver>::set_alarm(&$name, alarm, timestamp)
172 } 173 }
173 }; 174 };
diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs
index 2ddb2e604..fc7fd1979 100644
--- a/embassy-time/src/driver_std.rs
+++ b/embassy-time/src/driver_std.rs
@@ -127,12 +127,14 @@ impl Driver for TimeDriver {
127 alarm.ctx = ctx; 127 alarm.ctx = ctx;
128 } 128 }
129 129
130 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { 130 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
131 self.init(); 131 self.init();
132 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); 132 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
133 let alarm = &mut alarms[alarm.id() as usize]; 133 let alarm = &mut alarms[alarm.id() as usize];
134 alarm.timestamp = timestamp; 134 alarm.timestamp = timestamp;
135 unsafe { self.signaler.as_ref() }.signal(); 135 unsafe { self.signaler.as_ref() }.signal();
136
137 true
136 } 138 }
137} 139}
138 140
diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs
index e4497e6a2..d7a6b0d8d 100644
--- a/embassy-time/src/driver_wasm.rs
+++ b/embassy-time/src/driver_wasm.rs
@@ -81,13 +81,15 @@ impl Driver for TimeDriver {
81 } 81 }
82 } 82 }
83 83
84 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { 84 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) -> bool {
85 self.init(); 85 self.init();
86 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); 86 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
87 let alarm = &mut alarms[alarm.id() as usize]; 87 let alarm = &mut alarms[alarm.id() as usize];
88 alarm.closure.replace(Closure::new(move || { 88 alarm.closure.replace(Closure::new(move || {
89 callback(ctx); 89 callback(ctx);
90 })); 90 }));
91
92 true
91 } 93 }
92 94
93 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { 95 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs
index 1c4e5398b..83f734848 100644
--- a/embassy-time/src/queue_generic.rs
+++ b/embassy-time/src/queue_generic.rs
@@ -2,7 +2,6 @@ use core::cell::RefCell;
2use core::cmp::Ordering; 2use core::cmp::Ordering;
3use core::task::Waker; 3use core::task::Waker;
4 4
5use atomic_polyfill::{AtomicU64, Ordering as AtomicOrdering};
6use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 5use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
7use embassy_sync::blocking_mutex::Mutex; 6use embassy_sync::blocking_mutex::Mutex;
8use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; 7use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList};
@@ -71,7 +70,7 @@ impl InnerQueue {
71 } 70 }
72 } 71 }
73 72
74 fn schedule_wake(&mut self, at: Instant, waker: &Waker, alarm_schedule: &AtomicU64) { 73 fn schedule_wake(&mut self, at: Instant, waker: &Waker) {
75 self.queue 74 self.queue
76 .find_mut(|timer| timer.waker.will_wake(waker)) 75 .find_mut(|timer| timer.waker.will_wake(waker))
77 .map(|mut timer| { 76 .map(|mut timer| {
@@ -98,50 +97,54 @@ impl InnerQueue {
98 // dispatch all timers that are already due 97 // dispatch all timers that are already due
99 // 98 //
100 // Then update the alarm if necessary 99 // Then update the alarm if necessary
101 self.dispatch(alarm_schedule); 100 self.dispatch();
102 } 101 }
103 102
104 fn dispatch(&mut self, alarm_schedule: &AtomicU64) { 103 fn dispatch(&mut self) {
105 let now = Instant::now(); 104 loop {
105 let now = Instant::now();
106 106
107 while self.queue.peek().filter(|timer| timer.at <= now).is_some() { 107 while self.queue.peek().filter(|timer| timer.at <= now).is_some() {
108 self.queue.pop().unwrap().waker.wake(); 108 self.queue.pop().unwrap().waker.wake();
109 } 109 }
110 110
111 self.update_alarm(alarm_schedule); 111 if self.update_alarm() {
112 break;
113 }
114 }
112 } 115 }
113 116
114 fn update_alarm(&mut self, alarm_schedule: &AtomicU64) { 117 fn update_alarm(&mut self) -> bool {
115 if let Some(timer) = self.queue.peek() { 118 if let Some(timer) = self.queue.peek() {
116 let new_at = timer.at; 119 let new_at = timer.at;
117 120
118 if self.alarm_at != new_at { 121 if self.alarm_at != new_at {
119 self.alarm_at = new_at; 122 self.alarm_at = new_at;
120 alarm_schedule.store(new_at.as_ticks(), AtomicOrdering::SeqCst); 123
124 return set_alarm(self.alarm.unwrap(), self.alarm_at.as_ticks());
121 } 125 }
122 } else { 126 } else {
123 self.alarm_at = Instant::MAX; 127 self.alarm_at = Instant::MAX;
124 alarm_schedule.store(Instant::MAX.as_ticks(), AtomicOrdering::SeqCst);
125 } 128 }
129
130 true
126 } 131 }
127 132
128 fn handle_alarm(&mut self, alarm_schedule: &AtomicU64) { 133 fn handle_alarm(&mut self) {
129 self.alarm_at = Instant::MAX; 134 self.alarm_at = Instant::MAX;
130 135
131 self.dispatch(alarm_schedule); 136 self.dispatch();
132 } 137 }
133} 138}
134 139
135struct Queue { 140struct Queue {
136 inner: Mutex<CriticalSectionRawMutex, RefCell<InnerQueue>>, 141 inner: Mutex<CriticalSectionRawMutex, RefCell<InnerQueue>>,
137 alarm_schedule: AtomicU64,
138} 142}
139 143
140impl Queue { 144impl Queue {
141 const fn new() -> Self { 145 const fn new() -> Self {
142 Self { 146 Self {
143 inner: Mutex::new(RefCell::new(InnerQueue::new())), 147 inner: Mutex::new(RefCell::new(InnerQueue::new())),
144 alarm_schedule: AtomicU64::new(u64::MAX),
145 } 148 }
146 } 149 }
147 150
@@ -156,28 +159,12 @@ impl Queue {
156 set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); 159 set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _);
157 } 160 }
158 161
159 inner.schedule_wake(at, waker, &self.alarm_schedule) 162 inner.schedule_wake(at, waker)
160 }); 163 });
161
162 self.update_alarm();
163 }
164
165 fn update_alarm(&self) {
166 // Need to set the alarm when we are *not* holding the mutex on the inner queue
167 // because mutexes are not re-entrant, which is a problem because `set_alarm` might immediately
168 // call us back if the timestamp is in the past.
169 let alarm_at = self.alarm_schedule.swap(u64::MAX, AtomicOrdering::SeqCst);
170
171 if alarm_at < u64::MAX {
172 set_alarm(self.inner.lock(|inner| inner.borrow().alarm.unwrap()), alarm_at);
173 }
174 } 164 }
175 165
176 fn handle_alarm(&self) { 166 fn handle_alarm(&self) {
177 self.inner 167 self.inner.lock(|inner| inner.borrow_mut().handle_alarm());
178 .lock(|inner| inner.borrow_mut().handle_alarm(&self.alarm_schedule));
179
180 self.update_alarm();
181 } 168 }
182 169
183 fn handle_alarm_callback(ctx: *mut ()) { 170 fn handle_alarm_callback(ctx: *mut ()) {
@@ -196,7 +183,6 @@ crate::timer_queue_impl!(static QUEUE: Queue = Queue::new());
196#[cfg(test)] 183#[cfg(test)]
197mod tests { 184mod tests {
198 use core::cell::Cell; 185 use core::cell::Cell;
199 use core::sync::atomic::Ordering;
200 use core::task::{RawWaker, RawWakerVTable, Waker}; 186 use core::task::{RawWaker, RawWakerVTable, Waker};
201 use std::rc::Rc; 187 use std::rc::Rc;
202 use std::sync::Mutex; 188 use std::sync::Mutex;
@@ -282,20 +268,14 @@ mod tests {
282 inner.ctx = ctx; 268 inner.ctx = ctx;
283 } 269 }
284 270
285 fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) { 271 fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) -> bool {
286 let notify = { 272 let mut inner = self.0.lock().unwrap();
287 let mut inner = self.0.lock().unwrap();
288
289 if timestamp <= inner.now {
290 Some((inner.callback, inner.ctx))
291 } else {
292 inner.alarm = timestamp;
293 None
294 }
295 };
296 273
297 if let Some((callback, ctx)) = notify { 274 if timestamp <= inner.now {
298 (callback)(ctx); 275 false
276 } else {
277 inner.alarm = timestamp;
278 true
299 } 279 }
300 } 280 }
301 } 281 }
@@ -344,7 +324,6 @@ mod tests {
344 fn setup() { 324 fn setup() {
345 DRIVER.reset(); 325 DRIVER.reset();
346 326
347 QUEUE.alarm_schedule.store(u64::MAX, Ordering::SeqCst);
348 QUEUE.inner.lock(|inner| { 327 QUEUE.inner.lock(|inner| {
349 *inner.borrow_mut() = InnerQueue::new(); 328 *inner.borrow_mut() = InnerQueue::new();
350 }); 329 });