Skip to content

x86 has no requeue_thread_after_save() caller and no DEFERRED_REQUEUE, so wake_expired_timers()'s is_current_on_any_cpu arm has no owner there #691

Description

@ryanbreen

Summary

Scheduler::wake_expired_timers() has an arm that deliberately does not
enqueue a thread it has just published as Ready:

  • it pops the expired timer-heap entry,
  • publishes ThreadState::Ready,
  • clears wake_time_ns,
  • and returns without putting the thread on any ready queue,

on the documented assumption that either the running thread notices after its
own halt, or DEFERRED_REQUEUE picks it up. That arm is taken whenever
is_current_on_any_cpu is true — i.e. whenever the woken thread is still named
by some CPU's current_thread.

Both stated fallbacks are aarch64-only. DEFERRED_REQUEUE, and every caller
of requeue_thread_after_save(), live in
kernel/src/arch_impl/aarch64/context_switch.rs. x86 has zero of either. So on
x86 the enqueue in that arm is unowned: whether the thread ever runs again
depends entirely on the caller noticing for itself.

Status: latent, not demonstrated

Nothing reachable exercises it today, and this is explicitly not a claim
that a live wake is being lost on x86. The one caller that made the window
deterministic — sys_poll, which re-arms a 1 ms slice so its deadline has
essentially always expired by the time schedule() runs — was investigated
under #568 and the mechanism could not be demonstrated: the mutation offered as
proof did not separate, and an A/B battery on beast KVM (8 boots per arm,
differing only by the ordering hunk) showed zero poll-oracle failures on either
side. See docs/planning/green-program/sockets/EVIDENCE-2026-08-29.md §5 and §9.

What remains after that investigation is the structural asymmetry itself, which
is real regardless: an architecture-neutral scheduler path documents two
fallbacks that exist on one architecture only.

Why file it

Any future x86 caller that blocks with a short deadline lands in the same
arm with no owner for its enqueue, and the failure mode is silent — the thread
is Ready, in no queue, with no heap entry. The code reads as if a fallback
exists.

Suggested shape

Either give the arm an owner on x86 (the x86 equivalent of
requeue_thread_after_save() / DEFERRED_REQUEUE), or make the arm's comment
name the architecture its fallbacks belong to and refuse — loudly — when it is
reached on an architecture that has none. Silently relying on an absent
mechanism is the shape this campaign has been closing under the "unowned
delivery" heading (#669).

Disclosed by-catch from #568.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions