JoshCheek / seeing_is_believing

Displays the results of every line of code in your file
1.31k stars 54 forks source link

Fork/exec not recording in child #54

Closed JoshCheek closed 7 years ago

JoshCheek commented 9 years ago

Issue

When using fork, the child does not update its results. I'm not sure if exec matters, originally I thought it did since it triggers finalization, but more experimenting leads to the conclusion that the child doesn't affect the parent (either one execing is probably totally irrelevant to the other).

Cukes to test this behaviour are here.

Some experiments show the likely cause is that the thread in the producer dies when it forks.

Threads seem to die when forking

# It looks like the reason forked SiB code stops recording is that it kills the thread in the fork

q = Queue.new                                   # => #<Thread::Queue:0x007fe5408fbdf8>
t = Thread.new {                                # => Thread
  while value = q.shift                         # => :parent, :break
    break if value == :break                    # => false, true
    puts "Dequeued #{value.inspect} for (#$$)"  # => nil
  end                                           # => nil
  puts "Thread finished for (#$$)"              # => nil
}                                               # => #<Thread:0x007fe5408fb9e8 run>

if fork                                     # => 67750
  q << :parent                              # => #<Thread::Queue:0x007fe5408fbdf8>
  puts "Parent (#$$) thread: #{t.inspect}"  # => nil
else
  q << :child
  puts "Child (#$$) thread: #{t.inspect}"
end                                         # => nil

sleep 1                        # => 1
q << :break                    # => #<Thread::Queue:0x007fe5408fbdf8>
t.join                         # => #<Thread:0x007fe5408fb9e8 dead>
puts "PROCESS (#$$) FINISHED"  # => nil

# >> Parent (67749) thread: #<Thread:0x007fe5408fb9e8 run>
# >> Dequeued :parent for (67749)
# >> Child (67750) thread: #<Thread:0x007fe5408fb9e8 dead>
# >> PROCESS (67750) FINISHED
# >> Thread finished for (67749)
# >> PROCESS (67749) FINISHED

File Descriptor is still open for child, results would be recorded if they were being reported

event_stream_fd = Marshal.load(ENV["SIB_VARIABLES.MARSHAL.B64"].unpack('m0').first)[:event_stream_fd]  # => 12
stream          = nil                                                                                  # => nil
ObjectSpace.each_object(IO) { |io| stream = io if !io.closed? && io.to_i == event_stream_fd }          # => 28

# This shows that the FD is still open in the child, it is the thread inside the producer that died
'child will set a result here:'  # => "child will set a result here:", Result from child
target_line = __LINE__ - 1       # => 5

if fork                                                     # => 68326
  # parent is noop
else
  line_number = target_line
  type        = :inspect
  inspected   = "Result from child"
  tokenized   = [Marshal.dump(inspected.to_s)].pack('m0')
  stream << "result #{line_number} #{type} #{tokenized}\n"
end                                                         # => nil