Fix the resource release after a timeout

Review the AI's codeMedium18 min

Your task

A Python job runs under asyncio.wait_for with a 5 second timeout, and a finally block releases its temp resource. On a timeout the job is cancelled, but cancellation only lands at the job's next await. So finally can release the resource while the job is still writing to it. The safe version waits for the job's own cleanup before it releases, using asyncio.shield to protect that wait.

An assistant wrote finalize_jobs(event_lines, shield_cleanup), which replays the lifecycle events and logs each release. It is in the editor. It has a bug. Find it and fix it.

Input and output:

  • event_lines: the events in order, each op|job. The ops are acquire, cancel, cleanup_done (the job's own cleanup has finished) and release.
  • shield_cleanup: true when a release waits for a cancelled job's cleanup.
  • Return the log in event order. Each entry is released:<job> or unsafe_release:<job>.

Rules the tests check:

  • Split a line on |. Skip it unless it has exactly 2 parts and the job is not empty. Skip any other op.
  • Each job is in one of four states: not acquired, held, drained or released. Every job starts not acquired.
  • acquire: a job that is not acquired becomes held, and not cancelled. In any other state, nothing happens.
  • cancel: a held job is marked cancelled. In any other state, nothing happens.
  • cleanup_done: a held job becomes drained. If a release is waiting for it, the job becomes released at once and you log released:<job>. In any other state, nothing happens.
  • release on a held job that is not cancelled: it becomes released. Log released:<job>.
  • release on a held, cancelled job when shield_cleanup is true: log nothing. The release waits for the job's cleanup_done.
  • release on a held, cancelled job when shield_cleanup is false: it becomes released at once. Log unsafe_release:<job>.
  • release on a drained job: it becomes released. Log released:<job>.
  • release on a job that is not acquired or already released: nothing happens.

Examples

finalize_jobs(event_lines: list[str], shield_cleanup: bool) -> list[str]
Example 1
Inputfinalize_jobs(["acquire|j1", "cleanup_done|j1", "release|j1"], True)
Output["released:j1"]
Example 2
Inputfinalize_jobs(["acquire|j1", "cancel|j1", "release|j1", "cleanup_done|j1"], True)
Output["released:j1"]
Example 3
Inputfinalize_jobs(["acquire|j1", "cancel|j1", "release|j1", "cleanup_done|j1"], False)
Output["unsafe_release:j1"]