loompa 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/Manifest +4 -0
  2. data/Rakefile +15 -0
  3. data/lib/loompa.rb +260 -0
  4. data/loompa.gemspec +30 -0
  5. data/readme +25 -0
  6. metadata +77 -0
data/Manifest ADDED
@@ -0,0 +1,4 @@
1
+ Rakefile
2
+ lib/loompa.rb
3
+ readme
4
+ Manifest
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('loompa', '0.0.1') do |p|
6
+ p.description = "Manage a fork pool"
7
+ p.url = "http://github.com/pmamediagroup/loompa"
8
+ p.author = "Tracey Eubanks"
9
+ p.email = "traceye@pmamediagroup.com"
10
+ p.ignore_pattern = ["tmp/*", "script/*"]
11
+ p.development_dependencies = []
12
+ end
13
+
14
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
15
+
data/lib/loompa.rb ADDED
@@ -0,0 +1,260 @@
1
+ # a lot of code taken from tmtm's tserver v0.3.2a
2
+ # http://www.tmtm.org/ruby/tserver/
3
+
4
+ require 'socket'
5
+
6
+ module DefaultLogger
7
+ def DefaultLogger.info(msg)
8
+ STDOUT.puts msg
9
+ end
10
+
11
+ def DefaultLogger.debug(msg)
12
+ STDOUT.puts msg
13
+ end
14
+
15
+ def DefaultLogger.error(msg)
16
+ STDOUT.puts msg
17
+ end
18
+ end
19
+
20
+ class Loompa
21
+ include DefaultLogger
22
+ attr_reader :child_count
23
+
24
+ class Children < Array
25
+ def fds()
26
+ self.map{|c| c.active? ? c.from : nil}.compact.flatten
27
+ end
28
+
29
+ def pids()
30
+ self.map{|c| c.pid}
31
+ end
32
+
33
+ def active()
34
+ self.map{|c| c.active? ? c : nil}.compact
35
+ end
36
+
37
+ def idle()
38
+ self.map{|c| c.idle? ? c : nil}.compact
39
+ end
40
+
41
+ def by_fd(fd)
42
+ self.each do |c|
43
+ return c if c.from == fd
44
+ end
45
+ nil
46
+ end
47
+
48
+ def cleanup()
49
+ new = Children.new
50
+ self.each do |c|
51
+ begin
52
+ if Process.waitpid(c.pid, Process::WNOHANG) then
53
+ #Loompa.logger.debug "p: catch exited child #{c.pid}"
54
+ c.exit
55
+ else
56
+ new << c
57
+ end
58
+ rescue Errno::ECHILD
59
+ end
60
+ end
61
+ self.replace new
62
+ end
63
+ end
64
+
65
+ class Child
66
+ def initialize(pid, from, to)
67
+ @pid, @from, @to = pid, from, to
68
+ @status = :idle
69
+ end
70
+ # status is one of :idle, :connect, :close, :exit
71
+
72
+ attr_accessor :pid, :from, :to
73
+
74
+ def event(s)
75
+ if s == nil then
76
+ #Loompa.logger.debug "p: child #{pid} terminated"
77
+ self.exit
78
+ else
79
+ case s.chomp
80
+ when "connect" then @status = :connect
81
+ when "disconnect" then @status = :idle
82
+ else
83
+ $stderr.puts "unknown status: #{s}"
84
+ end
85
+ end
86
+ end
87
+
88
+ def close()
89
+ @to.close unless @to.closed?
90
+ @status = :close
91
+ end
92
+
93
+ def exit()
94
+ @from.close unless @from.closed?
95
+ @to.close unless @to.closed?
96
+ @status = :exit
97
+ end
98
+
99
+ def idle?()
100
+ @status == :idle
101
+ end
102
+
103
+ def active?()
104
+ @status == :idle or @status == :connect
105
+ end
106
+ end
107
+
108
+ attr_writer :on_child_start, :on_child_exit
109
+ attr_accessor :max_servers, :handle_signal
110
+
111
+ class << self
112
+ def logger
113
+ @logger
114
+ end
115
+
116
+ def logger=(log_meth)
117
+ @logger = log_meth
118
+ end
119
+ end
120
+
121
+ def initialize(forks_to_run, port, log_method = DefaultLogger)
122
+ @handle_signal = false
123
+ @min_forks = 1
124
+ @max_forks = forks_to_run
125
+ @port = port
126
+ @child_count = 0
127
+ Loompa.logger = log_method
128
+ end
129
+
130
+ @@children = Children.new
131
+
132
+ def on_child_start(&block)
133
+ if block == nil then
134
+ raise "block required"
135
+ end
136
+ @on_child_start = block
137
+ end
138
+
139
+ def on_child_exit(&block)
140
+ if block == nil then
141
+ raise "block required"
142
+ end
143
+ @on_child_exit = block
144
+ end
145
+
146
+ def start(&block)
147
+ if block == nil then
148
+ raise "block required"
149
+ end
150
+ (@min_forks - @@children.size).times do
151
+ make_child block
152
+ end
153
+ @flag = :in_loop
154
+ while @flag == :in_loop do
155
+ log = false
156
+ r, = IO.select(@@children.fds, nil, nil, 1)
157
+ if r then
158
+ log = true
159
+ r.each do |f|
160
+ c = @@children.by_fd f
161
+ c.event f.gets
162
+ end
163
+ end
164
+ as = @@children.active.size
165
+ @@children.cleanup if @@children.size > as
166
+ break if @flag != :in_loop
167
+ n = 0
168
+ if as < @min_forks then
169
+ n = @min_forks - as
170
+ else
171
+ if @@children.idle.size <= 2 then
172
+ n = 2
173
+ end
174
+ end
175
+ if as + n > @max_forks then
176
+ n = @max_forks - as
177
+ end
178
+ #Loompa.logger.debug "p: max:#{@max_forks}, min:#{@min_forks}, cur:#{as}, idle:#{@@children.idle.size}: new:#{n}" if n > 0 or log
179
+ n.times do
180
+ make_child block
181
+ end
182
+ end
183
+ @flag = :out_of_loop
184
+ terminate
185
+ end
186
+
187
+ def close()
188
+ if @flag != :out_of_loop then
189
+ raise "close() must be called out of start() loop"
190
+ end
191
+ @socks.each do |s|
192
+ s.close
193
+ end
194
+ end
195
+
196
+ def stop()
197
+ @flag = :exit_loop
198
+ end
199
+
200
+ def terminate()
201
+ @@children.each do |c|
202
+ c.close
203
+ end
204
+ end
205
+
206
+ def interrupt()
207
+ Process.kill "TERM", *(@@children.pids) rescue nil
208
+ end
209
+
210
+ private
211
+
212
+ def exit_child
213
+ #Loompa.logger.debug "c: exiting"
214
+ exit!
215
+ end
216
+
217
+ def make_child(block)
218
+ #Loompa.logger.debug "p: make child"
219
+ to_child = IO.pipe
220
+ to_parent = IO.pipe
221
+ pid = fork do
222
+ @@children.map do |c|
223
+ c.from.close unless c.from.closed?
224
+ c.to.close unless c.to.closed?
225
+ end
226
+ @from_parent = to_child[0]
227
+ @to_parent = to_parent[1]
228
+ to_child[1].close
229
+ to_parent[0].close
230
+ child block
231
+ end
232
+ #Loompa.logger.debug "p: child pid #{pid}"
233
+ @@children << Child.new(pid, to_parent[0], to_child[1])
234
+ to_child[0].close
235
+ to_parent[1].close
236
+ end
237
+
238
+ def child(block)
239
+ #Loompa.logger.debug "c: start"
240
+ handle_signals(["TERM", "INT", "HUP"])
241
+ @on_child_start.call if defined? @on_child_start
242
+ #Loompa.logger.debug "c: connect from client"
243
+ @to_parent.syswrite "connect\n"
244
+ begin
245
+ block.call
246
+ rescue => e
247
+ Loompa.logger.error e.message
248
+ Loompa.logger.error e.backtrace.join("\n")
249
+ end
250
+ #Loompa.logger.debug "c: disconnect from client"
251
+ @to_parent.syswrite "disconnect\n" rescue nil
252
+ exit_child
253
+ end
254
+
255
+ def handle_signals(sigs)
256
+ sigs.each do |signal|
257
+ trap(signal) { exit_child }
258
+ end
259
+ end
260
+ end
data/loompa.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{loompa}
5
+ s.version = "0.0.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Tracey Eubanks"]
9
+ s.date = %q{2010-05-14}
10
+ s.description = %q{Manage a fork pool}
11
+ s.email = %q{traceye@pmamediagroup.com}
12
+ s.extra_rdoc_files = ["lib/loompa.rb"]
13
+ s.files = ["Rakefile", "lib/loompa.rb", "readme", "Manifest", "loompa.gemspec"]
14
+ s.homepage = %q{http://github.com/pmamediagroup/loompa}
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Loompa", "--main", "readme"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = %q{loompa}
18
+ s.rubygems_version = %q{1.3.5}
19
+ s.summary = %q{Manage a fork pool}
20
+
21
+ if s.respond_to? :specification_version then
22
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
23
+ s.specification_version = 3
24
+
25
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
26
+ else
27
+ end
28
+ else
29
+ end
30
+ end
data/readme ADDED
@@ -0,0 +1,25 @@
1
+ gem install loompa
2
+
3
+ A library for managing forks
4
+
5
+ The mother process spawns a fork and opens an io from the child to the parent and vice versa.
6
+ If the mother process dies, the children commit suicide. They also accept HUP and INT calls and die then as well.
7
+
8
+ Used like so,
9
+
10
+ loompa = Loompa.start(num_of_childs, Logger)
11
+ loompa.start do
12
+ sleep 10
13
+ puts "hey!!!"
14
+ end
15
+
16
+ The above code will continuously print "hey!!!". Once the child process has slept 10 seconds and printed "hey!!!" it will die and the mother process will start another to replace it.
17
+ You can start this in a thread if you'd like to have control back after spawning the children.
18
+ Thread.new do
19
+ loompa.start do
20
+ ...
21
+ end
22
+ end
23
+
24
+ Have fun and enjoy :]
25
+
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: loompa
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Tracey Eubanks
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-05-14 00:00:00 -06:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Manage a fork pool
23
+ email: traceye@pmamediagroup.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - lib/loompa.rb
30
+ files:
31
+ - Rakefile
32
+ - lib/loompa.rb
33
+ - readme
34
+ - Manifest
35
+ - loompa.gemspec
36
+ has_rdoc: true
37
+ homepage: http://github.com/pmamediagroup/loompa
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options:
42
+ - --line-numbers
43
+ - --inline-source
44
+ - --title
45
+ - Loompa
46
+ - --main
47
+ - readme
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ hash: 3
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ hash: 11
65
+ segments:
66
+ - 1
67
+ - 2
68
+ version: "1.2"
69
+ requirements: []
70
+
71
+ rubyforge_project: loompa
72
+ rubygems_version: 1.3.7
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: Manage a fork pool
76
+ test_files: []
77
+