slave 0.0.0
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/README +42 -0
- data/VERSION +1 -0
- data/doc/classes/Slave.html +541 -0
- data/doc/classes/Slave/Heartbeat.html +342 -0
- data/doc/created.rid +1 -0
- data/doc/dot/f_0.dot +14 -0
- data/doc/dot/f_0.jpg +0 -0
- data/doc/dot/f_1.dot +14 -0
- data/doc/dot/f_1.jpg +0 -0
- data/doc/dot/f_2.dot +29 -0
- data/doc/dot/f_2.jpg +0 -0
- data/doc/files/README.html +169 -0
- data/doc/files/VERSION.html +107 -0
- data/doc/files/lib/slave_rb.html +119 -0
- data/doc/fr_class_index.html +28 -0
- data/doc/fr_file_index.html +29 -0
- data/doc/fr_method_index.html +39 -0
- data/doc/index.html +24 -0
- data/doc/rdoc-style.css +172 -0
- data/gemspec.rb +23 -0
- data/install.rb +201 -0
- data/lib/slave-0.0.0.rb +292 -0
- data/lib/slave.rb +292 -0
- data/rdoc.cmd +1 -0
- data/sample/a.rb +102 -0
- data/slave-0.0.0.gem +0 -0
- metadata +72 -0
data/gemspec.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
lib, version = File::basename(File::dirname(File::expand_path(__FILE__))).split %r/-/, 2
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
Gem::Specification::new do |spec|
|
6
|
+
spec.name = lib
|
7
|
+
spec.version = version
|
8
|
+
spec.platform = Gem::Platform::RUBY
|
9
|
+
spec.summary = lib
|
10
|
+
|
11
|
+
spec.files = Dir::glob "**/**"
|
12
|
+
spec.executables = Dir::glob("bin/*").map{|exe| File::basename exe}
|
13
|
+
|
14
|
+
spec.require_path = "lib"
|
15
|
+
spec.autorequire = lib
|
16
|
+
|
17
|
+
spec.has_rdoc = File::exist? "doc"
|
18
|
+
spec.test_suite_file = "test/#{ lib }.rb" if File::directory? "test"
|
19
|
+
|
20
|
+
spec.author = "Ara T. Howard"
|
21
|
+
spec.email = "ara.t.howard@noaa.gov"
|
22
|
+
spec.homepage = "http://codeforpeople.com/lib/ruby/#{ lib }/"
|
23
|
+
end
|
data/install.rb
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rbconfig'
|
3
|
+
require 'find'
|
4
|
+
require 'ftools'
|
5
|
+
require 'tempfile'
|
6
|
+
include Config
|
7
|
+
|
8
|
+
LIBDIR = "lib"
|
9
|
+
LIBDIR_MODE = 0644
|
10
|
+
|
11
|
+
BINDIR = "bin"
|
12
|
+
BINDIR_MODE = 0755
|
13
|
+
|
14
|
+
|
15
|
+
$srcdir = CONFIG["srcdir"]
|
16
|
+
$version = CONFIG["MAJOR"]+"."+CONFIG["MINOR"]
|
17
|
+
$libdir = File.join(CONFIG["libdir"], "ruby", $version)
|
18
|
+
$archdir = File.join($libdir, CONFIG["arch"])
|
19
|
+
$site_libdir = $:.find {|x| x =~ /site_ruby$/}
|
20
|
+
$bindir = CONFIG["bindir"] || CONFIG['BINDIR']
|
21
|
+
$ruby_install_name = CONFIG['ruby_install_name'] || CONFIG['RUBY_INSTALL_NAME'] || 'ruby'
|
22
|
+
$ruby_ext = CONFIG['EXEEXT'] || ''
|
23
|
+
$ruby = File.join($bindir, ($ruby_install_name + $ruby_ext))
|
24
|
+
|
25
|
+
if !$site_libdir
|
26
|
+
$site_libdir = File.join($libdir, "site_ruby")
|
27
|
+
elsif $site_libdir !~ %r/#{Regexp.quote($version)}/
|
28
|
+
$site_libdir = File.join($site_libdir, $version)
|
29
|
+
end
|
30
|
+
|
31
|
+
def install_rb(srcdir=nil, destdir=nil, mode=nil, bin=nil)
|
32
|
+
#{{{
|
33
|
+
path = []
|
34
|
+
dir = []
|
35
|
+
Find.find(srcdir) do |f|
|
36
|
+
next unless FileTest.file?(f)
|
37
|
+
next if (f = f[srcdir.length+1..-1]) == nil
|
38
|
+
next if (/CVS$/ =~ File.dirname(f))
|
39
|
+
next if f =~ %r/\.lnk/
|
40
|
+
path.push f
|
41
|
+
dir |= [File.dirname(f)]
|
42
|
+
end
|
43
|
+
for f in dir
|
44
|
+
next if f == "."
|
45
|
+
next if f == "CVS"
|
46
|
+
File::makedirs(File.join(destdir, f))
|
47
|
+
end
|
48
|
+
for f in path
|
49
|
+
next if (/\~$/ =~ f)
|
50
|
+
next if (/^\./ =~ File.basename(f))
|
51
|
+
unless bin
|
52
|
+
File::install(File.join(srcdir, f), File.join(destdir, f), mode, true)
|
53
|
+
else
|
54
|
+
from = File.join(srcdir, f)
|
55
|
+
to = File.join(destdir, f)
|
56
|
+
shebangify(from) do |sf|
|
57
|
+
$deferr.print from, " -> ", File::catname(from, to), "\n"
|
58
|
+
$deferr.printf "chmod %04o %s\n", mode, to
|
59
|
+
File::install(sf, to, mode, false)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
#}}}
|
64
|
+
end
|
65
|
+
def shebangify f
|
66
|
+
#{{{
|
67
|
+
open(f) do |fd|
|
68
|
+
buf = fd.read 42
|
69
|
+
if buf =~ %r/^\s*#\s*!.*ruby/o
|
70
|
+
ftmp = Tempfile::new("#{ $$ }_#{ File::basename(f) }")
|
71
|
+
begin
|
72
|
+
fd.rewind
|
73
|
+
ftmp.puts "#!#{ $ruby }"
|
74
|
+
while((buf = fd.read(8192)))
|
75
|
+
ftmp.write buf
|
76
|
+
end
|
77
|
+
ftmp.close
|
78
|
+
yield ftmp.path
|
79
|
+
ensure
|
80
|
+
ftmp.close!
|
81
|
+
end
|
82
|
+
else
|
83
|
+
yield f
|
84
|
+
end
|
85
|
+
end
|
86
|
+
#}}}
|
87
|
+
end
|
88
|
+
def ARGV.switch
|
89
|
+
#{{{
|
90
|
+
return nil if self.empty?
|
91
|
+
arg = self.shift
|
92
|
+
return nil if arg == '--'
|
93
|
+
if arg =~ /^-(.)(.*)/
|
94
|
+
return arg if $1 == '-'
|
95
|
+
raise 'unknown switch "-"' if $2.index('-')
|
96
|
+
self.unshift "-#{$2}" if $2.size > 0
|
97
|
+
"-#{$1}"
|
98
|
+
else
|
99
|
+
self.unshift arg
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
#}}}
|
103
|
+
end
|
104
|
+
def ARGV.req_arg
|
105
|
+
#{{{
|
106
|
+
self.shift || raise('missing argument')
|
107
|
+
#}}}
|
108
|
+
end
|
109
|
+
def linkify d, linked = []
|
110
|
+
#--{{{
|
111
|
+
if test ?d, d
|
112
|
+
versioned = Dir[ File::join(d, "*-[0-9].[0-9].[0-9].rb") ]
|
113
|
+
versioned.each do |v|
|
114
|
+
src, dst = v, v.gsub(%r/\-[\d\.]+\.rb$/, '.rb')
|
115
|
+
lnk = nil
|
116
|
+
begin
|
117
|
+
if test ?l, dst
|
118
|
+
lnk = "#{ dst }.lnk"
|
119
|
+
puts "#{ dst } -> #{ lnk }"
|
120
|
+
File::rename dst, lnk
|
121
|
+
end
|
122
|
+
unless test ?e, dst
|
123
|
+
puts "#{ src } -> #{ dst }"
|
124
|
+
File::copy src, dst
|
125
|
+
linked << dst
|
126
|
+
end
|
127
|
+
ensure
|
128
|
+
if lnk
|
129
|
+
at_exit do
|
130
|
+
puts "#{ lnk } -> #{ dst }"
|
131
|
+
File::rename lnk, dst
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
linked
|
138
|
+
#--}}}
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
#
|
143
|
+
# main program
|
144
|
+
#
|
145
|
+
|
146
|
+
libdir = $site_libdir
|
147
|
+
bindir = $bindir
|
148
|
+
no_linkify = false
|
149
|
+
linked = nil
|
150
|
+
help = false
|
151
|
+
|
152
|
+
usage = <<-usage
|
153
|
+
#{ File::basename $0 }
|
154
|
+
-d, --destdir <destdir>
|
155
|
+
-l, --libdir <libdir>
|
156
|
+
-b, --bindir <bindir>
|
157
|
+
-r, --ruby <ruby>
|
158
|
+
-n, --no_linkify
|
159
|
+
-h, --help
|
160
|
+
usage
|
161
|
+
|
162
|
+
begin
|
163
|
+
while switch = ARGV.switch
|
164
|
+
case switch
|
165
|
+
when '-d', '--destdir'
|
166
|
+
libdir = ARGV.req_arg
|
167
|
+
when '-l', '--libdir'
|
168
|
+
libdir = ARGV.req_arg
|
169
|
+
when '-b', '--bindir'
|
170
|
+
bindir = ARGV.req_arg
|
171
|
+
when '-r', '--ruby'
|
172
|
+
$ruby = ARGV.req_arg
|
173
|
+
when '-n', '--no_linkify'
|
174
|
+
no_linkify = true
|
175
|
+
when '-h', '--help'
|
176
|
+
help = true
|
177
|
+
else
|
178
|
+
raise "unknown switch #{switch.dump}"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
rescue
|
182
|
+
STDERR.puts $!.to_s
|
183
|
+
STDERR.puts usage
|
184
|
+
exit 1
|
185
|
+
end
|
186
|
+
|
187
|
+
if help
|
188
|
+
STDOUT.puts usage
|
189
|
+
exit
|
190
|
+
end
|
191
|
+
|
192
|
+
unless no_linkify
|
193
|
+
linked = linkify('lib') + linkify('bin')
|
194
|
+
end
|
195
|
+
|
196
|
+
install_rb(LIBDIR, libdir, LIBDIR_MODE)
|
197
|
+
install_rb(BINDIR, bindir, BINDIR_MODE, bin=true)
|
198
|
+
|
199
|
+
if linked
|
200
|
+
linked.each{|path| File::rm_f path}
|
201
|
+
end
|
data/lib/slave-0.0.0.rb
ADDED
@@ -0,0 +1,292 @@
|
|
1
|
+
require 'drb/drb'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'tmpdir'
|
4
|
+
require 'tempfile'
|
5
|
+
|
6
|
+
#
|
7
|
+
# the Slave class encapsulates the work of setting up a drb server in another
|
8
|
+
# process.
|
9
|
+
#
|
10
|
+
class Slave
|
11
|
+
#--{{{
|
12
|
+
DEFAULT_SOCKET_CREATION_ATTEMPTS =
|
13
|
+
Integer(ENV['SLAVE_SOCKET_CREATION_ATTEMPTS'] || 42)
|
14
|
+
|
15
|
+
DEFAULT_PULSE_RATE =
|
16
|
+
Float(ENV['SLAVE_PULSE_RATE'] || 8)
|
17
|
+
|
18
|
+
DEFAULT_DEBUG =
|
19
|
+
(ENV['SLAVE_DEBUG'] ? true : false)
|
20
|
+
|
21
|
+
@socket_creation_attempts = DEFAULT_SOCKET_CREATION_ATTEMPTS
|
22
|
+
@pulse_rate = DEFAULT_PULSE_RATE
|
23
|
+
@debug = DEFAULT_DEBUG
|
24
|
+
|
25
|
+
class << self
|
26
|
+
#--{{{
|
27
|
+
# defineds how many attempts will be made to create a temporary unix domain
|
28
|
+
# socket
|
29
|
+
attr :socket_creation_attempts, true
|
30
|
+
|
31
|
+
# defined the rate of pinging in the Heartbeat object
|
32
|
+
attr :pulse_rate, true
|
33
|
+
|
34
|
+
# if this is true and you are running from a terminal information is printed
|
35
|
+
# on STDERR
|
36
|
+
attr :debug, true
|
37
|
+
|
38
|
+
# look up a value in an option hash failing back to class defaults
|
39
|
+
def getval key, opts = {}
|
40
|
+
#--{{{
|
41
|
+
keys = [key, key.to_s, key.to_s.intern]
|
42
|
+
keys.each{|k| return opts[k] if opts.has_key?(k)}
|
43
|
+
send key rescue nil
|
44
|
+
#--}}}
|
45
|
+
end
|
46
|
+
# just fork with out silly warnings
|
47
|
+
def fork &block
|
48
|
+
#--{{{
|
49
|
+
v = $VERBOSE
|
50
|
+
begin
|
51
|
+
$VERBOSE = nil
|
52
|
+
Process::fork &block
|
53
|
+
ensure
|
54
|
+
$VERBOSE = v
|
55
|
+
end
|
56
|
+
#--}}}
|
57
|
+
end
|
58
|
+
#--}}}
|
59
|
+
end
|
60
|
+
|
61
|
+
attr :object
|
62
|
+
attr :obj
|
63
|
+
attr :psname
|
64
|
+
attr :pid
|
65
|
+
attr :ppid
|
66
|
+
attr :uri
|
67
|
+
attr :pulse_rate
|
68
|
+
attr :socket
|
69
|
+
attr :debug
|
70
|
+
|
71
|
+
# 'obj' can be any object and 'opts' may contain the keys
|
72
|
+
# 'socket_creation_attempts', 'pulse_rate', 'psname', or 'debug'
|
73
|
+
def initialize obj, opts = {}
|
74
|
+
#--{{{
|
75
|
+
@obj = obj
|
76
|
+
|
77
|
+
@socket_creation_attempts = getval('socket_creation_attempts', opts)
|
78
|
+
@pulse_rate = getval('pulse_rate', opts)
|
79
|
+
@debug = getval('debug', opts)
|
80
|
+
@psname = getval('psname', opts) || gen_psname(@obj)
|
81
|
+
|
82
|
+
trace{ "socket_creation_attempts <#{ @socket_creation_attempts }>" }
|
83
|
+
trace{ "pulse_rate <#{ @pulse_rate }>" }
|
84
|
+
trace{ "psname <#{ @psname }>" }
|
85
|
+
|
86
|
+
@shutdown = false
|
87
|
+
|
88
|
+
@heartbeat = Heartbeat::new @pulse_rate, @debug
|
89
|
+
@r, @w = IO::pipe
|
90
|
+
#
|
91
|
+
# child
|
92
|
+
#
|
93
|
+
unless((@pid = Slave::fork))
|
94
|
+
begin
|
95
|
+
$0 = @psname
|
96
|
+
@pid = Process::pid
|
97
|
+
@ppid = Process::ppid
|
98
|
+
|
99
|
+
@r.close
|
100
|
+
@socket = nil
|
101
|
+
@uri = nil
|
102
|
+
|
103
|
+
tmpdir = Dir::tmpdir
|
104
|
+
basename = File::basename @psname
|
105
|
+
|
106
|
+
@socket_creation_attempts.times do |attempt|
|
107
|
+
begin
|
108
|
+
s = File::join(tmpdir, "#{ basename }_#{ attempt }")
|
109
|
+
u = "drbunix://#{ s }"
|
110
|
+
DRb::start_service u, obj
|
111
|
+
@socket = s
|
112
|
+
@uri = u
|
113
|
+
trace{ "child - socket <#{ @socket }>" }
|
114
|
+
trace{ "child - uri <#{ @uri }>" }
|
115
|
+
break
|
116
|
+
rescue Errno::EADDRINUSE
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
if @socket and @uri
|
122
|
+
@heartbeat.start
|
123
|
+
@w.write @socket
|
124
|
+
@w.close
|
125
|
+
trap('SIGUSR2') do
|
126
|
+
@heartbeat.stop rescue nil
|
127
|
+
DBb::thread.kill rescue nil
|
128
|
+
FileUtils::rm_f @socket rescue nil
|
129
|
+
exit!
|
130
|
+
end
|
131
|
+
DRb::thread.join
|
132
|
+
else
|
133
|
+
@w.close
|
134
|
+
end
|
135
|
+
rescue => e
|
136
|
+
trace{ %Q[#{ e.message } (#{ e.class })\n#{ e.backtrace.join "\n" }] }
|
137
|
+
ensure
|
138
|
+
exit!
|
139
|
+
end
|
140
|
+
#
|
141
|
+
# parent
|
142
|
+
#
|
143
|
+
else
|
144
|
+
Process::detach @pid
|
145
|
+
@w.close
|
146
|
+
@socket = @r.read
|
147
|
+
@r.close
|
148
|
+
|
149
|
+
trace{ "parent - socket <#{ @socket }>" }
|
150
|
+
|
151
|
+
if @socket and File::exist? @socket
|
152
|
+
at_exit{ FileUtils::rm_f @socket }
|
153
|
+
@uri = "drbunix://#{ socket }"
|
154
|
+
trace{ "parent - uri <#{ @uri }>" }
|
155
|
+
@heartbeat.start
|
156
|
+
#
|
157
|
+
# starting drb on localhost avoids dns lookups!
|
158
|
+
#
|
159
|
+
DRb::start_service('druby://localhost:0', nil) unless DRb::thread
|
160
|
+
@object = DRbObject::new nil, @uri
|
161
|
+
else
|
162
|
+
raise "failed to find slave socket <#{ @socket }>"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
#--}}}
|
166
|
+
end
|
167
|
+
# stops the heartbeat thread and kills the child process
|
168
|
+
def shutdown
|
169
|
+
#--{{{
|
170
|
+
raise "already shutdown" if @shutdown
|
171
|
+
@heartbeat.stop rescue nil
|
172
|
+
Process::kill('SIGUSR2', @pid) rescue nil
|
173
|
+
Process::kill('SIGTERM', @pid) rescue nil
|
174
|
+
FileUtils::rm_f @socket
|
175
|
+
@shutdown = true
|
176
|
+
#--}}}
|
177
|
+
end
|
178
|
+
# generate a default name to appear in ps/top
|
179
|
+
def gen_psname obj
|
180
|
+
#--{{{
|
181
|
+
"#{ obj.class }_slave_of_#{ Process::pid }".downcase
|
182
|
+
#--}}}
|
183
|
+
end
|
184
|
+
# see docs for class.getval
|
185
|
+
def getval key, opts = {}
|
186
|
+
#--{{{
|
187
|
+
self.class.getval key
|
188
|
+
#--}}}
|
189
|
+
end
|
190
|
+
# debugging output
|
191
|
+
def trace
|
192
|
+
#--{{{
|
193
|
+
STDERR.puts(yield) if @debug and STDERR.tty?
|
194
|
+
#--}}}
|
195
|
+
end
|
196
|
+
|
197
|
+
#
|
198
|
+
# the Heartbeat class is essentially wrapper over an IPC channel that sends a
|
199
|
+
# ping on the channel indicating process health. if either end of the channel
|
200
|
+
# is detached the ping will fail and an error will be raised. in this was it
|
201
|
+
# is ensured that Slave object cannot continue to live without their parent
|
202
|
+
# being alive.
|
203
|
+
#
|
204
|
+
class Heartbeat
|
205
|
+
#--{{{
|
206
|
+
def initialize pulse_rate = 4.2, debug = false
|
207
|
+
#--{{{
|
208
|
+
@pulse_rate = Float pulse_rate
|
209
|
+
@debug = debug
|
210
|
+
@r, @w = IO::pipe
|
211
|
+
@pid = Process::pid
|
212
|
+
@ppid = Process::ppid
|
213
|
+
@cid = nil
|
214
|
+
@thread = nil
|
215
|
+
@ppid = nil
|
216
|
+
@whoami = nil
|
217
|
+
@beating = nil
|
218
|
+
@pipe = nil
|
219
|
+
#--}}}
|
220
|
+
end
|
221
|
+
def start
|
222
|
+
#--{{{
|
223
|
+
if Process::pid == @pid
|
224
|
+
@w.close
|
225
|
+
@pipe = @r
|
226
|
+
parent_start
|
227
|
+
else
|
228
|
+
@r.close
|
229
|
+
@pipe = @w
|
230
|
+
child_start
|
231
|
+
end
|
232
|
+
@beating = true
|
233
|
+
#--}}}
|
234
|
+
end
|
235
|
+
def parent_start
|
236
|
+
#--{{{
|
237
|
+
@whoami = 'parent'
|
238
|
+
@thread =
|
239
|
+
Thread::new(Thread::current) do |cur|
|
240
|
+
begin
|
241
|
+
loop do
|
242
|
+
buf = @pipe.gets
|
243
|
+
trace{ "<#{ @whoami }> <#{ @pid }> gets <#{ buf.inspect }>" }
|
244
|
+
@cid = Integer buf.strip if @cid.nil? and buf =~ %r/^\s*\d+\s*$/
|
245
|
+
end
|
246
|
+
rescue => e
|
247
|
+
cur.raise e
|
248
|
+
ensure
|
249
|
+
@pipe.close rescue nil
|
250
|
+
end
|
251
|
+
end
|
252
|
+
#--}}}
|
253
|
+
end
|
254
|
+
def child_start
|
255
|
+
#--{{{
|
256
|
+
@whoami = 'child'
|
257
|
+
@pid = Process::pid
|
258
|
+
@ppid = Process::ppid
|
259
|
+
@thread =
|
260
|
+
Thread::new(Thread::current) do |cur|
|
261
|
+
begin
|
262
|
+
loop do
|
263
|
+
trace{ "<#{ @whoami }> <#{ @pid }> puts <#{ @pid }>" }
|
264
|
+
@pipe.puts @pid
|
265
|
+
Process::kill 0, @ppid
|
266
|
+
sleep @pulse_rate
|
267
|
+
end
|
268
|
+
rescue => e
|
269
|
+
cur.raise e
|
270
|
+
ensure
|
271
|
+
@pipe.close rescue nil
|
272
|
+
end
|
273
|
+
end
|
274
|
+
#--}}}
|
275
|
+
end
|
276
|
+
def stop
|
277
|
+
#--{{{
|
278
|
+
raise "not beating" unless @beating
|
279
|
+
@thread.kill
|
280
|
+
@pipe.close rescue nil
|
281
|
+
@beating = false
|
282
|
+
#--}}}
|
283
|
+
end
|
284
|
+
def trace
|
285
|
+
#--{{{
|
286
|
+
STDERR.puts(yield) if @debug and STDERR.tty?
|
287
|
+
#--}}}
|
288
|
+
end
|
289
|
+
#--}}}
|
290
|
+
end # class Heartbeat
|
291
|
+
#--}}}
|
292
|
+
end # class Slave
|