Skip to content

Mutex' does not guarantee FIFO locking order #8365

Description

@kawadakk

Version

  • tokio v1.53.1

Platform
Linux $hostname 6.18.35 #1-NixOS SMP PREEMPT_DYNAMIC Tue Jun 9 10:28:53 UTC 2026 x86_64 GNU/Linux

Description
Mutex's documentation says:

Tokio’s Mutex operates on a guaranteed FIFO basis. This means that the order in which tasks call the lock method is the exact order in which they will acquire the lock.

However, the future returned by lock checks its cooperative-scheduling budget first. If the budget is exhausted, wait queue insertion is delayed until the next poll. Since this process is invisible to the caller, it's impossible to enforce any specific lock acquisition order.

pub async fn lock(&self) -> MutexGuard<'_, T> {
let acquire_fut = async {
self.acquire().await;

async fn acquire(&self) {
crate::trace::async_trace_leaf().await;
self.s.acquire(1).await.unwrap_or_else(|_| {

let inner = self.ll_sem.acquire(1);

#[cfg(not(all(tokio_unstable, feature = "tracing")))]
let coop = ready!(crate::task::coop::poll_proceed(cx));

This Codex-generated test program (unrelated to the production code where we found the issue) reliably reproduces lock order inversion: Playground

first lock-future poll order: [A, B]
lock().await completion order: ['B', 'A']

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-tokioArea: The main tokio crateC-bugCategory: This is a bug.M-syncModule: tokio/sync

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions