slave 1.2.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -38,6 +38,9 @@ URIS
38
38
  http://codeforpeople.com/lib/ruby/slave
39
39
 
40
40
  HISTORY
41
+ 1.3.0:
42
+ - fixes for 1.9.2 (undef object_id)
43
+ - fixes for osx (too long socket names)
41
44
 
42
45
  1.2.1:
43
46
  - jruby/ThreadSafe patches from skaar and ez. using slave.rb with jruby,
@@ -105,18 +108,19 @@ HISTORY
105
108
 
106
109
  SAMPLES
107
110
 
111
+
108
112
  <========< samples/a.rb >========>
109
113
 
110
114
  ~ > cat samples/a.rb
111
115
 
112
116
  require 'slave'
113
- #
117
+
114
118
  # simple usage is simply to stand up a server object as a slave. you do not
115
119
  # need to wait for the server, join it, etc. it will die when the parent
116
120
  # process dies - even under 'kill -9' conditions
117
121
  #
118
122
  class Server
119
- def add_two n
123
+ def add_two(n)
120
124
  n + 2
121
125
  end
122
126
  end
@@ -163,9 +167,9 @@ SAMPLES
163
167
  ~ > ruby samples/b.rb
164
168
 
165
169
  :postgresql
166
- samples/b.rb:22: undefined method `typo' for #<Server:0xb756ed18> (NoMethodError)
167
- from ./lib/slave.rb:369:in `[]'
168
- from ./lib/slave.rb:369:in `initialize'
170
+ samples/b.rb:22: undefined method `typo' for #<Server:0x10030aa60> (NoMethodError)
171
+ from ./lib/slave.rb:367:in `[]'
172
+ from ./lib/slave.rb:367:in `initialize'
169
173
  from samples/b.rb:22:in `new'
170
174
  from samples/b.rb:22
171
175
 
@@ -198,11 +202,11 @@ SAMPLES
198
202
 
199
203
  ~ > ruby samples/c.rb
200
204
 
201
- 7971
202
- 7972
205
+ 48871
206
+ 48872
203
207
  samples/c.rb:21: undefined local variable or method `fubar' for main:Object (NameError)
204
- from ./lib/slave.rb:361:in `call'
205
- from ./lib/slave.rb:361:in `initialize'
208
+ from ./lib/slave.rb:359:in `call'
209
+ from ./lib/slave.rb:359:in `initialize'
206
210
  from samples/c.rb:21:in `new'
207
211
  from samples/c.rb:21
208
212
 
@@ -297,6 +301,7 @@ SAMPLES
297
301
 
298
302
  ~ > ruby samples/g.rb
299
303
 
300
- {"that"=>7990, "this"=>7989}
301
- {"that"=>[7991, Fri, Apr 27 2007 16:38:30 -0600], "this"=>[7989, Fri, Apr 27 2007 16:38:28 -0600]}
304
+ {"that"=>48890, "this"=>48889}
305
+ {"that"=>[48891, Mon Oct 10 08:21:47 -0600 2011], "this"=>[48889, Mon Oct 10 08:21:45 -0600 2011]}
306
+
302
307
 
data/Rakefile CHANGED
@@ -187,6 +187,10 @@ task :readme do
187
187
  samples << Util.indent(`#{ cmd } 2>&1`, 4) << "\n"
188
188
  end
189
189
 
190
+ @lib = lib
191
+ @version = version
192
+ @samples = samples
193
+
190
194
  template =
191
195
  if test(?e, 'readme.erb')
192
196
  Template{ IO.read('readme.erb') }
@@ -304,19 +308,21 @@ BEGIN {
304
308
  #
305
309
  module Util
306
310
  def indent(s, n = 2)
307
- s = unindent(s)
308
- ws = ' ' * n
309
- s.gsub(%r/^/, ws)
311
+ margin = ' ' * Integer(n)
312
+ unindent(s).gsub!(%r/^/, margin)
313
+ s
310
314
  end
311
315
 
312
316
  def unindent(s)
313
- indent = nil
317
+ margin = nil
314
318
  s.each_line do |line|
315
- next if line =~ %r/^\s*$/
316
- indent = line[%r/^\s*/] and break
319
+ next if line =~ %r/^\s*$/
320
+ margin = line[%r/^\s*/] and break
321
+ end
322
+ s.gsub!(%r/^#{ margin }/, "") if margin
323
+ s
317
324
  end
318
- indent ? s.gsub(%r/^#{ indent }/, "") : s
319
- end
325
+
320
326
  extend self
321
327
  end
322
328
 
@@ -8,7 +8,7 @@ require 'sync'
8
8
 
9
9
  # TODO - lifeline need close-on-exec set in it!
10
10
 
11
- #
11
+
12
12
  # the Slave class encapsulates the work of setting up a drb server in another
13
13
  # process running on localhost via unix domain sockets. the slave process is
14
14
  # attached to it's parent via a LifeLine which is designed such that the slave
@@ -39,49 +39,48 @@ require 'sync'
39
39
  # of the two 'b' is preferred.
40
40
  #
41
41
  class Slave
42
- #--{{{
43
- VERSION = '1.2.2'
42
+ VERSION = '1.3.0'
44
43
  def self.version() VERSION end
45
- #
44
+
46
45
  # env config
47
46
  #
48
47
  DEFAULT_SOCKET_CREATION_ATTEMPTS = Integer(ENV['SLAVE_SOCKET_CREATION_ATTEMPTS'] || 42)
49
48
  DEFAULT_DEBUG = (ENV['SLAVE_DEBUG'] ? true : false)
50
49
  DEFAULT_THREADSAFE = (ENV['SLAVE_THREADSAFE'] ? true : false)
51
- #
50
+
52
51
  # class initialization
53
52
  #
54
53
  @socket_creation_attempts = DEFAULT_SOCKET_CREATION_ATTEMPTS
55
54
  @debug = DEFAULT_DEBUG
56
55
  @threadsafe = DEFAULT_THREADSAFE
57
- #
56
+
58
57
  # class methods
59
58
  #
60
59
  class << self
61
- #--{{{
62
- # defineds how many attempts will be made to create a temporary unix domain
63
- # socket
60
+ # defineds how many attempts will be made to create a temporary unix domain
61
+ # socket
62
+ #
64
63
  attr :socket_creation_attempts, true
65
64
 
66
- # if this is true and you are running from a terminal information is printed
67
- # on STDERR
65
+ # if this is true and you are running from a terminal information is printed
66
+ # on STDERR
67
+ #
68
68
  attr :debug, true
69
69
 
70
- # if this is true all slave objects will be wrapped such that any call
71
- # to the object is threadsafe. if you do not use this you must ensure
72
- # that your objects are threadsafe __yourself__ as this is required of
73
- # any object acting as a drb server
70
+ # if this is true all slave objects will be wrapped such that any call
71
+ # to the object is threadsafe. if you do not use this you must ensure
72
+ # that your objects are threadsafe __yourself__ as this is required of
73
+ # any object acting as a drb server
74
+ #
74
75
  attr :threadsafe, true
75
76
 
76
- # get a default value
77
- def default key
78
- #--{{{
77
+ # get a default value
78
+ #
79
+ def default(key)
79
80
  send key
80
- #--}}}
81
81
  end
82
82
 
83
- def getopts opts
84
- #--{{{
83
+ def getopts(opts)
85
84
  raise ArgumentError, opts.class unless
86
85
  opts.respond_to?('has_key?') and opts.respond_to?('[]')
87
86
 
@@ -91,12 +90,11 @@ require 'sync'
91
90
  key = keys.detect{|k| opts.has_key? k } and break opts[key]
92
91
  defval
93
92
  end
94
- #--}}}
95
93
  end
96
94
 
97
- # just fork with out silly warnings
98
- def fork &b
99
- #--{{{
95
+ # just fork with out silly warnings
96
+ #
97
+ def fork(&b)
100
98
  v = $VERBOSE
101
99
  begin
102
100
  $VERBOSE = nil
@@ -104,16 +102,14 @@ require 'sync'
104
102
  ensure
105
103
  $VERBOSE = v
106
104
  end
107
- #--}}}
108
105
  end
109
- #--}}}
110
106
  end
111
107
 
112
- #
108
+
113
109
  # helper classes
114
110
  #
115
111
 
116
- #
112
+
117
113
  # ThreadSafe is a delegate wrapper class used for implementing gross thread
118
114
  # safety around existing objects. when an object is wrapped with this class
119
115
  # as
@@ -125,37 +121,42 @@ require 'sync'
125
121
  # 'threadsafe'/:threadsafe keyword to Slave#initialize
126
122
  #
127
123
  class ThreadSafe
128
- #--{{{
129
- instance_methods.each{|m| undef_method m.to_sym unless m[%r/__/]}
130
- def initialize object
124
+ instance_methods.each{|m| undef_method m.to_sym unless m =~ %r/\A__|\Aobject_id\Z/}
125
+
126
+ def initialize(object)
131
127
  @object = object
132
128
  @sync = Sync.new
133
129
  end
130
+
134
131
  def ex
135
132
  @sync.synchronize{ yield }
136
133
  end
134
+
137
135
  def method_missing m, *a, &b
138
136
  ex{ @object.send m, *a, &b }
139
137
  end
138
+
140
139
  def respond_to? *a, &b
141
140
  ex{ @object.respond_to? *a, &b }
142
141
  end
142
+
143
143
  def inspect
144
144
  ex{ @object.inspect }
145
145
  end
146
+
146
147
  def class
147
148
  ex{ @object.class }
148
149
  end
149
- #--}}}
150
150
  end
151
- #
151
+
152
+
152
153
  # a simple thread safe hash used to map object_id to a set of file
153
154
  # descriptors in the LifeLine class. see LifeLine::FDS
154
155
  #
155
156
  class ThreadSafeHash < Hash
156
157
  def self.new(*a, &b) ThreadSafe.new(super) end
157
158
  end
158
- #
159
+
159
160
  # the LifeLine class is used to communitacte between child and parent
160
161
  # processes and to prevent child processes from ever becoming zombies or
161
162
  # otherwise abandoned by their parents. the basic concept is that a socket
@@ -165,11 +166,10 @@ require 'sync'
165
166
  # previous Slave versions.
166
167
  #
167
168
  class LifeLine
168
- #--{{{
169
169
  FDS = ThreadSafeHash.new
170
170
 
171
171
  def initialize
172
- @pair = Socket.pair Socket::AF_UNIX, Socket::SOCK_STREAM, 0
172
+ @pair = Socket.pair(Socket::AF_UNIX, Socket::SOCK_STREAM, 0)
173
173
  @owner = Process.pid
174
174
  @pid = nil
175
175
  @socket = nil
@@ -185,7 +185,7 @@ require 'sync'
185
185
  Process.pid == @owner
186
186
  end
187
187
 
188
- def throw *ignored
188
+ def throw(*ignored)
189
189
  raise unless owner?
190
190
  @pair[-1].close
191
191
  @pair[-1] = nil
@@ -194,7 +194,7 @@ require 'sync'
194
194
  @socket.sync = true
195
195
  end
196
196
 
197
- def catch *ignored
197
+ def catch(*ignored)
198
198
  raise if owner?
199
199
  @pair[0].close
200
200
  @pair[0] = nil
@@ -264,10 +264,9 @@ require 'sync'
264
264
  def cling &b
265
265
  on_cut{ begin; b.call if b; ensure; Kernel.exit; end }.join
266
266
  end
267
- #--}}}
268
267
  end
269
268
 
270
- #
269
+
271
270
  # attrs
272
271
  #
273
272
  attr :obj
@@ -283,7 +282,7 @@ require 'sync'
283
282
  attr :ppid
284
283
  attr :uri
285
284
  attr :socket
286
- #
285
+
287
286
  # sets up a child process serving any object as a DRb server running locally
288
287
  # on unix domain sockets. the child process has a LifeLine established
289
288
  # between it and the parent, making it impossible for the child to outlive
@@ -316,7 +315,6 @@ require 'sync'
316
315
  # threadsafe : wrap the slave object with ThreadSafe to implement gross thread safety
317
316
  #
318
317
  def initialize opts = {}, &block
319
- #--{{{
320
318
  getopt = getopts opts
321
319
 
322
320
  @obj = getopt['object']
@@ -345,7 +343,7 @@ require 'sync'
345
343
  @object = o
346
344
  end
347
345
 
348
- #
346
+
349
347
  # child
350
348
  #
351
349
  unless((@pid = Slave::fork))
@@ -382,16 +380,23 @@ require 'sync'
382
380
  @object = ThreadSafe.new @object
383
381
  end
384
382
 
385
- @ppid, @pid = Process::ppid, Process::pid
383
+ @ppid, @pid = Process.ppid, Process.pid
386
384
  @socket = nil
387
385
  @uri = nil
388
386
 
389
- tmpdir, basename = Dir::tmpdir, File::basename(@psname)
387
+ tmpdir = test(?d, '/tmp') ? '/tmp' : Dir.tmpdir
388
+ basename = File.basename(@psname)
390
389
 
391
390
  @socket_creation_attempts.times do |attempt|
392
391
  se = nil
393
392
  begin
394
- s = File::join(tmpdir, "#{ basename }_#{ attempt }_#{ rand }")
393
+ s =
394
+ if attempt > 0
395
+ File.join(tmpdir, "#{ basename }_#{ attempt }")
396
+ else
397
+ File.join(tmpdir, "#{ basename }")
398
+ end
399
+ raise("#{ s } is too long!") if s.size > 103
395
400
  u = "drbunix://#{ s }"
396
401
  DRb::start_service u, @object
397
402
  @socket = s
@@ -411,7 +416,7 @@ require 'sync'
411
416
  exit
412
417
  end
413
418
 
414
- @lifeline.puts @socket
419
+ @lifeline.puts(@socket)
415
420
  @lifeline.cling
416
421
  else
417
422
  @lifeline.release
@@ -424,7 +429,7 @@ require 'sync'
424
429
  status = e.respond_to?('status') ? e.status : 1
425
430
  exit(status)
426
431
  end
427
- #
432
+
428
433
  # parent
429
434
  #
430
435
  else
@@ -446,10 +451,10 @@ require 'sync'
446
451
  Kernel.at_exit{ FileUtils::rm_f @socket }
447
452
  @uri = "drbunix://#{ socket }"
448
453
  trace{ "parent - uri <#{ @uri }>" }
449
- #
454
+
450
455
  # starting drb on localhost avoids dns lookups!
451
456
  #
452
- DRb::start_service('druby://localhost:0', nil) unless DRb::thread
457
+ DRb::start_service('druby://0.0.0.0:0', nil) unless DRb::thread
453
458
  @object = DRbObject::new nil, @uri
454
459
  if @object.respond_to? '__slave_object_failure__'
455
460
  c, m, bt = Marshal.load @object.__slave_object_failure__
@@ -462,15 +467,13 @@ require 'sync'
462
467
  raise "failed to find slave socket <#{ @socket }>"
463
468
  end
464
469
  end
465
- #--}}}
466
470
  end
467
- #
471
+
468
472
  # starts a thread to collect the child status and sets up at_exit handler to
469
473
  # prevent zombies. the at_exit handler is canceled if the thread is able to
470
474
  # collect the status
471
475
  #
472
476
  def detach
473
- #--{{{
474
477
  reap = lambda do |cid|
475
478
  begin
476
479
  @status = Process::waitpid2(cid).last
@@ -493,81 +496,66 @@ require 'sync'
493
496
  reap = lambda{|cid| 'no-op' }
494
497
  end
495
498
  end
496
- #--}}}
497
499
  end
498
- #
500
+
499
501
  # wait for slave to finish. if the keyword 'non_block'=>true is given a
500
502
  # thread is returned to do the waiting in an async fashion. eg
501
503
  #
502
504
  # thread = slave.wait(:non_block=>true){|value| "background <#{ value }>"}
503
505
  #
504
506
  def wait opts = {}, &b
505
- #--{{{
506
507
  b ||= lambda{|exit_status|}
507
508
  non_block = getopts(opts)['non_block']
508
509
  non_block ? Thread.new{ b[ @waiter.value ] } : b[ @waiter.value ]
509
- #--}}}
510
510
  end
511
511
  alias :wait2 :wait
512
- #
512
+
513
513
  # cuts the lifeline and kills the child process - give the key 'quiet' to
514
514
  # ignore errors shutting down, including having already shutdown
515
515
  #
516
516
  def shutdown opts = {}
517
- #--{{{
518
517
  quiet = getopts(opts)['quiet']
519
518
  raise "already shutdown" if @shutdown unless quiet
520
519
  begin; Process::kill 'SIGUSR2', @pid; rescue Exception => e; end
521
520
  begin; @lifeline.cut; rescue Exception; end
522
521
  raise e if e unless quiet
523
522
  @shutdown = true
524
- #--}}}
525
523
  end
526
- #
524
+
527
525
  # true
528
526
  #
529
527
  def shutdown?
530
- #--{{{
531
528
  @shutdown
532
- #--}}}
533
529
  end
534
- #
530
+
535
531
  # generate a default name to appear in ps/top
536
532
  #
537
533
  def gen_psname obj
538
- #--{{{
539
- "slave_#{ obj.class }_#{ obj.object_id }_#{ Process::ppid }_#{ Process::pid }".downcase.gsub(%r/\s+/,'_')
540
- #--}}}
534
+ "slave_#{ obj.class }_#{ obj.object_id }_#{ Process.ppid }_#{ Process.pid }".downcase.gsub(%r/\s+/,'_')
541
535
  end
542
- #
536
+
543
537
  # see docs for Slave.default
544
538
  #
545
539
  def default key
546
- #--{{{
547
540
  self.class.default key
548
- #--}}}
549
541
  end
550
- #
542
+
551
543
  # see docs for Slave.getopts
552
544
  #
553
545
  def getopts opts
554
- #--{{{
555
546
  self.class.getopts opts
556
- #--}}}
557
547
  end
558
- #
548
+
559
549
  # debugging output - ENV['SLAVE_DEBUG']=1 to enable
560
550
  #
561
551
  def trace
562
- #--{{{
563
552
  if @debug
564
553
  STDERR.puts yield
565
554
  STDERR.flush
566
555
  end
567
- #--}}}
568
556
  end
569
557
 
570
- #
558
+
571
559
  # a simple convenience method which returns an *object* from another
572
560
  # process. the object returned is the result of the supplied block. eg
573
561
  #
@@ -584,7 +572,6 @@ require 'sync'
584
572
  # object = thread.value
585
573
  #
586
574
  def self.object opts = {}, &b
587
- #--{{{
588
575
  async = opts.delete('async') || opts.delete(:async)
589
576
 
590
577
  opts['object'] = opts[:object] = lambda(&b)
@@ -601,7 +588,5 @@ require 'sync'
601
588
  end
602
589
 
603
590
  async ? Thread.new{ value[slave] } : value[slave]
604
- #--}}}
605
591
  end
606
- #--}}}
607
592
  end # class Slave