Skip to content

Add failing repro for grandchild-pipe hang on sync Execute - #152

Draft
LukeButters wants to merge 4 commits into
mainfrom
luke/repro-grandchild-pipe-hang
Draft

Add failing repro for grandchild-pipe hang on sync Execute#152
LukeButters wants to merge 4 commits into
mainfrom
luke/repro-grandchild-pipe-hang

Conversation

@LukeButters

Copy link
Copy Markdown

Demonstrates that ShellfishProcess.WaitForExit falls through to the non-cancellable parameterless Process.WaitForExit() when a grandchild process inherits the redirected stdout/stderr pipes and outlives the immediate child. The async readers never see EOF and Execute() blocks forever.

The test backgrounds a sleep from bash so the orphaned sleep keeps the write-end of the pipe open after bash exits cleanly. It is guarded by a 15s task timeout so the runner reports a failure rather than hanging.

LukeButtersand others added 2 commits May 18, 2026 12:10
Demonstrates that ShellfishProcess.WaitForExit falls through to the
non-cancellable parameterless Process.WaitForExit() when a grandchild
process inherits the redirected stdout/stderr pipes and outlives the
immediate child. The async readers never see EOF and Execute() blocks
forever.
The test backgrounds a `sleep` from bash so the orphaned sleep keeps
the write-end of the pipe open after bash exits cleanly. It is guarded
by a 15s task timeout so the runner reports a failure rather than
hanging.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the [NixFact] repro using the same chain pattern as the
Tentacle SilentProcessRunner test: PowerShell -> cmd -> ping, where
cmd backgrounds ping via `start /b` then exits. ping inherits the
redirected pipes; PowerShell exits cleanly; the parameterless
process.WaitForExit() in ShellfishProcess.WaitForExit hangs waiting
for the async readers to drain.
Renamed the existing Nix test to ..._WhenUnixGrandchildHolds... so
the platform is obvious next to the new Windows counterpart.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@LukeButters
LukeButters marked this pull request as draft May 18, 2026 02:16
LukeButtersand others added 2 commits May 18, 2026 12:18
Both tests now follow the same shape as the Tentacle SilentProcessRunner
tests: spawn a child that backgrounds a grandchild holding the redirected
pipes, wait for the grandchild to come up, then cancel the CancellationToken
and assert Execute returns within the deadline.
On Shellfish the cancellation can't rescue Execute: once the immediate child
has exited, ShellfishProcess.WaitForExit is already past the cancellable
exitedEvent.Wait and inside the non-cancellable parameterless
Process.WaitForExit(), which blocks on async-reader drain that never EOFs
because the grandchild still holds the pipes.
Adds a Windows variant using the PowerShell -> cmd -> ping chain that
mirrors the Tentacle Windows test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant

@LukeButters
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
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;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Add failing repro for grandchild-pipe hang on sync Execute by LukeButters · Pull Request #152 · OctopusDeploy/Shellfish · GitHub
Skip to content

Add failing repro for grandchild-pipe hang on sync Execute - #152

Draft
LukeButters wants to merge 4 commits into
mainfrom
luke/repro-grandchild-pipe-hang
Draft

Add failing repro for grandchild-pipe hang on sync Execute#152
LukeButters wants to merge 4 commits into
mainfrom
luke/repro-grandchild-pipe-hang

Conversation

@LukeButters

Copy link
Copy Markdown

Demonstrates that ShellfishProcess.WaitForExit falls through to the non-cancellable parameterless Process.WaitForExit() when a grandchild process inherits the redirected stdout/stderr pipes and outlives the immediate child. The async readers never see EOF and Execute() blocks forever.

The test backgrounds a sleep from bash so the orphaned sleep keeps the write-end of the pipe open after bash exits cleanly. It is guarded by a 15s task timeout so the runner reports a failure rather than hanging.

LukeButtersand others added 2 commits May 18, 2026 12:10
Demonstrates that ShellfishProcess.WaitForExit falls through to the
non-cancellable parameterless Process.WaitForExit() when a grandchild
process inherits the redirected stdout/stderr pipes and outlives the
immediate child. The async readers never see EOF and Execute() blocks
forever.
The test backgrounds a `sleep` from bash so the orphaned sleep keeps
the write-end of the pipe open after bash exits cleanly. It is guarded
by a 15s task timeout so the runner reports a failure rather than
hanging.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the [NixFact] repro using the same chain pattern as the
Tentacle SilentProcessRunner test: PowerShell -> cmd -> ping, where
cmd backgrounds ping via `start /b` then exits. ping inherits the
redirected pipes; PowerShell exits cleanly; the parameterless
process.WaitForExit() in ShellfishProcess.WaitForExit hangs waiting
for the async readers to drain.
Renamed the existing Nix test to ..._WhenUnixGrandchildHolds... so
the platform is obvious next to the new Windows counterpart.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@LukeButters
LukeButters marked this pull request as draft May 18, 2026 02:16
LukeButtersand others added 2 commits May 18, 2026 12:18
Both tests now follow the same shape as the Tentacle SilentProcessRunner
tests: spawn a child that backgrounds a grandchild holding the redirected
pipes, wait for the grandchild to come up, then cancel the CancellationToken
and assert Execute returns within the deadline.
On Shellfish the cancellation can't rescue Execute: once the immediate child
has exited, ShellfishProcess.WaitForExit is already past the cancellable
exitedEvent.Wait and inside the non-cancellable parameterless
Process.WaitForExit(), which blocks on async-reader drain that never EOFs
because the grandchild still holds the pipes.
Adds a Windows variant using the PowerShell -> cmd -> ping chain that
mirrors the Tentacle Windows test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant

@LukeButters
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add failing repro for grandchild-pipe hang on sync Execute by LukeButters · Pull Request #152 · OctopusDeploy/Shellfish · GitHub
Skip to content

Add failing repro for grandchild-pipe hang on sync Execute - #152

Draft
LukeButters wants to merge 4 commits into
mainfrom
luke/repro-grandchild-pipe-hang
Draft

Add failing repro for grandchild-pipe hang on sync Execute#152
LukeButters wants to merge 4 commits into
mainfrom
luke/repro-grandchild-pipe-hang

Conversation

@LukeButters

Copy link
Copy Markdown

Demonstrates that ShellfishProcess.WaitForExit falls through to the non-cancellable parameterless Process.WaitForExit() when a grandchild process inherits the redirected stdout/stderr pipes and outlives the immediate child. The async readers never see EOF and Execute() blocks forever.

The test backgrounds a sleep from bash so the orphaned sleep keeps the write-end of the pipe open after bash exits cleanly. It is guarded by a 15s task timeout so the runner reports a failure rather than hanging.

LukeButtersand others added 2 commits May 18, 2026 12:10
Demonstrates that ShellfishProcess.WaitForExit falls through to the
non-cancellable parameterless Process.WaitForExit() when a grandchild
process inherits the redirected stdout/stderr pipes and outlives the
immediate child. The async readers never see EOF and Execute() blocks
forever.
The test backgrounds a `sleep` from bash so the orphaned sleep keeps
the write-end of the pipe open after bash exits cleanly. It is guarded
by a 15s task timeout so the runner reports a failure rather than
hanging.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the [NixFact] repro using the same chain pattern as the
Tentacle SilentProcessRunner test: PowerShell -> cmd -> ping, where
cmd backgrounds ping via `start /b` then exits. ping inherits the
redirected pipes; PowerShell exits cleanly; the parameterless
process.WaitForExit() in ShellfishProcess.WaitForExit hangs waiting
for the async readers to drain.
Renamed the existing Nix test to ..._WhenUnixGrandchildHolds... so
the platform is obvious next to the new Windows counterpart.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@LukeButters
LukeButters marked this pull request as draft May 18, 2026 02:16
LukeButtersand others added 2 commits May 18, 2026 12:18
Both tests now follow the same shape as the Tentacle SilentProcessRunner
tests: spawn a child that backgrounds a grandchild holding the redirected
pipes, wait for the grandchild to come up, then cancel the CancellationToken
and assert Execute returns within the deadline.
On Shellfish the cancellation can't rescue Execute: once the immediate child
has exited, ShellfishProcess.WaitForExit is already past the cancellable
exitedEvent.Wait and inside the non-cancellable parameterless
Process.WaitForExit(), which blocks on async-reader drain that never EOFs
because the grandchild still holds the pipes.
Adds a Windows variant using the PowerShell -> cmd -> ping chain that
mirrors the Tentacle Windows test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant

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

Add failing repro for grandchild-pipe hang on sync Execute - #152

Draft
LukeButters wants to merge 4 commits into
mainfrom
luke/repro-grandchild-pipe-hang
Draft

Add failing repro for grandchild-pipe hang on sync Execute#152
LukeButters wants to merge 4 commits into
mainfrom
luke/repro-grandchild-pipe-hang

Conversation

@LukeButters

Copy link
Copy Markdown

Demonstrates that ShellfishProcess.WaitForExit falls through to the non-cancellable parameterless Process.WaitForExit() when a grandchild process inherits the redirected stdout/stderr pipes and outlives the immediate child. The async readers never see EOF and Execute() blocks forever.

The test backgrounds a sleep from bash so the orphaned sleep keeps the write-end of the pipe open after bash exits cleanly. It is guarded by a 15s task timeout so the runner reports a failure rather than hanging.

LukeButtersand others added 2 commits May 18, 2026 12:10
Demonstrates that ShellfishProcess.WaitForExit falls through to the
non-cancellable parameterless Process.WaitForExit() when a grandchild
process inherits the redirected stdout/stderr pipes and outlives the
immediate child. The async readers never see EOF and Execute() blocks
forever.
The test backgrounds a `sleep` from bash so the orphaned sleep keeps
the write-end of the pipe open after bash exits cleanly. It is guarded
by a 15s task timeout so the runner reports a failure rather than
hanging.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the [NixFact] repro using the same chain pattern as the
Tentacle SilentProcessRunner test: PowerShell -> cmd -> ping, where
cmd backgrounds ping via `start /b` then exits. ping inherits the
redirected pipes; PowerShell exits cleanly; the parameterless
process.WaitForExit() in ShellfishProcess.WaitForExit hangs waiting
for the async readers to drain.
Renamed the existing Nix test to ..._WhenUnixGrandchildHolds... so
the platform is obvious next to the new Windows counterpart.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@LukeButters
LukeButters marked this pull request as draft May 18, 2026 02:16
LukeButtersand others added 2 commits May 18, 2026 12:18
Both tests now follow the same shape as the Tentacle SilentProcessRunner
tests: spawn a child that backgrounds a grandchild holding the redirected
pipes, wait for the grandchild to come up, then cancel the CancellationToken
and assert Execute returns within the deadline.
On Shellfish the cancellation can't rescue Execute: once the immediate child
has exited, ShellfishProcess.WaitForExit is already past the cancellable
exitedEvent.Wait and inside the non-cancellable parameterless
Process.WaitForExit(), which blocks on async-reader drain that never EOFs
because the grandchild still holds the pipes.
Adds a Windows variant using the PowerShell -> cmd -> ping chain that
mirrors the Tentacle Windows test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant

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

Add failing repro for grandchild-pipe hang on sync Execute - #152

Draft
LukeButters wants to merge 4 commits into
mainfrom
luke/repro-grandchild-pipe-hang
Draft

Add failing repro for grandchild-pipe hang on sync Execute#152
LukeButters wants to merge 4 commits into
mainfrom
luke/repro-grandchild-pipe-hang

Conversation

@LukeButters

Copy link
Copy Markdown

Demonstrates that ShellfishProcess.WaitForExit falls through to the non-cancellable parameterless Process.WaitForExit() when a grandchild process inherits the redirected stdout/stderr pipes and outlives the immediate child. The async readers never see EOF and Execute() blocks forever.

The test backgrounds a sleep from bash so the orphaned sleep keeps the write-end of the pipe open after bash exits cleanly. It is guarded by a 15s task timeout so the runner reports a failure rather than hanging.

LukeButtersand others added 2 commits May 18, 2026 12:10
Demonstrates that ShellfishProcess.WaitForExit falls through to the
non-cancellable parameterless Process.WaitForExit() when a grandchild
process inherits the redirected stdout/stderr pipes and outlives the
immediate child. The async readers never see EOF and Execute() blocks
forever.
The test backgrounds a `sleep` from bash so the orphaned sleep keeps
the write-end of the pipe open after bash exits cleanly. It is guarded
by a 15s task timeout so the runner reports a failure rather than
hanging.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the [NixFact] repro using the same chain pattern as the
Tentacle SilentProcessRunner test: PowerShell -> cmd -> ping, where
cmd backgrounds ping via `start /b` then exits. ping inherits the
redirected pipes; PowerShell exits cleanly; the parameterless
process.WaitForExit() in ShellfishProcess.WaitForExit hangs waiting
for the async readers to drain.
Renamed the existing Nix test to ..._WhenUnixGrandchildHolds... so
the platform is obvious next to the new Windows counterpart.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@LukeButters
LukeButters marked this pull request as draft May 18, 2026 02:16
LukeButtersand others added 2 commits May 18, 2026 12:18
Both tests now follow the same shape as the Tentacle SilentProcessRunner
tests: spawn a child that backgrounds a grandchild holding the redirected
pipes, wait for the grandchild to come up, then cancel the CancellationToken
and assert Execute returns within the deadline.
On Shellfish the cancellation can't rescue Execute: once the immediate child
has exited, ShellfishProcess.WaitForExit is already past the cancellable
exitedEvent.Wait and inside the non-cancellable parameterless
Process.WaitForExit(), which blocks on async-reader drain that never EOFs
because the grandchild still holds the pipes.
Adds a Windows variant using the PowerShell -> cmd -> ping chain that
mirrors the Tentacle Windows test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant

@LukeButters
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add failing repro for grandchild-pipe hang on sync Execute by LukeButters · Pull Request #152 · OctopusDeploy/Shellfish · GitHub
Skip to content

Add failing repro for grandchild-pipe hang on sync Execute - #152

Draft
LukeButters wants to merge 4 commits into
mainfrom
luke/repro-grandchild-pipe-hang
Draft

Add failing repro for grandchild-pipe hang on sync Execute#152
LukeButters wants to merge 4 commits into
mainfrom
luke/repro-grandchild-pipe-hang

Conversation

@LukeButters

Copy link
Copy Markdown

Demonstrates that ShellfishProcess.WaitForExit falls through to the non-cancellable parameterless Process.WaitForExit() when a grandchild process inherits the redirected stdout/stderr pipes and outlives the immediate child. The async readers never see EOF and Execute() blocks forever.

The test backgrounds a sleep from bash so the orphaned sleep keeps the write-end of the pipe open after bash exits cleanly. It is guarded by a 15s task timeout so the runner reports a failure rather than hanging.

LukeButtersand others added 2 commits May 18, 2026 12:10
Demonstrates that ShellfishProcess.WaitForExit falls through to the
non-cancellable parameterless Process.WaitForExit() when a grandchild
process inherits the redirected stdout/stderr pipes and outlives the
immediate child. The async readers never see EOF and Execute() blocks
forever.
The test backgrounds a `sleep` from bash so the orphaned sleep keeps
the write-end of the pipe open after bash exits cleanly. It is guarded
by a 15s task timeout so the runner reports a failure rather than
hanging.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the [NixFact] repro using the same chain pattern as the
Tentacle SilentProcessRunner test: PowerShell -> cmd -> ping, where
cmd backgrounds ping via `start /b` then exits. ping inherits the
redirected pipes; PowerShell exits cleanly; the parameterless
process.WaitForExit() in ShellfishProcess.WaitForExit hangs waiting
for the async readers to drain.
Renamed the existing Nix test to ..._WhenUnixGrandchildHolds... so
the platform is obvious next to the new Windows counterpart.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@LukeButters
LukeButters marked this pull request as draft May 18, 2026 02:16
LukeButtersand others added 2 commits May 18, 2026 12:18
Both tests now follow the same shape as the Tentacle SilentProcessRunner
tests: spawn a child that backgrounds a grandchild holding the redirected
pipes, wait for the grandchild to come up, then cancel the CancellationToken
and assert Execute returns within the deadline.
On Shellfish the cancellation can't rescue Execute: once the immediate child
has exited, ShellfishProcess.WaitForExit is already past the cancellable
exitedEvent.Wait and inside the non-cancellable parameterless
Process.WaitForExit(), which blocks on async-reader drain that never EOFs
because the grandchild still holds the pipes.
Adds a Windows variant using the PowerShell -> cmd -> ping chain that
mirrors the Tentacle Windows test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant

@LukeButters
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add failing repro for grandchild-pipe hang on sync Execute by LukeButters · Pull Request #152 · OctopusDeploy/Shellfish · GitHub
Skip to content

Add failing repro for grandchild-pipe hang on sync Execute - #152

Draft
LukeButters wants to merge 4 commits into
mainfrom
luke/repro-grandchild-pipe-hang
Draft

Add failing repro for grandchild-pipe hang on sync Execute#152
LukeButters wants to merge 4 commits into
mainfrom
luke/repro-grandchild-pipe-hang

Conversation

@LukeButters

Copy link
Copy Markdown

Demonstrates that ShellfishProcess.WaitForExit falls through to the non-cancellable parameterless Process.WaitForExit() when a grandchild process inherits the redirected stdout/stderr pipes and outlives the immediate child. The async readers never see EOF and Execute() blocks forever.

The test backgrounds a sleep from bash so the orphaned sleep keeps the write-end of the pipe open after bash exits cleanly. It is guarded by a 15s task timeout so the runner reports a failure rather than hanging.

LukeButtersand others added 2 commits May 18, 2026 12:10
Demonstrates that ShellfishProcess.WaitForExit falls through to the
non-cancellable parameterless Process.WaitForExit() when a grandchild
process inherits the redirected stdout/stderr pipes and outlives the
immediate child. The async readers never see EOF and Execute() blocks
forever.
The test backgrounds a `sleep` from bash so the orphaned sleep keeps
the write-end of the pipe open after bash exits cleanly. It is guarded
by a 15s task timeout so the runner reports a failure rather than
hanging.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the [NixFact] repro using the same chain pattern as the
Tentacle SilentProcessRunner test: PowerShell -> cmd -> ping, where
cmd backgrounds ping via `start /b` then exits. ping inherits the
redirected pipes; PowerShell exits cleanly; the parameterless
process.WaitForExit() in ShellfishProcess.WaitForExit hangs waiting
for the async readers to drain.
Renamed the existing Nix test to ..._WhenUnixGrandchildHolds... so
the platform is obvious next to the new Windows counterpart.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@LukeButters
LukeButters marked this pull request as draft May 18, 2026 02:16
LukeButtersand others added 2 commits May 18, 2026 12:18
Both tests now follow the same shape as the Tentacle SilentProcessRunner
tests: spawn a child that backgrounds a grandchild holding the redirected
pipes, wait for the grandchild to come up, then cancel the CancellationToken
and assert Execute returns within the deadline.
On Shellfish the cancellation can't rescue Execute: once the immediate child
has exited, ShellfishProcess.WaitForExit is already past the cancellable
exitedEvent.Wait and inside the non-cancellable parameterless
Process.WaitForExit(), which blocks on async-reader drain that never EOFs
because the grandchild still holds the pipes.
Adds a Windows variant using the PowerShell -> cmd -> ping chain that
mirrors the Tentacle Windows test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant

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

Add failing repro for grandchild-pipe hang on sync Execute - #152

Draft
LukeButters wants to merge 4 commits into
mainfrom
luke/repro-grandchild-pipe-hang
Draft

Add failing repro for grandchild-pipe hang on sync Execute#152
LukeButters wants to merge 4 commits into
mainfrom
luke/repro-grandchild-pipe-hang

Conversation

@LukeButters

Copy link
Copy Markdown

Demonstrates that ShellfishProcess.WaitForExit falls through to the non-cancellable parameterless Process.WaitForExit() when a grandchild process inherits the redirected stdout/stderr pipes and outlives the immediate child. The async readers never see EOF and Execute() blocks forever.

The test backgrounds a sleep from bash so the orphaned sleep keeps the write-end of the pipe open after bash exits cleanly. It is guarded by a 15s task timeout so the runner reports a failure rather than hanging.

LukeButtersand others added 2 commits May 18, 2026 12:10
Demonstrates that ShellfishProcess.WaitForExit falls through to the
non-cancellable parameterless Process.WaitForExit() when a grandchild
process inherits the redirected stdout/stderr pipes and outlives the
immediate child. The async readers never see EOF and Execute() blocks
forever.
The test backgrounds a `sleep` from bash so the orphaned sleep keeps
the write-end of the pipe open after bash exits cleanly. It is guarded
by a 15s task timeout so the runner reports a failure rather than
hanging.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the [NixFact] repro using the same chain pattern as the
Tentacle SilentProcessRunner test: PowerShell -> cmd -> ping, where
cmd backgrounds ping via `start /b` then exits. ping inherits the
redirected pipes; PowerShell exits cleanly; the parameterless
process.WaitForExit() in ShellfishProcess.WaitForExit hangs waiting
for the async readers to drain.
Renamed the existing Nix test to ..._WhenUnixGrandchildHolds... so
the platform is obvious next to the new Windows counterpart.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@LukeButters
LukeButters marked this pull request as draft May 18, 2026 02:16
LukeButtersand others added 2 commits May 18, 2026 12:18
Both tests now follow the same shape as the Tentacle SilentProcessRunner
tests: spawn a child that backgrounds a grandchild holding the redirected
pipes, wait for the grandchild to come up, then cancel the CancellationToken
and assert Execute returns within the deadline.
On Shellfish the cancellation can't rescue Execute: once the immediate child
has exited, ShellfishProcess.WaitForExit is already past the cancellable
exitedEvent.Wait and inside the non-cancellable parameterless
Process.WaitForExit(), which blocks on async-reader drain that never EOFs
because the grandchild still holds the pipes.
Adds a Windows variant using the PowerShell -> cmd -> ping chain that
mirrors the Tentacle Windows test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant

@LukeButters