Exececuting another application from within ruby

Hi folks

I have a smell application from which I wish to display a file using an external helper programme.

Lets say
app = the external programme
file = the file to be displayed

I've tried

system(app, file)

This works but it blocks my application. How can I fire up the helper application in the background?

Cheers
Nigel

def background command
     Thread::new(command, Thread::current) do |cmd, cur|
       begin
         pipe = IO::popen cmd
         true while pipe.gets and pipe.close
         $?.exitstatus
       rescue Exception => e
         cur.raise e
       end
     end
   end

   thread = background 'long-running'

   ...

   p thread.value # exitstatus

hth. if you need control over stdout and stderr check out session. it
provides thread safe execution.

   require 'session'

   def background command
     sh = Session::new
     Thread::new(command, Thread::current) do |cmd, cur|
       begin
         stdout, stderr = sh.execute cmd
         [stdout, stderr, sh.status]
       rescue Exception => e
         cur.raise e
       end
     end
   end

   thread = background 'long-running'

   ...

   p thread.value # [stdout, stderr, exitstatus]

session also allows thread safe stuff like

   def background command, widget
     sh = Session::new
     Thread::new(command, Thread::current) do |cmd, cur|
       begin
         sh.execute(cmd) do |stdout, stderr|
           widget.update stdout, stderr
         end
         sh.status
       rescue Exception => e
         cur.raise e
       end
     end
   end

hth.

-a

···

On Tue, 5 Jul 2005, Nigel Wilkinson wrote:

Hi folks

I have a smell application from which I wish to display a file using an external helper programme.

Lets say
app = the external programme
file = the file to be displayed

I've tried

system(app, file)

This works but it blocks my application. How can I fire up the helper application in the background?

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

Nigel Wilkinson wrote:

Hi folks

I have a smell application from which I wish to display a file using an
external helper programme.

Lets say
app = the external programme
file = the file to be displayed

I've tried

system(app, file)

This works but it blocks my application. How can I fire up the helper
application in the background?

Thread.new {system(app, file)}

if all you want to do is start the process and you don't care about the
output or return value. Otherwise, see Ara's solutions.

acutally, even if you do joel's may work

   Thread.new{system(app, file); $?.exitstatus}

and then it's available via thread.value. this is much simpler than my
example. i've just gotten in the habit of using popen or session since i
invariably end up needing to provide stdin or get stderr, etc.

cheers.

-a

···

On Tue, 5 Jul 2005, Joel VanderWerf wrote:

Nigel Wilkinson wrote:

Hi folks

I have a smell application from which I wish to display a file using an
external helper programme.

Lets say
app = the external programme
file = the file to be displayed

I've tried

system(app, file)

This works but it blocks my application. How can I fire up the helper
application in the background?

Thread.new {system(app, file)}

if all you want to do is start the process and you don't care about the
output or return value. Otherwise, see Ara's solutions.

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

It's worth mentioning that all that the system() call does is:

1. fork
2. exec (in child)
3. wait for child to terminate (in parent)

So a lower-level way to get what you want, without using Ruby threads, is:

    pid = Process.fork do
      exec(app, file)
    end

and later on, you may wish to do

    Process.waitpid(pid)

to reap the child and get its exit status. If it dies before then, it will
remain in the process table as a 'zombie' until you reap it or the parent
terminates. The Ruby thread solution effectively cleans up the child
automatically at the end for you.

If you want app to run as a daemon and never worry about when it terminates
(even if the Ruby program terminates first), then you can fork twice, run
the application in the grandchild, and the child terminates immediately:

    pid = Process.fork do
      Process.fork do
        # normally you would redirect STDIN/STDOUT/STDERR here
        exec(app, file)
      end
      exit
    end
    Process.waitpid(pid)
    ... continue here

I like being in full control like this, because system() hides the fact that
it is doing a fork, and fork can result in wierdness. For example, any
'atexit' script you've previously defined will run in both parent and child.
'teardown' scripts in Test::Unit can end up being called in both parent and
child. And so on.

Regards,

Brian.

···

On Tue, Jul 05, 2005 at 08:24:51AM +0900, Joel VanderWerf wrote:

> I have a smell application from which I wish to display a file using an
> external helper programme.
>
> Lets say
> app = the external programme
> file = the file to be displayed
>
> I've tried
>
> system(app, file)
>
> This works but it blocks my application. How can I fire up the helper
> application in the background?

Thread.new {system(app, file)}

if all you want to do is start the process and you don't care about the
output or return value. Otherwise, see Ara's solutions.

Hi,

At Tue, 5 Jul 2005 18:01:28 +0900,
Brian Candler wrote in [ruby-talk:147202]:

If you want app to run as a daemon and never worry about when it terminates
(even if the Ruby program terminates first), then you can fork twice, run

If the parent program terminates before the child, you don't
have to worry if the child becomes a zombie. Instead, It will
be adopted by the init process.

'atexit' script you've previously defined will run in both parent and child.

No, system nor exec don't invoke at_exit handlers.

  $ ruby -e 'at_exit{puts "#$$ exiting"}; system("echo foo")'
  foo
  1020 exiting

  $ ruby -e 'at_exit{puts "#$$ exiting"}; Process.waitpid(fork {exec("echo foo")})'
  foo
  1772 exiting

  $ ruby -e 'at_exit{puts "#$$ exiting"}; Process.waitpid(fork {puts "foo"})'
  foo
  2228 exiting
  2108 exiting

···

--
Nobu Nakada

I have a smell application from which I wish to display a file using an
external helper programme.

Lets say
app = the external programme
file = the file to be displayed

I've tried

system(app, file)

This works but it blocks my application. How can I fire up the helper
application in the background?

Thread.new {system(app, file)}

if all you want to do is start the process and you don't care about the
output or return value. Otherwise, see Ara's solutions.

It's worth mentioning that all that the system() call does is:

1. fork
2. exec (in child)
3. wait for child to terminate (in parent)

So a lower-level way to get what you want, without using Ruby threads, is:

   pid = Process.fork do
     exec(app, file)
   end

and later on, you may wish to do

   Process.waitpid(pid)

to reap the child and get its exit status. If it dies before then, it will
remain in the process table as a 'zombie' until you reap it or the parent
terminates. The Ruby thread solution effectively cleans up the child
automatically at the end for you.

If you want app to run as a daemon and never worry about when it terminates
(even if the Ruby program terminates first), then you can fork twice, run
the application in the grandchild, and the child terminates immediately:

   pid = Process.fork do
     Process.fork do
       # normally you would redirect STDIN/STDOUT/STDERR here
       exec(app, file)
     end
     exit

       # almost positively want 'exit!' here no?
       # otherwise you'll call any at_exit handlers defined in the parent like
       # removing tmpfiles....

   end
   Process.waitpid(pid)
   ... continue here

cheers.

-a

···

On Tue, 5 Jul 2005, Brian Candler wrote:

On Tue, Jul 05, 2005 at 08:24:51AM +0900, Joel VanderWerf wrote:

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

Brian Candler wrote:

It's worth mentioning that all that the system() call does is:

1. fork
2. exec (in child)
3. wait for child to terminate (in parent)

Not on windows. There's no fork(), so ruby uses CreateProcess to
implement #system.

Thanks everyone, I'll have a play when I get a bit of time.

Cheers
Nigel

···

--On Wednesday, July 06, 2005 02:03:07 +0900 Joel VanderWerf <vjoel@path.berkeley.edu> wrote:

Brian Candler wrote:

It's worth mentioning that all that the system() call does is:

1. fork
2. exec (in child)
3. wait for child to terminate (in parent)

Not on windows. There's no fork(), so ruby uses CreateProcess to
implement #system.

It's clear that if the exec succeeds then no at_exit handler can possibly be
called; more interesting is the case if it fails. It looks like the at_exit
handler is not called there - I hadn't tested it before.

$ ruby -e 'at_exit{puts "#$$ exiting"}; system("zxcvzxcv"); p $?'
#<Process::Status: pid=31775,exited(127)>
31774 exiting
$

However:

$ ruby -e 'at_exit{puts "#$$ exiting"}; Process.waitpid(fork {exec("zxcvzxcv")}); p $?'
44096 exiting
-e:1:in `exec': No such file or directory - zxcvzxcv (Errno::ENOENT)
        from -e:1
        from -e:1:in `fork'
        from -e:1
#<Process::Status: pid=44096,exited(1)>
44095 exiting

which is what I was expecting. Anyway, it's a useful mental note to keep :slight_smile:

Cheers,

Brian.

···

On Tue, Jul 05, 2005 at 06:20:38PM +0900, nobuyoshi nakada wrote:

> 'atexit' script you've previously defined will run in both parent and child.

No, system nor exec don't invoke at_exit handlers.

  $ ruby -e 'at_exit{puts "#$$ exiting"}; system("echo foo")'
  foo
  1020 exiting