aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobertTDowling <[email protected]>2023-12-17 15:11:03 -0800
committerRobertTDowling <[email protected]>2023-12-17 15:35:35 -0800
commitb857334f92fc188004567edb93e0d1dfce4c259e (patch)
tree78f90e470c3d477dc97b78badf321e31dfe599ce
parenta2d4bab2f8a4a9b994bc0289938a9f725950715f (diff)
STM32: Fix race in alarm setting, which impacted scheduling.
Detect potential race condition (should be rare) and return false back to caller, allowing them to handle the possibility that either the alarm was never set because it was in the past (old meaning of false), or that in fact the alarm was set and may have fired within the race window (new meaning of false). In either case, the caller needs to make sure the callback got called.
-rw-r--r--embassy-stm32/src/time_driver.rs19
-rw-r--r--embassy-time/src/driver.rs4
2 files changed, 20 insertions, 3 deletions
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index ea9c22d87..9981800b2 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -474,16 +474,29 @@ impl Driver for RtcDriver {
474 return false; 474 return false;
475 } 475 }
476 476
477 let safe_timestamp = timestamp.max(t + 3);
478
479 // Write the CCR value regardless of whether we're going to enable it now or not. 477 // Write the CCR value regardless of whether we're going to enable it now or not.
480 // This way, when we enable it later, the right value is already set. 478 // This way, when we enable it later, the right value is already set.
481 r.ccr(n + 1).write(|w| w.set_ccr(safe_timestamp as u16)); 479 r.ccr(n + 1).write(|w| w.set_ccr(timestamp as u16));
482 480
483 // Enable it if it'll happen soon. Otherwise, `next_period` will enable it. 481 // Enable it if it'll happen soon. Otherwise, `next_period` will enable it.
484 let diff = timestamp - t; 482 let diff = timestamp - t;
485 r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)); 483 r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000));
486 484
485 // Reevaluate if the alarm timestamp is still in the future
486 let t = self.now();
487 if timestamp <= t {
488 // If alarm timestamp has passed since we set it, we have a race condition and
489 // the alarm may or may not have fired.
490 // Disarm the alarm and return `false` to indicate that.
491 // It is the caller's responsibility to handle this ambiguity.
492 r.dier().modify(|w| w.set_ccie(n + 1, false));
493
494 alarm.timestamp.set(u64::MAX);
495
496 return false;
497 }
498
499 // We're confident the alarm will ring in the future.
487 true 500 true
488 }) 501 })
489 } 502 }
diff --git a/embassy-time/src/driver.rs b/embassy-time/src/driver.rs
index 5fe7becaf..81ee1b0f5 100644
--- a/embassy-time/src/driver.rs
+++ b/embassy-time/src/driver.rs
@@ -108,6 +108,10 @@ pub trait Driver: Send + Sync + 'static {
108 /// The `Driver` implementation should guarantee that the alarm callback is never called synchronously from `set_alarm`. 108 /// The `Driver` implementation should guarantee that the alarm callback is never called synchronously from `set_alarm`.
109 /// Rather - if `timestamp` is already in the past - `false` should be returned and alarm should not be set, 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 /// or alternatively, the driver should return `true` and arrange to call the alarm callback as soon as possible, but not synchronously.
111 /// There is a rare third possibility that the alarm was barely in the future, and by the time it was enabled, it had slipped into the
112 /// past. This is can be detected by double-checking that the alarm is still in the future after enabling it; if it isn't, `false`
113 /// should also be returned to indicate that the callback may have been called already by the alarm, but it is not guaranteed, so the
114 /// caller should also call the callback, just like in the more common `false` case. (Note: This requires idempotency of the callback.)
111 /// 115 ///
112 /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. 116 /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp.
113 /// 117 ///