PTY example code deadlock

Hello,

I want to use the PTY library to communicate with another local program.

I am running the example code and it seems to be deadlocking.

The example code calls this out as possible, and states:

  "we will change the buffering type in the factor command, assuming that
  factor uses stdio for stdout buffering.

  If IO.pipe is used instead of ::open, this code deadlocks because
  factor's stdout is fully buffered."

I take the above to mean that the example code has been written such that a
deadlock _won't_ occur, but I appear to be wrong. When I run the code it just
hangs.

  l4@ubuntu:~$ ./pty_test.rb

If I tweak the example code to send invalid input to factor then I get a
response on stderr immediately:

  l4@ubuntu:~$ ./pty_test.rb
  factor: ‘42\\n’ is not a valid positive integer

... at which point it continues to spin.

Are the docs wrong? Am I doing something wrong? What's happening here?

Thanks,
Jefferson

Here's a link to the example code:
https://ruby-doc.org/stdlib-3.0.0/libdoc/pty/rdoc/PTY.html#method-c-spawn

And a more succint reproduction of the same problem (with the invalid input
described above):

#!/usr/bin/env ruby
require 'pty'
IO.pipe do |r,w|
  PTY.open do |m,s|
    pid = spawn("factor", in: r, out: s)
    r.close
    s.close
    w.puts('42\n') # intentionally invalid to demonstrate stderr
    puts(m.gets)
  end
end

After poking at it some more, the documentation (code + explanation) must
just be bad. The following works just fine:

#!/usr/bin/env ruby
require 'pty'
PTY.spawn("factor") do |stdout,stdin,pid|
  stdin.puts('42')
  puts(stdout.gets)
end

···

On Mon, Sep 6, 2021 at 6:27 AM Jefferson Hudson <jefferson.hudson@gmail.com> wrote:

Hello,

I want to use the PTY library to communicate with another local program.

I am running the example code and it seems to be deadlocking.

The example code calls this out as possible, and states:

        "we will change the buffering type in the factor command, assuming
that
        factor uses stdio for stdout buffering.

        If IO.pipe is used instead of ::open, this code deadlocks because
        factor's stdout is fully buffered."

I take the above to mean that the example code has been written such that a
deadlock _won't_ occur, but I appear to be wrong. When I run the code it
just
hangs.

        l4@ubuntu:~$ ./pty_test.rb

If I tweak the example code to send invalid input to factor then I get a
response on stderr immediately:

        l4@ubuntu:~$ ./pty_test.rb

        factor: ‘42\\n’ is not a valid positive integer

... at which point it continues to spin.

Are the docs wrong? Am I doing something wrong? What's happening here?

Thanks,
Jefferson

Here's a link to the example code:
Module: PTY (Ruby 3.0.0)

And a more succint reproduction of the same problem (with the invalid input
described above):

#!/usr/bin/env ruby
require 'pty'
IO.pipe do |r,w|
  PTY.open do |m,s|
    pid = spawn("factor", in: r, out: s)
    r.close
    s.close
    w.puts('42\n') # intentionally invalid to demonstrate stderr
    puts(m.gets)
  end
end

I take it back. That's just printing 42 to the stdout of my own pty :slight_smile:

···

On Mon, Sep 6, 2021 at 8:26 AM Jefferson Hudson <jefferson.hudson@gmail.com> wrote:

After poking at it some more, the documentation (code + explanation) must
just be bad. The following works just fine:

#!/usr/bin/env ruby
require 'pty'
PTY.spawn("factor") do |stdout,stdin,pid|
  stdin.puts('42')
  puts(stdout.gets)
end

On Mon, Sep 6, 2021 at 6:27 AM Jefferson Hudson < > jefferson.hudson@gmail.com> wrote:

Hello,

I want to use the PTY library to communicate with another local program.

I am running the example code and it seems to be deadlocking.

The example code calls this out as possible, and states:

        "we will change the buffering type in the factor command,
assuming that
        factor uses stdio for stdout buffering.

        If IO.pipe is used instead of ::open, this code deadlocks because
        factor's stdout is fully buffered."

I take the above to mean that the example code has been written such that
a
deadlock _won't_ occur, but I appear to be wrong. When I run the code it
just
hangs.

        l4@ubuntu:~$ ./pty_test.rb

If I tweak the example code to send invalid input to factor then I get a
response on stderr immediately:

        l4@ubuntu:~$ ./pty_test.rb

        factor: ‘42\\n’ is not a valid positive integer

... at which point it continues to spin.

Are the docs wrong? Am I doing something wrong? What's happening here?

Thanks,
Jefferson

Here's a link to the example code:
Module: PTY (Ruby 3.0.0)

And a more succint reproduction of the same problem (with the invalid
input
described above):

#!/usr/bin/env ruby
require 'pty'
IO.pipe do |r,w|
  PTY.open do |m,s|
    pid = spawn("factor", in: r, out: s)
    r.close
    s.close
    w.puts('42\n') # intentionally invalid to demonstrate stderr
    puts(m.gets)
  end
end

Here's an actual working example.

#!/usr/bin/env ruby
require 'pty'
require 'io/console'
PTY.spawn('factor') do |r,w,pid|
  w.echo = false
  w.puts('42')
  w.iflush
  p r.readline
end

l4@ubuntu:~$ ./pty_test.rb
"42: 2 3 7\r\n"
l4@ubuntu:~$

···

On Mon, Sep 6, 2021 at 8:29 AM Jefferson Hudson <jefferson.hudson@gmail.com> wrote:

I take it back. That's just printing 42 to the stdout of my own pty :slight_smile:

On Mon, Sep 6, 2021 at 8:26 AM Jefferson Hudson < > jefferson.hudson@gmail.com> wrote:

After poking at it some more, the documentation (code + explanation) must
just be bad. The following works just fine:

#!/usr/bin/env ruby
require 'pty'
PTY.spawn("factor") do |stdout,stdin,pid|
  stdin.puts('42')
  puts(stdout.gets)
end

On Mon, Sep 6, 2021 at 6:27 AM Jefferson Hudson < >> jefferson.hudson@gmail.com> wrote:

Hello,

I want to use the PTY library to communicate with another local program.

I am running the example code and it seems to be deadlocking.

The example code calls this out as possible, and states:

        "we will change the buffering type in the factor command,
assuming that
        factor uses stdio for stdout buffering.

        If IO.pipe is used instead of ::open, this code deadlocks because
        factor's stdout is fully buffered."

I take the above to mean that the example code has been written such
that a
deadlock _won't_ occur, but I appear to be wrong. When I run the code it
just
hangs.

        l4@ubuntu:~$ ./pty_test.rb

If I tweak the example code to send invalid input to factor then I get a
response on stderr immediately:

        l4@ubuntu:~$ ./pty_test.rb

        factor: ‘42\\n’ is not a valid positive integer

... at which point it continues to spin.

Are the docs wrong? Am I doing something wrong? What's happening here?

Thanks,
Jefferson

Here's a link to the example code:
Module: PTY (Ruby 3.0.0)

And a more succint reproduction of the same problem (with the invalid
input
described above):

#!/usr/bin/env ruby
require 'pty'
IO.pipe do |r,w|
  PTY.open do |m,s|
    pid = spawn("factor", in: r, out: s)
    r.close
    s.close
    w.puts('42\n') # intentionally invalid to demonstrate stderr
    puts(m.gets)
  end
end