Repository navigation
fix(watcher): guard AlarmReceiver finish() with FinishOnce - #362
TimeToBuildBob wants to merge 2 commits into
Conversation
Applies the same OEM double-finish protection added to the widget broadcast in ActivityWatch#360. AlarmReceiver used the bare `goAsync()` + `finally { pendingResult.finish() }` pattern; an OEM pre-finishing the broadcast before the coroutine's finally block throws `IllegalStateException("Broadcast already finished")`, which escapes the coroutine and kills the process. `FinishOnce` wraps the finish call with an `AtomicBoolean` guard and catches (logs) `IllegalStateException` on the second call, matching the behaviour already in `widget/AsyncBroadcast.kt`. No timing budget is added: heartbeat parsing has no widget rendering equivalent, so the existing coroutine lifetime is preserved. Adds three regression tests covering: - OEM double-finish (the crash scenario) - Normal single finish - Exception during heartbeat work still finishes the broadcast Closes the follow-up noted in ActivityWatch#360. Git-Session-Id: 6d3fd149-5f73-4a7b-97c4-d50cc5ca7c5e
|
🤖 AI code reviewThis PR extracts the goAsync() completion path in AlarmReceiver into a new internal function launchAlarmHeartbeats() that wraps the pendingResult.finish() call in AsyncBroadcast.FinishOnce, so a second finish attempt (e.g. from an OEM pre-finishing the broadcast) is caught and logged instead of escaping the coroutine. It also adds a new test file AlarmReceiverFinishOnceTest with three tests covering double-finish, normal finish, and exception-during-work scenarios. Safe to merge — no P0/P1 findingsConfidence 5/5 ✅ No findings. The diff looks correct to me on this pass. Files changed (2) — the diff as I read it
Previous review passes
Reviewed Maintainer commands
|
Git-Session-Id: 0a1fa3a7-bab6-5521-8eb2-20f4461d0318
|
@greptileai review |
|
CI-green and mergeable (Greptile 5/5) — waiting only on a maintainer click. This PR is ready to merge, but the bot has pull-only access to this repo and can't self-merge — surfacing it here so it isn't lost. The monitoring loop will stop re-flagging it now that this note is posted. |
Problem
AlarmReceiveruses the samegoAsync()+finally { pendingResult.finish() }pattern that caused the widget's top Play crash (#360). An OEM pre-finishing the broadcast before the coroutine'sfinallyblock throwsIllegalStateException("Broadcast already finished"), which escapes the coroutine and kills the process.This was noted in #360's body:
Changes
AsyncBroadcastand wrappendingResult.finish()withFinishOncein theLOG_DATAbranch ofAlarmReceiverFinishOnceguards withAtomicBooleanand catches/logsIllegalStateExceptionon the second call — same protection as the widget fixTests
Adds
AlarmReceiverFinishOnceTestwith three cases:IllegalStateExceptionfromPendingResult.finish()) does not escape the coroutineScope
This is only the
FinishOnceguard slice from the task note in ActivityWatch/activitywatch#1435. It does not implement:aw-notify.enabled(separate slice)Closes the follow-up noted in #360.