Skip to content

eval: feed stdin from an empty temporary file to avoid hangs - #4963

Merged
VeryMilkyJoe merged 2 commits into
haskell:masterfrom
dschrempf:dom/eval/stdin
Oct 11, 2026
Merged

VeryMilkyJoe merged 2 commits into
haskell:masterfrom
dschrempf:dom/eval/stdin

Conversation

@dschrempf

@dschrempf dschrempf commented Jun 8, 2026 •

Copy link
Copy Markdown
Collaborator

This is a followup of #4726.

Feed stdin from an empty file.

The test explains the behavior:

-- 1. Support `stdin`
module TIO where

{- Feed `stdin` with empty data.

Avoid server hangs indefinitely, waiting for `stdin` to terminate.

Shows a clear error message.

>>> getLine >>= print
<stdin>: hGetLine: end of file
-}

@xsebek

Fixes #2913.

@dschrempf
dschrempf force-pushed the dom/eval/stdin branch 3 times, most recently from 532cf28 to 91bdc88 Compare June 8, 2026 12:48
@dschrempf
dschrempf requested a review from fendor as a code owner June 8, 2026 12:48
@dschrempf
dschrempf force-pushed the dom/eval/stdin branch 3 times, most recently from 6b87429 to 1928f7a Compare June 8, 2026 17:11
@dschrempf

Copy link
Copy Markdown
Collaborator Author

Somehow

cabal run func-test -- -p '/eval plugin sends progress reports/'

times out.

Any ideas?

@dschrempf
dschrempf force-pushed the dom/eval/stdin branch 2 times, most recently from a5cde07 to 161cb5f Compare October 9, 2026 13:18
@dschrempf
dschrempf requested a review from wz1000 as a code owner October 9, 2026 13:18
@dschrempf
dschrempf force-pushed the dom/eval/stdin branch 2 times, most recently from e6635c5 to 2a37df0 Compare October 9, 2026 14:34
@dschrempf

Copy link
Copy Markdown
Collaborator Author

The cause of the timeout was a deadlock.

  1. The server reads messages from stdin, blocking the handle, waiting for the next message.
  2. The eval plugin runs in the same process. The getLine wants to read the same handle, waiting for the server lock to be released.
  3. Nothing happens until the timeout is reached.

The reason was to clone the stdin handle, similar to what we already do for stdout.

In detail, the original stdin and stdout are duplicated and exclusively used for the JSON-RPC connection. The stdin is closed (end of file), tested by the eval plugin test. stdout is redirected to stderr (stray messages end up in the logs).

@dschrempf
dschrempf force-pushed the dom/eval/stdin branch 4 times, most recently from 59ffbb9 to 33dc7bd Compare October 10, 2026 14:49

@VeryMilkyJoe VeryMilkyJoe left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a little nit about documentation.
Thank you for this, I think it's really cool that you are working on the eval plugin!

| LogEvalFailedSettingInteractivePrintFunction
| LogEvalCaptureStdHandles CapturePhase String

-- | Step of redirecting the interpreted standard handles around a statement.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand what this means, specifically what "around a statement".

@dschrempf dschrempf Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea.. internals of the evil plugin; it should be explained in a better way.
I have updated the documentation. A opy of the new version is:

-- | Phases required to capture standard handles
--
-- Phases of the @eval@ plugin:
-- - fire up a GHCi session
-- - inject code into the GHCi session
-- - run the code
-- - capture the output
--
-- The code injected into the GHCi session contains a setup and a teardown
-- preparing and closing the involved handels, see 'captureSetup' and
-- 'captureTeardown'.

The server read JSON-RPC messages from `stdin`, which all code in the server
process shares. An expression evaluated by the eval plugin could consume LSP
messages (`getLine`), or block on the handle while the server's reader thread
holds it.

As for `stdout`, read LSP messages from a duplicate of `stdin`, and point
`stdin` at an empty pipe, so reads see end of file. The in-process test
harness, which supplies its own input handle, does the same.

Also log failures of the eval plugin's capture setup and teardown, and test
capturing `stdout` and `stderr` repeatedly, as well as reading `stdin`.

@VeryMilkyJoe VeryMilkyJoe left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, just a typo then we can merge!

@VeryMilkyJoe
VeryMilkyJoe enabled auto-merge (squash) October 11, 2026 08:54
@VeryMilkyJoe
VeryMilkyJoe merged commit 3a9f646 into haskell:master Oct 11, 2026
42 checks passed
@dschrempf

Copy link
Copy Markdown
Collaborator Author

🎉 thanks for the review @VeryMilkyJoe !

Sign up for free to 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.

getLine in eval code lens causes connection got disposed

2 participants