Skip to content

0.7.2 --- a channel's ends reach only the program they are placed in - #21

Merged
Sunrisepeak merged 1 commit into
mainfrom
channel-ends-not-inherited
Sep 13, 2026
Merged

0.7.2 --- a channel's ends reach only the program they are placed in#21
Sunrisepeak merged 1 commit into
mainfrom
channel-ends-not-inherited

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

A child whose standard input was a channel never saw the end of its input on Windows, and a program started while a channel existed kept that channel open.

CreateProcessW with inheritance enabled gives the started program every inheritable handle of the process, not only the three in its start-up record. kal_process_channel created the far end inheritable from the moment it existed, so it reached:

  • the program at the other end of the channel, when the channel carried that program's input. The child held the writing end of its own input, so a parent that wrote a request and closed its end waited for ever for a child still reading. lsp-mcpp's process test, which echoes stdin back through a spawned copy of itself, hangs in exactly this way under Wine and on windows-2022;
  • any program another context started meanwhile, which kept the pipe open after the program it belonged to had ended, so a reader waiting for the end of that program's output waited for the other program instead.

The two descriptor implementations create both ends close-on-exec for this reason and let the spawn place the far one deliberately. This makes Windows do the same:

  • kal_process_channel: both ends are created non-inheritable.
  • kal_process_spawn: the handles it places are marked inheritable for the length of CreateProcessW only, each is restored to its previous state afterwards (a borrowed standard stream is the caller's), and an SRW lock serializes that window so a start on another context cannot receive them.
  • src/win32.h, port/kernel32.def: GetHandleInformation, AcquireSRWLockExclusive, ReleaseSRWLockExclusive.
  • 0.7.2.

Measured locally, with lsp-mcpp built for x86_64-windows-gnu over openkal-llvm-runtime → openkal-musl → this tree, under Wine:

before: [ run ] echo round trip through pipes      (no further output; killed after 60s)
after:  test_process  9 test cases, 0 failed; 25 assertions, 0 failed
        all 15 lsp-mcpp unit-test programs pass

The objects still reference no C runtime symbol in the dev and release profiles, and every declared name is exported by a .def.

A start with inheritance enabled gives the program every inheritable handle of
the process. kal_process_channel created the far end inheritable, so it reached
every program started while it existed: a child whose input was a channel held
the writing end of its own input and never saw the end of it, and a program
started meanwhile kept another program's pipe open. Both ends are now created
non-inheritable, as the descriptor implementations create both close-on-exec,
and kal_process_spawn marks the handles it places only for the length of the
start, restoring each and holding a lock across it.
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.

1 participant