Class: Exeggutor::ProcessHandle
- Inherits:
-
Object
- Object
- Exeggutor::ProcessHandle
- Defined in:
- lib/exeggutor.rb
Overview
A handle to a process, with IO handles to communicate with it and a ProcessResult object when it’s done. It’s largely similar to the array of 4 values return by Open3.popen3. However, it doesn’t suffer from that library’s dead-locking issue. For example, even if lots of data has been written to stdout that hasn’t been read, the subprocess can still write to stdout and stderr without blocking
Instance Method Summary collapse
-
#initialize(args, env: nil, chdir: nil) ⇒ ProcessHandle
constructor
A new instance of ProcessHandle.
-
#stderr ⇒ Object
An IO object for stderr that can be written to.
-
#stdin ⇒ Object
An IO object for stdin that can be written to.
-
#stdout ⇒ Object
An IO object for stdout that can be written to.
-
#wait_thr ⇒ Object
An object containing process metadata and which can be waited on to wait until the subprocess ends.
Constructor Details
#initialize(args, env: nil, chdir: nil) ⇒ ProcessHandle
Returns a new instance of ProcessHandle.
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/exeggutor.rb', line 13 def initialize(args, env: nil, chdir: nil) @stdin_io, @stdout_io, @stderr_io, @wait_thread = Exeggutor::run_popen3(args, env, chdir) # Make the streams as synchronous as possible, to minimize the possibility of a surprising lack # of output @stdout_io.sync = true @stderr_io.sync = true @stdout_queue = Queue.new @stderr_queue = Queue.new @stdout_pipe_reader, @stdout_pipe_writer = IO.pipe @stderr_pipe_reader, @stderr_pipe_writer = IO.pipe @stdout_write_thread = Thread.new do loop do data = @stdout_queue.pop break if !data # Queue is closed @stdout_pipe_writer.write(data) end @stdout_pipe_writer.close end @stderr_write_thread = Thread.new do loop do data = @stderr_queue.pop break if !data # Queue is closed @stderr_pipe_writer.write(data) end @stderr_pipe_writer.close end # popen3 can deadlock if one stream is written to too much without being read, # so it's important to continuously read from both streams. This is why # we can't just let the user call .gets on the streams themselves @read_thread = Thread.new do remaining_ios = [@stdout_io, @stderr_io] while remaining_ios.size > 0 readable_ios, = IO.select(remaining_ios) for readable_io in readable_ios begin data = readable_io.read_nonblock(100_000) if readable_io == @stdout_io @stdout_queue.push(data) else @stderr_queue.push(data) end rescue IO::WaitReadable # Shouldn't usually happen because IO.select indicated data is ready, but maybe due to EINTR or something next rescue EOFError if readable_io == @stdout_io @stdout_queue.close else @stderr_queue.close end remaining_ios.delete(readable_io) end end end end end |
Instance Method Details
#stderr ⇒ Object
An IO object for stderr that can be written to
93 94 95 |
# File 'lib/exeggutor.rb', line 93 def stderr @stderr_pipe_reader end |
#stdin ⇒ Object
An IO object for stdin that can be written to
83 84 85 |
# File 'lib/exeggutor.rb', line 83 def stdin @stdin_io end |
#stdout ⇒ Object
An IO object for stdout that can be written to
88 89 90 |
# File 'lib/exeggutor.rb', line 88 def stdout @stdout_pipe_reader end |
#wait_thr ⇒ Object
An object containing process metadata and which can be waited on to wait until the subprocess ends. Identical to popen3’s wait_thr
78 79 80 |
# File 'lib/exeggutor.rb', line 78 def wait_thr @wait_thread end |