Comments (3)
Update: This program, when run will hang indefinitely. When interrupted from the shell (both fish and bash do this), it kills the process, its child, and its grandchild. When interrupted with SIGINT, from another process, it kills the parent and the child, but not the grandchild. The documentation assumes a deeper knowledge of Unix than I have. Trying to read explanations of processes / process groups / sessions / etc has not improved clarity. It may be possible to prevent it from hanging, though, which would be something, at least.
require 'timeout'
require 'rbconfig'
# helper methods
def annotate(description, *allowed_exceptions, &block)
success = false
puts "\e[33mPRE #{description}\e[39m"
block.call
success = true
rescue *allowed_exceptions
success = true
ensure
puts "\e[3#{success ? 2 : 1}mPOST #{description}\e[39m"
end
def close_all(*streams)
streams.each { |io| io && !io.closed? && io.close }
end
# setup streams
parentout, childout = IO.pipe
parenterr, childerr = IO.pipe
childin, parentin = IO.pipe
# clean up when exiting the program
at_exit do
annotate("ensure streams closed") { close_all parentout, childout, parenterr, childerr, childin, parentin }
end
# the child program has its own child, both of which will hang waiting on the pipe to be closed
parentin.write <<-RUBY
puts "hello, world"
read, write = IO.pipe
if fork
write.puts "parent"
else
write.puts "child"
end
read.read
RUBY
# evaluate the code in a child process
child_pid = Kernel.spawn(RbConfig.ruby, '-', in: childin, out: childout, err: childerr)
# close streams talking to the child so that it could exit
[parentin, childout, childerr, childin].each(&:close)
# print anything we see the child print
consumer_thread = Thread.new do
parentout.each_line { |line| p stdout: line }
parenterr.each_line { |line| p stderr: line }
end
# wait for child to complete, timing out if it does not
begin
Timeout.timeout 2 do
annotate("wait for the child we started") { Process.wait child_pid }
annotate("wait for all children", Errno::ECHILD) { Process.wait }
annotate("joining consumer thread") { consumer_thread.join }
end
rescue Timeout::Error
annotate("killing child") { Process.kill "KILL", child_pid }
annotate("joining consumer thread") { consumer_thread.join }
end
from seeing_is_believing.
While debugging, I changed the way the child is invoked. IDK if this is relevant, but it took a bit to figure out, so here's the diff (print statements were because I was trying to understand why it didn't resolve this issue)
diff --git a/lib/seeing_is_believing/evaluate_by_moving_files.rb b/lib/seeing_is_believing/evaluate_by_moving_files.rb
index a48e69e..fa07d29 100644
--- a/lib/seeing_is_believing/evaluate_by_moving_files.rb
+++ b/lib/seeing_is_believing/evaluate_by_moving_files.rb
@@ -108,7 +108,7 @@ class SeeingIsBelieving
out: child_stdout,
err: child_stderr,
child_eventstream => child_eventstream }
- child = Process.detach Kernel.spawn(env, *popen_args, opts)
+ child = Kernel.spawn(env, *popen_args, opts)
# close child streams b/c they won't emit EOF
# until both child and parent references are closed
@@ -130,15 +130,26 @@ class SeeingIsBelieving
# wait for completion
Timeout.timeout timeout_seconds do
- exitstatus = child.value.exitstatus
- consumer.process_exitstatus exitstatus
+ puts "pre wait"
+ Process.wait child # for the child we started
+ status = $?.exitstatus
+ puts "status: #{status.inspect}"
+ begin Process.wait # for any children it may have started
+ rescue Errno::ECHILD
+ end
+ puts "post wait"
+ consumer.process_exitstatus status
consumer_thread.join
end
rescue Timeout::Error
- Process.kill "KILL", child.pid
+ puts "rescue timeout"
+ Process.kill "SIGKILL", child
consumer.process_timeout timeout_seconds
+ puts "pre join"
consumer_thread.join # finish consuming events
+ puts "post join"
ensure
+ puts "ensure streams closed"
[stdin, stdout, stderr, eventstream].each { |io| io.close unless io.closed? }
end
from seeing_is_believing.
Ugh, I think the solution I have kills parents, too -.-
Here's a diff of where I'm at:
diff --git a/lib/seeing_is_believing/evaluate_by_moving_files.rb b/lib/seeing_is_believing/evaluate_by_moving_files.rb
index 9f8a5eb..6d12abe 100644
--- a/lib/seeing_is_believing/evaluate_by_moving_files.rb
+++ b/lib/seeing_is_believing/evaluate_by_moving_files.rb
@@ -104,11 +104,14 @@ class SeeingIsBelieving
)].pack('m0')
# evaluate the code in a child process
- opts = { in: child_stdin,
+ opts = {
+ child_eventstream => child_eventstream,
+ in: child_stdin,
out: child_stdout,
err: child_stderr,
- child_eventstream => child_eventstream }
- child = Process.detach Kernel.spawn(env, *popen_args, opts)
+ pgroup: true, # run in its own process group so we can kill any children it spawned
+ }
+ child_pid = Kernel.spawn(env, *popen_args, opts)
# close child streams b/c they won't emit EOF
# until both child and parent references are closed
@@ -130,17 +133,22 @@ class SeeingIsBelieving
# wait for completion
Timeout.timeout timeout_seconds do
- exitstatus = child.value.exitstatus
- consumer.process_exitstatus exitstatus
+ Process.wait child_pid
+ consumer.process_exitstatus $?.exitstatus
+ # begin Process.wait # for any grandchildren
+ # rescue Errno::ECHILD
+ # end
consumer_thread.join
end
rescue Timeout::Error
- pgid = Process.getpgid(child.pid)
- handler = trap("INT") { trap("INT", handler) } # noop
- Process.kill "-INT", pgid
consumer.process_timeout timeout_seconds
- consumer_thread.join # finish consuming events
ensure
+ begin
+ child_pgid = Process.getpgid(child_pid)
+ Process.kill "-INT", child_pgid
+ rescue Errno::ESRCH
+ end
+ consumer_thread.join
[stdin, stdout, stderr, eventstream].each { |io| io.close unless io.closed? }
end
from seeing_is_believing.
Related Issues (20)
- Multiple inspects HOT 1
- Printing tabs replaces the output with "\t"
- Output is horrible when run against a moved file
- Thoughts after reading the help screen
- Uuhhhmm, add an in-place update flag
- Remove custom String#pp
- Should handle missing constants
- cross-invocation state HOT 1
- Outdated parser runtime dependency generates deprecation warning in Ruby 2.7 HOT 6
- VSCode Description isn't complete HOT 1
- Feature request: line which updates obviously to confirm file was reevaluated
- Endless method support
- Let errors in inspect raise
- Getting WARN -- : posix_spawn is not yet supported on x86_64-cygwin (x86_64-cygwin), falling back to default implementation. HOT 4
- Relaxing the FFI dependency version constraint HOT 9
- Ruby3 endless method bug
- Something funky with frozen string literals
- segfault *sigh*
- in `find_type': unable to resolve type 'intptr_t' (TypeError) HOT 3
- New release to support Apple Silicon (M1) Macs? HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from seeing_is_believing.