slave 1.2.2 → 1.3.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 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