forkme 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest CHANGED
@@ -1,4 +1,8 @@
1
1
  Rakefile
2
2
  lib/forkme.rb
3
+ lib/forkme/child.rb
4
+ lib/forkme/children.rb
5
+ lib/forkme/default_logger.rb
6
+ lib/forkme/forkme.rb
3
7
  readme
4
8
  Manifest
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{forkme}
5
- s.version = "0.0.6"
5
+ s.version = "0.0.7"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Tracey Eubanks"]
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
10
10
  s.description = %q{Manage a fork pool}
11
11
  s.email = %q{traceye@pmamediagroup.com}
12
12
  s.extra_rdoc_files = ["lib/forkme.rb"]
13
- s.files = ["Rakefile", "lib/forkme.rb", "readme", "Manifest", "forkme.gemspec"]
13
+ s.files = ["Rakefile", "lib/forkme.rb", "readme", "Manifest", "forkme.gemspec", "lib/forkme/child.rb", "lib/forkme/children.rb", "lib/forkme/default_logger.rb", "lib/forkme/forkme.rb"]
14
14
  s.homepage = %q{http://github.com/narshlob/forkme}
15
15
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Forkme", "--main", "readme"]
16
16
  s.require_paths = ["lib"]
@@ -0,0 +1,47 @@
1
+ class Child < Forkme
2
+ def initialize(pid, from, to)
3
+ @pid, @from, @to = pid, from, to
4
+ @status = :idle
5
+ end
6
+ # status is one of :idle, :connect, :close, :exit
7
+
8
+ attr_accessor :pid, :from, :to
9
+
10
+ def event(s)
11
+ if s == nil then
12
+ #Forkme.logger.debug "p: child #{pid} terminated"
13
+ self.exit
14
+ else
15
+ case s.chomp
16
+ when "connect" then @status = :connect
17
+ when "disconnect" then @status = :idle
18
+ else
19
+ begin
20
+ Forkme.logger.error "unknown status: #{s}"
21
+ rescue NoMethodError
22
+ end
23
+
24
+ return "unknown status: #{s}"
25
+ end
26
+ end
27
+ end
28
+
29
+ def close()
30
+ @to.close unless @to.closed?
31
+ @status = :close
32
+ end
33
+
34
+ def exit()
35
+ @from.close unless @from.closed?
36
+ @to.close unless @to.closed?
37
+ @status = :exit
38
+ end
39
+
40
+ def idle?()
41
+ @status == :idle
42
+ end
43
+
44
+ def active?()
45
+ @status == :idle or @status == :connect
46
+ end
47
+ end
@@ -0,0 +1,40 @@
1
+ class Children < Array
2
+ def fds()
3
+ self.map{|c| c.active? ? c.from : nil}.compact.flatten
4
+ end
5
+
6
+ def pids()
7
+ self.map{|c| c.pid}
8
+ end
9
+
10
+ def active()
11
+ self.map{|c| c.active? ? c : nil}.compact
12
+ end
13
+
14
+ def idle()
15
+ self.map{|c| c.idle? ? c : nil}.compact
16
+ end
17
+
18
+ def by_fd(fd)
19
+ self.each do |c|
20
+ return c if c.from == fd
21
+ end
22
+ nil
23
+ end
24
+
25
+ def cleanup()
26
+ new = Children.new
27
+ self.each do |c|
28
+ begin
29
+ if Process.waitpid(c.pid, Process::WNOHANG) then
30
+ #Forkme.logger.debug "p: catch exited child #{c.pid}"
31
+ c.exit
32
+ else
33
+ new << c
34
+ end
35
+ rescue Errno::ECHILD
36
+ end
37
+ end
38
+ self.replace new
39
+ end
40
+ end
@@ -0,0 +1,13 @@
1
+ module DefaultLogger
2
+ def DefaultLogger.info(msg)
3
+ $stdout.puts msg
4
+ end
5
+
6
+ def DefaultLogger.debug(msg)
7
+ $stdout.puts msg
8
+ end
9
+
10
+ def DefaultLogger.error(msg)
11
+ $stdout.puts msg
12
+ end
13
+ end
@@ -0,0 +1,182 @@
1
+ class Forkme
2
+ include DefaultLogger
3
+ attr_reader :child_count
4
+ attr_accessor :suppress_exceptions
5
+ attr_writer :on_child_start_blk, :on_child_exit_blk, :max_forks
6
+
7
+ # These class methods actually set up the logger that's used
8
+ # to print out useful information
9
+ class << self
10
+ def logger
11
+ @logger
12
+ end
13
+
14
+ def logger=(log_meth)
15
+ @logger = log_meth
16
+ end
17
+ end
18
+
19
+ def self.start(forks_to_run, suppress_exceptions = false, log_method = DefaultLogger, &block)
20
+ self.new(forks_to_run, suppress_exceptions, log_method).start(block)
21
+ end
22
+
23
+ def initialize(forks_to_run, suppress_exceptions = false, log_method = DefaultLogger)
24
+ @min_forks = 1
25
+ @max_forks = forks_to_run
26
+ @suppress_exceptions = suppress_exceptions
27
+ Forkme.logger = log_method
28
+ end
29
+
30
+ # class variable holding all the children
31
+ @@children = Children.new
32
+
33
+ # A block to be executed just before calling the block a child
34
+ # will be executing
35
+ #
36
+ # [block] block The block that will be executed
37
+ def on_child_start(&block)
38
+ raise "block required" unless block_given?
39
+ @on_child_start_blk = block
40
+ end
41
+
42
+ # A block to be executed upon exiting a child process.
43
+ #
44
+ # [block] block The block that will be executed upon termination of a child process
45
+ def on_child_exit(&block)
46
+ raise "block required" unless block_given?
47
+ @on_child_exit_blk = block
48
+ end
49
+
50
+ # Starts the child processes, the number of which is determined by the @max_forks variable
51
+ #
52
+ # [block] &block The block that will be executed by the child processes
53
+ def start(&block)
54
+ raise "block required" unless block_given?
55
+
56
+ (@min_forks - @@children.size).times do
57
+ make_child block
58
+ end
59
+ @flag = :in_loop
60
+ while @flag == :in_loop do
61
+ log = false
62
+ r, = IO.select(@@children.fds, nil, nil, 1)
63
+ if r then
64
+ log = true
65
+ r.each do |f|
66
+ c = @@children.by_fd f
67
+ c.event f.gets
68
+ end
69
+ end
70
+ as = @@children.active.size
71
+ @@children.cleanup if @@children.size > as
72
+ break if @flag != :in_loop
73
+ n = 0
74
+ if as < @min_forks then
75
+ n = @min_forks - as
76
+ else
77
+ if @@children.idle.size <= 2 then
78
+ n = 2
79
+ end
80
+ end
81
+ if as + n > @max_forks then
82
+ n = @max_forks - as
83
+ end
84
+ #Forkme.logger.debug "p: max:#{@max_forks}, min:#{@min_forks}, cur:#{as}, idle:#{@@children.idle.size}: new:#{n}" if n > 0 or log
85
+ n.times do
86
+ make_child block
87
+ end
88
+ end
89
+ @flag = :out_of_loop
90
+ terminate
91
+ end
92
+
93
+ # sets the @flag to :exit_loop, essentially stopping the parent and child processes
94
+ # since the loop will die and all children will receive the close() call
95
+ def stop()
96
+ @flag = :exit_loop
97
+ terminate
98
+ end
99
+
100
+ # Calls the close method on each child which sets its status to :closed
101
+ def terminate()
102
+ raise "Cannot terminate while still in the loop" if @flag == :in_loop
103
+ @@children.each do |c|
104
+ c.close
105
+ end
106
+ end
107
+
108
+ # Sends the TERM signal to all child processes via their pids
109
+ def interrupt()
110
+ Process.kill "TERM", *(@@children.pids) rescue nil
111
+ end
112
+
113
+ private
114
+
115
+ # called by the child process when it's finished the block passed to it
116
+ def exit_child
117
+ exit!
118
+ end
119
+
120
+ # Creates a child process and tells that process to execute a block
121
+ # It also sets up the to and from pipes to be shared between the
122
+ # parent and child processes.
123
+ #
124
+ # [block] block The block to be executed by the child
125
+ def make_child(block)
126
+ #Forkme.logger.debug "p: make child"
127
+ to_child = IO.pipe
128
+ to_parent = IO.pipe
129
+ pid = fork do
130
+ @@children.map do |c|
131
+ c.from.close unless c.from.closed?
132
+ c.to.close unless c.to.closed?
133
+ end
134
+ @from_parent = to_child[0]
135
+ @to_parent = to_parent[1]
136
+ to_child[1].close
137
+ to_parent[0].close
138
+
139
+ child block
140
+ exit_child
141
+ end
142
+ #Forkme.logger.debug "p: child pid #{pid}"
143
+ @@children << Child.new(pid, to_parent[0], to_child[1])
144
+ to_child[0].close
145
+ to_parent[1].close
146
+ end
147
+
148
+ # Method to call the block that's been passed to it
149
+ #
150
+ # [block] the block that will be called within the child process
151
+ def child(block)
152
+ #Forkme.logger.debug "c: start"
153
+ # Handle these different signals the child might encounter
154
+ # This signal trapping actually does get handled within the child
155
+ # since it's called from within the fork method
156
+ handle_signals(["TERM", "INT", "HUP"])
157
+ #Forkme.logger.debug "c: connect from client"
158
+ @to_parent.syswrite "connect\n"
159
+ @on_child_start_blk.call if(defined?(@on_child_start_blk))
160
+ begin
161
+ block.call
162
+ rescue => e
163
+ unless(@suppress_exceptions)
164
+ Forkme.logger.error e.message
165
+ Forkme.logger.error e.backtrace.join("\n")
166
+ raise e
167
+ end
168
+ end
169
+ #Forkme.logger.debug "c: disconnect from client"
170
+ @to_parent.syswrite "disconnect\n" rescue nil
171
+ @on_child_exit_blk.call if(defined?(@on_child_exit_blk))
172
+ end
173
+
174
+ # Creates signal traps for the array of signals passed to it
175
+ #
176
+ # [Array] sigs The signals that will be trapped
177
+ def handle_signals(sigs)
178
+ sigs.each do |signal|
179
+ trap(signal) { exit_child }
180
+ end
181
+ end
182
+ end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forkme
3
3
  version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.0.6
4
+ version: 0.0.7
6
5
  platform: ruby
7
6
  authors:
8
7
  - Tracey Eubanks
@@ -28,6 +27,10 @@ files:
28
27
  - readme
29
28
  - Manifest
30
29
  - forkme.gemspec
30
+ - lib/forkme/child.rb
31
+ - lib/forkme/children.rb
32
+ - lib/forkme/default_logger.rb
33
+ - lib/forkme/forkme.rb
31
34
  has_rdoc: true
32
35
  homepage: http://github.com/narshlob/forkme
33
36
  licenses: []
@@ -43,21 +46,21 @@ rdoc_options:
43
46
  require_paths:
44
47
  - lib
45
48
  required_ruby_version: !ruby/object:Gem::Requirement
46
- none: false
47
49
  requirements:
48
50
  - - ">="
49
51
  - !ruby/object:Gem::Version
50
52
  version: "0"
53
+ version:
51
54
  required_rubygems_version: !ruby/object:Gem::Requirement
52
- none: false
53
55
  requirements:
54
56
  - - ">="
55
57
  - !ruby/object:Gem::Version
56
58
  version: "1.2"
59
+ version:
57
60
  requirements: []
58
61
 
59
62
  rubyforge_project: forkme
60
- rubygems_version: 1.5.0
63
+ rubygems_version: 1.3.5
61
64
  signing_key:
62
65
  specification_version: 3
63
66
  summary: Manage a fork pool