Skip to content

Commit 881a33b

Browse files
author
Kun.Long
committed
fix: defer session-lock release on dispose/abort inside active write
When disposeHeldLockAfterRetainedIdle or releaseHeldLockWithFence are called from within the active retained-write context (waitForRetainedLockIdle returns false), register a deferred release callback on retainedLockIdleWaiters instead of silently returning. This preserves write serialization (no concurrent writes from force-release) while ensuring the lock is eventually released when the retained-use counter drains to zero. Previously the early return leaked the lock in memory and on disk, causing subsequent sub-agents to time out with "session file locked" errors when the web GUI rapidly switches sessions.
1 parent 2939ac6 commit 881a33b

1 file changed

Lines changed: 17 additions & 0 deletions

File tree

src/agents/embedded-agent-runner/run/attempt.session-lock.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,15 @@ export async function createEmbeddedAttemptSessionLockController(params: {
797797
const drainOwner = await beginHeldLockDrain();
798798
try {
799799
if (!(await waitForRetainedLockIdle())) {
800+
// Inside the active retained-write context: releasing synchronously
801+
// would deadlock, and force-releasing would let another attempt
802+
// overlap writes and stale the session fence. Defer release to
803+
// when the retained-use counter drains to zero.
804+
const pending = heldLock;
805+
retainedLockIdleWaiters.add(() => {
806+
if (heldLock === pending) heldLock = undefined;
807+
pending?.release().catch(() => {});
808+
});
800809
return;
801810
}
802811
if (!heldLock) {
@@ -854,6 +863,14 @@ export async function createEmbeddedAttemptSessionLockController(params: {
854863
const drainOwner = await beginHeldLockDrain();
855864
try {
856865
if (!(await waitForRetainedLockIdle())) {
866+
// Inside the active retained-write context during teardown.
867+
// Defer release to after the in-flight write completes so we
868+
// preserve write serialization and release the lock reliably.
869+
const pending = heldLock;
870+
retainedLockIdleWaiters.add(() => {
871+
if (heldLock === pending) heldLock = undefined;
872+
pending?.release().catch(() => {});
873+
});
857874
return;
858875
}
859876
if (!heldLock) {

0 commit comments

Comments
 (0)