slave 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,117 +0,0 @@
1
- <?xml version="1.0" encoding="iso-8859-1"?>
2
- <!DOCTYPE html
3
- PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4
- "DTD/xhtml1-transitional.dtd">
5
-
6
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
7
- <head>
8
- <title>Class: object</title>
9
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
10
- <meta http-equiv="Content-Script-Type" content="text/javascript" />
11
- <link rel="stylesheet" href=".././rdoc-style.css" type="text/css" media="screen" />
12
- <script type="text/javascript">
13
- // <![CDATA[
14
-
15
- function popupCode( url ) {
16
- window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
17
- }
18
-
19
- function toggleCode( id ) {
20
- if ( document.getElementById )
21
- elem = document.getElementById( id );
22
- else if ( document.all )
23
- elem = eval( "document.all." + id );
24
- else
25
- return false;
26
-
27
- elemStyle = elem.style;
28
-
29
- if ( elemStyle.display != "block" ) {
30
- elemStyle.display = "block"
31
- } else {
32
- elemStyle.display = "none"
33
- }
34
-
35
- return true;
36
- }
37
-
38
- // Make codeblocks hidden by default
39
- document.writeln( "<style type=\"text/css\">div.method-source-code { display: none }</style>" )
40
-
41
- // ]]>
42
- </script>
43
-
44
- </head>
45
- <body>
46
-
47
-
48
-
49
- <div id="classHeader">
50
- <h1>object <sup class="type-note">(Class)</sup></h1>
51
- <table class="header-table">
52
- <tr class="top-aligned-row">
53
- <td><strong>In:</strong></td>
54
- <td>
55
- <a href="../files/lib/slave_rb.html">
56
- lib/slave.rb
57
- </a>
58
- <br />
59
- </td>
60
- </tr>
61
-
62
- </table>
63
- </div>
64
- <!-- banner header -->
65
-
66
- <div id="bodyContent">
67
-
68
-
69
- <div id="contextContent">
70
- <div id="diagram">
71
- <map name="map">
72
- <area shape="RECT" coords="123,98,195,50" href="Slave.html" alt="Slave">
73
- <area shape="RECT" coords="27,98,99,50" href="object.html" alt="object">
74
- </map>
75
- <img src="../dot/f_1.jpg" usemap="#map" border=0 alt="TopLevel">
76
- </div>
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
- <div id="attribute-list">
85
- <h2 class="section-bar">Attributes</h2>
86
-
87
- <div class="name-list">
88
- <table>
89
- <tr class="top-aligned-row context-row">
90
- <td class="context-item-name">__slave_object_failure__</td>
91
- <td class="context-item-value">&nbsp;[RW]&nbsp;</td>
92
- <td class="context-item-desc"></td>
93
- </tr>
94
- </table>
95
- </div>
96
- </div>
97
-
98
-
99
- </div>
100
-
101
-
102
-
103
- <!-- if includes -->
104
-
105
-
106
- <!-- if method_list -->
107
-
108
-
109
- </div>
110
-
111
-
112
- <div id="validator-badges">
113
- <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
114
- </div>
115
-
116
- </body>
117
- </html>
data/doc/dot/f_2.dot DELETED
@@ -1,29 +0,0 @@
1
- digraph TopLevel {
2
- compound = true
3
- bgcolor = lightcyan1
4
- fontname = Arial
5
- fontsize = 8
6
- label = "lib/slave.rb"
7
- node [
8
- fontname = Arial,
9
- fontsize = 8,
10
- color = black
11
- ]
12
-
13
- subgraph cluster_1 {
14
- fontname = Arial
15
- color = red
16
- label = "lib/slave.rb"
17
- Slave [
18
- fontcolor = black,
19
- URL = "classes/Slave.html",
20
- shape = ellipse,
21
- color = palegoldenrod,
22
- style = filled,
23
- label = "Slave"
24
- ]
25
-
26
- }
27
-
28
- }
29
-
data/doc/dot/f_2.jpg DELETED
Binary file
@@ -1,107 +0,0 @@
1
- <?xml version="1.0" encoding="iso-8859-1"?>
2
- <!DOCTYPE html
3
- PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4
- "DTD/xhtml1-transitional.dtd">
5
-
6
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
7
- <head>
8
- <title>File: VERSION</title>
9
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
10
- <meta http-equiv="Content-Script-Type" content="text/javascript" />
11
- <link rel="stylesheet" href=".././rdoc-style.css" type="text/css" media="screen" />
12
- <script type="text/javascript">
13
- // <![CDATA[
14
-
15
- function popupCode( url ) {
16
- window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
17
- }
18
-
19
- function toggleCode( id ) {
20
- if ( document.getElementById )
21
- elem = document.getElementById( id );
22
- else if ( document.all )
23
- elem = eval( "document.all." + id );
24
- else
25
- return false;
26
-
27
- elemStyle = elem.style;
28
-
29
- if ( elemStyle.display != "block" ) {
30
- elemStyle.display = "block"
31
- } else {
32
- elemStyle.display = "none"
33
- }
34
-
35
- return true;
36
- }
37
-
38
- // Make codeblocks hidden by default
39
- document.writeln( "<style type=\"text/css\">div.method-source-code { display: none }</style>" )
40
-
41
- // ]]>
42
- </script>
43
-
44
- </head>
45
- <body>
46
-
47
-
48
-
49
- <div id="fileHeader">
50
- <h1>VERSION</h1>
51
- <table class="header-table">
52
- <tr class="top-aligned-row">
53
- <td><strong>Path:</strong></td>
54
- <td>VERSION
55
- </td>
56
- </tr>
57
- <tr class="top-aligned-row">
58
- <td><strong>Last Update:</strong></td>
59
- <td>Fri Nov 12 11:53:15 MST 2004</td>
60
- </tr>
61
- </table>
62
- </div>
63
- <!-- banner header -->
64
-
65
- <div id="bodyContent">
66
-
67
-
68
- <div id="contextContent">
69
- <div id="diagram">
70
- <map name="map">
71
- </map>
72
- <img src="../dot/f_1.jpg" usemap="#map" border=0 alt="TopLevel">
73
- </div>
74
-
75
- <div id="description">
76
- <p>
77
- 0.0.0
78
- </p>
79
-
80
- </div>
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
- </div>
90
-
91
-
92
-
93
- <!-- if includes -->
94
-
95
-
96
- <!-- if method_list -->
97
-
98
-
99
- </div>
100
-
101
-
102
- <div id="validator-badges">
103
- <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
104
- </div>
105
-
106
- </body>
107
- </html>
data/lib/slave-1.0.0.rb DELETED
@@ -1,533 +0,0 @@
1
- require 'drb/drb'
2
- require 'fileutils'
3
- require 'tmpdir'
4
- require 'tempfile'
5
- require 'fcntl'
6
-
7
- #
8
- # the Slave class encapsulates the work of setting up a drb server in another
9
- # process running on localhost. the slave process is attached to it's parent
10
- # via a Heartbeat which is designed such that the slave cannot out-live it's
11
- # parent and become a zombie, even if the parent dies and early death, such as
12
- # by 'kill -9'. the concept and purpose of the Slave class is to be able to
13
- # setup any server object in another process so easily that using a
14
- # multi-process, drb/ipc, based design is as easy, or easier, than a
15
- # multi-threaded one. eg
16
- #
17
- # class Server
18
- # def add_two n
19
- # n + 2
20
- # end
21
- # end
22
- #
23
- # slave = Slave.new 'object' => Server.new
24
- # server = slave.object
25
- #
26
- # p server.add_two(40) #=> 42
27
- #
28
- # two other methods of providing server objects exist:
29
- #
30
- # a) server = Server.new "this is called the parent" }
31
- # Slave.new(:object=>server){|s| puts "#{ s.inspect } passed to block in child process"}
32
- #
33
- # b) Slave.new{ Server.new "this is called only in the child" }
34
- #
35
- # of the two 'b' is preferred.
36
- #
37
- class Slave
38
- #--{{{
39
- VERSION = '1.0.0'
40
- def self.version() VERSION end
41
- #
42
- # config
43
- #
44
- DEFAULT_SOCKET_CREATION_ATTEMPTS =
45
- Integer(ENV['SLAVE_SOCKET_CREATION_ATTEMPTS'] || 42)
46
-
47
- DEFAULT_PULSE_RATE =
48
- Float(ENV['SLAVE_PULSE_RATE'] || 8)
49
-
50
- DEFAULT_DEBUG =
51
- (ENV['SLAVE_DEBUG'] ? true : false)
52
-
53
- @socket_creation_attempts = DEFAULT_SOCKET_CREATION_ATTEMPTS
54
- @pulse_rate = DEFAULT_PULSE_RATE
55
- @debug = DEFAULT_DEBUG
56
- #
57
- # class methods
58
- #
59
- class << self
60
- #--{{{
61
- # defineds how many attempts will be made to create a temporary unix domain
62
- # socket
63
- attr :socket_creation_attempts, true
64
-
65
- # defined the rate of pinging in the Heartbeat object
66
- attr :pulse_rate, true
67
-
68
- # if this is true and you are running from a terminal information is printed
69
- # on STDERR
70
- attr :debug, true
71
-
72
- # get a default value
73
- def default key
74
- #--{{{
75
- send key
76
- #--}}}
77
- end
78
-
79
- def getopts opts
80
- #--{{{
81
- raise ArgumentError, opts.class unless
82
- opts.respond_to?('has_key?') and opts.respond_to?('[]')
83
-
84
- lambda do |key, *defval|
85
- defval = defval.shift
86
- keys = [key, key.to_s, key.to_s.intern]
87
- key = keys.detect{|k| opts.has_key? k } and break opts[key]
88
- defval
89
- end
90
- #--}}}
91
- end
92
-
93
- # just fork with out silly warnings
94
- def fork &block
95
- #--{{{
96
- v = $VERBOSE
97
- begin
98
- $VERBOSE = nil
99
- Process::fork &block
100
- ensure
101
- $VERBOSE = v
102
- end
103
- #--}}}
104
- end
105
- #--}}}
106
- end
107
-
108
-
109
- attr :obj
110
- attr :socket_creation_attempts
111
- attr :pulse_rate
112
- attr :debug
113
- attr :psname
114
- attr :at_exit
115
-
116
- attr :shutdown
117
- attr :status
118
- attr :object
119
- attr :pid
120
- attr :ppid
121
- attr :uri
122
- attr :socket
123
-
124
- #
125
- # opts may contain the keys 'object', 'socket_creation_attempts',
126
- # 'pulse_rate', 'psname', 'dumped', or 'debug'
127
- #
128
- def initialize opts = {}, &block
129
- #--{{{
130
- getopt = getopts opts
131
-
132
- @obj = getopt['object']
133
- @socket_creation_attempts = getopt['socket_creation_attempts'] || default('socket_creation_attempts')
134
- @pulse_rate = getopt['pulse_rate'] || default('pulse_rate')
135
- @debug = getopt['debug'] || default('debug')
136
- @psname = getopt['psname']
137
- @at_exit = getopt['at_exit']
138
- @dumped = getopt['dumped']
139
-
140
- raise ArgumentError, 'no slave object!' if
141
- @obj.nil? and block.nil?
142
-
143
- @shutdown = false
144
- @waiter = @status = nil
145
-
146
- @heartbeat = Heartbeat::new @pulse_rate, @debug
147
- @r, @w = IO::pipe
148
- @r2, @w2 = IO::pipe
149
-
150
- # weird syntax because dot/rdoc chokes on this!?!?
151
- init_failure = lambda do |e|
152
- o = Object.new
153
- class << o
154
- attr_accessor '__slave_object_failure__'
155
- end
156
- o.__slave_object_failure__ = Marshal.dump [e.class, e.message, e.backtrace]
157
- @object = o
158
- end
159
-
160
- #
161
- # child
162
- #
163
- unless((@pid = Slave::fork))
164
- e = nil
165
- begin
166
- Kernel.at_exit{ Kernel.exit! }
167
-
168
- if @obj
169
- @object = @obj
170
- else
171
- begin
172
- @object = block.call
173
- rescue Exception => e
174
- init_failure[e]
175
- end
176
- end
177
-
178
- if block and @obj
179
- begin
180
- block[@obj]
181
- rescue Exception => e
182
- init_failure[e]
183
- end
184
- end
185
-
186
- $0 = (@psname ||= gen_psname(@object))
187
- unless @dumped or @object.respond_to?('__slave_object_failure__')
188
- @object.extend DRbUndumped
189
- end
190
-
191
- @ppid, @pid = Process::ppid, Process::pid
192
-
193
- @r.close
194
- @r2.close
195
- @socket = nil
196
- @uri = nil
197
-
198
- tmpdir, basename = Dir::tmpdir, File::basename(@psname)
199
-
200
- @socket_creation_attempts.times do |attempt|
201
- se = nil
202
- begin
203
- s = File::join(tmpdir, "#{ basename }_#{ attempt }")
204
- u = "drbunix://#{ s }"
205
- DRb::start_service u, @object
206
- @socket = s
207
- @uri = u
208
- trace{ "child - socket <#{ @socket }>" }
209
- trace{ "child - uri <#{ @uri }>" }
210
- break
211
- rescue Errno::EADDRINUSE => se
212
- nil
213
- end
214
- end
215
-
216
- if @socket and @uri
217
- @heartbeat.start
218
-
219
- trap('SIGUSR2') do
220
- # @heartbeat.stop rescue nil
221
- DBb::thread.kill rescue nil
222
- FileUtils::rm_f @socket rescue nil
223
- exit
224
- end
225
-
226
- @w.write @socket
227
- @w.close
228
- DRb::thread.join
229
- else
230
- @w.close
231
- end
232
- rescue Exception => e
233
- trace{ %Q[#{ e.message } (#{ e.class })\n#{ e.backtrace.join "\n" }] }
234
- ensure
235
- status = e.respond_to?('status') ? e.status : 1
236
- exit(status)
237
- end
238
- #
239
- # parent
240
- #
241
- else
242
- detach
243
- @w.close
244
- @w2.close
245
- @socket = @r.read
246
- @r.close
247
-
248
- trace{ "parent - socket <#{ @socket }>" }
249
-
250
- if @at_exit
251
- @at_exit_thread = Thread.new{
252
- Thread.current.abort_on_exception = true
253
-
254
- @r2.read rescue 42
255
-
256
- if @at_exit.respond_to? 'call'
257
- @at_exit.call self
258
- else
259
- send @at_exit.to_s, self
260
- end
261
- }
262
- end
263
-
264
- if @socket and File::exist? @socket
265
- Kernel.at_exit{ FileUtils::rm_f @socket }
266
- @uri = "drbunix://#{ socket }"
267
- trace{ "parent - uri <#{ @uri }>" }
268
- @heartbeat.start
269
- #
270
- # starting drb on localhost avoids dns lookups!
271
- #
272
- DRb::start_service('druby://localhost:0', nil) unless DRb::thread
273
- @object = DRbObject::new nil, @uri
274
- if @object.respond_to? '__slave_object_failure__'
275
- c, m, bt = Marshal.load @object.__slave_object_failure__
276
- (e = c.new(m)).set_backtrace bt
277
- raise e
278
- end
279
- @psname ||= gen_psname(@object)
280
- else
281
- raise "failed to find slave socket <#{ @socket }>"
282
- end
283
- end
284
- #--}}}
285
- end
286
- #
287
- # starts a thread to collect the child status and sets up at_exit handler to
288
- # prevent zombies. the at_exit handler is canceled if the thread is able to
289
- # collect the status
290
- #
291
- def detach
292
- #--{{{
293
- reap = lambda do |cid|
294
- begin
295
- @status = Process::waitpid2(cid).last
296
- rescue Exception => e
297
- m, c, b = e.message, e.class, e.backtrace.join("\n")
298
- warn "#{ m } (#{ c })\n#{ b }" unless e.is_a? Errno::ECHILD
299
- end
300
- end
301
-
302
- Kernel.at_exit do
303
- shutdown rescue nil
304
- reap[@pid] rescue nil
305
- end
306
-
307
- @waiter =
308
- Thread.new do
309
- begin
310
- @status = Process::waitpid2(@pid).last
311
- ensure
312
- reap = lambda{|cid| 'no-op' }
313
- end
314
- end
315
- #--}}}
316
- end
317
- #
318
- # wait for slave to finish. if the keyword 'non_block'=>true is given a
319
- # thread is returned to do the waiting in an async fashion. eg
320
- #
321
- # thread = slave.wait(:non_block=>true){|value| "background <#{ value }>"}
322
- #
323
- def wait opts = {}, &b
324
- #--{{{
325
- b ||= lambda{|exit_status|}
326
- non_block = getopts(opts)['non_block']
327
- non_block ? Thread.new{ b[ @waiter.value ] } : b[ @waiter.value ]
328
- #--}}}
329
- end
330
- alias :wait2 :wait
331
- #
332
- # stops the heartbeat thread and kills the child process - give the key
333
- # 'quiet' to ignore errors shutting down, including having already shutdown
334
- #
335
- def shutdown opts = {}
336
- #--{{{
337
- quiet = getopts(opts)['quiet']
338
- raise "already shutdown" if @shutdown unless quiet
339
- failure = lambda{ raise $! unless quiet }
340
- @heartbeat.stop rescue failure.call
341
- Process::kill('SIGUSR2', @pid) rescue failure.call
342
- @shutdown = true
343
- #--}}}
344
- end
345
- #
346
- # true
347
- #
348
- def shutdown?
349
- #--{{{
350
- @shutdown
351
- #--}}}
352
- end
353
- #
354
- # generate a default name to appear in ps/top
355
- #
356
- def gen_psname obj
357
- #--{{{
358
- "#{ obj.class }_#{ obj.object_id }_#{ Process::ppid }_#{ Process::pid }".downcase.gsub(%r/\s+/,'_')
359
- #--}}}
360
- end
361
- #
362
- # see docs for Slave.default
363
- #
364
- def default key
365
- #--{{{
366
- self.class.default key
367
- #--}}}
368
- end
369
- #
370
- # see docs for Slave.getopts
371
- #
372
- def getopts opts
373
- #--{{{
374
- self.class.getopts opts
375
- #--}}}
376
- end
377
- #
378
- # debugging output - ENV['SLAVE_DEBUG']=1 to enable
379
- #
380
- def trace
381
- #--{{{
382
- STDERR.puts(yield) if @debug and STDERR.tty?
383
- #--}}}
384
- end
385
- #
386
- # the Heartbeat class is essentially wrapper over an IPC channel that sends
387
- # a ping on the channel indicating process health. if either end of the
388
- # channel is detached the ping will fail and an error will be raised. in
389
- # this way it is ensured that Slave objects cannot continue to live without
390
- # their parent being alive.
391
- #
392
- class Heartbeat
393
- #--{{{
394
- def initialize pulse_rate = 4.2, debug = false
395
- #--{{{
396
- @pulse_rate = Float pulse_rate
397
- @debug = debug
398
- @r, @w = IO::pipe
399
- @pid = Process::pid
400
- @ppid = Process::ppid
401
- @cid = nil
402
- @thread = nil
403
- @ppid = nil
404
- @whoami = nil
405
- @beating = nil
406
- @pipe = nil
407
- #--}}}
408
- end
409
- def start
410
- #--{{{
411
- if Process::pid == @pid
412
- @w.close
413
- @pipe = @r
414
- @pipe.fcntl Fcntl::F_SETFD, Fcntl::FD_CLOEXEC
415
- parent_start
416
- else
417
- @r.close
418
- @pipe = @w
419
- child_start
420
- end
421
- @beating = true
422
- #--}}}
423
- end
424
- def parent_start
425
- #--{{{
426
- @whoami = 'parent'
427
- @thread =
428
- Thread::new(Thread::current) do |cur|
429
- begin
430
- loop do
431
- buf = @pipe.gets
432
- trace{ "<#{ @whoami }> <#{ @pid }> gets <#{ buf.inspect }>" }
433
- @cid = Integer buf.strip if @cid.nil? and buf =~ %r/^\s*\d+\s*$/
434
- end
435
- rescue => e
436
- cur.raise e
437
- ensure
438
- @pipe.close rescue nil
439
- end
440
- end
441
- #--}}}
442
- end
443
- def child_start
444
- #--{{{
445
- @whoami = 'child'
446
- @pid = Process::pid
447
- @ppid = Process::ppid
448
- @thread =
449
- Thread::new(Thread::current) do |cur|
450
- begin
451
- loop do
452
- trace{ "<#{ @whoami }> <#{ @pid }> puts <#{ @pid }>" }
453
- @pipe.puts @pid
454
- Process::kill 0, @ppid
455
- sleep @pulse_rate
456
- end
457
- rescue => e
458
- cur.raise e
459
- ensure
460
- @pipe.close rescue nil
461
- end
462
- end
463
- #--}}}
464
- end
465
-
466
- def start
467
- #--{{{
468
- if Process::pid == @pid
469
- @r.close
470
- @pipe = @w
471
- @pipe.fcntl Fcntl::F_SETFD, Fcntl::FD_CLOEXEC
472
- parent_start
473
- else
474
- @w.close
475
- @pipe = @r
476
- child_start
477
- end
478
- @beating = true
479
- #--}}}
480
- end
481
- def parent_start
482
- #--{{{
483
- @whoami = 'parent'
484
- @thread =
485
- Thread::new(Thread::current) do |cur|
486
- begin
487
- sleep
488
- rescue => e
489
- cur.raise e
490
- ensure
491
- @pipe.close rescue nil
492
- end
493
- end
494
- #--}}}
495
- end
496
- def child_start
497
- #--{{{
498
- @whoami = 'child'
499
- @pid = Process::pid
500
- @ppid = Process::ppid
501
- @thread =
502
- Thread::new(Thread::current) do |cur|
503
- begin
504
- trace{ "child reading..." }
505
- @pipe.read
506
- trace{ "child read." }
507
- trace{ "child exiting." }
508
- exit
509
- rescue => e
510
- cur.raise e
511
- ensure
512
- @pipe.close rescue nil
513
- end
514
- end
515
- #--}}}
516
- end
517
- def stop
518
- #--{{{
519
- raise "not beating" unless @beating
520
- @thread.kill
521
- @pipe.close rescue nil
522
- @beating = false
523
- #--}}}
524
- end
525
- def trace
526
- #--{{{
527
- STDERR.puts(yield) if @debug and STDERR.tty?
528
- #--}}}
529
- end
530
- #--}}}
531
- end # class Heartbeat
532
- #--}}}
533
- end # class Slave