Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Erlang/OTP 26 write to stdout hangs in latin1 encoding on Windows #7459

Closed
lukaszsamson opened this issue Jul 1, 2023 · 3 comments · Fixed by #7473
Closed

Erlang/OTP 26 write to stdout hangs in latin1 encoding on Windows #7459

lukaszsamson opened this issue Jul 1, 2023 · 3 comments · Fixed by #7473
Labels
bug Issue is reported as a bug team:VM Assigned to OTP team VM

Comments

@lukaszsamson
Copy link
Contributor

Describe the bug
After changing standard_io encoding to latin1 all writes to the output device hang.

To Reproduce

Run from windows command prompt

erl -noshell -eval "Path = os:find_executable('erl'), Port1 = erlang:open_port({spawn_executable, Path}, [binary, {args, ['-noshell', '-kernel', 'standard_io_encoding', 'latin1', '-eval', \"io:setopts(standard_io, [{encoding, latin1}]), io:format('duπa~n', []), erlang:halt()\"]}, exit_status]), receive {Port1, {data, A}} -> io:fwrite(A, []) after 2000 -> io:fwrite('nothing~n') end, erlang:halt()"

Result on windows

nothing

Expected behavior
Should print

du\x{3C0}a

it works correctly on OTP 25 both windows and *nix, and on OTP 26.0.2 on *nix

Affected versions
OTP 26.0.2

Additional context

This bug was tricky to track as it hangs only with a spawned process. If the same code is run directly from cmd shell it works correctly

erl -noshell -kernel standard_io_encoding latin1 -eval "io:setopts(standard_io, [{encoding, latin1}]), io:format('duπa~n', []), erlang:halt()"

prints

du\x{3C0}a

This bug breaks ElixirLS on windows elixir-lsp/elixir-ls#927

Possibly this is a regression of #7261 once fixed in 2bbeed3. The original script from that bug as well as a modified one hangs as well

-module(foo).
-compile(export_all).

run() ->
  io:put_chars("START\n"),

  open_port(
    {spawn_executable, os:find_executable("erl")},
    [exit_status, binary,
     {args, ["-noshell", "-kernel", "standard_io_encoding", "latin1", "-eval", "io:setopts(standard_io, [{encoding, latin1}]), io:write('teπt'), erlang:halt()."]}]
  ),

  loop().

loop() ->
  receive
    {_, {data, Data}} ->
      io:put_chars(Data),
      loop();

    {_, {exit_status, _}} ->
      erlang:halt()
  end.

Related to #7230, #7384

@lukaszsamson lukaszsamson added the bug Issue is reported as a bug label Jul 1, 2023
@IngelaAndin IngelaAndin added the team:VM Assigned to OTP team VM label Jul 3, 2023
@garazdawi
Copy link
Contributor

It is indeed #7261 that failed to solve the entire problem. Windows seems to treat input differently depending on whether it is a console, a file, or a pipe. The code in #7261 works when the input is a console or a file, but it does not work for a pipe...

I'm unsure if it is at all possible to achieve non-blocking I/O when STDIN is a pipe... I'll continue digging and see what I get figure out, worse case we may have to live with a blocking API and just make sure that when it happens the rest of the system does not dead-lock.

@lukaszsamson lukaszsamson changed the title Erlang/OTP 26 write to stdout hangs in latin1 encoding hangs on Windows Erlang/OTP 26 write to stdout hangs in latin1 encoding on Windows Jul 3, 2023
@garazdawi
Copy link
Contributor

I think I've fixed the issue, but as I did I stumbled across other problems in this area that I need to look closer at before releasing a patch.

One interesting thing I found was that apparently -oldshell and -noshell were documented always be byte-oriented (See io:setopts/1,2 here: https://www.erlang.org/doc/apps/stdlib/unicode_usage.html#summary-of-options). Had I seen that documentation earlier we may have avoided introducing standard_io_encoding altogether, but alas I did not so now we have it...

garazdawi added a commit to garazdawi/otp that referenced this issue Jul 4, 2023
In some situations the call to read_nif can block until
data is available (for example if input is a pipe on Windows)
so the code to set the encoding must be asynchronous otherwise
we can end up with a deadlock.

closes erlang#7459
garazdawi added a commit to garazdawi/otp that referenced this issue Jul 4, 2023
In some situations the call to read_nif can block until
data is available (for example if input is a pipe on Windows)
so the code to set the encoding must be asynchronous otherwise
we can end up with a deadlock.

closes erlang#7459
@garazdawi
Copy link
Contributor

The fix is implemented in #7473 together with some changes in how bytewise (latin1) encoding is handled in general. I've also included some documentation about how things are supposed to work and be used. Hopefully, it will make it easier for anyone else trying to do the same thing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issue is reported as a bug team:VM Assigned to OTP team VM
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants