Skip to content

Commit

Permalink
Introduce ForkTracker.before_fork
Browse files Browse the repository at this point in the history
Also add ForkTracker.unregister_before_fork and rename
ForkTracker.unregister to unregister_after_fork for consistency.
  • Loading branch information
flavorjones committed Sep 14, 2024
1 parent b3234a7 commit afd7578
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def initialize(files, dirs)
def finalizer
proc do
stop
ActiveSupport::ForkTracker.unregister(@after_fork)
ActiveSupport::ForkTracker.unregister_after_fork(@after_fork)
end
end

Expand Down
25 changes: 20 additions & 5 deletions activesupport/lib/active_support/fork_tracker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module ActiveSupport
module ForkTracker # :nodoc:
module CoreExt
def _fork
ForkTracker.before_fork_callback
pid = super
if pid == 0
ForkTracker.after_fork_callback
Expand All @@ -13,13 +14,18 @@ def _fork
end

@pid = Process.pid
@callbacks = []
@before_callbacks = []
@after_callbacks = []

class << self
def before_fork_callback
@before_callbacks.each(&:call)
end

def after_fork_callback
new_pid = Process.pid
if @pid != new_pid
@callbacks.each(&:call)
@after_callbacks.each(&:call)
@pid = new_pid
end
end
Expand All @@ -28,13 +34,22 @@ def hook!
::Process.singleton_class.prepend(CoreExt)
end

def before_fork(&block)
@before_callbacks << block
block
end

def after_fork(&block)
@callbacks << block
@after_callbacks << block
block
end

def unregister(callback)
@callbacks.delete(callback)
def unregister_before_fork(callback)
@before_callbacks.delete(callback)
end

def unregister_after_fork(callback)
@after_callbacks.delete(callback)
end
end
end
Expand Down
41 changes: 28 additions & 13 deletions activesupport/test/fork_tracker_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,22 @@ def setup
super

@read, @write = IO.pipe
@called = false
@before_called = false
@after_called = false

@handler = ActiveSupport::ForkTracker.after_fork do
@called = true
@before_handler = ActiveSupport::ForkTracker.before_fork do
@before_called = true
end

@after_handler = ActiveSupport::ForkTracker.after_fork do
@after_called = true
@write.write "forked"
end
end

def teardown
ActiveSupport::ForkTracker.unregister(@handler)
ActiveSupport::ForkTracker.unregister_before_fork(@before_handler)
ActiveSupport::ForkTracker.unregister_after_fork(@after_handler)

super
end
Expand All @@ -35,7 +41,8 @@ def test_object_fork
assert_equal "forked", @read.read
@read.close

assert_not @called
assert @before_called
assert_not @after_called
end

def test_object_fork_without_block
Expand All @@ -44,7 +51,8 @@ def test_object_fork_without_block
Process.waitpid(pid)
assert_equal "forked", @read.read
@read.close
assert_not @called
assert @before_called
assert_not @after_called
else
@read.close
@write.close
Expand All @@ -64,7 +72,8 @@ def test_process_fork
Process.waitpid(pid)
assert_equal "forked", @read.read
@read.close
assert_not @called
assert @before_called
assert_not @after_called
end

def test_process_fork_without_block
Expand All @@ -73,7 +82,8 @@ def test_process_fork_without_block
Process.waitpid(pid)
assert_equal "forked", @read.read
@read.close
assert_not @called
assert @before_called
assert_not @after_called
else
@read.close
@write.close
Expand All @@ -93,7 +103,8 @@ def test_kernel_fork
Process.waitpid(pid)
assert_equal "forked", @read.read
@read.close
assert_not @called
assert @before_called
assert_not @after_called
end

def test_kernel_fork_without_block
Expand All @@ -102,7 +113,8 @@ def test_kernel_fork_without_block
Process.waitpid(pid)
assert_equal "forked", @read.read
@read.close
assert_not @called
assert @before_called
assert_not @after_called
else
@read.close
@write.close
Expand Down Expand Up @@ -132,18 +144,21 @@ def fark(&block)
assert_equal "forked", @read.read
@read.close

assert_not @called
assert @before_called
assert_not @after_called
end

def test_unregister_callback
ActiveSupport::ForkTracker.unregister(@handler)
ActiveSupport::ForkTracker.unregister_before_fork(@before_handler)
ActiveSupport::ForkTracker.unregister_after_fork(@after_handler)

if pid = Process.fork
@write.close
Process.waitpid(pid)
assert_equal "", @read.read
@read.close
assert_not @called
assert_not @before_called
assert_not @after_called
else
@read.close
@write.close
Expand Down

0 comments on commit afd7578

Please sign in to comment.