kernel: Schedule retries for deferred timeouts

920f8fc3a34f161d9350d08812b39397d0fe2c76eae1bb7d11d6dce202ba7787
Alexis Sellier committed ago 1 parent b6ea2575
kernel/kernel/dispatch.rad +2 -2
133 133
        if let case remote::Action::Ready(context) = delivery.request.action { signalContext(context, clock); }
134 134
    }
135 135
    lifecycle::cancelRequests(&mut domains::STORE, &mut remote::STORE);
136 136
    let cancelled = lifecycle::cancelAllocations(&mut lifecycle::CALLS, &mut domains::STORE, &mut pages::STORE, &mut loader::STATE, &mut registry::STORE);
137 137
    let maintenanceClock = now(HARTS[id].timer);
138 -
    let mut maintenance = cancelled or domains::STORE.dead > 0 or pages::STORE.backings.pool.retiredCount > 0;
138 +
    let mut maintenance = timeout.pending or cancelled or domains::STORE.dead > 0 or pages::STORE.backings.pool.retiredCount > 0;
139 139
    if maintenance and maintenanceClock >= HARTS[id].nextMaintenance {
140 140
        let reaped = lifecycle::reapNext(&mut domains::STORE, &mut pages::STORE, &mut budgets::STORE, &mut interrupts::STORE);
141 141
        let reclaimed = frames::reclaim(&mut pages::STORE.backings.pool, 256);
142 -
        set maintenance = cancelled or reaped or pages::STORE.backings.pool.retiredCount > 0;
142 +
        set maintenance = timeout.pending or cancelled or reaped or pages::STORE.backings.pool.retiredCount > 0;
143 143
        let after = now(HARTS[id].timer);
144 144
        let interval = HARTS[id].maintenance;
145 145
        set HARTS[id].nextMaintenance = 0xffffffffffffffff;
146 146
        if interval < 0xffffffffffffffff - after { set HARTS[id].nextMaintenance = after + interval; }
147 147
    }
kernel/kernel/tests/timers.rad +16 -3
28 28
    set DOMAINS.records[owner.index].memory = &mut MEMORY;
29 29
    try! events::open(&mut DOMAINS.events, owner, &mut MEMORY.ring);
30 30
    return owner;
31 31
}
32 32
33 -
/// A timeout reports the hart of a receiver whose context moved before delivery.
33 +
/// A deferred timeout retries on its owning hart after its receiver waits elsewhere.
34 34
@test unsafe fn remoteReceiver() throws (testing::TestError) {
35 35
    let owner = initialize();
36 36
    set DOMAINS.contextSlots[0] = slots::Slot { generation: 1, state: slots::State::Live };
37 37
    let context = abi::Ref { index: 0, generation: 1 };
38 38
    set DOMAINS.contexts[0].owner = owner;
40 40
    set BUDGETS.slots[0] = slots::Slot { generation: 1, state: slots::State::Live };
41 41
    set BUDGETS.windows[0] = budgets::Window {
42 42
        hart: 1, start: 10, end: 100, owner, handle: abi::Handle(0), context,
43 43
    };
44 44
    try! timers::request(&mut TIMERS, &DOMAINS, owner, 0, 0, 15, 17);
45 +
    for i in 0..events::ORDINARY {
46 +
        assert try! domains::notify(&mut DOMAINS, owner,
47 +
            events::Event { kind: events::WAKEUP, reserved: 0, code: i, value: 0 });
48 +
    }
49 +
    let deferred = try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 15);
50 +
    assert deferred.pending and deferred.harts == 0;
51 +
    for i in 0..events::ORDINARY { let event = try! events::pop(&mut MEMORY.ring) else panic "wake"; }
52 +
    set DOMAINS.contexts[0].state = domains::ContextState::Waiting;
53 +
    let remote = try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 1, 15);
54 +
    assert not remote.pending and remote.harts == 0;
45 55
    let result = try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 15);
46 -
    assert result.harts == 2 and result.deadline == 0xffffffffffffffff;
56 +
    assert result.harts == 2 and result.deadline == 0xffffffffffffffff and not result.pending;
47 57
    assert DOMAINS.contexts[0].state == domains::ContextState::Ready;
48 58
    let again = try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 16);
49 59
    assert again.harts == 0;
50 60
}
51 61
75 85
    let mut full = false;
76 86
    try timers::request(&mut TIMERS, &DOMAINS, owner, 0, 0, 1, 99) catch error {
77 87
        assert (error == abi::Error::Busy); set full = true;
78 88
    };
79 89
    assert (full);
80 -
    assert ((try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 1)).deadline == 0xffffffffffffffff);
90 +
    let deferred = try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 1);
91 +
    assert deferred.pending and deferred.deadline == 0xffffffffffffffff;
92 +
    assert ((try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 2)).pending);
81 93
    for i in 0..events::ORDINARY { let event = try! events::pop(&mut MEMORY.ring) else panic "wake"; }
82 94
    let next = try! timers::service(&mut TIMERS, &mut DOMAINS, &BUDGETS, 0, 2);
95 +
    assert not next.pending;
83 96
    for i in 0..limits::TIMEOUTS_PER_HART {
84 97
        let event = try! events::pop(&mut MEMORY.ring) else panic "retained timeout";
85 98
        assert (event.kind == events::TIMEOUT and event.code == i);
86 99
    }
87 100
    try! timers::request(&mut TIMERS, &DOMAINS, owner, 0, 2, 1, 100);
kernel/kernel/timers.rad +6 -2
51 51
export record Result: Copy {
52 52
    /// Earliest future timeout, or the maximum tick when none is armed.
53 53
    deadline: u64,
54 54
    /// Harts with retained receiver authority that must refresh dispatch.
55 55
    harts: u32,
56 +
    /// Due notifications that require a scheduled maintenance retry.
57 +
    pending: bool,
56 58
}
57 59
58 60
/// Deliver due entries and report the next boundary and affected dispatch harts.
59 -
/// Full queues retain due entries for the next kernel entry without a timer spin.
61 +
/// Full queues retain due entries and request a scheduled maintenance retry.
60 62
export unsafe fn service(store: &mut Store, receivers: &mut domains::Store, windows: &budgets::Store, hart: u32, now: u64) -> Result throws (abi::Error) {
61 63
    if hart >= limits::HARTS { throw abi::Error::InvalidArg; }
62 -
    let mut result = Result { deadline: 0xffffffffffffffff, harts: 0 };
64 +
    let mut result = Result { deadline: 0xffffffffffffffff, harts: 0, pending: false };
63 65
    for i in 0..limits::TIMEOUTS_PER_HART {
64 66
        let entry = store.entries[hart][i];
65 67
        let owner = entry.owner else { continue; };
66 68
        if not slots::matches(&receivers.slots[..], owner, slots::State::Live)
67 69
            or receivers.records[owner.index].state == domains::Lifecycle::Dead {
73 75
            continue;
74 76
        }
75 77
        if try domains::notify(receivers, owner, events::Event { kind: events::TIMEOUT, reserved: 0, code: entry.token, value: 0 }) {
76 78
            set store.entries[hart][i].owner = nil;
77 79
            set result.harts |= budgets::wakeTargets(windows, receivers, owner, now);
80 +
        } else {
81 +
            set result.pending = true;
78 82
        }
79 83
    }
80 84
    return result;
81 85
}