Forum: Ruby Problem with method that starts process, yields pid then yie

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
E2b3bbf241a5daee3ee7696a7d4b2015?d=identicon&s=25 Vrtwo Lastname (d0t1q)
on 2005-12-02 14:46
(Received via mailing list)
I'm trying to create a method that will kick off a new process, return
it's pid.. while allowing the return code to also be captured for
later examination..
[on windows platform]

class Exec
   def process_handler
       # if I do the following below, it will yield the
       # pid which is great, but because I did not
       # write pipe it to {}, I don't think it's possible to capture
the return_level
       return IO.popen("ruby -e 'sleep 2' &exit 33").pid
   end
end

puts Exec.new.process_handler.to_s # =>  3389
puts $? >> 8 # => gives me an error because I didnt start IO.popen with
{}

As a work around, I tried the following:
class Exec
   def process_handler
       # if I do the following below, it will yield the
       # pid which is great, but because I did not
       # write pipe it to {}, I don't think it's possible to capture
the return_level
       Thread.new {
           p = IO.popen("ruby -e 'sleep 2' &exit 33") {}
           $p_return_code = $? >> 8
       }
   return p, $p_return_code
   end
end

The problem here is, p will not be defined until the process has
finished writing to the pipe {}, which will prevent the return of the
pid..

Any suggestions on this?  I'm not able to test this code as I'm using
a machine @ an internet cafe.

Kind regards,
Chris
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 ara.t.howard (Guest)
on 2005-12-02 22:06
(Received via mailing list)
On Fri, 2 Dec 2005, x1 wrote:

> I'm trying to create a method that will kick off a new process, return it's
> pid.. while allowing the return code to also be captured for later
> examination..  [on windows platform]

should work, but untested on windows:

   harp:~ > cat a.rb

   require "thread"
   class Exec
     %w( cmd queue pipe pid thread exitstatus ).each{|a| attr_accessor
a}
     def initialize cmd
       self.cmd = cmd
       self.queue = ::Queue::new
       self.thread =
         ::Thread::new(queue) do |q|
           pipe = ::IO::popen cmd
           q.push pipe
           q.push pipe.pid
           begin
             loop{ stdout = pipe.gets or break }
             pipe.close
             q.push $?.exitstatus
           rescue => e
             q.push e
           end
         end
       self.pipe = queue.pop
       self.pid = queue.pop
       self.exitstatus = nil
     end
     def wait
       self.exitstatus = self.queue.pop
     end
     def self::[](*a, &b) new *a, &b end
   end

   require "yaml"

   y "start" => Time::now
   sleep = Exec[ " ruby -e' sleep 2 and exit 42 ' " ]
   y "cmd" => sleep.cmd
   y "pid" => sleep.pid
   y "exitstatus" => sleep.wait
   y "finish" => Time::now

   harp:~ > ruby a.rb
   ---
   start: 2005-12-02 14:03:29.920649 -07:00
   ---
   cmd: " ruby -e' sleep 2 and exit 42 ' "
   ---
   pid: 19947
   ---
   exitstatus: 42
   ---
   finish: 2005-12-02 14:03:31.930995 -07:00


kind regards.

-a
E2b3bbf241a5daee3ee7696a7d4b2015?d=identicon&s=25 Vrtwo Lastname (d0t1q)
on 2005-12-03 04:44
(Received via mailing list)
it seems to work!!

Let me spend some time trying to make sense of it... Thanks
soooooooooooooooo much!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*grins*
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 ara.t.howard (Guest)
on 2005-12-03 05:33
(Received via mailing list)
On Sat, 3 Dec 2005, x1 wrote:

> it seems to work!!
>
> Let me spend some time trying to make sense of it... Thanks
> soooooooooooooooo much!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
> *grins*

great.

i learned a bit too : didn't know about pipe.pid

cheers.

-a
E2b3bbf241a5daee3ee7696a7d4b2015?d=identicon&s=25 Vrtwo Lastname (d0t1q)
on 2005-12-04 00:04
(Received via mailing list)
I've hacked at it and managed to learn quite a few things in the
process but I cant seem to assign the Exec class to a DRb server to
handle requests.

Ideally, the <client> would be able to do something like:

DRb.start_service
sleep =  DRbObject.new(nil, 'druby://server:2001').execute("ruby -e
'sleep 3 and exit 43'")
p sleep.pid
p sleep.wait

I tried everything I could think of but the script kept breaking.
Here's what I've got that DOES work:

require 'drb'
require 'thread'

class Execute
	%w( cmd queue pipe pid thread exitstatus alive ).each{|a| attr_accessor
a}
	def initialize(cmd)
		@cmd = cmd
		@queue = Queue.new
		@thread = Thread.new(queue) do |q|
			pipe = IO.popen(cmd)
			q.push pipe
			q.push pipe.pid
			begin
				loop{ stdout = pipe.gets or break }
				pipe.close
				q.push $?.exitstatus
			rescue => e
				q.push e
			end
		end
		@pipe = queue.pop
		@pid = queue.pop
		@exitstatus = nil
	end
	def wait
		@exitstatus = @queue.pop
	end
end

hostname = `hostname`.chomp
port = "4501"

#DRb.start_service "druby://#{hostname}:#{port}", Execute.new
#DRb.thread.join

job = Execute.new("cmd /c ruby -e 'sleep 3 and exit 42'")
puts job.cmd
puts job.pid
puts job.wait

If I uncomment the DRb lines, the server starts but clients throw an
error :(
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 ara.t.howard (Guest)
on 2005-12-04 01:01
(Received via mailing list)
On Sun, 4 Dec 2005, x1 wrote:

> I've hacked at it and managed to learn quite a few things in the
> process but I cant seem to assign the Exec class to a DRb server to
> handle requests.
>
> Ideally, the <client> would be able to do something like:

<snip>

     harp:~ > cat servant.rb
     #! /usr/bin/env ruby
     %w( thread drb socket time yaml ).each{|lib| require lib}

     class Execute
       %w( cmd queue pipe pid thread exitstatus alive ).each{|a|
attr_accessor a}
       def initialize(cmd)
         @cmd = cmd
         @queue = Queue.new
         @thread = Thread.new(queue) do |q|
           pipe = IO.popen(cmd)
           q.push pipe
           q.push pipe.pid
           begin
             loop{ stdout = pipe.gets or break }
             pipe.close
             q.push $?.exitstatus
           rescue => e
             q.push e
           end
         end
         @pipe = queue.pop
         @pid = queue.pop
         @exitstatus = nil
       end
       def wait
         @exitstatus = @queue.pop
       end
     end

     class Executioner
       def execute cmd
         Execute::new cmd
       end
     end

     mode = ARGV.shift
     hostname = Socket.gethostname
     port = 4501

     case mode
       when %r/server/i
         DRb.start_service "druby://#{ hostname }:#{ port }",
Executioner.new
         DRb.thread.join

       when %r/client/i
         DRb.start_service
         executioner = DRbObject.new nil, "druby://#{ hostname }:#{ port
}"
         sleep = executioner.execute "ruby -e' sleep 3 and exit 43 '"
         y "start" => Time::now.iso8601
         y "pid" => sleep.pid
         y "exitstatus" => sleep.wait
         y "finish" => Time::now.iso8601
     end



     harp:~ > ./servant.rb server &
     [1] 15727



     harp:~ > ./servant.rb client
     ---
     start: "2005-12-03T16:58:19-07:00"
     ---
     pid: 15729
     ---
     exitstatus: 43
     ---
     finish: "2005-12-03T16:58:22-07:00"


hth.


-a
E2b3bbf241a5daee3ee7696a7d4b2015?d=identicon&s=25 Vrtwo Lastname (d0t1q)
on 2005-12-05 22:44
(Received via mailing list)
What we have so far works, however ideally, printing the pid should
not rely on the completion of the job. The only thing that should rely
on the completion of the job would be outputting the exitstatus.
Here's my vision..



class JobServer

            def execute(cmd)

                        # kicks off the job and returns the pid

                        return pid

            end

            def status(pid)

                        # returns the status of the job. IE is the pid
alive? or dead?

                        return pid.alive?

            end

            def exitstatus(pid)

                        # returns the exitstatus based on the PID

                        return pid.exitstatus

            end

end



# Scenerio: I need to kick off two jobs and a third upon completion of
the first (when exit statis is 0).

# Ideally, one would be able to kick off a job with execute() and know
about it's pid. Then later on, be able to reference the status of that
pid, such as alive? or dead?.  If dead, one could call the
exitstatus() method to find out what the return code of that job was.



case mode

            when %r/server/i

                        DRb.start_service "druby://#{ hostname }:#{ port
}",

                        JobServer.new

                        DRb.thread.join

            when %r/client/i

                        DRb.start_service

                        job = DRbObject.new(nil, "druby://#{ hostname
}:#{ port }")

                        pid = job.execute("ruby -e' sleep 3 and exit 43
'")

                        # without having to wait on the execution
above to finish:

                        if job.status(pid) == true

                                    puts "exit status = " +
job.existstatus(pid)

                        else

                                    puts "job still running"

                        end

end





#Again, this code isnt functional, just a concept of how I would see.
What's preventing me from turning waht we have into this is my lack of
knowledge in the following w/ your code:

# 1) not exactly sure attr_accessor does

# 2) not sure what Queue.new does

# 3) not sure what def initialize does



# Again, I really² appreciate your help in understanding and assistance.
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 ara.t.howard (Guest)
on 2005-12-06 00:38
(Received via mailing list)
On Tue, 6 Dec 2005, x1 wrote:

> What we have so far works, however ideally, printing the pid should not rely
> on the completion of the job. The only thing that should rely on the
> completion of the job would be outputting the exitstatus.

but that is the exactly the case? example:

   harp:~ > ruby servant.rb server &
   [2] 31570


   harp:~ > ruby servant.rb client
   ---
   start: "2005-12-05T16:29:21-07:00"
   ---
   pid: 31572
   ---
   after getting pid...: "2005-12-05T16:29:21-07:00"
   ---
   exitstatus: 43
   ---
   finish: "2005-12-05T16:29:24-07:00"


note the times.  __only__ getting the exitstatus relies on job
completion (by
definition) and is a blocking operation.

before i read more of your post make sure you understand the above.  the
code
again:

   harp:~ > cat servant.rb
   #! /usr/bin/env ruby
   %w( thread drb socket time yaml ).each{|lib| require lib}
   class Execute
     %w( cmd queue pipe pid thread exitstatus alive ).each{|a|
attr_accessor a}
     def initialize(cmd)
       @cmd = cmd
       @queue = Queue.new
       @thread = Thread.new(queue) do |q|
         pipe = IO.popen(cmd)
         q.push pipe
         q.push pipe.pid
         begin
           loop{ stdout = pipe.gets or break }
           pipe.close
           q.push $?.exitstatus
         rescue => e
           q.push e
         end
       end
       @pipe = queue.pop
       @pid = queue.pop
       @exitstatus = nil
     end
     def wait
       @exitstatus = @queue.pop
     end
   end
   class Executioner
     def execute cmd
       Execute::new cmd
     end
   end

   mode, hostname, port = ARGV.shift, Socket.gethostname, 4501

   case mode
     when %r/server/i
       DRb.start_service "druby://#{ hostname }:#{ port }",
Executioner.new
       DRb.thread.join

     when %r/client/i
       DRb.start_service
       executioner = DRbObject.new nil, "druby://#{ hostname }:#{ port
}"
       sleep = executioner.execute "ruby -e' sleep 3 and exit 43 '"
       y "start" => Time::now.iso8601
       y "pid" => sleep.pid
       y "after getting pid..." => Time::now.iso8601
       y "exitstatus" => sleep.wait
       y "finish" => Time::now.iso8601
   end


regarding your questions about 'initialize' and 'new' - ruby is object
orientied.  everything is an object constructed from a class.  the
method new,
when called on a class returns an object which was created an
initialized
using it's initialize method.  check out the 'pickaxe' and the 'ruby
way' for
deep insight into ruby - you can't code without them.

kind regards.

-a
--
===============================================================================
| ara [dot] t [dot] howard [at] noaa [dot] gov
| all happiness comes from the desire for others to be happy.  all misery
| comes from the desire for oneself to be happy.
| -- bodhicaryavatara
===============================================================================


cat a.rb
E2b3bbf241a5daee3ee7696a7d4b2015?d=identicon&s=25 Vrtwo Lastname (d0t1q)
on 2005-12-06 01:35
(Received via mailing list)
weird thing is, with your first example, the pid prints out before the
command finishes... where with the second example, nothing is printed
out until the command exits.
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 ara.t.howard (Guest)
on 2005-12-06 02:24
(Received via mailing list)
On Tue, 6 Dec 2005, x1 wrote:

> weird thing is, with your first example, the pid prints out before the
> command finishes... where with the second example, nothing is printed
> out until the command exits.

trying putting

   STDOUT.sync = true

at the top of the script.

>>    harp:~ > ruby servant.rb server &
>>    ---
>>
>>          q.push pipe
>>        @pid = queue.pop
>>    end
>>        executioner = DRbObject.new nil, "druby://#{ hostname }:#{ port }"
>> orientied.  everything is an object constructed from a class.  the method new,
>> | all happiness comes from the desire for others to be happy.  all misery
>> | comes from the desire for oneself to be happy.
>> | -- bodhicaryavatara
>> ===============================================================================
>>
>>
>> cat a.rb
>>
>>
>
>

-a
E2b3bbf241a5daee3ee7696a7d4b2015?d=identicon&s=25 Vrtwo Lastname (d0t1q)
on 2005-12-06 03:08
(Received via mailing list)
So... I just restarted and booted into SUSE..

The example works perfect under linux... --weird.
To explain what I'm seeing, I added the output of Time.now directly
after printing the pid & exit status..

linux>ruby process.rb client
---
start: "2005-12-05T20:20:32-05:00"
---
pid: 7712
---
x: "2005-12-05T20:20:32-05:00"
---
exitstatus: 43
---
x: "2005-12-05T20:20:35-05:00"
---
finish: "2005-12-05T20:20:35-05:00"

As we see, it instantly gave me the pid and took two seconds to tell
me the exitstatus. (which is what I want) but with windows, it takes 2
seconds to get anything back. I'll reboot and run the same script in
windows..
E2b3bbf241a5daee3ee7696a7d4b2015?d=identicon&s=25 Vrtwo Lastname (d0t1q)
on 2005-12-06 03:12
(Received via mailing list)
And here's the results from windows:

Desktop>process.rb client
starting @ Mon Dec 05 20:27:42 Eastern Standard Time 2005
940
Mon Dec 05 20:27:42 Eastern Standard Time 2005
43
Mon Dec 05 20:27:42 Eastern Standard Time 2005
Finished @ Mon Dec 05 20:27:42 Eastern Standard Time 2005

What's actually happening, is when this is executed:
sleep = executioner.execute "ruby -e' sleep 3 and exit 43 '"

Windows waits until the job finishes, and then continues to the next
line...(no fork I guess) Just weird how it worked in the original
example...
This topic is locked and can not be replied to.