slave 0.0.0 → 0.0.1

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/doc/created.rid CHANGED
@@ -1 +1 @@
1
- Fri Nov 12 12:27:08 MST 2004
1
+ Thu Jun 08 15:55:38 MDT 2006
data/doc/dot/f_1.dot CHANGED
@@ -3,12 +3,27 @@ digraph TopLevel {
3
3
  bgcolor = lightcyan1
4
4
  fontname = Arial
5
5
  fontsize = 8
6
- label = "VERSION"
6
+ label = "lib/slave.rb"
7
7
  node [
8
8
  fontname = Arial,
9
9
  fontsize = 8,
10
10
  color = black
11
11
  ]
12
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
+
13
28
  }
14
29
 
data/doc/dot/f_1.jpg CHANGED
Binary file
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Fri Nov 12 12:27:06 MST 2004</td>
59
+ <td>Thu Jun 08 14:54:53 MDT 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Fri Nov 12 12:24:23 MST 2004</td>
59
+ <td>Thu Jun 08 15:54:54 MDT 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -70,7 +70,7 @@
70
70
  <map name="map">
71
71
  <area shape="RECT" coords="28,88,99,40" href="../../classes/Slave.html" alt="Slave">
72
72
  </map>
73
- <img src="../../dot/f_2.jpg" usemap="#map" border=0 alt="TopLevel">
73
+ <img src="../../dot/f_1.jpg" usemap="#map" border=0 alt="TopLevel">
74
74
  </div>
75
75
 
76
76
 
@@ -82,6 +82,7 @@
82
82
  fileutils&nbsp;&nbsp;
83
83
  tmpdir&nbsp;&nbsp;
84
84
  tempfile&nbsp;&nbsp;
85
+ fcntl&nbsp;&nbsp;
85
86
  </div>
86
87
  </div>
87
88
 
@@ -21,7 +21,6 @@
21
21
  <h1 class="section-bar">Files</h1>
22
22
  <div id="index-entries">
23
23
  <a href="files/README.html">README</a><br />
24
- <a href="files/VERSION.html">VERSION</a><br />
25
24
  <a href="files/lib/slave_rb.html">lib/slave.rb</a><br />
26
25
  </div>
27
26
  </div>
@@ -20,19 +20,25 @@
20
20
  <div id="index">
21
21
  <h1 class="section-bar">Methods</h1>
22
22
  <div id="index-entries">
23
- <a href="classes/Slave/Heartbeat.html#M000011">child_start (Slave::Heartbeat)</a><br />
23
+ <a href="classes/Slave/Heartbeat.html#M000017">child_start (Slave::Heartbeat)</a><br />
24
+ <a href="classes/Slave/Heartbeat.html#M000014">child_start (Slave::Heartbeat)</a><br />
25
+ <a href="classes/Slave.html#M000004">detach (Slave)</a><br />
24
26
  <a href="classes/Slave.html#M000002">fork (Slave)</a><br />
25
- <a href="classes/Slave.html#M000005">gen_psname (Slave)</a><br />
26
- <a href="classes/Slave.html#M000006">getval (Slave)</a><br />
27
+ <a href="classes/Slave.html#M000008">gen_psname (Slave)</a><br />
27
28
  <a href="classes/Slave.html#M000001">getval (Slave)</a><br />
28
- <a href="classes/Slave/Heartbeat.html#M000008">new (Slave::Heartbeat)</a><br />
29
+ <a href="classes/Slave.html#M000009">getval (Slave)</a><br />
30
+ <a href="classes/Slave/Heartbeat.html#M000011">new (Slave::Heartbeat)</a><br />
29
31
  <a href="classes/Slave.html#M000003">new (Slave)</a><br />
30
- <a href="classes/Slave/Heartbeat.html#M000010">parent_start (Slave::Heartbeat)</a><br />
31
- <a href="classes/Slave.html#M000004">shutdown (Slave)</a><br />
32
- <a href="classes/Slave/Heartbeat.html#M000009">start (Slave::Heartbeat)</a><br />
33
- <a href="classes/Slave/Heartbeat.html#M000012">stop (Slave::Heartbeat)</a><br />
34
- <a href="classes/Slave.html#M000007">trace (Slave)</a><br />
35
- <a href="classes/Slave/Heartbeat.html#M000013">trace (Slave::Heartbeat)</a><br />
32
+ <a href="classes/Slave/Heartbeat.html#M000016">parent_start (Slave::Heartbeat)</a><br />
33
+ <a href="classes/Slave/Heartbeat.html#M000013">parent_start (Slave::Heartbeat)</a><br />
34
+ <a href="classes/Slave.html#M000007">shutdown (Slave)</a><br />
35
+ <a href="classes/Slave/Heartbeat.html#M000012">start (Slave::Heartbeat)</a><br />
36
+ <a href="classes/Slave/Heartbeat.html#M000015">start (Slave::Heartbeat)</a><br />
37
+ <a href="classes/Slave/Heartbeat.html#M000018">stop (Slave::Heartbeat)</a><br />
38
+ <a href="classes/Slave.html#M000010">trace (Slave)</a><br />
39
+ <a href="classes/Slave/Heartbeat.html#M000019">trace (Slave::Heartbeat)</a><br />
40
+ <a href="classes/Slave.html#M000005">wait (Slave)</a><br />
41
+ <a href="classes/Slave.html#M000006">wait2 (Slave)</a><br />
36
42
  </div>
37
43
  </div>
38
44
  </body>
@@ -2,13 +2,43 @@ require 'drb/drb'
2
2
  require 'fileutils'
3
3
  require 'tmpdir'
4
4
  require 'tempfile'
5
+ require 'fcntl'
6
+
7
+ class Pipe
8
+ attr 'r'
9
+ attr 'w'
10
+ def initialize
11
+ @r, @w = IO.pipe
12
+ end
13
+ end
14
+
15
+ class Lifeline
16
+ class Error < ::StandardError
17
+ def initialize
18
+ end
19
+ end
20
+ def initialize
21
+ @pid = Process.pid
22
+ @pipe = Pipe.new
23
+ end
24
+ def throw
25
+ end
26
+ def cling
27
+ @w.close
28
+ Thread.new(@r, Thread.current) do |r, t|
29
+ r.read rescue t.raise($!)
30
+ end
31
+ end
32
+ end
5
33
 
6
34
  #
7
35
  # the Slave class encapsulates the work of setting up a drb server in another
8
- # process.
36
+ # process.
9
37
  #
10
38
  class Slave
11
39
  #--{{{
40
+ VERSION = '0.0.1'
41
+
12
42
  DEFAULT_SOCKET_CREATION_ATTEMPTS =
13
43
  Integer(ENV['SLAVE_SOCKET_CREATION_ATTEMPTS'] || 42)
14
44
 
@@ -68,9 +98,11 @@ require 'tempfile'
68
98
  attr :socket
69
99
  attr :debug
70
100
 
101
+ #
71
102
  # 'obj' can be any object and 'opts' may contain the keys
72
103
  # 'socket_creation_attempts', 'pulse_rate', 'psname', or 'debug'
73
- def initialize obj, opts = {}
104
+ #
105
+ def initialize obj = nil, opts = {}, &block
74
106
  #--{{{
75
107
  @obj = obj
76
108
 
@@ -85,7 +117,7 @@ require 'tempfile'
85
117
 
86
118
  @shutdown = false
87
119
 
88
- @heartbeat = Heartbeat::new @pulse_rate, @debug
120
+ # @heartbeat = Heartbeat::new @pulse_rate, @debug
89
121
  @r, @w = IO::pipe
90
122
  #
91
123
  # child
@@ -119,15 +151,16 @@ require 'tempfile'
119
151
  end
120
152
 
121
153
  if @socket and @uri
122
- @heartbeat.start
154
+ # @heartbeat.start
123
155
  @w.write @socket
124
156
  @w.close
125
157
  trap('SIGUSR2') do
126
- @heartbeat.stop rescue nil
158
+ # @heartbeat.stop rescue nil
127
159
  DBb::thread.kill rescue nil
128
160
  FileUtils::rm_f @socket rescue nil
129
161
  exit!
130
162
  end
163
+ block[obj] if block
131
164
  DRb::thread.join
132
165
  else
133
166
  @w.close
@@ -152,7 +185,7 @@ require 'tempfile'
152
185
  at_exit{ FileUtils::rm_f @socket }
153
186
  @uri = "drbunix://#{ socket }"
154
187
  trace{ "parent - uri <#{ @uri }>" }
155
- @heartbeat.start
188
+ # @heartbeat.start
156
189
  #
157
190
  # starting drb on localhost avoids dns lookups!
158
191
  #
@@ -164,30 +197,38 @@ require 'tempfile'
164
197
  end
165
198
  #--}}}
166
199
  end
200
+ #
167
201
  # stops the heartbeat thread and kills the child process
202
+ #
168
203
  def shutdown
169
204
  #--{{{
170
205
  raise "already shutdown" if @shutdown
171
- @heartbeat.stop rescue nil
206
+ # @heartbeat.stop rescue nil
172
207
  Process::kill('SIGUSR2', @pid) rescue nil
173
208
  Process::kill('SIGTERM', @pid) rescue nil
174
209
  FileUtils::rm_f @socket
175
210
  @shutdown = true
176
211
  #--}}}
177
212
  end
213
+ #
178
214
  # generate a default name to appear in ps/top
215
+ #
179
216
  def gen_psname obj
180
217
  #--{{{
181
218
  "#{ obj.class }_slave_of_#{ Process::pid }".downcase
182
219
  #--}}}
183
220
  end
184
- # see docs for class.getval
221
+ #
222
+ # see docs for Slave.getval
223
+ #
185
224
  def getval key, opts = {}
186
225
  #--{{{
187
226
  self.class.getval key
188
227
  #--}}}
189
228
  end
229
+ #
190
230
  # debugging output
231
+ #
191
232
  def trace
192
233
  #--{{{
193
234
  STDERR.puts(yield) if @debug and STDERR.tty?
@@ -223,6 +264,7 @@ require 'tempfile'
223
264
  if Process::pid == @pid
224
265
  @w.close
225
266
  @pipe = @r
267
+ @pipe.fcntl Fcntl::F_SETFD, Fcntl::FD_CLOEXEC
226
268
  parent_start
227
269
  else
228
270
  @r.close
@@ -273,6 +315,58 @@ require 'tempfile'
273
315
  end
274
316
  #--}}}
275
317
  end
318
+
319
+ def start
320
+ #--{{{
321
+ if Process::pid == @pid
322
+ @r.close
323
+ @pipe = @w
324
+ @pipe.fcntl Fcntl::F_SETFD, Fcntl::FD_CLOEXEC
325
+ parent_start
326
+ else
327
+ @w.close
328
+ @pipe = @r
329
+ child_start
330
+ end
331
+ @beating = true
332
+ #--}}}
333
+ end
334
+ def parent_start
335
+ #--{{{
336
+ @whoami = 'parent'
337
+ @thread =
338
+ Thread::new(Thread::current) do |cur|
339
+ begin
340
+ sleep
341
+ rescue => e
342
+ cur.raise e
343
+ ensure
344
+ @pipe.close rescue nil
345
+ end
346
+ end
347
+ #--}}}
348
+ end
349
+ def child_start
350
+ #--{{{
351
+ @whoami = 'child'
352
+ @pid = Process::pid
353
+ @ppid = Process::ppid
354
+ @thread =
355
+ Thread::new(Thread::current) do |cur|
356
+ begin
357
+ trace{ "child reading..." }
358
+ @pipe.read
359
+ trace{ "child read." }
360
+ trace{ "child exiting." }
361
+ exit!
362
+ rescue => e
363
+ cur.raise e
364
+ ensure
365
+ @pipe.close rescue nil
366
+ end
367
+ end
368
+ #--}}}
369
+ end
276
370
  def stop
277
371
  #--{{{
278
372
  raise "not beating" unless @beating
data/lib/slave.rb CHANGED
@@ -2,13 +2,35 @@ require 'drb/drb'
2
2
  require 'fileutils'
3
3
  require 'tmpdir'
4
4
  require 'tempfile'
5
+ require 'fcntl'
5
6
 
6
7
  #
7
8
  # the Slave class encapsulates the work of setting up a drb server in another
8
- # process.
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 Server.new
24
+ # server = slave.object
25
+ #
26
+ # p server.add_two(40) #=> 42
9
27
  #
10
28
  class Slave
11
29
  #--{{{
30
+ VERSION = '0.0.1'
31
+ #
32
+ # config
33
+ #
12
34
  DEFAULT_SOCKET_CREATION_ATTEMPTS =
13
35
  Integer(ENV['SLAVE_SOCKET_CREATION_ATTEMPTS'] || 42)
14
36
 
@@ -21,7 +43,9 @@ require 'tempfile'
21
43
  @socket_creation_attempts = DEFAULT_SOCKET_CREATION_ATTEMPTS
22
44
  @pulse_rate = DEFAULT_PULSE_RATE
23
45
  @debug = DEFAULT_DEBUG
24
-
46
+ #
47
+ # class methods
48
+ #
25
49
  class << self
26
50
  #--{{{
27
51
  # defineds how many attempts will be made to create a temporary unix domain
@@ -67,10 +91,13 @@ require 'tempfile'
67
91
  attr :pulse_rate
68
92
  attr :socket
69
93
  attr :debug
94
+ attr :status
70
95
 
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 = {}
96
+ #
97
+ # 'obj' can be any object and 'opts' may contain the keys
98
+ # 'socket_creation_attempts', 'pulse_rate', 'psname', or 'debug'
99
+ #
100
+ def initialize obj = nil, opts = {}, &block
74
101
  #--{{{
75
102
  @obj = obj
76
103
 
@@ -84,6 +111,7 @@ require 'tempfile'
84
111
  trace{ "psname <#{ @psname }>" }
85
112
 
86
113
  @shutdown = false
114
+ @waiter = @status = nil
87
115
 
88
116
  @heartbeat = Heartbeat::new @pulse_rate, @debug
89
117
  @r, @w = IO::pipe
@@ -91,6 +119,7 @@ require 'tempfile'
91
119
  # child
92
120
  #
93
121
  unless((@pid = Slave::fork))
122
+ e = nil
94
123
  begin
95
124
  $0 = @psname
96
125
  @pid = Process::pid
@@ -123,25 +152,28 @@ require 'tempfile'
123
152
  @w.write @socket
124
153
  @w.close
125
154
  trap('SIGUSR2') do
126
- @heartbeat.stop rescue nil
155
+ # @heartbeat.stop rescue nil
127
156
  DBb::thread.kill rescue nil
128
157
  FileUtils::rm_f @socket rescue nil
129
158
  exit!
130
159
  end
160
+ block[obj] if block
131
161
  DRb::thread.join
132
162
  else
133
163
  @w.close
134
164
  end
135
- rescue => e
165
+ rescue Exception => e
136
166
  trace{ %Q[#{ e.message } (#{ e.class })\n#{ e.backtrace.join "\n" }] }
137
167
  ensure
138
- exit!
168
+ status = e.respond_to?('status') ? e.status : 1
169
+ exit!(status)
139
170
  end
140
171
  #
141
172
  # parent
142
173
  #
143
174
  else
144
- Process::detach @pid
175
+ #Process::detach @pid
176
+ detach
145
177
  @w.close
146
178
  @socket = @r.read
147
179
  @r.close
@@ -164,7 +196,27 @@ require 'tempfile'
164
196
  end
165
197
  #--}}}
166
198
  end
167
- # stops the heartbeat thread and kills the child process
199
+ #
200
+ # starts a thread to attempt collecting the child status
201
+ #
202
+ def detach
203
+ #--{{{
204
+ @waiter =
205
+ Thread.new{ @status = Process::waitpid2(@pid).last }
206
+ #--}}}
207
+ end
208
+ #
209
+ # wait for slave to finish
210
+ #
211
+ def wait
212
+ #--{{{
213
+ @waiter.value
214
+ #--}}}
215
+ end
216
+ alias :wait2 :wait
217
+ #
218
+ # stops the heartbeat thread and kills the child process
219
+ #
168
220
  def shutdown
169
221
  #--{{{
170
222
  raise "already shutdown" if @shutdown
@@ -175,19 +227,25 @@ require 'tempfile'
175
227
  @shutdown = true
176
228
  #--}}}
177
229
  end
178
- # generate a default name to appear in ps/top
230
+ #
231
+ # generate a default name to appear in ps/top
232
+ #
179
233
  def gen_psname obj
180
234
  #--{{{
181
- "#{ obj.class }_slave_of_#{ Process::pid }".downcase
235
+ "#{ obj.class }_slave_of_#{ Process::pid }".downcase.gsub(%r/\s*/,'_')
182
236
  #--}}}
183
237
  end
184
- # see docs for class.getval
238
+ #
239
+ # see docs for Slave.getval
240
+ #
185
241
  def getval key, opts = {}
186
242
  #--{{{
187
243
  self.class.getval key
188
244
  #--}}}
189
245
  end
190
- # debugging output
246
+ #
247
+ # debugging output - ENV['SLAVE_DEBUG']=1 to enable
248
+ #
191
249
  def trace
192
250
  #--{{{
193
251
  STDERR.puts(yield) if @debug and STDERR.tty?
@@ -195,11 +253,11 @@ require 'tempfile'
195
253
  end
196
254
 
197
255
  #
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.
256
+ # the Heartbeat class is essentially wrapper over an IPC channel that sends
257
+ # a ping on the channel indicating process health. if either end of the
258
+ # channel is detached the ping will fail and an error will be raised. in
259
+ # this way it is ensured that Slave objects cannot continue to live without
260
+ # their parent being alive.
203
261
  #
204
262
  class Heartbeat
205
263
  #--{{{
@@ -223,6 +281,7 @@ require 'tempfile'
223
281
  if Process::pid == @pid
224
282
  @w.close
225
283
  @pipe = @r
284
+ @pipe.fcntl Fcntl::F_SETFD, Fcntl::FD_CLOEXEC
226
285
  parent_start
227
286
  else
228
287
  @r.close
@@ -273,6 +332,58 @@ require 'tempfile'
273
332
  end
274
333
  #--}}}
275
334
  end
335
+
336
+ def start
337
+ #--{{{
338
+ if Process::pid == @pid
339
+ @r.close
340
+ @pipe = @w
341
+ @pipe.fcntl Fcntl::F_SETFD, Fcntl::FD_CLOEXEC
342
+ parent_start
343
+ else
344
+ @w.close
345
+ @pipe = @r
346
+ child_start
347
+ end
348
+ @beating = true
349
+ #--}}}
350
+ end
351
+ def parent_start
352
+ #--{{{
353
+ @whoami = 'parent'
354
+ @thread =
355
+ Thread::new(Thread::current) do |cur|
356
+ begin
357
+ sleep
358
+ rescue => e
359
+ cur.raise e
360
+ ensure
361
+ @pipe.close rescue nil
362
+ end
363
+ end
364
+ #--}}}
365
+ end
366
+ def child_start
367
+ #--{{{
368
+ @whoami = 'child'
369
+ @pid = Process::pid
370
+ @ppid = Process::ppid
371
+ @thread =
372
+ Thread::new(Thread::current) do |cur|
373
+ begin
374
+ trace{ "child reading..." }
375
+ @pipe.read
376
+ trace{ "child read." }
377
+ trace{ "child exiting." }
378
+ exit!
379
+ rescue => e
380
+ cur.raise e
381
+ ensure
382
+ @pipe.close rescue nil
383
+ end
384
+ end
385
+ #--}}}
386
+ end
276
387
  def stop
277
388
  #--{{{
278
389
  raise "not beating" unless @beating
data/rdoc.cmd CHANGED
@@ -1 +1 @@
1
- rdoc -a -d -F -S -m README -I jpg -N README VERSION lib/slave.rb
1
+ rdoc -a -d -F -S -m README -I jpg -N README lib/slave.rb
data/sample/a.rb CHANGED
@@ -1,102 +1,19 @@
1
- $:.unshift 'lib'
2
- $:.unshift '../lib'
3
-
4
1
  require 'slave'
5
2
 
6
- class Incrementer
7
- attr :decrementer, true
8
- def increment n
9
- n + 1
10
- end
11
- def decrement n
12
- @decrementer.decrement n
13
- end
14
- end
15
-
16
- class Decrementer
17
- attr :incrementer, true
18
- def increment n
19
- @incrementer.increment n
20
- end
21
- def decrement n
22
- n - 1
23
- end
24
- end
25
-
26
-
27
- #
28
- #
29
- # here we set up a triangle of communicating processes
30
3
  #
31
- # incrementer------decrementer
32
- # \ /
33
- # \ /
34
- # \ /
35
- # \ /
36
- # \ /
37
- # parent
4
+ # simple usage is simply to stand up a server object as a slave. you do not
5
+ # need to wait for the server, join it, etc. it will die when the parent
6
+ # process dies - even under 'kill -9' conditions
38
7
  #
39
- #
40
-
41
- incrementer_slave = Slave::new Incrementer::new
42
- incrementer = incrementer_slave.object
43
8
 
44
- puts '---'
45
- puts 'incrementer :'
46
- puts " sockect : #{ incrementer_slave.socket.inspect }"
47
- puts " uri : #{ incrementer_slave.uri.inspect }"
48
- puts " pid : #{ incrementer_slave.pid.inspect }"
49
- puts
50
-
51
- decrementer_slave = Slave::new Decrementer::new
52
- decrementer = decrementer_slave.object
53
-
54
- puts '---'
55
- puts 'decrementer :'
56
- puts " sockect : #{ decrementer_slave.socket.inspect }"
57
- puts " uri : #{ decrementer_slave.uri.inspect }"
58
- puts " pid : #{ decrementer_slave.pid.inspect }"
59
- puts
60
-
61
- #
62
- # connect incrementer and decrementer
63
- #
64
-
65
- incrementer.decrementer = decrementer
66
- decrementer.incrementer = incrementer
67
-
68
- #
69
- # now we can call methods on each drb object, and they can also call methods on
70
- # each other
71
- #
72
-
73
- n = 0
74
-
75
- n = incrementer.increment n
76
- p n #=> 1
77
-
78
- n = decrementer.decrement n
79
- p n #=> 0
80
-
81
- n = decrementer.increment n
82
- p n #=> 1
83
-
84
- n = incrementer.decrement n
85
- p n #=> 0
86
-
87
- #
88
- # we can explicitly shutdown certain slaves
89
- #
9
+ class Server
10
+ def add_two n
11
+ n + 2
12
+ end
13
+ end
90
14
 
91
- incrementer_slave.shutdown
92
- n = decrementer.decrement 43
93
- p n #=> 42
15
+ slave = Slave.new Server.new
16
+ server = slave.object
94
17
 
95
- #
96
- # Slaves cannot live beyond their parent so we simply exit and all living slaves
97
- # will eventually die. how long it takes to die is determined by the
98
- # pulse_rate - the next time the Heartbeat trys to ping the parent the Slave
99
- # will die.
100
- #
18
+ p server.add_two(40) #=> 42
101
19
 
102
- exit
data/sample/b.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'slave'
2
+
3
+ #
4
+ # you can wait for a slave to finish if required
5
+ #
6
+ slave = Slave.new{ p $$; sleep 2; exit 42 }
7
+
8
+ p slave.wait