Skip to content

Commit 3f913aa

Browse files
committed
Add NotificationBits type and use it in sys_recv_notification
1 parent 99255fd commit 3f913aa

5 files changed

Lines changed: 82 additions & 31 deletions

File tree

drv/psc-seq-server/src/main.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -470,9 +470,7 @@ fn main() -> ! {
470470
// Wait for a pin change or timer.
471471
let n = sys_recv_notification(sleep_notifications);
472472
// If the timer bit is set _and the timer has actually fired_...
473-
if n & notifications::TIMER_MASK != 0
474-
&& sys_get_timer().deadline.is_none()
475-
{
473+
if n.has_timer_fired(notifications::TIMER_MASK) {
476474
// Reset our timer forward.
477475
sys_set_timer(
478476
Some(now.saturating_add(POLL_MS)),

drv/stm32h7-sprot-server/src/main.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ impl<S: SpiServer> Io<S> {
389389
.unwrap_lite();
390390

391391
// Determine the deadline after which we'll give up, and start the clock.
392-
let expected_wake = set_timer_relative(max_sleep, TIMER_MASK);
392+
set_timer_relative(max_sleep, TIMER_MASK);
393393

394394
let mut irq_fired = false;
395395
while self.is_rot_irq_asserted() != desired {
@@ -406,21 +406,21 @@ impl<S: SpiServer> Io<S> {
406406
// notification bit, because it's possible *both* notifications were
407407
// posted before we were scheduled again, and if the IRQ did fire,
408408
// we'd prefer to honor that.
409-
irq_fired = notif & ROT_IRQ_MASK != 0
410-
&& self
411-
.sys
409+
irq_fired = notif.check_condition(ROT_IRQ_MASK, || {
410+
self.sys
412411
// If the IRQ hasn't fired, leave it enabled, otherwise,
413412
// if it has fired, don't re-enable the IRQ.
414413
.gpio_irq_control(ROT_IRQ_MASK, IrqControl::Check)
415414
// Sys task shouldn't panic.
416-
.unwrap_lite();
415+
.unwrap_lite()
416+
});
417417
if irq_fired {
418418
break;
419419
}
420420

421421
// If the timer notification was posted, and the GPIO IRQ
422422
// notification wasn't, we've waited for the timeout. Too bad!
423-
if notif & TIMER_MASK != 0 && sys_get_timer().now >= expected_wake {
423+
if notif.has_timer_fired(TIMER_MASK) {
424424
// Disable the IRQ, so that we don't get the notification later
425425
// while in `recv`.
426426
self.sys

drv/stm32xx-i2c/src/lib.rs

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,30 +1199,27 @@ fn wfi_raw(event_mask: u32, timeout: I2cTimeout) -> I2cControlResult {
11991199
loop {
12001200
let received = sys_recv_notification(event_mask | TIMER_NOTIFICATION);
12011201

1202-
// If the event arrived _and_ our timer went off, prioritize the event and
1203-
// ignore the timeout.
1204-
if received & event_mask != 0 {
1202+
// If the event arrived _and_ our timer went off, prioritize the event
1203+
// and ignore the timeout.
1204+
if received.check_notification_mask(event_mask) {
12051205
// Attempt to clear our timer. Note that this does not protect against
12061206
// the race where the kernel decided to wake us up, but the timer went
12071207
// off before we got to this point.
12081208
sys_set_timer(None, TIMER_NOTIFICATION);
12091209
break I2cControlResult::Interrupted;
1210-
} else {
1211-
// The timer bit must have been set. Verify that our timer has
1212-
// actually expired:
1213-
if sys_get_timer().now >= dead {
1214-
break I2cControlResult::TimedOut;
1215-
}
1216-
1217-
// Otherwise, one of two things has happened:
1218-
// 1. Some joker has posted to our timer bit. Ha ha very funny.
1219-
// 2. The timer bit was _already set_ on entry to this routine, as a
1220-
// hangover from a previous run.
1221-
//
1222-
// We don't need to re-set our timer here because we sampled
1223-
// sys_get_timer _after_ receiving notifications, meaning it either
1224-
// hasn't gone off, or it has gone off but that fact is still stored
1225-
// in our notification bits.
1210+
} else if received.has_timer_fired(TIMER_NOTIFICATION) {
1211+
// The timer has actually expired!
1212+
break I2cControlResult::TimedOut;
12261213
}
1214+
1215+
// Otherwise, one of two things has happened:
1216+
// 1. Some joker has posted to our timer bit. Ha ha very funny.
1217+
// 2. The timer bit was _already set_ on entry to this routine, as a
1218+
// hangover from a previous run.
1219+
//
1220+
// We don't need to re-set our timer here because we sampled
1221+
// sys_get_timer _after_ receiving notifications, meaning it either
1222+
// hasn't gone off, or it has gone off but that fact is still stored in
1223+
// our notification bits.
12271224
}
12281225
}

sys/userlib/src/lib.rs

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,16 +330,72 @@ pub fn sys_recv(
330330
}
331331
}
332332

333+
/// Bitmask representing notifications from the kernel
334+
///
335+
/// The raw notification bits are available in [`get_raw_bits`], but
336+
/// higher-level functions make it harder to misuse notifications.
337+
#[derive(Copy, Clone)]
338+
#[repr(transparent)]
339+
pub struct NotificationBits(u32);
340+
341+
impl NotificationBits {
342+
/// Wraps a `u32`
343+
#[inline]
344+
pub fn new(bits: u32) -> Self {
345+
Self(bits)
346+
}
347+
348+
/// Returns the raw notification bitmask
349+
#[inline]
350+
pub fn get_raw_bits(&self) -> u32 {
351+
self.0
352+
}
353+
354+
/// Checks if a condition signaled by notification bits holds
355+
///
356+
/// The `cond` function should verify the underlying condition, but as an
357+
/// optimization, it will only be called if any bits set in mask are also
358+
/// set in `self`. This double-check is important, because notification bits
359+
/// can potentially be set even if the underlying condition is not true,
360+
/// such as with extra pended interrupts or inter-task use of `sys_post`.
361+
///
362+
/// If you find yourself passing `|| true` here, see
363+
/// [`check_notification_mask`].
364+
#[inline]
365+
pub fn check_condition<F: Fn() -> bool>(&self, mask: u32, cond: F) -> bool {
366+
self.check_notification_mask(mask) && cond()
367+
}
368+
369+
/// Checks whether the given notification mask is active
370+
///
371+
/// Notifications may occur spuriously; it is recommended to use
372+
/// `check_condition` instead, if there's a way to verify that the event has
373+
/// actually occurred.
374+
#[inline]
375+
pub fn check_notification_mask(&self, mask: u32) -> bool {
376+
(self.0 & mask) != 0
377+
}
378+
379+
/// Checks whether a timer appears to have gone off, because the timer_mask
380+
/// bit is set and the kernel timer is no longer set.
381+
///
382+
/// `mask` must the notification bitmask for the timer notification
383+
#[inline]
384+
pub fn has_timer_fired(&self, timer_mask: u32) -> bool {
385+
self.check_condition(timer_mask, || sys_get_timer().deadline.is_none())
386+
}
387+
}
388+
333389
/// Convenience wrapper for `sys_recv` for the specific, but common, task of
334390
/// listening for notifications. In this specific use, it has the advantage of
335391
/// never panicking and not returning a `Result` that must be checked.
336392
#[inline(always)]
337-
pub fn sys_recv_notification(notification_mask: u32) -> u32 {
393+
pub fn sys_recv_notification(notification_mask: u32) -> NotificationBits {
338394
match sys_recv(&mut [], notification_mask, Some(TaskId::KERNEL)) {
339395
Ok(rm) => {
340396
// The notification bits come back from the kernel in the operation
341397
// code field.
342-
rm.operation
398+
NotificationBits(rm.operation)
343399
}
344400
Err(_) => {
345401
// Safety: Because we passed Some(TaskId::KERNEL), this is defined

task/nucleo-user-button/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ pub fn main() -> ! {
104104

105105
// Wait for the user button to be pressed.
106106
let notif = sys_recv_notification(notifications::BUTTON_MASK);
107-
ringbuf_entry!(Trace::Notification(notif));
107+
ringbuf_entry!(Trace::Notification(notif.get_raw_bits()));
108108
}
109109
}
110110

0 commit comments

Comments
 (0)