pwrake 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +22 -9
  3. data/bin/gfwhere-pipe +33 -9
  4. data/bin/pwrake +5 -2
  5. data/bin/pwrake_branch +5 -3
  6. data/lib/pwrake/branch/branch.rb +95 -86
  7. data/lib/pwrake/branch/branch_application.rb +4 -0
  8. data/lib/pwrake/branch/communicator.rb +173 -0
  9. data/lib/pwrake/branch/communicator_set.rb +100 -0
  10. data/lib/pwrake/branch/fiber_queue.rb +10 -0
  11. data/lib/pwrake/branch/shell.rb +68 -24
  12. data/lib/pwrake/branch/shell_profiler.rb +2 -0
  13. data/lib/pwrake/gfarm/gfarm_postprocess.rb +8 -7
  14. data/lib/pwrake/logger.rb +5 -0
  15. data/lib/pwrake/master/master.rb +190 -87
  16. data/lib/pwrake/master/master_application.rb +8 -0
  17. data/lib/pwrake/nbio.rb +525 -0
  18. data/lib/pwrake/option/host_map.rb +36 -4
  19. data/lib/pwrake/option/option.rb +7 -1
  20. data/lib/pwrake/option/option_filesystem.rb +13 -3
  21. data/lib/pwrake/queue/locality_aware_queue.rb +41 -6
  22. data/lib/pwrake/queue/queue_array.rb +31 -11
  23. data/lib/pwrake/queue/task_queue.rb +15 -18
  24. data/lib/pwrake/report/report.rb +2 -0
  25. data/lib/pwrake/task/task_algorithm.rb +4 -1
  26. data/lib/pwrake/task/task_manager.rb +2 -0
  27. data/lib/pwrake/task/task_property.rb +1 -0
  28. data/lib/pwrake/task/task_wrapper.rb +40 -21
  29. data/lib/pwrake/version.rb +1 -1
  30. data/lib/pwrake/worker/invoker.rb +4 -29
  31. data/pwrake.gemspec +3 -2
  32. metadata +24 -12
  33. data/lib/pwrake/branch.rb +0 -22
  34. data/lib/pwrake/branch/worker_communicator.rb +0 -104
  35. data/lib/pwrake/iomux/channel.rb +0 -70
  36. data/lib/pwrake/iomux/handler.rb +0 -124
  37. data/lib/pwrake/iomux/handler_set.rb +0 -35
  38. data/lib/pwrake/iomux/runner.rb +0 -62
  39. data/lib/pwrake/master.rb +0 -30
@@ -1,3 +1,5 @@
1
+ require "pwrake/master/master"
2
+
1
3
  module Pwrake
2
4
 
3
5
  # a mixin for managing Rake application.
@@ -34,6 +36,12 @@ module Pwrake
34
36
  top_level
35
37
  Log.debug "main: #{Time.now-t} sec"
36
38
  t = Time.now
39
+ rescue Exception => e
40
+ # Exit with error message
41
+ m = Log.bt(e)
42
+ Log.fatal m
43
+ $stderr.puts m
44
+ @master.signal_trap("INT")
37
45
  ensure
38
46
  @failed = @master.finish
39
47
  Log.debug "finish: #{Time.now-t} sec"
@@ -0,0 +1,525 @@
1
+ require "fiber"
2
+
3
+ module Pwrake
4
+ module NBIO
5
+
6
+ class TimeoutError < IOError
7
+ end
8
+
9
+ class Selector
10
+
11
+ def initialize
12
+ @reader = {}
13
+ @writer = {}
14
+ @hb_time = {}
15
+ @running = false
16
+ end
17
+
18
+ attr_reader :reader, :writer
19
+
20
+ def add_reader(hdl)
21
+ @reader[hdl.io] = hdl
22
+ heartbeat(hdl.io) if hdl.timeout
23
+ end
24
+
25
+ def delete_reader(hdl)
26
+ @reader.delete(hdl.io)
27
+ delete_heartbeat(hdl.io)
28
+ end
29
+
30
+ def add_writer(hdl)
31
+ @writer[hdl.io] = hdl
32
+ end
33
+
34
+ def delete_writer(hdl)
35
+ @writer.delete(hdl.io)
36
+ end
37
+
38
+ def empty?
39
+ @reader.empty? && @writer.empty?
40
+ end
41
+
42
+ def halt
43
+ @running = false
44
+ @writer.each_value{|w| w.halt}
45
+ @reader.each_value{|r| r.halt}
46
+ end
47
+
48
+ # used to print an error message
49
+ def get_host(io)
50
+ hdl = @reader[io] || @writer[io]
51
+ hdl.respond_to?(:host) ? hdl.host : nil
52
+ end
53
+
54
+ # called when IO start and receive heartbeat
55
+ def heartbeat(io)
56
+ @hb_time[io] = Time.now
57
+ @hb_earliest = @hb_time.values.min
58
+ end
59
+
60
+ def delete_heartbeat(io)
61
+ @hb_time.delete(io)
62
+ @hb_earliest = @hb_time.values.min
63
+ end
64
+
65
+ def run(timeout=nil)
66
+ @running = true
67
+ while @running && !empty?
68
+ if $debug
69
+ Log.debug "Selector#run: "+caller[0..1].join(", ")+
70
+ " @reader.size=#{@reader.size} @writer.size=#{@writer.size}"
71
+ $stderr.puts "Selector#run: "+caller[0]
72
+ end
73
+ run_select(timeout)
74
+ end
75
+ ensure
76
+ @running = false
77
+ end
78
+
79
+ private
80
+ def run_select(timeout)
81
+ r, w = IO.select(@reader.keys,@writer.keys,[],timeout)
82
+ r.each{|io| @reader[io].call} if r
83
+ w.each{|io| @writer[io].call} if w
84
+ while timeout && @hb_earliest && Time.now - @hb_earliest > timeout
85
+ io = @hb_time.key(@hb_earliest)
86
+ if hdl = @reader[io]
87
+ e = TimeoutError.new("HB Timeout (#{timeout}s) #<Reader:%x> #{io.inspect}"%hdl.__id__)
88
+ hdl.error(e)
89
+ end
90
+ delete_heartbeat(io)
91
+ end
92
+ rescue IOError => e
93
+ em = "#{e.class.name}: #{e.message}"
94
+ @reader.keys.each do |io|
95
+ if io.closed?
96
+ m = "#{em} io=#{io}"
97
+ Log.error(m)
98
+ $stderr.puts m
99
+ hdl = @reader.delete(io)
100
+ hdl.error(e)
101
+ end
102
+ end
103
+ @writer.keys.each do |io|
104
+ if io.closed?
105
+ m = "#{em} io=#{io}"
106
+ Log.error(m)
107
+ $stderr.puts m
108
+ hdl = @writer.delete(io)
109
+ hdl.error(e)
110
+ end
111
+ end
112
+ #raise e
113
+ end
114
+
115
+ end
116
+
117
+ #------------------------------------------------------------------
118
+
119
+ class Writer
120
+ def initialize(selector, io)
121
+ @selector = selector
122
+ @io = io
123
+ @waiter = []
124
+ @pool = []
125
+ end
126
+ attr_reader :io
127
+
128
+ # call from Selector
129
+ def call
130
+ w = @waiter
131
+ @waiter = []
132
+ w.each{|f| f.resume}
133
+ ensure
134
+ @selector.delete_writer(self) if @waiter.empty?
135
+ end
136
+
137
+ # call from Fiber context
138
+ def put_line(line, ch=nil)
139
+ line = line.chomp
140
+ line = "#{ch}:#{line}" if ch
141
+ write(line+"\n")
142
+ end
143
+
144
+ def halt
145
+ @halting = true
146
+ call
147
+ ensure
148
+ @halting = false
149
+ end
150
+
151
+ def error(e)
152
+ @closed = true
153
+ raise e
154
+ end
155
+
156
+ # from Bartender
157
+
158
+ def write(buf, buffered=false)
159
+ push(buf)
160
+ flush unless buffered
161
+ end
162
+
163
+ def flush
164
+ until @pool.empty?
165
+ len = _write(@pool[0])
166
+ pop(len)
167
+ end
168
+ end
169
+
170
+ def select_io
171
+ @selector.add_writer(self) if @waiter.empty?
172
+ @waiter.push(Fiber.current)
173
+ Fiber.yield
174
+ end
175
+
176
+ private
177
+ def _write(buf)
178
+ return @io.write_nonblock(buf)
179
+ rescue IO::WaitWritable
180
+ return nil if @halting
181
+ select_io
182
+ retry
183
+ end
184
+
185
+ def push(string)
186
+ if string.bytesize > 0
187
+ @pool << string
188
+ end
189
+ end
190
+
191
+ def pop(size)
192
+ return if size < 0
193
+ raise if @pool[0].bytesize < size
194
+
195
+ if @pool[0].bytesize == size
196
+ @pool.shift
197
+ else
198
+ unless @pool[0].encoding == Encoding::BINARY
199
+ @pool[0] = @pool[0].dup.force_encoding(Encoding::BINARY)
200
+ end
201
+ @pool[0].slice!(0...size)
202
+ end
203
+ end
204
+ end
205
+
206
+ #------------------------------------------------------------------
207
+
208
+ class Reader
209
+
210
+ def initialize(selector, io)
211
+ @selector = selector
212
+ @io = io
213
+ @waiter = []
214
+ @buf = ''
215
+ @sep = "\n"
216
+ @chunk_size = 8192
217
+ end
218
+ attr_reader :io
219
+ attr_accessor :timeout
220
+
221
+ # call from Selector#run
222
+ def call
223
+ @waiter.each{|f| f.resume}
224
+ end
225
+
226
+ # call from MultiReader#call
227
+ def read_line_nonblock
228
+ until index = @buf.index(@sep)
229
+ @buf << @io.read_nonblock(@chunk_size)
230
+ end
231
+ @buf.slice!(0, index+@sep.bytesize)
232
+ rescue EOFError => e
233
+ if @buf.empty?
234
+ #return nil
235
+ raise e
236
+ else
237
+ buf = @buf; @buf = ''
238
+ return buf
239
+ end
240
+ #rescue IO::WaitReadable
241
+ end
242
+
243
+ # call from Reader#_read and FiberReaderQueue#deq
244
+ def select_io
245
+ @selector.add_reader(self) if @waiter.empty?
246
+ @waiter.push(Fiber.current)
247
+ Fiber.yield
248
+ ensure
249
+ @waiter.delete(Fiber.current)
250
+ @selector.delete_reader(self) if @waiter.empty?
251
+ end
252
+
253
+ def error(e)
254
+ @closed = true
255
+ raise e
256
+ end
257
+
258
+ def halt
259
+ @halting = true
260
+ call
261
+ ensure
262
+ @halting = false
263
+ end
264
+
265
+ # from Bartender
266
+
267
+ def _read(sz)
268
+ @io.read_nonblock(sz)
269
+ rescue EOFError
270
+ nil
271
+ rescue IO::WaitReadable
272
+ return nil if @halting
273
+ select_io
274
+ retry
275
+ end
276
+
277
+ def read(n)
278
+ while @buf.bytesize < n
279
+ chunk = _read(n)
280
+ break if chunk.nil? || chunk.empty?
281
+ @buf += chunk
282
+ end
283
+ @buf.slice!(0, n)
284
+ end
285
+
286
+ def read_until(sep="\r\n", chunk_size=8192)
287
+ until i = @buf.index(sep)
288
+ if s = _read(chunk_size)
289
+ @buf += s
290
+ else
291
+ if @buf.empty?
292
+ return nil
293
+ else
294
+ buf = @buf; @buf = ''
295
+ return buf
296
+ end
297
+ end
298
+ end
299
+ @buf.slice!(0, i+sep.bytesize)
300
+ end
301
+
302
+ def readln
303
+ read_until(@sep)
304
+ end
305
+
306
+ alias get_line :readln
307
+
308
+ end
309
+
310
+ #------------------------------------------------------------------
311
+
312
+ class MultiReader < Reader
313
+
314
+ def initialize(selector, io, n_chan=0)
315
+ super(selector, io)
316
+ @n_chan = n_chan
317
+ @queue = @n_chan.times.map{|i| FiberReaderQueue.new(self)}
318
+ @default_queue = FiberReaderQueue.new(self)
319
+ @timeout = true
320
+ end
321
+ attr_reader :queue
322
+ attr_accessor :timeout
323
+ attr_accessor :default_queue
324
+
325
+ def [](ch)
326
+ @queue[ch]
327
+ end
328
+
329
+ def new_queue
330
+ n = @n_chan
331
+ @queue << q = FiberReaderQueue.new(self)
332
+ @n_chan += 1
333
+ [n,q]
334
+ end
335
+
336
+ # call from Fiber context
337
+ def get_line(ch=nil)
338
+ if ch && !@queue.empty?
339
+ @queue[ch].deq
340
+ else
341
+ @default_queue.deq
342
+ end
343
+ end
344
+
345
+ def call
346
+ while line = read_line_nonblock
347
+ if /^(\d+):(.*)$/ =~ line
348
+ ch,str = $1,$2
349
+ if q = @queue[ch.to_i]
350
+ q.enq(str)
351
+ else
352
+ raise "No queue ##{ch}, received: #{line}"
353
+ end
354
+ elsif @default_queue
355
+ @default_queue.enq(line)
356
+ else
357
+ raise "No default_queue, received: #{line}"
358
+ end
359
+ end
360
+ rescue EOFError
361
+ halt
362
+ rescue IO::WaitReadable
363
+ #p IO::WaitReadable
364
+ end
365
+
366
+ def error(e)
367
+ @closed = true
368
+ @queue.each{|q| q.enq(e)}
369
+ @default_queue.enq(e)
370
+ end
371
+
372
+ def halt
373
+ @queue.each{|q| q.halt}
374
+ @default_queue.halt
375
+ end
376
+ end
377
+
378
+ #------------------------------------------------------------------
379
+
380
+ class FiberReaderQueue
381
+
382
+ def initialize(reader)
383
+ @reader = reader
384
+ @q = []
385
+ @waiter = []
386
+ @halting = false
387
+ end
388
+
389
+ def enq(x)
390
+ @q.push(x)
391
+ f = @waiter.shift
392
+ f.resume if f
393
+ end
394
+
395
+ def deq
396
+ while @q.empty?
397
+ return nil if @halting
398
+ @waiter.push(Fiber.current)
399
+ @reader.select_io
400
+ end
401
+ @q.shift
402
+ end
403
+
404
+ alias get_line :deq
405
+
406
+ def halt
407
+ @halting = true
408
+ while f = @waiter.shift
409
+ f.resume
410
+ end
411
+ ensure
412
+ @halting = false
413
+ end
414
+
415
+ end
416
+
417
+ #------------------------------------------------------------------
418
+
419
+ class Handler
420
+
421
+ def initialize(reader,writer,hostname=nil)
422
+ @writer = writer
423
+ @reader = reader
424
+ @host = hostname
425
+ end
426
+ attr_reader :reader, :writer, :host
427
+
428
+ def get_line
429
+ @reader.get_line
430
+ end
431
+
432
+ def put_line(s)
433
+ @writer.put_line(s)
434
+ end
435
+
436
+ def put_kill(sig="INT")
437
+ #@writer.put_line("kill:#{sig}")
438
+ @writer.io.puts("kill:#{sig}")
439
+ @writer.io.flush
440
+ end
441
+
442
+ def put_exit
443
+ @writer.put_line "exit"
444
+ end
445
+
446
+ def exit
447
+ exit_msg = "exited"
448
+ iow = @writer.io
449
+ Log.debug "Handler#exit iow=#{iow.inspect}"
450
+ return if iow.closed?
451
+ @writer.put_line "exit"
452
+ while line = @reader.get_line
453
+ # here might receive "retire:0" from branch...
454
+ line.chomp!
455
+ Log.debug "Handler#exit: #{line} host=#{@host}"
456
+ return if line == exit_msg
457
+ end
458
+ rescue Errno::EPIPE => e
459
+ if Rake.application.options.debug
460
+ #$stderr.puts "Errno::EPIPE in #{self.class}#exit iow=#{iow.inspect}"
461
+ #$stderr.puts e.backtrace.join("\n")
462
+ end
463
+ Log.error "Errno::EPIPE in #{self.class}.exit iow=#{iow.inspect}\n"+
464
+ e.backtrace.join("\n")
465
+ end
466
+
467
+ def halt
468
+ @writer.halt
469
+ @reader.halt
470
+ end
471
+
472
+ def self.kill(hdl_set,sig)
473
+ hdl_set.each do |hdl|
474
+ Fiber.new do
475
+ hdl.put_kill(sig)
476
+ end.resume
477
+ end
478
+ end
479
+
480
+ def self.exit(hdl_set)
481
+ hdl_set.each do |hdl|
482
+ Fiber.new do
483
+ hdl.exit
484
+ end.resume
485
+ end
486
+ end
487
+
488
+ end
489
+
490
+ end
491
+
492
+ #------------------------------------------------------------------
493
+
494
+ if __FILE__ == $0
495
+ iosel = NBIO::Selector.new
496
+
497
+ io = 5.times.map do
498
+ IO.pipe
499
+ end
500
+
501
+ io.each do |ior,iow|
502
+ rd = NBIO::MultiReader.new(iosel,ior,1)
503
+ Fiber.new do
504
+ while s = rd.get_line(0)
505
+ puts s
506
+ end
507
+ puts "fiber end"
508
+ end.resume
509
+ end
510
+
511
+ io.each do |ior,iow|
512
+ wr = NBIO::Writer.new(iosel,iow)
513
+ Fiber.new do
514
+ 2000.times do |i|
515
+ wr.put_line("test str#{i}"+"-"*80,0)
516
+ #iow.puts "0:test str#{i}"+"-"*80
517
+ end
518
+ #iow.print "hage"
519
+ iow.close
520
+ end.resume
521
+ end
522
+
523
+ iosel.run
524
+ end
525
+ end