Skip to content

[P3] Kotlin Mutex reentrancy is missed after a waited lock, tryLock or select acquisition #1220

Open

Description

@PIsberg

Split out of #1007 by the Kotlin Mutex reentrancy detector PR (feat/kotlin-mutex-reentrancy).

What is missing. KotlinMutexReentrancyDetector knows who holds a kotlinx Mutex only when a woven lock took it without suspending: the hook reads the caller's Job from the continuation before the call and records it once the call returns without COROUTINE_SUSPENDED. Three acquisitions leave the holder unknown (recordHolderUnknown):

  • a lock that suspended and was resumed later, because the resumption happens inside kotlinx, outside any woven call site;
  • tryLock, which takes no continuation, so there is no Job to read;
  • select { mutex.onLock { } }.

A coroutine that then re-enters the mutex it holds hangs exactly as in the detected case, and nothing is reported. Under the contention @AsyncTest creates, many acquisitions wait, so in a given round the uncontended first acquisition is what the finding depends on.

Why not now. Naming the holder of a resumed lock needs a hook at the point kotlinx hands the mutex over (the resumed continuation, or the owner field MutexImpl keeps when an owner token is passed), which is a different weaving shape from the call-site replacement the PR adds. tryLock needs the caller's coroutine from somewhere other than a continuation argument.

What it would take. Weave or wrap the resumption path (or read MutexImpl's owner state at the next woven call), plus a row in the Kotlin fixture where the reentrant lock follows a contended first lock, and one where the first acquisition is tryLock, both of which must report.

Documented as a limit in docs/JVM_LANGUAGES.md and the detector's javadoc.

Activity

  1. PIsberg commented on Nov 8, 2026

    @PIsberg
    OwnerAuthor

    P3: a missed detection on a narrower path; the uncontended first acquisition in a round still reports, so the reentrancy is caught in most runs. Below #1221 since a miss is cheaper than a false report.

  2. PIsberg commented on Nov 8, 2026

    @PIsberg
    OwnerAuthor

    The waited-lock part shipped in #1223. The tryLock part is in the PR from branch fix/1220-mutex-trylock: the weaver passes the enclosing suspend function's or lambda's continuation to the tryLock hook, which records the caller's Job as holder. Still open here: select { onLock }, and a tryLock outside any suspend function (no coroutine to name). Priority stays P3.

  3. added a commit that references this issue on Nov 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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