pwrake 2.0.1 → 2.1.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.
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