fix: don't break a live lock on staleness alone (#432) - #445

Open
arzaanxeng wants to merge 5 commits into
agentrhq:mainfrom
arzaanxeng:main
Open

fix: don't break a live lock on staleness alone (#432)#445
arzaanxeng wants to merge 5 commits into
agentrhq:mainfrom
arzaanxeng:main

Conversation

@arzaanxeng

Copy link
Copy Markdown
Contributor

Fixes#432

Bug

breakIfAbandoned() decided a lock was abandoned if expired OR ownerGone.
That meant a same-host owner that was still alive and running could have its
lock stolen just because its critical section took longer than staleMs
(big file, slow disk, loaded machine). Two processes would then race the same
file, and whichever rename() landed last silently discarded the other's work.

Fix

  • breakIfAbandoned() now checks owner liveness first: if the owner's pid is
    checkable and confirmed alive on the same host, the lock is never broken on
    staleness alone — liveness is decisive, not just one of two OR'd signals.
  • withFileLock() now runs a heartbeat while the critical section is active,
    refreshing the lock file's mtime every staleMs / 3. This covers the case
    where liveness can't be checked (cross-host lock files), so staleMs still
    reflects real inactivity rather than just how long the section has run.
  • Updated the header comment, which documented the old (buggy) "staleMs
    breaks regardless" behavior.

Testing

Added two regression tests in file-lock.test.ts that reproduce the exact
scenario from the issue: a live same-host holder whose lock is older than
staleMs, and a second acquirer racing it mid-critical-section. Both new
tests fail against the old code and pass with this fix. Full suite:
8/8 passing.

Enhance file lock mechanism with heartbeat and mtime refresh to prevent premature lock expiration.
@github-actions

github-actionsBot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

🟢 No documentation gap found — medium confidence

The automated review found no documentation gap in the supplied changes.

This review is advisory and does not block merging.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

site-memory file lock breaks mutual exclusion when a critical section runs longer than staleMs

1 participant

@arzaanxeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix: don't break a live lock on staleness alone (#432) - #445

Open
arzaanxeng wants to merge 5 commits into
agentrhq:mainfrom
arzaanxeng:main
Open

fix: don't break a live lock on staleness alone (#432)#445
arzaanxeng wants to merge 5 commits into
agentrhq:mainfrom
arzaanxeng:main

Conversation

@arzaanxeng

Copy link
Copy Markdown
Contributor

Fixes#432

Bug

breakIfAbandoned() decided a lock was abandoned if expired OR ownerGone.
That meant a same-host owner that was still alive and running could have its
lock stolen just because its critical section took longer than staleMs
(big file, slow disk, loaded machine). Two processes would then race the same
file, and whichever rename() landed last silently discarded the other's work.

Fix

  • breakIfAbandoned() now checks owner liveness first: if the owner's pid is
    checkable and confirmed alive on the same host, the lock is never broken on
    staleness alone — liveness is decisive, not just one of two OR'd signals.
  • withFileLock() now runs a heartbeat while the critical section is active,
    refreshing the lock file's mtime every staleMs / 3. This covers the case
    where liveness can't be checked (cross-host lock files), so staleMs still
    reflects real inactivity rather than just how long the section has run.
  • Updated the header comment, which documented the old (buggy) "staleMs
    breaks regardless" behavior.

Testing

Added two regression tests in file-lock.test.ts that reproduce the exact
scenario from the issue: a live same-host holder whose lock is older than
staleMs, and a second acquirer racing it mid-critical-section. Both new
tests fail against the old code and pass with this fix. Full suite:
8/8 passing.

Enhance file lock mechanism with heartbeat and mtime refresh to prevent premature lock expiration.
@github-actions

github-actionsBot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

🟢 No documentation gap found — medium confidence

The automated review found no documentation gap in the supplied changes.

This review is advisory and does not block merging.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

site-memory file lock breaks mutual exclusion when a critical section runs longer than staleMs

1 participant

@arzaanxeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: don't break a live lock on staleness alone (#432) - #445

Open
arzaanxeng wants to merge 5 commits into
agentrhq:mainfrom
arzaanxeng:main
Open

fix: don't break a live lock on staleness alone (#432)#445
arzaanxeng wants to merge 5 commits into
agentrhq:mainfrom
arzaanxeng:main

Conversation

@arzaanxeng

Copy link
Copy Markdown
Contributor

Fixes#432

Bug

breakIfAbandoned() decided a lock was abandoned if expired OR ownerGone.
That meant a same-host owner that was still alive and running could have its
lock stolen just because its critical section took longer than staleMs
(big file, slow disk, loaded machine). Two processes would then race the same
file, and whichever rename() landed last silently discarded the other's work.

Fix

  • breakIfAbandoned() now checks owner liveness first: if the owner's pid is
    checkable and confirmed alive on the same host, the lock is never broken on
    staleness alone — liveness is decisive, not just one of two OR'd signals.
  • withFileLock() now runs a heartbeat while the critical section is active,
    refreshing the lock file's mtime every staleMs / 3. This covers the case
    where liveness can't be checked (cross-host lock files), so staleMs still
    reflects real inactivity rather than just how long the section has run.
  • Updated the header comment, which documented the old (buggy) "staleMs
    breaks regardless" behavior.

Testing

Added two regression tests in file-lock.test.ts that reproduce the exact
scenario from the issue: a live same-host holder whose lock is older than
staleMs, and a second acquirer racing it mid-critical-section. Both new
tests fail against the old code and pass with this fix. Full suite:
8/8 passing.

Enhance file lock mechanism with heartbeat and mtime refresh to prevent premature lock expiration.
@github-actions

github-actionsBot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

🟢 No documentation gap found — medium confidence

The automated review found no documentation gap in the supplied changes.

This review is advisory and does not block merging.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

site-memory file lock breaks mutual exclusion when a critical section runs longer than staleMs

1 participant

@arzaanxeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: don't break a live lock on staleness alone (#432) - #445

Open
arzaanxeng wants to merge 5 commits into
agentrhq:mainfrom
arzaanxeng:main
Open

fix: don't break a live lock on staleness alone (#432)#445
arzaanxeng wants to merge 5 commits into
agentrhq:mainfrom
arzaanxeng:main

Conversation

@arzaanxeng

Copy link
Copy Markdown
Contributor

Fixes#432

Bug

breakIfAbandoned() decided a lock was abandoned if expired OR ownerGone.
That meant a same-host owner that was still alive and running could have its
lock stolen just because its critical section took longer than staleMs
(big file, slow disk, loaded machine). Two processes would then race the same
file, and whichever rename() landed last silently discarded the other's work.

Fix

  • breakIfAbandoned() now checks owner liveness first: if the owner's pid is
    checkable and confirmed alive on the same host, the lock is never broken on
    staleness alone — liveness is decisive, not just one of two OR'd signals.
  • withFileLock() now runs a heartbeat while the critical section is active,
    refreshing the lock file's mtime every staleMs / 3. This covers the case
    where liveness can't be checked (cross-host lock files), so staleMs still
    reflects real inactivity rather than just how long the section has run.
  • Updated the header comment, which documented the old (buggy) "staleMs
    breaks regardless" behavior.

Testing

Added two regression tests in file-lock.test.ts that reproduce the exact
scenario from the issue: a live same-host holder whose lock is older than
staleMs, and a second acquirer racing it mid-critical-section. Both new
tests fail against the old code and pass with this fix. Full suite:
8/8 passing.

Enhance file lock mechanism with heartbeat and mtime refresh to prevent premature lock expiration.
@github-actions

github-actionsBot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

🟢 No documentation gap found — medium confidence

The automated review found no documentation gap in the supplied changes.

This review is advisory and does not block merging.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

site-memory file lock breaks mutual exclusion when a critical section runs longer than staleMs

1 participant

@arzaanxeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix: don't break a live lock on staleness alone (#432) - #445

Open
arzaanxeng wants to merge 5 commits into
agentrhq:mainfrom
arzaanxeng:main
Open

fix: don't break a live lock on staleness alone (#432)#445
arzaanxeng wants to merge 5 commits into
agentrhq:mainfrom
arzaanxeng:main

Conversation

@arzaanxeng

Copy link
Copy Markdown
Contributor

Fixes#432

Bug

breakIfAbandoned() decided a lock was abandoned if expired OR ownerGone.
That meant a same-host owner that was still alive and running could have its
lock stolen just because its critical section took longer than staleMs
(big file, slow disk, loaded machine). Two processes would then race the same
file, and whichever rename() landed last silently discarded the other's work.

Fix

  • breakIfAbandoned() now checks owner liveness first: if the owner's pid is
    checkable and confirmed alive on the same host, the lock is never broken on
    staleness alone — liveness is decisive, not just one of two OR'd signals.
  • withFileLock() now runs a heartbeat while the critical section is active,
    refreshing the lock file's mtime every staleMs / 3. This covers the case
    where liveness can't be checked (cross-host lock files), so staleMs still
    reflects real inactivity rather than just how long the section has run.
  • Updated the header comment, which documented the old (buggy) "staleMs
    breaks regardless" behavior.

Testing

Added two regression tests in file-lock.test.ts that reproduce the exact
scenario from the issue: a live same-host holder whose lock is older than
staleMs, and a second acquirer racing it mid-critical-section. Both new
tests fail against the old code and pass with this fix. Full suite:
8/8 passing.

Enhance file lock mechanism with heartbeat and mtime refresh to prevent premature lock expiration.
@github-actions

github-actionsBot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

🟢 No documentation gap found — medium confidence

The automated review found no documentation gap in the supplied changes.

This review is advisory and does not block merging.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

site-memory file lock breaks mutual exclusion when a critical section runs longer than staleMs

1 participant

@arzaanxeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: don't break a live lock on staleness alone (#432) - #445

Open
arzaanxeng wants to merge 5 commits into
agentrhq:mainfrom
arzaanxeng:main
Open

fix: don't break a live lock on staleness alone (#432)#445
arzaanxeng wants to merge 5 commits into
agentrhq:mainfrom
arzaanxeng:main

Conversation

@arzaanxeng

Copy link
Copy Markdown
Contributor

Fixes#432

Bug

breakIfAbandoned() decided a lock was abandoned if expired OR ownerGone.
That meant a same-host owner that was still alive and running could have its
lock stolen just because its critical section took longer than staleMs
(big file, slow disk, loaded machine). Two processes would then race the same
file, and whichever rename() landed last silently discarded the other's work.

Fix

  • breakIfAbandoned() now checks owner liveness first: if the owner's pid is
    checkable and confirmed alive on the same host, the lock is never broken on
    staleness alone — liveness is decisive, not just one of two OR'd signals.
  • withFileLock() now runs a heartbeat while the critical section is active,
    refreshing the lock file's mtime every staleMs / 3. This covers the case
    where liveness can't be checked (cross-host lock files), so staleMs still
    reflects real inactivity rather than just how long the section has run.
  • Updated the header comment, which documented the old (buggy) "staleMs
    breaks regardless" behavior.

Testing

Added two regression tests in file-lock.test.ts that reproduce the exact
scenario from the issue: a live same-host holder whose lock is older than
staleMs, and a second acquirer racing it mid-critical-section. Both new
tests fail against the old code and pass with this fix. Full suite:
8/8 passing.

Enhance file lock mechanism with heartbeat and mtime refresh to prevent premature lock expiration.
@github-actions

github-actionsBot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

🟢 No documentation gap found — medium confidence

The automated review found no documentation gap in the supplied changes.

This review is advisory and does not block merging.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

site-memory file lock breaks mutual exclusion when a critical section runs longer than staleMs

1 participant

@arzaanxeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: don't break a live lock on staleness alone (#432) - #445

Open
arzaanxeng wants to merge 5 commits into
agentrhq:mainfrom
arzaanxeng:main
Open

fix: don't break a live lock on staleness alone (#432)#445
arzaanxeng wants to merge 5 commits into
agentrhq:mainfrom
arzaanxeng:main

Conversation

@arzaanxeng

Copy link
Copy Markdown
Contributor

Fixes#432

Bug

breakIfAbandoned() decided a lock was abandoned if expired OR ownerGone.
That meant a same-host owner that was still alive and running could have its
lock stolen just because its critical section took longer than staleMs
(big file, slow disk, loaded machine). Two processes would then race the same
file, and whichever rename() landed last silently discarded the other's work.

Fix

  • breakIfAbandoned() now checks owner liveness first: if the owner's pid is
    checkable and confirmed alive on the same host, the lock is never broken on
    staleness alone — liveness is decisive, not just one of two OR'd signals.
  • withFileLock() now runs a heartbeat while the critical section is active,
    refreshing the lock file's mtime every staleMs / 3. This covers the case
    where liveness can't be checked (cross-host lock files), so staleMs still
    reflects real inactivity rather than just how long the section has run.
  • Updated the header comment, which documented the old (buggy) "staleMs
    breaks regardless" behavior.

Testing

Added two regression tests in file-lock.test.ts that reproduce the exact
scenario from the issue: a live same-host holder whose lock is older than
staleMs, and a second acquirer racing it mid-critical-section. Both new
tests fail against the old code and pass with this fix. Full suite:
8/8 passing.

Enhance file lock mechanism with heartbeat and mtime refresh to prevent premature lock expiration.
@github-actions

github-actionsBot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

🟢 No documentation gap found — medium confidence

The automated review found no documentation gap in the supplied changes.

This review is advisory and does not block merging.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

site-memory file lock breaks mutual exclusion when a critical section runs longer than staleMs

1 participant

@arzaanxeng
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix: don't break a live lock on staleness alone (#432) - #445

Open
arzaanxeng wants to merge 5 commits into
agentrhq:mainfrom
arzaanxeng:main
Open

fix: don't break a live lock on staleness alone (#432)#445
arzaanxeng wants to merge 5 commits into
agentrhq:mainfrom
arzaanxeng:main

Conversation

@arzaanxeng

Copy link
Copy Markdown
Contributor

Fixes#432

Bug

breakIfAbandoned() decided a lock was abandoned if expired OR ownerGone.
That meant a same-host owner that was still alive and running could have its
lock stolen just because its critical section took longer than staleMs
(big file, slow disk, loaded machine). Two processes would then race the same
file, and whichever rename() landed last silently discarded the other's work.

Fix

  • breakIfAbandoned() now checks owner liveness first: if the owner's pid is
    checkable and confirmed alive on the same host, the lock is never broken on
    staleness alone — liveness is decisive, not just one of two OR'd signals.
  • withFileLock() now runs a heartbeat while the critical section is active,
    refreshing the lock file's mtime every staleMs / 3. This covers the case
    where liveness can't be checked (cross-host lock files), so staleMs still
    reflects real inactivity rather than just how long the section has run.
  • Updated the header comment, which documented the old (buggy) "staleMs
    breaks regardless" behavior.

Testing

Added two regression tests in file-lock.test.ts that reproduce the exact
scenario from the issue: a live same-host holder whose lock is older than
staleMs, and a second acquirer racing it mid-critical-section. Both new
tests fail against the old code and pass with this fix. Full suite:
8/8 passing.

Enhance file lock mechanism with heartbeat and mtime refresh to prevent premature lock expiration.
@github-actions

github-actionsBot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

🟢 No documentation gap found — medium confidence

The automated review found no documentation gap in the supplied changes.

This review is advisory and does not block merging.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

site-memory file lock breaks mutual exclusion when a critical section runs longer than staleMs

1 participant

@arzaanxeng