Can not catch PTY::ChildExited exception

Hi guys!

I have problem that I can not catch PTY::ChildExited exception. Here is
the code:

require ‘pty’

cmd = “/home3/cman/work/product/integrator/test/syb2/OCS-15_0/bin/isql
-Uuser2 -Puser2 -Scman2”

begin

pipe_read, pipe_write, pid = PTY.spawn(cmd)

rescue PTY::ChildExited => msg
puts “Caught child exited exception!”
rescue => msg
puts “Caught exception!”
end

sleep 10

printf “TEST END\n”

When I run it I have the following:

pty_test.rb:7: pty - exited: 1278 (PTY::ChildExited)

I am wondering why exception is not caught? What is more when I remove
sleep operator then there is no exception at all. Script works and
prints TEST END.

My environment:

ruby 1.8.7 (2008-06-20 patchlevel 22) [x86_64-linux]
Linux 2.6.24-23-server #1 SMP Mon Jan 26 01:36:05 UTC 2009 x86_64
GNU/Linux

What I am doing wrong?

Best regards.
Denis

Hi,

In message “Re: Can not catch PTY::ChildExited exception”
on Wed, 9 Sep 2009 15:14:09 +0900, Denis Berezhnoy
[email protected] writes:

|I have problem that I can not catch PTY::ChildExited exception. Here is
|the code:

The exception was raised when the child process terminated
(asynchronously), so you need to wrap not only spawn but everything.

          matz.

Yukihiro M. wrote:

Hi,

In message “Re: Can not catch PTY::ChildExited exception”
on Wed, 9 Sep 2009 15:14:09 +0900, Denis Berezhnoy
[email protected] writes:

|I have problem that I can not catch PTY::ChildExited exception. Here is
|the code:

The exception was raised when the child process terminated
(asynchronously), so you need to wrap not only spawn but everything.

          matz.

Hello Matsumoto-san,

Thank you very much for clarifications! I tried to wrap around whole
code with begin rescue block and I can catch ChildExited exception. So
now I have code like this:

require ‘pty’

cmd = “/home3/cman/work/product/integrator/test/syb2/OCS-15_0/bin/isql
-Uuser2 -Puser2 -Scman2”

begin

pipe_read, pipe_write, pid = PTY.spawn(cmd)

sleep 10

printf “TEST END\n”

rescue PTY::ChildExited => msg
puts “Caught child exited exception!”
rescue => msg
puts “Caught exception!”
rescue => e
puts e.msg
puts e.type
end

But now the problem is that ChildExited exception can occur at any
moment after spawn and interrupt program in random place. In my test
above I never get TEST END printed.

How can I handle this in right way?

Best regards,
Denis

Well, I’m sure I have nowhere as good an idea of PTY.spawn() as
Matsumoto does, but let me try to explain what your code is doing, and
how to get your desired result.

First, you might want to consider using other methods for what you’re
doing. You probably don’t really need a pseudo terminal, which is what
PTY gives you, and you can use more well documented methods. These two
links give a good overview of some of the other ways you can start
subprocesses in ruby:
http://devver.net/blog/2009/06/a-dozen-or-so-ways-to-start-sub-processes-in-ruby-part-1/
http://devver.net/blog/2009/07/a-dozen-or-so-ways-to-start-sub-processes-in-ruby-part-2/

If you still think you really need to use a pseudo terminal, then read
on.

PTY.spawn() doesn’t block your ruby program like a call to system()
would. It simply starts the process given the command, and lets you
continue. In your first email, you mentioned that when you took out the
“sleep 10” line in your code, no exception took place at all. The reason
for this is that your script ended before the child did, since the ten
seconds of sleep were taken out. And so ruby did not have a chance to
raise an exception. Similarly, in your latest code example, “TEST END”
was never printed because the
begin block had an exception before you even reached the ‘printf “TEST
END\n”’ line. The exception occurred while
it was in “sleep 10”. I hope that’s clear, it messed me up too in the
past.

To get what you want, you might want to use the block form for
PTY.spawn() instead. I just find it cleaner, and using the limited scope
for the variables pipe_read etc makes sense. The variables are gone when
the pseudo terminal is gone.

What I’ve done is put a loop inside the PTY.spawn() block, which can be
used to handle the output from the subprocess, or feed it input. If you
don’t need to do that, you probably don’t need to use PTY anyways, but
you can be the judge of that. Now, this loop stops when the subprocess
exits, and then the rest of the ruby script continues.

It seems like you also want to run some code in parallel to the
subprocess, but not in the loop I mentioned. For this case, the code
below also has a thread. Only when the child exits does the thread stop,
and “t.join” finally allows the code to continue. Before “t.join” and
after the thread block though, you can run code in parallel to the
subprocess. And then, “Test End” is printed after the subprocess has
exited. Here’s the code:

require ‘pty’

cmd = “/home3/cman/work/product/integrator/test/syb2/OCS-15_0/bin/isql
-Uuser2 -Puser2 -Scman2”

t = Thread.new do
begin
PTY.spawn(cmd) do |pipe_read, pipe_write, pid|
loop do
# Code here can handle output given by your command, and feed it
input, etc
# But the infinite loop will be broken by the exception caused
when the child process exits, and your script continues
# Or you could just terminate this loop in advance, but
remember, you might reach the end of your ruby script
# before the child exits, in case that’s a problem.
end
end
rescue PTY::ChildExited => msg
puts “The child has exited”
rescue => msg
puts “A different exception:\n” + msg
end
end

puts “Doing stuff in parallel to the subprocess here”
t.join
puts “Test End”

I hope that was useful/clear. It was certainly too long.

  • Ehsan

Hi,

In message “Re: Can not catch PTY::ChildExited exception”
on Fri, 11 Sep 2009 09:56:15 +0900, Denis Berezhnoy
[email protected] writes:

|But now the problem is that ChildExited exception can occur at any
|moment after spawn and interrupt program in random place. In my test
|above I never get TEST END printed.
|
|How can I handle this in right way?

Define the “right way” first.

I admit asynchronous exception is a bad design choice. pty bundled
with 1.9 has changed to use polling via pty.check method.

          matz.

Yukihiro M. wrote:

Hi,

In message “Re: Can not catch PTY::ChildExited exception”
on Fri, 11 Sep 2009 09:56:15 +0900, Denis Berezhnoy
[email protected] writes:

|But now the problem is that ChildExited exception can occur at any
|moment after spawn and interrupt program in random place. In my test
|above I never get TEST END printed.
|
|How can I handle this in right way?

Define the “right way” first.

I admit asynchronous exception is a bad design choice. pty bundled
with 1.9 has changed to use polling via pty.check method.

          matz.

Hi,

Saying about “right way” I meant that may be there is a standard Ruby
mechanism to handle such async exceptions besides begin rescue
operators. I am newbie in Ruby so I am not sure about its capabilities.

But you answered my question. This is a flaw in design of Ruby PTY
component and we can not avoid interrupting code by child exited
exception.

Anyway thanks for clarification. I will look how I can patch PTY in Ruby
to eliminate generating PTY::ChildExited exception at all.

Best regards,
Denis

Denis Berezhnoy wrote:

Ehsanul H. wrote:

Well, I’m sure I have nowhere as good an idea of PTY.spawn() as
Matsumoto does, but let me try to explain what your code is doing, and
how to get your desired result.

First, you might want to consider using other methods for what you’re
doing. You probably don’t really need a pseudo terminal, which is what
PTY gives you, and you can use more well documented methods.

Hi Ehsan,

Thanks for detailed explanation. The reason why I use PTY is because I
need to interact with application that does not flush its output to
pipe. So when I use IO.popen or IO.pipe there is no output.

PTY.spawn() solves this problem so I use it intentionally.

Best regards,
Denis

Hi Ehsan,

One more question. Can I use Process.wait to wait for termination of
the process which was run by PTY.spawn?

Best regards,
Denis

Denis,

That’s a good reason, exactly the same reason I needed to find out so
much about PTY in fact.

As for Process.wait, as long as you have the pid, it should work. I
tried it out in irb now, and it seems to work. But you will get the
child exited exception still. I hadn’t thought of this in my
explanation, it’s definitely a better way to wait for the process to
finish compared to my “t.join”. Here’s what I tried in irb:

def test
a,b,pid = PTY.spawn(“sleep 5”)
Process.wait(pid)
end
test

Ehsanul H. wrote:

Well, I’m sure I have nowhere as good an idea of PTY.spawn() as
Matsumoto does, but let me try to explain what your code is doing, and
how to get your desired result.

First, you might want to consider using other methods for what you’re
doing. You probably don’t really need a pseudo terminal, which is what
PTY gives you, and you can use more well documented methods.

Hi Ehsan,

Thanks for detailed explanation. The reason why I use PTY is because I
need to interact with application that does not flush its output to
pipe. So when I use IO.popen or IO.pipe there is no output.

PTY.spawn() solves this problem so I use it intentionally.

Best regards,
Denis