ruby_is_forked 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "rspec", "~> 2.3.0"
10
+ gem "bundler"
11
+ gem "jeweler"
12
+ gem "rcov", ">= 0"
13
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Kurt Stephens
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,62 @@
1
+ = ruby_is_forked
2
+
3
+ Forked Ruby process inherit some resources from parent processes, on most platforms.
4
+
5
+ For example a File opened in a parent process is visible and shared in forked children:
6
+
7
+ file = File.open("/dev/null")
8
+ puts "Parent: #{$$}: #{file.object_id}"
9
+ fork { puts "Child: #{$$}: #{file.object_id}" }
10
+ puts "Parent after fork: #{$$}: #{file.object_id}"
11
+ Thread.new { puts "Thread: #{file.object_id}" }
12
+
13
+ Sample Output:
14
+
15
+ Parent: 27282: 153210860
16
+ Child: 29777: 153210860
17
+ Parent after fork: 27282: 153210860
18
+ Thread: 153210860
19
+
20
+ Using ruby_is_forked:
21
+
22
+ require 'ruby_is_forked/process_callbacks'
23
+ file = File.open("/dev/null")
24
+ Process.add_callback_in_child! lambda { | pid | file = File.open("/dev/null") }
25
+ puts "Parent #{$$}: #{file.object_id}"
26
+ fork { puts "Child: #{$$}: #{file.object_id}" }
27
+ puts "Parent after fork: #{$$}: #{file.object_id}"
28
+ Thread.new { puts "Thread: #{file.object_id}" }
29
+
30
+ Sample Output:
31
+
32
+ Parent 31583: 90260550
33
+ Child: 31627: 152197900
34
+ Parent after fork: 31583: 90260550
35
+ Thread: 90260550
36
+
37
+ == Functionality
38
+
39
+ * RubyIsForked::ForkCallback
40
+ ** Callbacks in children (and parents) of Process.fork (and Kernel#fork).
41
+ * RubyIsForked::Process
42
+ ** Provides Process.current and Process.local variable abstraction.
43
+
44
+ == See Also
45
+
46
+ * http://github.com/kstephens/rails_is_forked
47
+
48
+ == Contributing to ruby_is_forked
49
+
50
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
51
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
52
+ * Fork the project
53
+ * Start a feature/bugfix branch
54
+ * Commit and push until you are happy with your contribution
55
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
56
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
57
+
58
+ == Copyright
59
+
60
+ Copyright (c) 2011 Kurt Stephens. See LICENSE.txt for
61
+ further details.
62
+
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "ruby_is_forked"
16
+ gem.homepage = "http://github.com/kstephens/rails_is_forked"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{Deal with resources (DB connections, etc) in forked Ruby processes.}
19
+ gem.description = %Q{See http://github.com/kstephens/ruby_is_forked}
20
+ gem.email = "ks.github@kurtstephens.com"
21
+ gem.authors = ["Kurt Stephens"]
22
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
23
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
25
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rspec/core'
30
+ require 'rspec/core/rake_task'
31
+ RSpec::Core::RakeTask.new(:spec) do |spec|
32
+ spec.rspec_opts = [ '-f', 'd' ]
33
+ spec.pattern = FileList['spec/**/*_spec.rb']
34
+ end
35
+
36
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
37
+ spec.pattern = 'spec/**/*_spec.rb'
38
+ spec.rcov = true
39
+ end
40
+
41
+ task :default => :spec
42
+
43
+ require 'rake/rdoctask'
44
+ Rake::RDocTask.new do |rdoc|
45
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "ruby_is_forked #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ end
@@ -0,0 +1,4 @@
1
+ module RubyIsForked
2
+ end
3
+ require 'ruby_is_forked/fork_callback'
4
+
@@ -0,0 +1,83 @@
1
+ require 'ruby_is_forked'
2
+
3
+ module RubyIsForked
4
+ module ForkCallback
5
+ def self.included target
6
+ super
7
+ target.class_eval do
8
+ include ModuleMethods
9
+ alias :fork_without_callback :fork unless method_defined? :fork_without_callback
10
+ alias :fork :fork_with_callback
11
+ end
12
+ end
13
+
14
+ def self.extended target
15
+ super
16
+ target.extend(ModuleMethods)
17
+ target.instance_eval do
18
+ alias :fork_without_callback :fork unless method_defined? :fork_without_callback
19
+ alias :fork :fork_with_callback
20
+ end
21
+ end
22
+
23
+ CALLBACK_IN_CHILD = [ ] unless defined? CALLBACK_IN_CHILD
24
+ CALLBACK_BEFORE_CHILD = [ ] unless defined? CALLBACK_BEFORE_CHILD
25
+ CALLBACK_IN_PARENT = [ ] unless defined? CALLBACK_IN_PARENT
26
+
27
+ def self.add_callback_in_child! proc = nil, &block
28
+ proc ||= block
29
+ CALLBACK_IN_CHILD << proc
30
+ proc
31
+ end
32
+ def self.remove_callback_in_child! proc
33
+ CALLBACK_IN_CHILD.delete(proc)
34
+ end
35
+
36
+ def self.add_callback_before_child! proc = nil, &block
37
+ proc ||= block
38
+ CALLBACK_BEFORE_CHILD << proc
39
+ proc
40
+ end
41
+ def self.remove_callback_before_child! proc
42
+ CALLBACK_BEFORE_CHILD.delete(proc)
43
+ end
44
+
45
+ def self.add_callback_in_parent! proc = nil, &block
46
+ proc ||= block
47
+ CALLBACK_IN_PARENT << proc
48
+ proc
49
+ end
50
+ def self.remove_callback_in_parent! proc
51
+ CALLBACK_IN_PARENT.delete(proc)
52
+ end
53
+
54
+ module ModuleMethods
55
+ # TODO: Might need to do this in a critical section.
56
+ def fork_with_callback *args
57
+ CALLBACK_BEFORE_CHILD.each { | proc | proc.call($$) }
58
+ if block_given?
59
+ result = fork_without_callback do
60
+ CALLBACK_IN_CHILD.each { | proc | proc.call($$) }
61
+ yield
62
+ end
63
+ CALLBACK_IN_PARENT.each { | proc | proc.call(result) }
64
+ result
65
+ else
66
+ result = fork_without_callback
67
+ if result
68
+ # in parent
69
+ CALLBACK_IN_PARENT.each { | proc | proc.call(result) }
70
+ else
71
+ # in child
72
+ CALLBACK_IN_CHILD.each { | proc | proc.call($$) }
73
+ end
74
+ result
75
+ end
76
+ end
77
+ end
78
+
79
+ ::Kernel.send(:include, self)
80
+ ::Process.send(:extend, self)
81
+ end
82
+
83
+ end
@@ -0,0 +1,56 @@
1
+ require 'ruby_is_forked'
2
+ require 'ruby_is_forked/fork_callback'
3
+ require 'ostruct'
4
+ require 'thread' # Mutex
5
+
6
+ module RubyIsForked
7
+ # Adds Process.current[] variables that are cleared in child processes.
8
+ module Process
9
+ def self.included target
10
+ super
11
+ target.extend(ModuleMethods)
12
+ ForkCallback.add_callback_in_child! do
13
+ ::Process.parent = ::Process._current
14
+ ::Process.current = nil
15
+ end
16
+ end
17
+
18
+ module ModuleMethods
19
+ @@mutex = Mutex.new
20
+
21
+ @@current = nil
22
+ def _current
23
+ @@current
24
+ end
25
+ def current
26
+ @@mutex.synchronize do
27
+ unless @@current
28
+ @@current = OpenStruct.new(:process_id => $$, :variables => { }, :parent => parent)
29
+ def @@current.[] k
30
+ variables[k]
31
+ end
32
+ def @@current.[]= k, v
33
+ variables[k] = v
34
+ end
35
+ end
36
+ @@current
37
+ end
38
+ end
39
+
40
+ def current= x
41
+ @@current = x
42
+ end
43
+
44
+ @@parent = nil
45
+ def parent
46
+ @@parent
47
+ end
48
+ def parent= x
49
+ @@parent = x
50
+ end
51
+ end
52
+
53
+ ::Process.send(:include, self)
54
+ end
55
+
56
+ end
@@ -0,0 +1,74 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ require 'ruby_is_forked/fork_callback'
4
+
5
+ describe "RubyIsForked::ForkCallback" do
6
+ before(:each) do
7
+ @parent_pid = $$
8
+ @child_pid = nil
9
+ @child_proc_called = 0
10
+ @child_proc = RubyIsForked::ForkCallback.add_callback_in_child! do | child_pid |
11
+ @child_pid = child_pid
12
+ $$.should == child_pid
13
+ $$.should_not == @parent_pid
14
+ @child_proc_called += 1
15
+ end
16
+ @parent_proc_called = 0
17
+ @parent_proc = RubyIsForked::ForkCallback.add_callback_in_parent! do | child_pid |
18
+ @child_pid = child_pid
19
+ $$.should_not == child_pid
20
+ $$.should == @parent_pid
21
+ @parent_proc_called += 1
22
+ end
23
+ end
24
+
25
+ after(:each) do
26
+ RubyIsForked::ForkCallback.remove_callback_in_child!(@child_proc).should == @child_proc
27
+ RubyIsForked::ForkCallback.remove_callback_in_parent!(@parent_proc).should == @parent_proc
28
+ end
29
+
30
+ it "should invoke callbacks with Process.fork { ... }" do
31
+ result = Process.fork do
32
+ @child_pid = $$
33
+ @child_proc_called.should == 1
34
+ end
35
+ # puts "#{$$} result = #{result.inspect}"
36
+ sleep 1
37
+ result.should_not == @parent_pid
38
+ result.should == @child_pid
39
+ @parent_proc_called.should == 1
40
+ end
41
+
42
+ it "should invoke callbacks with Process.fork" do
43
+ result = Process.fork
44
+ # puts "#{$$} result = #{result.inspect}"
45
+ if result
46
+ # In parent
47
+ sleep 1
48
+ @child_proc_called.should == 0
49
+ @parent_proc_called.should == 1
50
+ Process.wait(result)
51
+ else
52
+ # In child
53
+ result.should == nil
54
+ @child_pid.should == $$
55
+ @child_proc_called.should == 1
56
+ @parent_proc_called.should == 0
57
+ Process.exit! 0
58
+ end
59
+ end
60
+
61
+ it "should invoke callbacks with fork { ... }" do
62
+ result = fork do
63
+ @child_pid = $$
64
+ @child_proc_called.should == 1
65
+ end
66
+ # puts "#{$$} result = #{result.inspect}"
67
+ sleep 1
68
+ result.should_not == @parent_pid
69
+ result.should == @child_pid
70
+ @parent_proc_called.should == 1
71
+ end
72
+
73
+ end
74
+
@@ -0,0 +1,31 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ require 'ruby_is_forked/process'
4
+
5
+ describe "RubyIsForked::Process" do
6
+ before(:each) do
7
+ Process.current = nil
8
+ end
9
+
10
+ it "Process.current should return new objects in child." do
11
+ Process.current.object_id.should == Process.current.object_id
12
+ parent = Process.current
13
+ parent.process_id.should == $$
14
+ parent[:var] = $$
15
+ parent[:var].should == $$
16
+
17
+ child_pid = Process.fork do
18
+ child = Process.current
19
+ child[:var].should == nil
20
+ child.parent.should_not == nil
21
+ child.parent.process_id.should == parent.process_id
22
+ child.parent[:var].should == parent.process_id
23
+ Process.exit!(0)
24
+ end
25
+ w = Process.waitpid2(child_pid)
26
+ w[1].exitstatus.should == 0
27
+ w[0].should == child_pid
28
+ w[1].pid.should == child_pid
29
+ end
30
+ end # unless
31
+
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "RailsIsForked" do
4
+ it "Has no tests" do
5
+ 1.should == 1
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ # require 'ruby_is_forked'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+
12
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby_is_forked
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kurt Stephens
9
+ - Robert Fletcher
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-05-01 00:00:00.000000000 Z
14
+ dependencies: []
15
+ description: Handle resource issues in forked Ruby processes - File descriptors, DB
16
+ connections, etc.
17
+ email: lobatifricha@gmail.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - lib/ruby_is_forked/process.rb
23
+ - lib/ruby_is_forked/fork_callback.rb
24
+ - lib/ruby_is_forked.rb
25
+ - spec/ruby_is_forked_spec.rb
26
+ - spec/ruby_is_forked/process_spec.rb
27
+ - spec/fork_callback_spec.rb
28
+ - spec/spec_helper.rb
29
+ - README.rdoc
30
+ - Gemfile
31
+ - LICENSE.txt
32
+ - Rakefile
33
+ homepage: https://github.com/mockdeep/ruby_is_forked
34
+ licenses: []
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubyforge_project:
53
+ rubygems_version: 1.8.25
54
+ signing_key:
55
+ specification_version: 3
56
+ summary: Handle resource issues in forked Ruby processes - File descriptors, DB connections,
57
+ etc.
58
+ test_files: []