jkr 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,5 @@
1
1
 
2
2
  require 'fileutils'
3
- require 'popen4'
4
3
  require 'thread'
5
4
 
6
5
  require 'jkr/userutils'
@@ -27,7 +26,7 @@ class Barrier
27
26
  end
28
27
  end
29
28
 
30
- class Jkr
29
+ module Jkr
31
30
  class Utils
32
31
  def self.reserve_next_dir(dir, suffix = "")
33
32
  dirs = Dir.glob("#{dir}#{File::SEPARATOR}???*")
@@ -37,341 +36,55 @@ class Jkr
37
36
  max_num = [$~[0].to_i, max_num].max
38
37
  end
39
38
  end
40
-
39
+
41
40
  num = max_num + 1
42
- dir = "#{dir}#{File::SEPARATOR}" + sprintf("%03d%s", num, suffix)
41
+ dir = "#{dir}#{File::SEPARATOR}" + sprintf("%05d%s", num, suffix)
43
42
  FileUtils.mkdir(dir)
44
43
  dir
45
44
  end
46
45
  end
47
46
 
48
- module PlanUtils
49
- # info about processes spawned by me
50
- def procdb
51
- @procdb ||= Hash.new
52
- end
53
- def procdb_spawn(pid, command, owner_thread)
54
- @procdb_mutex.synchronize do
55
- self.procdb[pid] = {
56
- :pid => pid,
57
- :command => command,
58
- :thread => owner_thread,
59
- :status => nil
60
- }
61
- end
62
- end
63
- def procdb_waitpid(pid)
64
- t = nil
65
- @procdb_mutex.synchronize do
66
- if self.procdb[pid]
67
- t = self.procdb[pid][:thread]
68
- end
69
- end
70
- t.join if t
71
- end
72
- def procdb_resetpid(pid)
73
- @procdb_mutex.synchronize do
74
- if self.procdb[pid]
75
- self.procdb.delete(pid)
76
- end
77
- end
78
- end
79
- def procdb_update_status(pid, status)
80
- @procdb_mutex.synchronize do
81
- if self.procdb[pid]
82
- self.procdb[pid][:status] = status
83
- end
84
- end
85
- end
86
- def procdb_get(pid)
87
- @procdb_mutex.synchronize do
88
- self.procdb[pid]
89
- end
90
- end
91
- def procdb_get_status(pid)
92
- proc = self.procdb_get(pid)
93
- proc && proc[:status]
94
- end
95
- def procdb_get_command(pid)
96
- proc = self.procdb_get(pid)
97
- proc && proc[:command]
98
- end
99
-
100
- def cmd(*args)
101
- @procdb_mutex ||= Mutex.new
102
- options = (if args.last.is_a? Hash
103
- args.pop
104
- else
105
- {}
106
- end)
107
- options = {
108
- :wait => true,
109
- :timeout => 0,
110
- :raise_failure => true,
111
- :stdin => nil,
112
- :stdout => [$stdout],
113
- :stderr => [$stderr]
114
- }.merge(options)
115
-
116
- if options[:timeout] > 0 && ! options[:wait]
117
- raise ArgumentError.new("cmd: 'wait' must be true if 'timeout' specified.")
118
- end
119
-
120
- start_time = Time.now
121
- pid = nil
122
- status = nil
123
- args.flatten!
124
- args.map!(&:to_s)
125
- command = args.join(" ")
126
- barrier = Barrier.new(2)
127
- process_exited = false
128
-
129
- t = Thread.new {
130
- pipers = []
131
- status = POpen4::popen4(command){|p_stdout, p_stderr, p_stdin, p_id|
132
- pid = p_id
133
- barrier.wait
134
- stdouts = if options[:stdout].is_a? Array
135
- options[:stdout]
136
- else
137
- [options[:stdout]]
138
- end
139
- stderrs = if options[:stderr].is_a? Array
140
- options[:stderr]
141
- else
142
- [options[:stderr]]
143
- end
144
- pipers << Thread.new{
145
- target = p_stdout
146
- timeout_count = 0
147
- while true
148
- begin
149
- if (ready = IO.select([target], [], [], 1))
150
- ready.first.each do |fd|
151
- buf = fd.read_nonblock(4096)
152
- stdouts.each{|out| out.print buf}
153
- end
154
- Thread.exit if target.eof?
155
- else
156
- if process_exited
157
- timeout_count += 1
158
- if timeout_count > 5
159
- target.close_read
160
- Thread.exit
161
- end
162
- end
163
- end
164
- rescue IOError => err
165
- if target.closed?
166
- Thread.exit
167
- end
168
- end
169
- end
170
- }
171
- pipers << Thread.new{
172
- target = p_stderr
173
- timeout_count = 0
174
- while true
175
- begin
176
- if (ready = IO.select([target], [], [], 1))
177
- ready.first.each do |fd|
178
- buf = fd.read_nonblock(4096)
179
- stderrs.each{|out| out.print buf}
180
- end
181
- Thread.exit if target.eof?
182
- else
183
- if process_exited
184
- timeout_count += 1
185
- if timeout_count > 5
186
- target.close_read
187
- Thread.exit
188
- end
189
- end
190
- end
191
- rescue IOError => err
192
- if target.closed?
193
- Thread.exit
194
- end
195
- end
196
- end
197
- }
198
- if options[:stdin]
199
- pipers << Thread.new{
200
- target = options[:stdin]
201
- timeout_count = 0
202
- while true
203
- begin
204
- if (ready = IO.select([target], [], [], 1))
205
- ready.first.each do |fd|
206
- buf = fd.read_nonblock(4096)
207
- p_stdin.print buf
208
- end
209
- else
210
- if process_exited
211
- timeout_count += 1
212
- if timeout_count > 5
213
- p_stdin.close_write
214
- Thread.exit
215
- end
216
- end
217
- end
218
- rescue IOError => err
219
- if target.closed?
220
- Thread.exit
221
- end
222
- end
223
- end
224
- }
225
- end
226
- }
227
- pipers.each{|t| t.join}
228
- raise ArgumentError.new("Invalid command: #{command}") unless status
229
- procdb_update_status(pid, status)
230
- }
231
- barrier.wait
232
- procdb_spawn(pid, command, t)
233
- timekeeper = nil
234
-
235
- killed = false
236
- timekeeper = nil
237
- if options[:timeout] > 0
238
- timekeeper = Thread.new do
239
- sleep(options[:timeout])
240
- begin
241
- Process.kill(:INT, pid)
242
- killed = true
243
- rescue Errno::ESRCH # No such process
244
- end
245
- end
246
- end
247
- if options[:wait]
248
- timekeeper.join if timekeeper
249
- t.join
250
- if (! killed) && options[:raise_failure] && status.exitstatus != 0
251
- raise RuntimeError.new("'#{command}' failed.")
252
- end
253
- end
254
- while ! pid
255
- sleep 0.001 # do nothing
256
- end
257
-
258
- pid
259
- end
260
-
261
- def with_process2(*args)
262
- options = (if args.last.is_a? Hash
263
- args.pop
264
- else
265
- {}
266
- end )
267
- options = {
268
- :kill_on_exit => false
269
- }.merge(options)
270
-
271
- command = args.flatten.map(&:to_s).join(" ")
272
- pid = Process.spawn(command)
273
-
274
- err = nil
275
- begin
276
- yield
277
- rescue Exception => e
278
- err = e
279
- end
280
-
281
- if options[:kill_on_exit]
282
- Process.kill(:INT, pid)
283
- else
284
- if err
285
- begin
286
- Process.kill(:TERM, pid)
287
- rescue Exception
288
- end
289
- else
290
- begin
291
- status = Process.waitpid(pid)
292
- p status
293
- rescue Errno::ESRCH
294
- end
295
- end
296
- end
297
- raise err if err
298
- end
299
-
300
- def with_process(*args)
301
- options = (if args.last.is_a? Hash
302
- args.pop
303
- else
304
- {}
305
- end )
306
- options = {
307
- :kill_on_exit => false
308
- }.merge(options)
309
- options[:wait] = false
310
-
311
- args.push(options)
312
- pid = cmd(*args)
313
-
314
- err = nil
315
- begin
316
- yield
317
- rescue Exception => e
318
- err = e
319
- end
320
-
321
- if options[:kill_on_exit]
322
- Process.kill(:INT, pid)
323
- else
324
- if err
325
- begin
326
- Process.kill(:TERM, pid)
327
- rescue Exception
328
- end
329
- else
330
- procdb_waitpid(pid)
331
- status = procdb_get_status(pid)
332
- unless status && status.exitstatus == 0
333
- command = procdb_get_command(pid) || "Unknown command"
334
- raise RuntimeError.new("'#{command}' failed.")
335
- end
336
- procdb_resetpid(pid)
337
- end
338
- end
339
-
340
- raise err if err
341
- end
342
-
343
- def use_script(name)
344
- name = name.to_s
345
- name = name + ".rb" unless name =~ /\.rb$/
346
- dir = @plan.jkr_env.jkr_script_dir
347
- path = File.join(dir, name)
348
- script = File.open(path, "r").read
349
- self.instance_eval(script, path, 1)
350
- true
351
- end
352
- end
353
-
354
47
  class TrialUtils
355
48
  def self.undef_routine_utils(plan)
356
- plan.routine.binding.eval <<EOS
49
+ _plan = plan
50
+ begin
51
+ if _plan.routine
52
+ _plan.routine.binding.eval <<EOS
357
53
  undef result_file
358
54
  undef result_file_name
55
+ undef rname
56
+ undef common_file_name
57
+ undef cname
359
58
  undef touch_result_file
360
59
  undef with_result_file
361
60
  EOS
61
+ end
62
+ end while _plan = _plan.base_plan
362
63
  end
363
64
 
364
65
  def self.define_routine_utils(result_dir, plan, params)
66
+ _plan = plan
67
+ begin
365
68
  line = __LINE__; src = <<EOS
366
69
  def result_file_name(basename)
367
70
  File.join(#{result_dir.inspect}, basename)
368
71
  end
72
+ def rname(basename)
73
+ result_file_name(basename)
74
+ end
369
75
 
370
76
  def result_file(basename, mode = "a+")
371
77
  path = result_file_name(basename)
372
78
  File.open(path, mode)
373
79
  end
374
80
 
81
+ def common_file_name(basename)
82
+ File.join(File.dirname(#{result_dir.inspect}), basename)
83
+ end
84
+ def cname(basename)
85
+ result_file_name(basename)
86
+ end
87
+
375
88
  def touch_result_file(basename, options = {})
376
89
  path = result_file_name(basename)
377
90
  FileUtils.touch(path, options)
@@ -391,7 +104,10 @@ def with_result_file(basename, mode = "a+")
391
104
  file.path
392
105
  end
393
106
  EOS
394
- plan.routine.binding.eval(src, __FILE__, line)
107
+ if _plan.routine
108
+ _plan.routine.binding.eval(src, __FILE__, line)
109
+ end
110
+ end while _plan = _plan.base_plan
395
111
  end
396
112
  end
397
113
 
@@ -414,7 +130,7 @@ def resultset()
414
130
  dirs = Dir.glob(File.join(#{resultset_dir.inspect}, "*"))
415
131
  dirs.map{|dir| File.basename dir}.select{|basename|
416
132
  basename =~ /\\A\\d{3,}\\Z/
417
- }
133
+ }.sort
418
134
  end
419
135
 
420
136
  def result_file_name(num, basename)
@@ -423,6 +139,9 @@ def result_file_name(num, basename)
423
139
  end
424
140
  File.join(#{resultset_dir.inspect}, num, basename)
425
141
  end
142
+ def rname(num, basename)
143
+ result_file_name(num, basename)
144
+ end
426
145
 
427
146
  def result_file(num, basename, mode = "r")
428
147
  path = result_file_name(num, basename)
@@ -432,6 +151,9 @@ end
432
151
  def common_file_name(basename)
433
152
  File.join(#{resultset_dir.inspect}, basename)
434
153
  end
154
+ def cname(basename)
155
+ common_file_name(basename)
156
+ end
435
157
 
436
158
  def common_file(basename, mode = "r")
437
159
  path = common_file_name(basename)
@@ -464,7 +186,9 @@ def with_result_file(basename, mode = "r")
464
186
  file.path
465
187
  end
466
188
  EOS
467
- plan.routine.binding.eval(src, __FILE__, line)
189
+ if plan.analysis
190
+ plan.analysis.binding.eval(src, __FILE__, line)
191
+ end
468
192
  end
469
193
  end
470
194
  end
@@ -0,0 +1,3 @@
1
+ module Jkr
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,52 @@
1
+ # -*- mode: ruby -*-
2
+
3
+ TERMWIDTH = 80
4
+
5
+ title "Default experiment plan"
6
+ description "This is a default experiment plan specification file."
7
+
8
+ def_parameters do
9
+ # use 'variable' for defining variables
10
+ variable :foo => ["fooA", "fooB"]
11
+ variable :bar => 1..5
12
+
13
+ # use 'parameter' for defining static parameters
14
+ parameter :baz => "baz"
15
+ end
16
+
17
+ # preparation
18
+ def_prep do |plan|
19
+ puts '*' * TERMWIDTH
20
+ puts "Start execution: #{plan.title}"
21
+ end
22
+
23
+ # routine executed for each combination of values of variables
24
+ def_routine do |plan, params|
25
+ puts '-' * TERMWIDTH
26
+
27
+ # the value of "parameter/variable :foo" can be obtained by
28
+ # "param[:foo]"
29
+ puts "hello world, #{params[:foo]}"
30
+ puts "hello world, #{params[:bar]}"
31
+
32
+ puts(params[:foo] + params[:baz])
33
+
34
+ touch = touch_result_file("touched-file.log")
35
+
36
+ with_result_file("test.log") do |test_log|
37
+ test_log.puts "hello world, #{params[:foo]}"
38
+ test_log.puts "hello world, #{params[:bar]}"
39
+ test_log.puts(params[:foo] + params[:baz])
40
+ [$stdout, test_log].map{|io| io.puts "touched #{touch}"}
41
+ end
42
+
43
+ puts '-' * TERMWIDTH
44
+ end
45
+
46
+ # cleanup
47
+ def_cleanup do |plan|
48
+ puts '*' * TERMWIDTH
49
+ puts "Finished execution: #{plan.title}"
50
+ puts "FileUtils.remove(#{plan.file_path.inspect})"
51
+ # FileUtils.remove(plan.file_path)
52
+ end