loompa 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +4 -0
- data/Rakefile +15 -0
- data/lib/loompa.rb +260 -0
- data/loompa.gemspec +30 -0
- data/readme +25 -0
- metadata +77 -0
data/Manifest
ADDED
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
|
+
|