forkme 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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