Fix the lock checker for a shared hit cache

Review the AI's codeEasy12 min

Your task

An AI agent wrote a hit cache for your game server's worker threads: a shared hash map, a find, and an insert on a miss. It called this "safely memoized", but nothing synchronized the map, and its single-threaded test passed. Your team now replays recorded thread interleavings through a checker. Every access to the shared map must happen while the acting thread holds the one mutex.

An assistant wrote check_lock_discipline(operations), the checker. It is in the editor. It has a bug. Find it and fix it.

Input and output:

  • operations: one operation per line, in the order they happened, written thread|action or thread|action|key. The actions are lock, unlock, find and insert. Thread names are never empty.
  • Return the violations, in the order they occur.

Rules the tests check:

  • Split a line on |. Skip it if it has fewer than 2 parts. The key is the third part, or empty if there is none. Parts after the third are ignored.
  • There is one mutex, and it starts free. Skip any action other than the four above.
  • In every report, <thread> is the thread on that line.
  • lock: if the mutex is free, the thread now holds it. If the same thread already holds it, report double_lock:<thread>. If another thread holds it, report lock_held_by_other:<thread>, naming the thread that tried to lock. In both of those cases nothing changes.
  • unlock: if the thread holds the mutex, it becomes free. Otherwise report unlock_without_lock:<thread>.
  • find or insert by a thread that does not hold the mutex: report unsynchronized_find:<thread>:<key> or unsynchronized_insert:<thread>:<key>.
  • After the last line, if a thread still holds the mutex, report lock_not_released:<thread> for that thread.

Examples

check_lock_discipline(operations: list[str]) -> list[str]
Example 1
Inputcheck_lock_discipline(["t1|find|7", "t2|find|7", "t1|insert|7", "t2|insert|7"])
Output["unsynchronized_find:t1:7", "unsynchronized_find:t2:7", "unsynchronized_insert:t1:7", "unsynchronized_insert:t2:7"]
Example 2
Inputcheck_lock_discipline(["t1|lock", "t1|find|7", "t1|insert|7", "t1|unlock", "t2|lock", "t2|find|7", "t2|unlock"])
Output[]