pwrake 2.1.2 → 2.1.3
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 +3 -1
- data/lib/pwrake/branch/communicator.rb +0 -1
- data/lib/pwrake/branch/shell.rb +4 -0
- data/lib/pwrake/master/master.rb +3 -2
- data/lib/pwrake/master/master_application.rb +10 -3
- data/lib/pwrake/nbio.rb +35 -27
- data/lib/pwrake/option/option.rb +2 -0
- data/lib/pwrake/option/option_filesystem.rb +2 -1
- data/lib/pwrake/report/parallelism.rb +16 -16
- data/lib/pwrake/report/report.rb +7 -5
- data/lib/pwrake/report/report_multi.rb +10 -10
- data/lib/pwrake/version.rb +1 -1
- data/lib/pwrake/worker/executor.rb +111 -132
- data/lib/pwrake/worker/invoker.rb +47 -63
- data/lib/pwrake/worker/log_executor.rb +11 -7
- data/lib/pwrake/worker/reader.rb +73 -0
- data/lib/pwrake/worker/writer.rb +22 -16
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db880d8483f79d6812bd1920557350a7b4311378
|
4
|
+
data.tar.gz: a1d2779a59eb21dd4df2b8ee5e9a00054a50654e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4bc21c0d7a5ef317d5d03883ca918e2c2218e0ec28e13167079127820e0c4800923f4d23c6891e4eff5dcdfb010db4b2d6214a6d9eedf5c6c5597a1353467d7a
|
7
|
+
data.tar.gz: b851a5ffb98ddccf77b52bbd5c4339a900d617ce8018e9c68ee665999ac9a70a8b5295be21c88832a8d9f20cd257da81b9b4c91715f487fde0711a60db4e9931
|
data/README.md
CHANGED
@@ -86,7 +86,8 @@ In this case, you need the rehash of command paths:
|
|
86
86
|
-d, --debug [Pw] Output Debug messages
|
87
87
|
--pwrake-conf [FILE] [Pw] Pwrake configuration file in YAML
|
88
88
|
--show-conf, --show-config [Pw] Show Pwrake configuration options
|
89
|
-
--report LOGDIR [Pw] Report workflow statistics
|
89
|
+
--report LOGDIR [Pw] Generate `report.html' (Report of workflow statistics) in LOGDIR and exit.
|
90
|
+
--report-image IMAGE_TYPE [Pw] Gnuplot output format (png,jpg,svg etc.) in report.html.
|
90
91
|
--clear-gfarm2fs [Pw] Clear gfarm2fs mountpoints left after failure.
|
91
92
|
|
92
93
|
### pwrake_conf.yaml
|
@@ -125,6 +126,7 @@ In this case, you need the rehash of command paths:
|
|
125
126
|
NOACTION_QUEUE_PRIORITY FIFO(default)|LIFO|RAND
|
126
127
|
SHELL_START_INTERVAL default=0.012 (sec)
|
127
128
|
GRAPH_PARTITION false(default)|true
|
129
|
+
REPORT_IMAGE default=png
|
128
130
|
|
129
131
|
* Options for Gfarm system:
|
130
132
|
|
data/lib/pwrake/branch/shell.rb
CHANGED
@@ -193,12 +193,15 @@ module Pwrake
|
|
193
193
|
msg = "Shell#io_read_loop: exit"
|
194
194
|
$stderr.puts(msg)
|
195
195
|
Log.error(msg)
|
196
|
+
@exited = true
|
196
197
|
@chan.halt
|
197
198
|
return "exit"
|
198
199
|
when IOError
|
200
|
+
@exited = true
|
199
201
|
@chan.halt
|
200
202
|
return "ioerror"
|
201
203
|
when NBIO::TimeoutError
|
204
|
+
@exited = true
|
202
205
|
@chan.halt
|
203
206
|
return "timeout"
|
204
207
|
end
|
@@ -236,6 +239,7 @@ module Pwrake
|
|
236
239
|
Rake.application.display_error_message(e)
|
237
240
|
Log.error e
|
238
241
|
result = "taskfail:#{@id}:#{task.name}"
|
242
|
+
break if @exited
|
239
243
|
ensure
|
240
244
|
master_w.put_line result
|
241
245
|
end
|
data/lib/pwrake/master/master.rb
CHANGED
@@ -143,6 +143,7 @@ module Pwrake
|
|
143
143
|
@selector.run
|
144
144
|
|
145
145
|
Log.info "num_cores=#{sum_ncore}"
|
146
|
+
@option.total_cores = sum_ncore
|
146
147
|
@hostinfo_by_id.each do |id,host|
|
147
148
|
if ncore = @hostinfo_by_id[id].idle_cores
|
148
149
|
Log.info "#{host} id=#{id} ncore=#{ncore}"
|
@@ -436,10 +437,10 @@ module Pwrake
|
|
436
437
|
Log.debug "Master#finish @selector.run end"
|
437
438
|
if !@exited
|
438
439
|
@exited = true
|
439
|
-
Log.debug "Master#finish
|
440
|
+
Log.debug "Master#finish Handler.exit begin"
|
440
441
|
NBIO::Handler.exit(@hdl_set)
|
441
442
|
@selector.run(60)
|
442
|
-
Log.debug "Master#finish
|
443
|
+
Log.debug "Master#finish Handler.exit end"
|
443
444
|
end
|
444
445
|
TaskWrapper.close_task_logger
|
445
446
|
Log.debug "Master#finish end"
|
@@ -26,10 +26,10 @@ module Pwrake
|
|
26
26
|
standard_exception_handling do
|
27
27
|
init("pwrake") # <- parse options here
|
28
28
|
@role = @master = Master.new
|
29
|
-
load_rakefile
|
30
29
|
t = Time.now
|
31
30
|
@master.init
|
32
31
|
@master.setup_branches
|
32
|
+
load_rakefile
|
33
33
|
begin
|
34
34
|
Log.debug "init: #{Time.now-t} sec"
|
35
35
|
t = Time.now
|
@@ -62,7 +62,11 @@ module Pwrake
|
|
62
62
|
opts.each_with_index do |a,i|
|
63
63
|
if a[0] == '--version'
|
64
64
|
a[3] = lambda { |value|
|
65
|
-
|
65
|
+
if defined? RAKEVERSION
|
66
|
+
puts "rake, version #{RAKEVERSION}"
|
67
|
+
elsif defined? Rake::VERSION
|
68
|
+
puts "rake, version #{Rake::VERSION}"
|
69
|
+
end
|
66
70
|
puts "pwrake, version #{Pwrake::VERSION}"
|
67
71
|
exit
|
68
72
|
}
|
@@ -139,9 +143,12 @@ module Pwrake
|
|
139
143
|
"[Pw] Show Pwrake configuration options",
|
140
144
|
lambda {|value| options.show_conf = true }
|
141
145
|
],
|
142
|
-
['--report LOGDIR',"[Pw] Report workflow statistics
|
146
|
+
['--report LOGDIR',"[Pw] Generate `report.html' (Report of workflow statistics) in LOGDIR and exit.",
|
143
147
|
lambda {|value| options.report_dir = value }
|
144
148
|
],
|
149
|
+
['--report-image IMAGE_TYPE',"[Pw] Gnuplot output format (png,jpg,svg etc.) in report.html.",
|
150
|
+
lambda {|value| options.report_image = value }
|
151
|
+
],
|
145
152
|
['--clear-gfarm2fs',"[Pw] Clear gfarm2fs mountpoints left after failure.",
|
146
153
|
lambda { |value|
|
147
154
|
Option.new.clear_gfarm2fs
|
data/lib/pwrake/nbio.rb
CHANGED
@@ -3,7 +3,7 @@ require "fiber"
|
|
3
3
|
module Pwrake
|
4
4
|
module NBIO
|
5
5
|
|
6
|
-
class TimeoutError <
|
6
|
+
class TimeoutError < StandardError
|
7
7
|
end
|
8
8
|
|
9
9
|
class Selector
|
@@ -11,7 +11,6 @@ module NBIO
|
|
11
11
|
def initialize
|
12
12
|
@reader = {}
|
13
13
|
@writer = {}
|
14
|
-
@hb_time = {}
|
15
14
|
@running = false
|
16
15
|
end
|
17
16
|
|
@@ -19,12 +18,10 @@ module NBIO
|
|
19
18
|
|
20
19
|
def add_reader(hdl)
|
21
20
|
@reader[hdl.io] = hdl
|
22
|
-
heartbeat(hdl.io) if hdl.timeout
|
23
21
|
end
|
24
22
|
|
25
23
|
def delete_reader(hdl)
|
26
24
|
@reader.delete(hdl.io)
|
27
|
-
delete_heartbeat(hdl.io)
|
28
25
|
end
|
29
26
|
|
30
27
|
def add_writer(hdl)
|
@@ -51,19 +48,9 @@ module NBIO
|
|
51
48
|
hdl.respond_to?(:host) ? hdl.host : nil
|
52
49
|
end
|
53
50
|
|
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
51
|
def run(timeout=nil)
|
66
52
|
@running = true
|
53
|
+
init_heartbeat if timeout
|
67
54
|
while @running && !empty?
|
68
55
|
if $debug
|
69
56
|
Log.debug "Selector#run: "+caller[0..1].join(", ")+
|
@@ -74,21 +61,16 @@ module NBIO
|
|
74
61
|
end
|
75
62
|
ensure
|
76
63
|
@running = false
|
64
|
+
@hb_time = nil
|
77
65
|
end
|
78
66
|
|
79
67
|
private
|
80
68
|
def run_select(timeout)
|
81
|
-
|
69
|
+
to = (timeout) ? timeout*0.75 : nil
|
70
|
+
r, w, = IO.select(@reader.keys,@writer.keys,[],to)
|
71
|
+
check_heartbeat(r,timeout) if timeout
|
82
72
|
r.each{|io| @reader[io].call} if r
|
83
73
|
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
74
|
rescue IOError => e
|
93
75
|
em = "#{e.class.name}: #{e.message}"
|
94
76
|
@reader.keys.each do |io|
|
@@ -112,6 +94,33 @@ module NBIO
|
|
112
94
|
#raise e
|
113
95
|
end
|
114
96
|
|
97
|
+
def init_heartbeat
|
98
|
+
t = Time.now
|
99
|
+
@hb_time = {}
|
100
|
+
@reader.each_key{|io| @hb_time[io] = t}
|
101
|
+
end
|
102
|
+
|
103
|
+
def check_heartbeat(ios,timeout)
|
104
|
+
t = Time.now
|
105
|
+
rds = @reader.dup
|
106
|
+
if ios
|
107
|
+
ios.each do |io|
|
108
|
+
@hb_time[io] = t
|
109
|
+
rds.delete(io)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
rds.each do |io,hdl|
|
113
|
+
if hdl.check_timeout
|
114
|
+
tdif = t - @hb_time[io]
|
115
|
+
if tdif > timeout
|
116
|
+
m = "Heartbeat Timeout: no response during #{tdif}s "+
|
117
|
+
"> timeout #{timeout}s from host=#{get_host(io)}"
|
118
|
+
hdl.error(TimeoutError.new(m))
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
115
124
|
end
|
116
125
|
|
117
126
|
#------------------------------------------------------------------
|
@@ -216,7 +225,7 @@ module NBIO
|
|
216
225
|
@chunk_size = 8192
|
217
226
|
end
|
218
227
|
attr_reader :io
|
219
|
-
attr_accessor :
|
228
|
+
attr_accessor :check_timeout
|
220
229
|
|
221
230
|
# call from Selector#run
|
222
231
|
def call
|
@@ -316,10 +325,9 @@ module NBIO
|
|
316
325
|
@n_chan = n_chan
|
317
326
|
@queue = @n_chan.times.map{|i| FiberReaderQueue.new(self)}
|
318
327
|
@default_queue = FiberReaderQueue.new(self)
|
319
|
-
@
|
328
|
+
@check_timeout = true
|
320
329
|
end
|
321
330
|
attr_reader :queue
|
322
|
-
attr_accessor :timeout
|
323
331
|
attr_accessor :default_queue
|
324
332
|
|
325
333
|
def [](ch)
|
data/lib/pwrake/option/option.rb
CHANGED
@@ -39,6 +39,7 @@ module Pwrake
|
|
39
39
|
|
40
40
|
attr_reader :counter
|
41
41
|
attr_reader :logger
|
42
|
+
attr_accessor :total_cores
|
42
43
|
|
43
44
|
DEFAULT_CONFFILES = ["pwrake_conf.yaml","PwrakeConf.yaml"]
|
44
45
|
|
@@ -121,6 +122,7 @@ module Pwrake
|
|
121
122
|
'PLOT_PARALLELISM',
|
122
123
|
'SHOW_CONF',
|
123
124
|
['REPORT_DIR','REPORT'],
|
125
|
+
'REPORT_IMAGE',
|
124
126
|
'FAILED_TARGET', # rename(default), delete, leave
|
125
127
|
'FAILURE_TERMINATION', # wait, kill, continue
|
126
128
|
'QUEUE_PRIORITY', # RANK(default), FIFO, LIFO, DFS
|
@@ -64,7 +64,7 @@ module Pwrake
|
|
64
64
|
d
|
65
65
|
end
|
66
66
|
|
67
|
-
def plot_parallelism(file)
|
67
|
+
def plot_parallelism(file,fmt)
|
68
68
|
a = count_start_end_from_csv(file)
|
69
69
|
return if a.size < 4
|
70
70
|
|
@@ -101,8 +101,8 @@ module Pwrake
|
|
101
101
|
|
102
102
|
IO.popen("gnuplot","r+") do |f|
|
103
103
|
f.puts "
|
104
|
-
set terminal
|
105
|
-
set output '#{base}
|
104
|
+
set terminal #{fmt}
|
105
|
+
set output '#{base}.#{fmt}'
|
106
106
|
#set rmargin 10
|
107
107
|
set title '#{base}'
|
108
108
|
set xlabel 'time (sec)'
|
@@ -115,17 +115,17 @@ plot '#{fpara}' w l axis x1y1 title 'parallelism'
|
|
115
115
|
"
|
116
116
|
end
|
117
117
|
|
118
|
-
#puts "Parallelism plot: #{base}
|
118
|
+
#puts "Parallelism plot: #{base}.#{fmt}"
|
119
119
|
end
|
120
120
|
|
121
121
|
|
122
|
-
def plot_parallelism2(csvtable, base)
|
122
|
+
def plot_parallelism2(csvtable, base, fmt)
|
123
123
|
a = count_start_end_from_csv_table(csvtable)
|
124
124
|
return if a.size < 4
|
125
125
|
|
126
126
|
density = exec_density(a)
|
127
127
|
|
128
|
-
fimg = base+'/parallelism.
|
128
|
+
fimg = base+'/parallelism.'+fmt
|
129
129
|
|
130
130
|
n = a.size
|
131
131
|
i = 0
|
@@ -155,7 +155,7 @@ plot '#{fpara}' w l axis x1y1 title 'parallelism'
|
|
155
155
|
if system("which gnuplot >/dev/null 2>&1")
|
156
156
|
IO.popen("gnuplot","r+") do |f|
|
157
157
|
f.print "
|
158
|
-
set terminal
|
158
|
+
set terminal #{fmt}
|
159
159
|
set output '#{fimg}'
|
160
160
|
#set rmargin 10
|
161
161
|
set title '#{base}'
|
@@ -259,7 +259,7 @@ plot '-' w l axis x1y1 title 'parallelism', '-' w l axis x1y2 title 'exec/sec'
|
|
259
259
|
h
|
260
260
|
end
|
261
261
|
|
262
|
-
def plot_parallelism_by_pattern(csvtable, base, pattern)
|
262
|
+
def plot_parallelism_by_pattern(csvtable, base, pattern, fmt)
|
263
263
|
y_max = 0
|
264
264
|
t_end = 0
|
265
265
|
para = {}
|
@@ -290,13 +290,13 @@ plot '-' w l axis x1y1 title 'parallelism', '-' w l axis x1y2 title 'exec/sec'
|
|
290
290
|
end
|
291
291
|
end
|
292
292
|
|
293
|
-
fimg = base+'/para_cmd.
|
293
|
+
fimg = base+'/para_cmd.'+fmt
|
294
294
|
|
295
295
|
if system("which gnuplot >/dev/null 2>&1")
|
296
296
|
IO.popen("gnuplot","r+") do |f|
|
297
297
|
#begin f = $stdout
|
298
298
|
f.print "
|
299
|
-
set terminal
|
299
|
+
set terminal #{fmt}
|
300
300
|
set output '#{fimg}'
|
301
301
|
set title '#{base}'
|
302
302
|
set xlabel 'time (sec)'
|
@@ -338,10 +338,10 @@ set ylabel 'parallelism'
|
|
338
338
|
return grid
|
339
339
|
end
|
340
340
|
|
341
|
-
def plot_parallelism_by_host(csvtable,base)
|
342
|
-
|
341
|
+
def plot_parallelism_by_host(csvtable,base,fmt)
|
342
|
+
fimg = base+"/para_host."+fmt
|
343
343
|
data = read_time_by_host_from_csv(csvtable)
|
344
|
-
return
|
344
|
+
return fimg if data.size == 0
|
345
345
|
|
346
346
|
grid = []
|
347
347
|
hosts = data.keys.sort
|
@@ -353,8 +353,8 @@ set ylabel 'parallelism'
|
|
353
353
|
if system("which gnuplot >/dev/null 2>&1")
|
354
354
|
IO.popen("gnuplot","r+") do |f|
|
355
355
|
f.puts "
|
356
|
-
set terminal
|
357
|
-
set output '#{
|
356
|
+
set terminal #{fmt}
|
357
|
+
set output '#{fimg}'
|
358
358
|
#set rmargin 7
|
359
359
|
set lmargin 16
|
360
360
|
set pm3d map
|
@@ -385,7 +385,7 @@ set format y ''
|
|
385
385
|
f.printf "e\n"
|
386
386
|
end
|
387
387
|
end
|
388
|
-
|
388
|
+
fimg
|
389
389
|
end
|
390
390
|
|
391
391
|
end
|
data/lib/pwrake/report/report.rb
CHANGED
@@ -43,6 +43,8 @@ EOL
|
|
43
43
|
@id = @@id
|
44
44
|
@base = @dir
|
45
45
|
|
46
|
+
@img_fmt = option['REPORT_IMAGE'] || 'png'
|
47
|
+
|
46
48
|
@cmd_file = File.join(@dir,option['COMMAND_CSV_FILE'])
|
47
49
|
@task_file = File.join(@dir,option['TASK_CSV_FILE'])
|
48
50
|
@html_file = File.join(@dir,'report.html')
|
@@ -196,15 +198,15 @@ EOL
|
|
196
198
|
end
|
197
199
|
html << "</table>\n"
|
198
200
|
html << "<h2>Parallelism</h2>\n"
|
199
|
-
fimg = Parallelism.plot_parallelism2(@sh_table,@base)
|
201
|
+
fimg = Parallelism.plot_parallelism2(@sh_table,@base,@img_fmt)
|
200
202
|
html << "<img src='./#{File.basename(fimg)}' align='top'/></br>\n"
|
201
203
|
|
202
204
|
html << "<h2>Parallelism by command</h2>\n"
|
203
|
-
fimg3 = Parallelism.plot_parallelism_by_pattern(@sh_table,@base,@pattern)
|
205
|
+
fimg3 = Parallelism.plot_parallelism_by_pattern(@sh_table,@base,@pattern,@img_fmt)
|
204
206
|
html << "<img src='./#{File.basename(fimg3)}' align='top'/></br>\n"
|
205
207
|
|
206
208
|
html << "<h2>Parallelism by host</h2>\n"
|
207
|
-
fimg2 = Parallelism.plot_parallelism_by_host(@sh_table,@base)
|
209
|
+
fimg2 = Parallelism.plot_parallelism_by_host(@sh_table,@base,@img_fmt)
|
208
210
|
html << "<img src='./#{File.basename(fimg2)}' align='top'/></br>\n"
|
209
211
|
|
210
212
|
html << "<h2>Command statistics</h2>\n"
|
@@ -285,11 +287,11 @@ EOL
|
|
285
287
|
command_list << cmd
|
286
288
|
end
|
287
289
|
end
|
288
|
-
hist_image = @base+"/hist.
|
290
|
+
hist_image = @base+"/hist."+@img_fmt
|
289
291
|
if system("which gnuplot >/dev/null 2>&1")
|
290
292
|
IO.popen("gnuplot","r+") do |f|
|
291
293
|
f.puts "
|
292
|
-
set terminal
|
294
|
+
set terminal #{@img_fmt} # size 480,360
|
293
295
|
set output '#{hist_image}'
|
294
296
|
set ylabel 'histogram'
|
295
297
|
set xlabel 'Execution time (sec)'
|
@@ -2,14 +2,15 @@ module Pwrake
|
|
2
2
|
|
3
3
|
class ReportMulti
|
4
4
|
|
5
|
-
def initialize(list,pattern)
|
5
|
+
def initialize(list,pattern,options={})
|
6
6
|
@reports = list.map do |base|
|
7
7
|
r = Report.new(base,pattern)
|
8
8
|
puts r.base+" elap=#{r.elap}"
|
9
9
|
r
|
10
10
|
end
|
11
11
|
@pattern = pattern
|
12
|
-
@
|
12
|
+
@img_fmt = options['REPORT_IMAGE'] || 'png'
|
13
|
+
@elap_img = 'elap.#{@img_fmt}'
|
13
14
|
end
|
14
15
|
|
15
16
|
def report(stat_html)
|
@@ -29,7 +30,7 @@ module Pwrake
|
|
29
30
|
end
|
30
31
|
html << "</table>\n"
|
31
32
|
html << "<h2>Elapsed time</h2>\n"
|
32
|
-
html << "<img src='./#{File.basename(@
|
33
|
+
html << "<img src='./#{File.basename(@elap_img)}' align='top'/></br>\n"
|
33
34
|
|
34
35
|
html << "<h2>Histogram of Execution time</h2>\n"
|
35
36
|
html << report_histogram()
|
@@ -51,8 +52,8 @@ module Pwrake
|
|
51
52
|
ymax = 10**(mid+wid)
|
52
53
|
IO.popen("gnuplot","r+") do |f|
|
53
54
|
f.puts "
|
54
|
-
set terminal
|
55
|
-
set output '#{@
|
55
|
+
set terminal #{@img_fmt} size 640,480
|
56
|
+
set output '#{@elap_img}'
|
56
57
|
set xlabel 'ncore'
|
57
58
|
set ylabel 'time (sec)'
|
58
59
|
set yrange [#{ymin}:#{ymax}]
|
@@ -64,7 +65,7 @@ plot #{a}/x,'-' w lp lw 2 ps 2 title 'elapsed time'
|
|
64
65
|
end
|
65
66
|
f.puts "e"
|
66
67
|
end
|
67
|
-
puts "Ncore-time plot: "+@
|
68
|
+
puts "Ncore-time plot: "+@elap_img
|
68
69
|
end
|
69
70
|
|
70
71
|
def report_histogram
|
@@ -81,7 +82,7 @@ plot #{a}/x,'-' w lp lw 2 ps 2 title 'elapsed time'
|
|
81
82
|
end
|
82
83
|
|
83
84
|
@cmd_rep.each_key do |cmd|
|
84
|
-
@images[cmd] = 'hist_'+cmd.gsub(/[\/.]/,'_')+'.
|
85
|
+
@images[cmd] = 'hist_'+cmd.gsub(/[\/.]/,'_')+'.'+@img_fmt
|
85
86
|
end
|
86
87
|
histogram_plot
|
87
88
|
histogram_html
|
@@ -106,7 +107,7 @@ plot #{a}/x,'-' w lp lw 2 ps 2 title 'elapsed time'
|
|
106
107
|
@cmd_rep.each do |cmd,cmd_rep|
|
107
108
|
IO.popen("gnuplot","r+") do |f|
|
108
109
|
f.puts "
|
109
|
-
set terminal
|
110
|
+
set terminal #{@img_fmt} # size 480,360
|
110
111
|
set output '#{@images[cmd]}'
|
111
112
|
set ylabel 'histogram'
|
112
113
|
set xlabel 'Execution time (sec)'
|
@@ -139,7 +140,7 @@ set title '#{cmd}'"
|
|
139
140
|
@cmd_rep.each do |cmd,cmd_rep|
|
140
141
|
IO.popen("gnuplot","r+") do |f|
|
141
142
|
f.puts "
|
142
|
-
set terminal
|
143
|
+
set terminal #{@img_fmt} # size 480,360
|
143
144
|
set output '#{@images[cmd]}'
|
144
145
|
set nohidden3d
|
145
146
|
set palette rgb 33,13,10
|
@@ -193,4 +194,3 @@ set title '#{cmd}'"
|
|
193
194
|
|
194
195
|
end
|
195
196
|
end
|
196
|
-
|
data/lib/pwrake/version.rb
CHANGED
@@ -2,168 +2,147 @@ module Pwrake
|
|
2
2
|
|
3
3
|
class Executor
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
TLEN=32
|
8
|
-
|
9
|
-
def initialize(dir_class,id,shell_cmd,shell_rc)
|
5
|
+
def initialize(selector,dir_class,id)
|
6
|
+
@selector = selector
|
10
7
|
@id = id
|
11
|
-
@shell_rc = shell_rc
|
12
|
-
@shell_cmd = shell_cmd || ENV['SHELL'] || '/bin/sh'
|
13
|
-
@terminator = ""
|
14
|
-
TLEN.times{ @terminator << CHARS[rand(CHARS.length)] }
|
15
8
|
@out = Writer.instance
|
16
9
|
@log = LogExecutor.instance
|
17
|
-
@queue =
|
10
|
+
@queue = []
|
11
|
+
@rd_list = []
|
18
12
|
@dir = dir_class.new
|
19
|
-
@
|
20
|
-
@
|
21
|
-
@
|
22
|
-
LIST[@id] = self
|
23
|
-
@exec_thread = start_exec_thread
|
13
|
+
@dir.open
|
14
|
+
@dir.open_messages.each{|m| @log.info(m)}
|
15
|
+
@out.puts "#{@id}:open"
|
24
16
|
end
|
25
17
|
|
26
|
-
def
|
27
|
-
@
|
18
|
+
def stop
|
19
|
+
@stopped = true
|
20
|
+
@queue.clear
|
28
21
|
end
|
29
22
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
@dir.open_messages.each{|m| @log.info(m)}
|
35
|
-
@pid = Kernel.spawn(@shell_cmd,
|
36
|
-
:out=>@spawn_out,
|
37
|
-
:err=>@spawn_err,
|
38
|
-
:in=>@spawn_in,
|
39
|
-
:chdir=>@dir.current)
|
40
|
-
begin
|
41
|
-
@out.puts "#{@id}:open"
|
42
|
-
@shell_rc.each do |cmd|
|
43
|
-
run_rc(cmd)
|
44
|
-
end
|
45
|
-
while cmd = @queue.deq
|
46
|
-
run(cmd)
|
47
|
-
end
|
48
|
-
@sh_in.puts("exit")
|
49
|
-
@sh_in.flush
|
50
|
-
ensure
|
51
|
-
status = nil
|
52
|
-
begin
|
53
|
-
Timeout.timeout(5){
|
54
|
-
pid,status = Process.waitpid2(@pid)
|
55
|
-
}
|
56
|
-
rescue => exc
|
57
|
-
@log.error(([exc.to_s]+exc.backtrace).join("\n"))
|
58
|
-
@log.info("#{@id}:kill INT sh @pid=#{@pid}")
|
59
|
-
Process.kill("INT",@pid)
|
60
|
-
pid,status = Process.waitpid2(@pid)
|
61
|
-
end
|
62
|
-
@log.info("shell exit status: "+status.inspect)
|
63
|
-
end
|
64
|
-
rescue => exc
|
65
|
-
@out.puts "#{@id}:exc:#{exc}"
|
66
|
-
@log.error(([exc.to_s]+exc.backtrace).join("\n"))
|
67
|
-
ensure
|
68
|
-
@dir.close_messages.each{|m| @log.info(m)}
|
69
|
-
@dir.close
|
70
|
-
end
|
23
|
+
def close
|
24
|
+
if @thread
|
25
|
+
@thread.join(15)
|
26
|
+
sleep 0.1
|
71
27
|
end
|
28
|
+
@thread = Thread.new do
|
29
|
+
@dir.close_messages.each{|m| @log.info(m)}
|
30
|
+
@dir.close
|
31
|
+
end
|
32
|
+
rescue => exc
|
33
|
+
@log.error(([exc.to_s]+exc.backtrace).join("\n"))
|
72
34
|
end
|
73
35
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
77
|
-
cmd.call
|
78
|
-
when "cd"
|
79
|
-
@dir.cd
|
80
|
-
run_command("cd "+@dir.current)
|
81
|
-
#
|
82
|
-
when /^cd\s+(.*)$/
|
83
|
-
@dir.cd($1)
|
84
|
-
run_command("cd "+@dir.current)
|
85
|
-
#
|
86
|
-
when /^exit\b/
|
87
|
-
close
|
88
|
-
@out.puts "#{@id}:exit"
|
89
|
-
#
|
90
|
-
when String
|
91
|
-
run_command(cmd)
|
92
|
-
#
|
93
|
-
else
|
94
|
-
raise RuntimeError,"invalid cmd: #{cmd.inspect}"
|
36
|
+
def join
|
37
|
+
if @thread
|
38
|
+
@thread.join(15)
|
95
39
|
end
|
40
|
+
rescue => exc
|
41
|
+
@log.error(([exc.to_s]+exc.backtrace).join("\n"))
|
96
42
|
end
|
97
43
|
|
98
|
-
def
|
99
|
-
|
44
|
+
def execute(cmd)
|
45
|
+
return if @stopped
|
46
|
+
@queue.push(cmd)
|
47
|
+
start_process
|
100
48
|
end
|
101
49
|
|
102
|
-
def
|
103
|
-
|
50
|
+
def start_process
|
51
|
+
return if @thread # running
|
52
|
+
command = @queue.shift
|
53
|
+
return if !command # empty queue
|
54
|
+
@spawn_in, @sh_in = IO.pipe
|
55
|
+
@sh_out, @spawn_out = IO.pipe
|
56
|
+
@sh_err, @spawn_err = IO.pipe
|
57
|
+
|
58
|
+
@pid = Kernel.spawn(command,
|
59
|
+
:in=>@spawn_in,
|
60
|
+
:out=>@spawn_out,
|
61
|
+
:err=>@spawn_err,
|
62
|
+
:chdir=>@dir.current,
|
63
|
+
:pgroup=>true
|
64
|
+
)
|
65
|
+
@log.info "pid=#{@pid} started. command=#{command.inspect}"
|
66
|
+
|
67
|
+
@thread = Thread.new do
|
68
|
+
@pid2,@status = Process.waitpid2(@pid)
|
69
|
+
@spawn_in.close
|
70
|
+
@spawn_out.close
|
71
|
+
@spawn_err.close
|
72
|
+
end
|
73
|
+
|
74
|
+
@rd_out = Reader.new(@sh_out,"o")
|
75
|
+
@rd_err = Reader.new(@sh_err,"e")
|
76
|
+
@rd_list = [@rd_out,@rd_err]
|
77
|
+
|
78
|
+
@selector.add_reader(@sh_out){callback(@rd_out)}
|
79
|
+
@selector.add_reader(@sh_err){callback(@rd_err)}
|
104
80
|
end
|
105
81
|
|
106
|
-
def
|
107
|
-
|
108
|
-
@
|
109
|
-
@sh_in.flush
|
110
|
-
return
|
82
|
+
def callback(rd)
|
83
|
+
while s = rd.gets
|
84
|
+
@out.puts "#{@id}:#{rd.mode}:#{s.chomp}"
|
111
85
|
end
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
if s[0,TLEN] == @terminator
|
124
|
-
status = s[TLEN+1..-1]
|
125
|
-
io_set.delete(@sh_out)
|
126
|
-
else
|
127
|
-
yield "#{@id}:o:"+s
|
128
|
-
end
|
129
|
-
when @sh_err
|
130
|
-
if s[0,TLEN] == @terminator
|
131
|
-
io_set.delete(@sh_err)
|
132
|
-
else
|
133
|
-
yield "#{@id}:e:"+s
|
134
|
-
end
|
135
|
-
end
|
86
|
+
if rd.eof?
|
87
|
+
@selector.delete_reader(rd.io)
|
88
|
+
@rd_list.delete(rd)
|
89
|
+
if @rd_list.empty? # process_end
|
90
|
+
@thread = @pid = nil
|
91
|
+
@log.info inspect_status
|
92
|
+
@out.puts "#{@id}:z:#{exit_status}"
|
93
|
+
@sh_in.close
|
94
|
+
@sh_out.close
|
95
|
+
@sh_err.close
|
96
|
+
start_process # next process
|
136
97
|
end
|
137
|
-
break if io_set.empty?
|
138
98
|
end
|
139
|
-
|
99
|
+
rescue => exc
|
100
|
+
@log.error(([exc.to_s]+exc.backtrace).join("\n"))
|
101
|
+
stop
|
140
102
|
end
|
141
103
|
|
142
|
-
def
|
143
|
-
|
104
|
+
def inspect_status
|
105
|
+
s = @status
|
106
|
+
case
|
107
|
+
when s.signaled?
|
108
|
+
if s.coredump?
|
109
|
+
"pid=#{s.pid} dumped core."
|
110
|
+
else
|
111
|
+
"pid=#{s.pid} was killed by signal #{s.termsig}"
|
112
|
+
end
|
113
|
+
when s.stopped?
|
114
|
+
"pid=#{s.pid} was stopped by signal #{s.stopsig}"
|
115
|
+
when s.exited?
|
116
|
+
"pid=#{s.pid} exited normally. status=#{s.exitstatus}"
|
117
|
+
else
|
118
|
+
"unknown status %#x" % s.to_i
|
119
|
+
end
|
144
120
|
end
|
145
121
|
|
146
|
-
def
|
147
|
-
|
148
|
-
|
122
|
+
def exit_status
|
123
|
+
s = @status
|
124
|
+
case
|
125
|
+
when s.signaled?
|
126
|
+
if s.coredump?
|
127
|
+
"core_dumped"
|
128
|
+
else
|
129
|
+
"killed:#{s.termsig}"
|
130
|
+
end
|
131
|
+
when s.stopped?
|
132
|
+
"stopped:#{s.stopsig}"
|
133
|
+
when s.exited?
|
134
|
+
"#{s.exitstatus}"
|
135
|
+
else
|
136
|
+
"unknown:%#x" % s.to_i
|
137
|
+
end
|
149
138
|
end
|
150
139
|
|
151
140
|
def kill(sig)
|
152
|
-
|
141
|
+
stop
|
153
142
|
if @pid
|
154
|
-
|
155
|
-
|
156
|
-
s.each_line do |x|
|
157
|
-
pid = x.to_i
|
158
|
-
Process.kill(sig,pid)
|
159
|
-
@log.warn "Executor(id=#{@id})#kill pid=#{pid} sig=#{sig}"
|
160
|
-
end
|
161
|
-
if s.empty?
|
162
|
-
@log.warn "Executor(id=#{@id})#kill nothing killed"
|
163
|
-
end
|
143
|
+
Process.kill(sig,-@pid)
|
144
|
+
@log.warn "Executor(id=#{@id})#kill pid=#{@pid} sig=#{sig}"
|
164
145
|
end
|
165
|
-
@spawn_out.flush
|
166
|
-
@spawn_err.flush
|
167
146
|
end
|
168
147
|
|
169
148
|
end
|
@@ -1,24 +1,32 @@
|
|
1
1
|
module Pwrake
|
2
2
|
|
3
3
|
class Invoker
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
begin
|
5
|
+
# use Michael Grosser's Parallel module
|
6
|
+
# https://github.com/grosser/parallel
|
7
|
+
include Parallel::ProcessorCount
|
8
|
+
rescue
|
9
|
+
def processor_count
|
10
|
+
# only for Linux
|
11
|
+
IO.read("/proc/cpuinfo").scan(/^processor/).size
|
12
|
+
end
|
13
|
+
end
|
7
14
|
|
8
15
|
def initialize(dir_class, ncore, option)
|
9
16
|
@dir_class = dir_class
|
10
17
|
@option = option
|
18
|
+
@selector = Selector.new
|
19
|
+
@ex_list = {}
|
11
20
|
@out = Writer.instance # firstly replace $stderr
|
12
21
|
@log = LogExecutor.instance
|
13
22
|
@log.init(@option)
|
14
23
|
@log.open(@dir_class)
|
15
24
|
@out.add_logger(@log)
|
16
|
-
ncore_max = processor_count()
|
17
25
|
if ncore.kind_of?(Integer)
|
18
26
|
if ncore > 0
|
19
27
|
@ncore = ncore
|
20
28
|
else
|
21
|
-
@ncore =
|
29
|
+
@ncore = processor_count() + ncore
|
22
30
|
end
|
23
31
|
if @ncore <= 0
|
24
32
|
m = "Out of range: ncore=#{ncore.inspect}"
|
@@ -26,7 +34,7 @@ module Pwrake
|
|
26
34
|
raise ArgumentError,m
|
27
35
|
end
|
28
36
|
elsif ncore.nil?
|
29
|
-
@ncore =
|
37
|
+
@ncore = processor_count()
|
30
38
|
else
|
31
39
|
m = "Invalid argument: ncore=#{ncore.inspect}"
|
32
40
|
@out.puts "ncore:"+m
|
@@ -37,32 +45,31 @@ module Pwrake
|
|
37
45
|
Signal.trap("PIPE", "SIG_IGN")
|
38
46
|
end
|
39
47
|
|
40
|
-
def get_line
|
41
|
-
|
42
|
-
|
43
|
-
exit if !line
|
48
|
+
def get_line(io)
|
49
|
+
line = io.gets
|
50
|
+
if line
|
44
51
|
line.chomp!
|
45
52
|
line.strip!
|
46
53
|
@log.info ">#{line}"
|
47
|
-
return line
|
48
|
-
rescue
|
49
|
-
exit
|
50
54
|
end
|
55
|
+
return line
|
51
56
|
end
|
52
57
|
|
53
58
|
def run
|
54
59
|
setup_option
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
60
|
+
setup_loop
|
61
|
+
@rd = Reader.new($stdin)
|
62
|
+
@selector.add_reader($stdin){command_callback(@rd)}
|
63
|
+
@selector.loop
|
64
|
+
rescue => exc
|
65
|
+
@log.error(([exc.to_s]+exc.backtrace).join("\n"))
|
59
66
|
ensure
|
60
67
|
close_all
|
61
68
|
end
|
62
69
|
|
63
70
|
def setup_option
|
64
71
|
@log.info @option.inspect
|
65
|
-
@
|
72
|
+
@out.heartbeat = @option[:heartbeat]
|
66
73
|
@shell_cmd = @option[:shell_command]
|
67
74
|
@shell_rc = @option[:shell_rc] || []
|
68
75
|
(@option[:pass_env]||{}).each do |k,v|
|
@@ -71,64 +78,54 @@ module Pwrake
|
|
71
78
|
end
|
72
79
|
|
73
80
|
def setup_loop
|
74
|
-
while line = get_line
|
81
|
+
while line = get_line($stdin)
|
75
82
|
case line
|
76
83
|
when /^(\d+):open$/o
|
77
84
|
$1.split.each do |id|
|
78
|
-
Executor.new(@dir_class,id
|
85
|
+
@ex_list[id] = Executor.new(@selector,@dir_class,id)
|
79
86
|
end
|
80
87
|
when "setup_end"
|
81
|
-
return
|
88
|
+
return
|
82
89
|
else
|
83
|
-
|
84
|
-
|
85
|
-
end
|
86
|
-
false
|
87
|
-
end
|
88
|
-
|
89
|
-
def start_heartbeat
|
90
|
-
if @heartbeat_interval
|
91
|
-
@heartbeat_thread = Thread.new do
|
92
|
-
while true
|
93
|
-
@out.puts "heartbeat"
|
94
|
-
sleep @heartbeat_interval
|
90
|
+
if common_line(line)
|
91
|
+
raise RuntimeError,"exit during setup_loop"
|
95
92
|
end
|
96
93
|
end
|
97
94
|
end
|
95
|
+
raise RuntimeError,"incomplete setup_loop"
|
98
96
|
end
|
99
97
|
|
100
|
-
def
|
101
|
-
while line = get_line
|
98
|
+
def command_callback(rd)
|
99
|
+
while line = get_line(rd) # rd returns nil if line is incomplete
|
102
100
|
case line
|
103
101
|
when /^(\d+):(.*)$/o
|
104
102
|
id,cmd = $1,$2
|
105
|
-
|
106
|
-
if ex.nil?
|
107
|
-
if cmd=="exit"
|
108
|
-
@out.puts "#{id}:end"
|
109
|
-
next
|
110
|
-
else
|
111
|
-
ex = Executor.new(@dir_class,id,@shell_cmd,@shell_rc)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
ex.execute(cmd)
|
103
|
+
@ex_list[id].execute(cmd.chomp)
|
115
104
|
else
|
116
105
|
break if common_line(line)
|
117
106
|
end
|
118
107
|
end
|
108
|
+
if rd.eof?
|
109
|
+
# connection lost
|
110
|
+
raise RuntimeError,"lost connection to master"
|
111
|
+
end
|
119
112
|
end
|
120
113
|
|
121
114
|
def common_line(line)
|
122
115
|
case line
|
123
116
|
when /^exit$/o
|
117
|
+
@selector.delete_reader($stdin)
|
124
118
|
return true
|
125
119
|
#
|
126
120
|
when /^kill:(.*)$/o
|
127
|
-
|
121
|
+
sig = $1
|
122
|
+
sig = sig.to_i if /^\d+$/o =~ sig
|
123
|
+
@log.warn "killing worker, signal=#{sig}"
|
124
|
+
@ex_list.each{|id,ex| ex.kill(sig)}
|
128
125
|
return false
|
129
126
|
#
|
130
127
|
when /^p$/o
|
131
|
-
puts "
|
128
|
+
$stderr.puts "@ex_list = #{@ex_list.inspect}"
|
132
129
|
return false
|
133
130
|
#
|
134
131
|
else
|
@@ -138,26 +135,13 @@ module Pwrake
|
|
138
135
|
end
|
139
136
|
end
|
140
137
|
|
141
|
-
def kill_all(sig)
|
142
|
-
sig = sig.to_i if /^\d+$/o =~ sig
|
143
|
-
@log.warn "worker_killed:signal=#{sig}"
|
144
|
-
Executor::LIST.each{|id,exc| exc.kill(sig)}
|
145
|
-
end
|
146
|
-
|
147
138
|
def close_all
|
148
139
|
@log.info "close_all"
|
149
140
|
@heartbeat_thread.kill if @heartbeat_thread
|
150
141
|
Dir.chdir
|
151
|
-
|
152
|
-
ex_list
|
153
|
-
|
154
|
-
begin
|
155
|
-
ex_list.each{|ex| ex.join}
|
156
|
-
rescue => e
|
157
|
-
@log.error e
|
158
|
-
@log.error e.backtrace.join("\n")
|
159
|
-
end
|
160
|
-
@log.info "worker:end:#{id_list.inspect}"
|
142
|
+
@ex_list.each_value{|ex| ex.close}
|
143
|
+
@ex_list.each_value{|ex| ex.join}
|
144
|
+
@log.info "worker:end:#{@ex_list.keys.inspect}"
|
161
145
|
begin
|
162
146
|
Timeout.timeout(20){@log.close}
|
163
147
|
rescue => e
|
@@ -4,8 +4,17 @@ require "logger"
|
|
4
4
|
|
5
5
|
module Pwrake
|
6
6
|
|
7
|
+
DELEGATE_METHODS = [
|
8
|
+
:debug, :info, :error, :fatal, :warn, :unknown,
|
9
|
+
:debug?, :info?, :error?, :fatal?, :warn?, :unknown?,
|
10
|
+
:level, :level=,
|
11
|
+
:formatter, :formatter=,
|
12
|
+
:datetime_format, :datetime_format=
|
13
|
+
]
|
14
|
+
|
7
15
|
class DummyLogger
|
8
|
-
|
16
|
+
DELEGATE_METHODS.each do |m|
|
17
|
+
define_method(m){|*a|}
|
9
18
|
end
|
10
19
|
end
|
11
20
|
|
@@ -13,15 +22,10 @@ module Pwrake
|
|
13
22
|
include Singleton
|
14
23
|
extend Forwardable
|
15
24
|
|
16
|
-
def_delegators :@logger,
|
17
|
-
def_delegators :@logger, :debug?, :info?, :error?, :fatal?, :warn?, :unknown?
|
18
|
-
def_delegators :@logger, :level, :level=
|
19
|
-
def_delegators :@logger, :formatter, :formatter=
|
20
|
-
def_delegators :@logger, :datetime_format, :datetime_format=
|
25
|
+
def_delegators :@logger, *DELEGATE_METHODS
|
21
26
|
|
22
27
|
def initialize
|
23
28
|
@level = ::Logger::DEBUG
|
24
|
-
#@logger = @logger_stderr = ::Logger.new($stderr)
|
25
29
|
@logger = @logger_stderr = DummyLogger.new
|
26
30
|
@logger.level = @level
|
27
31
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Pwrake
|
2
|
+
|
3
|
+
class Reader
|
4
|
+
|
5
|
+
def initialize(io,mode="")
|
6
|
+
@io = io
|
7
|
+
@buf = ''
|
8
|
+
@eof = false
|
9
|
+
@mode = mode
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :io, :mode
|
13
|
+
|
14
|
+
def eof?
|
15
|
+
@eof && @buf.empty?
|
16
|
+
end
|
17
|
+
|
18
|
+
def gets
|
19
|
+
read_until("\n")
|
20
|
+
end
|
21
|
+
|
22
|
+
def read_until(sep="\r\n", chunk_size=8192)
|
23
|
+
until i = @buf.index(sep)
|
24
|
+
if s = _read(chunk_size)
|
25
|
+
@buf += s
|
26
|
+
else
|
27
|
+
if !@buf.empty? && @eof
|
28
|
+
buf = @buf; @buf = ''
|
29
|
+
return buf
|
30
|
+
else
|
31
|
+
return nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
@buf.slice!(0, i+sep.bytesize)
|
36
|
+
end
|
37
|
+
|
38
|
+
def _read(sz)
|
39
|
+
@io.read_nonblock(sz)
|
40
|
+
rescue EOFError
|
41
|
+
@eof = true
|
42
|
+
nil
|
43
|
+
rescue IO::WaitReadable
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
class Selector
|
51
|
+
|
52
|
+
def initialize
|
53
|
+
@readers = {}
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_reader(io,&callback)
|
57
|
+
@readers[io] = callback
|
58
|
+
end
|
59
|
+
|
60
|
+
def delete_reader(io)
|
61
|
+
@readers.delete(io)
|
62
|
+
end
|
63
|
+
|
64
|
+
def loop
|
65
|
+
while !@readers.empty?
|
66
|
+
r, = IO.select(@readers.keys,nil,nil)
|
67
|
+
r.each{|io| @readers[io].call} if r
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
data/lib/pwrake/worker/writer.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "singleton"
|
2
|
+
require "timeout"
|
2
3
|
|
3
4
|
module Pwrake
|
4
5
|
|
@@ -8,14 +9,26 @@ module Pwrake
|
|
8
9
|
def initialize
|
9
10
|
@out = $stdout
|
10
11
|
@mutex = Mutex.new
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
@queue = Queue.new
|
13
|
+
@heartbeat = 0
|
14
|
+
@thread = Thread.new do
|
15
|
+
loop do
|
16
|
+
begin
|
17
|
+
Timeout.timeout(@heartbeat) do
|
18
|
+
if s = @queue.deq
|
19
|
+
_puts s
|
20
|
+
end
|
21
|
+
end
|
22
|
+
rescue Timeout::Error
|
23
|
+
_puts "heartbeat"
|
24
|
+
end
|
16
25
|
end
|
17
26
|
end
|
18
|
-
|
27
|
+
end
|
28
|
+
|
29
|
+
def heartbeat=(heartbeat)
|
30
|
+
@heartbeat = heartbeat
|
31
|
+
@queue.enq(nil)
|
19
32
|
end
|
20
33
|
|
21
34
|
def add_logger(log)
|
@@ -23,20 +36,13 @@ module Pwrake
|
|
23
36
|
end
|
24
37
|
|
25
38
|
def puts(s)
|
26
|
-
|
27
|
-
@mutex.synchronize do
|
28
|
-
@out.print s+"\n"
|
29
|
-
end
|
30
|
-
@out.flush
|
31
|
-
rescue Errno::EPIPE
|
32
|
-
end
|
33
|
-
@log.info "<#{s}" if @log
|
39
|
+
@queue.enq(s)
|
34
40
|
end
|
35
41
|
|
36
|
-
def
|
42
|
+
def _puts(s)
|
37
43
|
begin
|
38
44
|
@mutex.synchronize do
|
39
|
-
@out.print s
|
45
|
+
@out.print s+"\n"
|
40
46
|
end
|
41
47
|
@out.flush
|
42
48
|
rescue Errno::EPIPE
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pwrake
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Masahiro TANAKA
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parallel
|
@@ -87,6 +87,7 @@ files:
|
|
87
87
|
- lib/pwrake/worker/invoker.rb
|
88
88
|
- lib/pwrake/worker/load.rb
|
89
89
|
- lib/pwrake/worker/log_executor.rb
|
90
|
+
- lib/pwrake/worker/reader.rb
|
90
91
|
- lib/pwrake/worker/shared_directory.rb
|
91
92
|
- lib/pwrake/worker/worker_main.rb
|
92
93
|
- lib/pwrake/worker/writer.rb
|
@@ -129,7 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
130
|
version: '0'
|
130
131
|
requirements: []
|
131
132
|
rubyforge_project:
|
132
|
-
rubygems_version: 2.
|
133
|
+
rubygems_version: 2.6.8
|
133
134
|
signing_key:
|
134
135
|
specification_version: 4
|
135
136
|
summary: Parallel Workflow engine based on Rake
|
@@ -151,4 +152,3 @@ test_files:
|
|
151
152
|
- spec/helper.rb
|
152
153
|
- spec/hosts
|
153
154
|
- spec/pwrake_spec.rb
|
154
|
-
has_rdoc:
|