Skip to content

Commit

Permalink
Reset Async queue on fork
Browse files Browse the repository at this point in the history
  • Loading branch information
wjordan committed Jul 1, 2020
1 parent 7dc6eb0 commit 3035ec5
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 0 deletions.
9 changes: 9 additions & 0 deletions lib/concurrent-ruby/concurrent/async.rb
Expand Up @@ -309,6 +309,7 @@ def initialize(delegate)
@delegate = delegate
@queue = []
@executor = Concurrent.global_io_executor
@ruby_pid = $$
end

# Delegates method calls to the wrapped object.
Expand All @@ -326,6 +327,7 @@ def method_missing(method, *args, &block)

ivar = Concurrent::IVar.new
synchronize do
reset_if_forked
@queue.push [ivar, method, args, block]
@executor.post { perform } if @queue.length == 1
end
Expand Down Expand Up @@ -361,6 +363,13 @@ def perform
end
end
end

def reset_if_forked
if $$ != @ruby_pid
@queue.clear
@ruby_pid = $$
end
end
end
private_constant :AsyncDelegator

Expand Down
13 changes: 13 additions & 0 deletions spec/concurrent/async_spec.rb
Expand Up @@ -296,5 +296,18 @@ def gather(seconds, first, *rest)
expect(object.bucket).to eq [:a, :b, :c, :d]
end
end

context 'fork safety' do
it 'does not hang when forked' do
skip "Platform does not support fork" unless Process.respond_to?(:fork)
object = Class.new {
include Concurrent::Async
def foo; end
}.new
object.async.foo
_, status = Process.waitpid2(fork {object.await.foo})
expect(status.exitstatus).to eq 0
end
end
end
end

0 comments on commit 3035ec5

Please sign in to comment.