Skip to content

Instantly share code, notes, and snippets.

@mrk21
Last active May 2, 2024 07:17
Show Gist options
  • Save mrk21/b2a029d5601d4a1db9770f81f8b4127d to your computer and use it in GitHub Desktop.
Save mrk21/b2a029d5601d4a1db9770f81f8b4127d to your computer and use it in GitHub Desktop.
Simple fork-safe proxy for Ruby
class ForkSafeProxy < BasicObject
class << self
def unwrap(instance)
instance.__send__(:__value__)
end
end
def initialize(&initializer)
@mutex = ::Thread::Mutex.new
@initializer = initializer
@value = nil
@pid = nil
end
def method_missing(method, *args, &block)
__value__.__send__(method, *args, &block)
end
private
def __value__
return @value if @pid == $$
@mutex.synchronize do
@value = @initializer.call
@pid = $$
$stdout.puts "# initialized: #{$$}"
@value
end
end
end
class Runner
def initialize
@rand = ForkSafeProxy.new { Random.new }
@rand = ForkSafeProxy.unwrap(@rand) if ENV['UNWRAP'] == '1'
end
def run
puts_rand
child_pids = 3.times.map { fork { puts_rand } }
child_pids.each(&Process.method(:waitpid))
end
def puts_rand
3.times { puts "#{$$}: #{@rand.rand(100)}" }
end
end
Runner.new.run
=begin
$ ruby fork_safe_proxy.rb
# initialized: 11488
11488: 75
11488: 50
11488: 22
# initialized: 11517
11517: 55
11517: 77
11517: 49
# initialized: 11518
11518: 20
11518: 65
11518: 24
# initialized: 11519
11519: 90
11519: 96
11519: 67
=end
=begin
$ UNWRAP=1 ruby fork_safe_proxy.rb
# initialized: 12750
12750: 62
12750: 22
12750: 51
12779: 51
12779: 92
12779: 70
12780: 51
12780: 92
12780: 70
12781: 51
12781: 92
12781: 70
=end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment