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.
- checksums.yaml +4 -4
- data/README.md +22 -9
- data/bin/gfwhere-pipe +33 -9
- data/bin/pwrake +5 -2
- data/bin/pwrake_branch +5 -3
- data/lib/pwrake/branch/branch.rb +95 -86
- data/lib/pwrake/branch/branch_application.rb +4 -0
- data/lib/pwrake/branch/communicator.rb +173 -0
- data/lib/pwrake/branch/communicator_set.rb +100 -0
- data/lib/pwrake/branch/fiber_queue.rb +10 -0
- data/lib/pwrake/branch/shell.rb +68 -24
- data/lib/pwrake/branch/shell_profiler.rb +2 -0
- data/lib/pwrake/gfarm/gfarm_postprocess.rb +8 -7
- data/lib/pwrake/logger.rb +5 -0
- data/lib/pwrake/master/master.rb +190 -87
- data/lib/pwrake/master/master_application.rb +8 -0
- data/lib/pwrake/nbio.rb +525 -0
- data/lib/pwrake/option/host_map.rb +36 -4
- data/lib/pwrake/option/option.rb +7 -1
- data/lib/pwrake/option/option_filesystem.rb +13 -3
- data/lib/pwrake/queue/locality_aware_queue.rb +41 -6
- data/lib/pwrake/queue/queue_array.rb +31 -11
- data/lib/pwrake/queue/task_queue.rb +15 -18
- data/lib/pwrake/report/report.rb +2 -0
- data/lib/pwrake/task/task_algorithm.rb +4 -1
- data/lib/pwrake/task/task_manager.rb +2 -0
- data/lib/pwrake/task/task_property.rb +1 -0
- data/lib/pwrake/task/task_wrapper.rb +40 -21
- data/lib/pwrake/version.rb +1 -1
- data/lib/pwrake/worker/invoker.rb +4 -29
- data/pwrake.gemspec +3 -2
- metadata +24 -12
- data/lib/pwrake/branch.rb +0 -22
- data/lib/pwrake/branch/worker_communicator.rb +0 -104
- data/lib/pwrake/iomux/channel.rb +0 -70
- data/lib/pwrake/iomux/handler.rb +0 -124
- data/lib/pwrake/iomux/handler_set.rb +0 -35
- data/lib/pwrake/iomux/runner.rb +0 -62
- 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"
|
data/lib/pwrake/nbio.rb
ADDED
@@ -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
|