scout-gear 5.2.0 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 5.2.0
1
+ 7.1.0
@@ -71,8 +71,28 @@ end
71
71
 
72
72
  class LockInterrupted < TryAgain; end
73
73
 
74
- #
75
- #class ClosedStream < StandardError; end
74
+ class ClosedStream < StandardError; end
75
+
76
+ class DoneProcessing < Exception
77
+ attr_accessor :pid
78
+ def initialize(pid = Process.pid)
79
+ @pid = pid
80
+ end
81
+
82
+ def message
83
+ "Done processing pid #{pid}"
84
+ end
85
+ end
86
+
87
+ class WorkerException < ScoutException
88
+ attr_accessor :exception, :pid
89
+ def initialize(exception, pid)
90
+ @exception = exception
91
+ @pid = pid
92
+ end
93
+ end
94
+
95
+
76
96
  #class OpenGzipError < StandardError; end
77
97
  #
78
98
  #
@@ -2,6 +2,7 @@ require_relative 'color_class'
2
2
  require_relative '../indiferent_hash'
3
3
 
4
4
  require 'term/ansicolor'
5
+ require 'colorist'
5
6
 
6
7
  module Colorize
7
8
  def self.colors=(colors)
@@ -9,7 +10,13 @@ module Colorize
9
10
  end
10
11
 
11
12
  def self.colors
12
- @colors ||= IndiferentHash.setup({green: "#00cd00" , red: "#cd0000" , yellow: "#ffd700" })
13
+ @colors ||= IndiferentHash.setup(Hash[<<-EOF.split("\n").collect{|l| l.split(" ")}])
14
+ green #00cd00
15
+ red #cd0000
16
+ yellow #ffd700
17
+ blue #0000cd
18
+ path blue
19
+ EOF
13
20
  end
14
21
 
15
22
  def self.diverging_colors=(colors)
@@ -33,7 +40,6 @@ module Colorize
33
40
  EOF
34
41
  end
35
42
 
36
-
37
43
  def self.from_name(color)
38
44
  return color if color =~ /^#?[0-9A-F]+$/i
39
45
  return colors[color.to_s] if colors.include?(color.to_s)
@@ -52,7 +58,7 @@ module Colorize
52
58
  when 'blue'
53
59
  colors["RoyalBlue"]
54
60
  else
55
- colors[color.to_s] || color
61
+ colors[color.to_s] || color.to_s
56
62
  end
57
63
  end
58
64
 
@@ -134,8 +140,22 @@ module Log
134
140
  WHITE, DARK, GREEN, YELLOW, RED = Color::SOLARIZED.values_at :base0, :base00, :green, :yellow, :magenta
135
141
 
136
142
  SEVERITY_COLOR = [reset, cyan, green, magenta, blue, yellow, red] #.collect{|e| "\033[#{e}"}
143
+ CONCEPT_COLORS = IndiferentHash.setup({
144
+ :title => magenta,
145
+ :path => blue,
146
+ :input => cyan,
147
+ :value => green,
148
+ :integer => green,
149
+ :negative => red,
150
+ :float => green,
151
+ :waiting => yellow,
152
+ :started => cyan,
153
+ :start => cyan,
154
+ :done => green,
155
+ :error => red,
156
+ })
137
157
  HIGHLIGHT = "\033[1m"
138
-
158
+
139
159
  def self.uncolor(str)
140
160
  "" << Term::ANSIColor.uncolor(str)
141
161
  end
@@ -144,15 +164,46 @@ module Log
144
164
  reset
145
165
  end
146
166
 
147
- def self.color(severity, str = nil, reset = false)
167
+ def self.color(color, str = nil, reset = false)
148
168
  return str.dup || "" if nocolor
149
- color = reset ? Term::ANSIColor.reset : ""
150
- color << SEVERITY_COLOR[severity] if Integer === severity
151
- color << Term::ANSIColor.send(severity) if Symbol === severity and Term::ANSIColor.respond_to? severity
169
+
170
+ if (color == :integer || color == :float) && Numeric === str
171
+ color = if str < 0
172
+ :red
173
+ elsif str > 1
174
+ :cyan
175
+ else
176
+ :green
177
+ end
178
+ end
179
+
180
+ if color == :status
181
+ color = case str.to_sym
182
+ when :done
183
+ :green
184
+ when :error, :aborted
185
+ :red
186
+ when :waiting, :queued
187
+ :yellow
188
+ when :started, :start, :streamming
189
+ :cyan
190
+ else
191
+ :cyan
192
+ end
193
+ end
194
+
195
+ color = SEVERITY_COLOR[color] if Integer === color
196
+ color = CONCEPT_COLORS[color] if CONCEPT_COLORS.include?(color)
197
+ color = Term::ANSIColor.send(color) if Symbol === color and Term::ANSIColor.respond_to?(color)
198
+
199
+ str = str.to_s unless str.nil?
200
+ return str if Symbol === color
201
+ color_str = reset ? Term::ANSIColor.reset : ""
202
+ color_str << color
152
203
  if str.nil?
153
- color
204
+ color_str
154
205
  else
155
- color + str.to_s + self.color(0)
206
+ color_str + str.to_s + Term::ANSIColor.reset
156
207
  end
157
208
  end
158
209
 
@@ -195,15 +195,16 @@ module Log
195
195
  def done(io = STDERR)
196
196
  done_msg = Log.color(:magenta, "· ") << Log.color(:green, "done")
197
197
  if @start
198
- ellapsed = (Time.now - @start).to_i
198
+ ellapsed = (Time.now - @start)
199
199
  else
200
200
  ellapsed = 0
201
201
  end
202
- ellapsed = [ellapsed/3600, ellapsed/60 % 60, ellapsed % 60].map{|t| "%02i" % t }.join(':')
203
- done_msg << " " << Log.color(:blue, (@ticks).to_s) << " #{bytes ? 'bytes' : 'items'} in " << Log.color(:green, ellapsed)
202
+ ellapsed_str = [ellapsed/3600, ellapsed/60 % 60, ellapsed % 60].map{|t| "%02i" % t }.join(':')
203
+ done_msg << " " << Log.color(:blue, (@ticks).to_s) << " #{bytes ? 'bytes' : 'items'} in " << Log.color(:green, ellapsed_str)
204
204
  @last_count = 0
205
205
  @last_time = @start
206
- done_msg << " - " << thr_msg
206
+ thr = ellapsed > 0 ? (@ticks / ellapsed).to_i.to_s : 0
207
+ done_msg << " - " << Log.color(:blue, thr) << " per second"
207
208
  done_msg << Log.color(:magenta, " · " << desc)
208
209
  print(io, Log.up_lines(@depth) << done_msg << Log.down_lines(@depth))
209
210
 
@@ -28,6 +28,8 @@ module Log
28
28
  end
29
29
 
30
30
  def self.new_bar(max, options = {})
31
+ options, max = max, nil if Hash === max
32
+ max = options[:max] if max.nil?
31
33
  cleanup_bars
32
34
  BAR_MUTEX.synchronize do
33
35
  Log::LAST.replace "new_bar" if Log::LAST == "progress"
@@ -24,6 +24,8 @@ module Log
24
24
  IndiferentHash.process_options options, :depth, :desc, :file, :bytes, :frequency, :process, :callback,
25
25
  :depth => 0, :frequency => 2
26
26
 
27
+ max = nil if TrueClass === max
28
+
27
29
  @max = max
28
30
  @ticks = 0
29
31
  @frequency = frequency
data/lib/scout/log.rb CHANGED
@@ -206,7 +206,11 @@ module Log
206
206
  line = line.sub('`',"'")
207
207
  color = :green if line =~ /workflow/
208
208
  color = :blue if line =~ /scout-/
209
- Log.color color, line
209
+ if color
210
+ Log.color color, line
211
+ else
212
+ line
213
+ end
210
214
  end unless stack.nil?
211
215
  end
212
216
 
@@ -41,9 +41,7 @@ module Misc
41
41
 
42
42
  def self.digest(obj)
43
43
  str = Misc.digest_str(obj)
44
- hash = Digest::MD5.hexdigest(str)
45
- Log.debug "Digest #{hash} - #{str}"
46
- hash
44
+ Digest::MD5.hexdigest(str)
47
45
  end
48
46
 
49
47
  def self.file_md5(file)
@@ -20,4 +20,22 @@ module Misc
20
20
  end
21
21
  res
22
22
  end
23
+
24
+ def self.profile(options = {})
25
+ require 'ruby-prof'
26
+ profiler = RubyProf::Profile.new
27
+ profiler.start
28
+ begin
29
+ res = yield
30
+ rescue Exception
31
+ puts "Profiling aborted"
32
+ raise $!
33
+ ensure
34
+ result = profiler.stop
35
+ printer = RubyProf::FlatPrinter.new(result)
36
+ printer.print(STDOUT, options)
37
+ end
38
+
39
+ res
40
+ end
23
41
  end
@@ -31,13 +31,16 @@ module Open
31
31
  Thread.current.report_on_exception = false
32
32
  consume_stream(io, false, into, into_close)
33
33
  end
34
+
34
35
  io.threads.push(consumer_thread) if io.respond_to?(:threads)
36
+ Thread.pass until consumer_thread["name"]
37
+
35
38
  consumer_thread
36
39
  else
37
40
  if into
38
- Log.medium "Consuming stream #{Log.fingerprint io} -> #{Log.fingerprint into}"
41
+ Log.low "Consuming stream #{Log.fingerprint io} -> #{Log.fingerprint into}"
39
42
  else
40
- Log.medium "Consuming stream #{Log.fingerprint io}"
43
+ Log.low "Consuming stream #{Log.fingerprint io}"
41
44
  end
42
45
 
43
46
  begin
@@ -53,7 +56,6 @@ module Open
53
56
  into_close = false unless into.respond_to? :close
54
57
  io.sync = true
55
58
 
56
- Log.high "started consuming stream #{Log.fingerprint io}"
57
59
  begin
58
60
  while c = io.readpartial(BLOCK_SIZE)
59
61
  into << c if into
@@ -67,15 +69,14 @@ module Open
67
69
  into.close if into and into_close and not into.closed?
68
70
  block.call if block_given?
69
71
 
70
- Log.high "Done consuming stream #{Log.fingerprint io} into #{into_path || into}"
71
72
  c
72
73
  rescue Aborted
73
- Log.high "Consume stream Aborted #{Log.fingerprint io} into #{into_path || into}"
74
+ Log.low "Consume stream Aborted #{Log.fingerprint io} into #{into_path || into}"
74
75
  io.abort $! if io.respond_to? :abort
75
76
  into.close if into.respond_to?(:closed?) && ! into.closed?
76
77
  FileUtils.rm into_path if into_path and File.exist?(into_path)
77
78
  rescue Exception
78
- Log.high "Consume stream Exception reading #{Log.fingerprint io} into #{into_path || into} - #{$!.message}"
79
+ Log.low "Consume stream Exception reading #{Log.fingerprint io} into #{into_path || into} - #{$!.message}"
79
80
  exception = io.stream_exception || $!
80
81
  io.abort exception if io.respond_to? :abort
81
82
  into.close if into.respond_to?(:closed?) && ! into.closed?
@@ -145,12 +146,12 @@ module Open
145
146
 
146
147
  Open.notify_write(path)
147
148
  rescue Aborted
148
- Log.medium "Aborted sensible_write -- #{ Log.reset << Log.color(:blue, path) }"
149
+ Log.low "Aborted sensible_write -- #{ Log.reset << Log.color(:blue, path) }"
149
150
  content.abort if content.respond_to? :abort
150
151
  Open.rm path if File.exist? path
151
152
  rescue Exception
152
153
  exception = (AbortedStream === content and content.exception) ? content.exception : $!
153
- Log.medium "Exception in sensible_write: [#{Process.pid}] #{exception.message} -- #{ Log.color :blue, path }"
154
+ Log.low "Exception in sensible_write: [#{Process.pid}] #{exception.message} -- #{ Log.color :blue, path }"
154
155
  content.abort if content.respond_to? :abort
155
156
  Open.rm path if File.exist? path
156
157
  raise exception
@@ -219,16 +220,15 @@ module Open
219
220
 
220
221
  #parent_pid = Process.pid
221
222
  pid = Process.fork {
222
- purge_pipes(sin)
223
- sout.close
224
223
  begin
224
+ purge_pipes(sin)
225
+ sout.close
225
226
 
226
227
  yield sin
227
228
  sin.close if close and not sin.closed?
228
229
 
229
230
  rescue Exception
230
231
  Log.exception $!
231
- #Process.kill :INT, parent_pid
232
232
  Kernel.exit!(-1)
233
233
  end
234
234
  Kernel.exit! 0
@@ -242,18 +242,18 @@ module Open
242
242
  ConcurrentStream.setup sout, :pair => sin
243
243
 
244
244
  thread = Thread.new do
245
- Thread.current["name"] = "Pipe input #{Log.fingerprint sin} => #{Log.fingerprint sout}"
246
- Thread.current.report_on_exception = false
247
245
  begin
246
+ Thread.current.report_on_exception = false
247
+ Thread.current["name"] = "Pipe input #{Log.fingerprint sin} => #{Log.fingerprint sout}"
248
248
 
249
249
  yield sin
250
250
 
251
251
  sin.close if close and not sin.closed? and not sin.aborted?
252
252
  rescue Aborted
253
- Log.medium "Aborted open_pipe: #{$!.message}"
253
+ Log.low "Aborted open_pipe: #{$!.message}"
254
254
  raise $!
255
255
  rescue Exception
256
- Log.medium "Exception in open_pipe: #{$!.message}"
256
+ Log.low "Exception in open_pipe: #{$!.message}"
257
257
  begin
258
258
  sout.threads.delete(Thread.current)
259
259
  sout.pair = []
@@ -269,6 +269,7 @@ module Open
269
269
 
270
270
  sin.threads = [thread]
271
271
  sout.threads = [thread]
272
+ Thread.pass until thread["name"]
272
273
  end
273
274
 
274
275
  sout
@@ -287,8 +288,8 @@ module Open
287
288
 
288
289
  splitter_thread = Thread.new(Thread.current) do |parent|
289
290
  begin
290
- Thread.current["name"] = "Splitter #{Log.fingerprint stream}"
291
291
  Thread.current.report_on_exception = false
292
+ Thread.current["name"] = "Splitter #{Log.fingerprint stream}"
292
293
 
293
294
  skip = [false] * num
294
295
  begin
@@ -317,7 +318,7 @@ module Open
317
318
  out_pipes.each do |sout|
318
319
  sout.abort if sout.respond_to? :abort
319
320
  end
320
- Log.medium "Tee aborting #{Log.fingerprint stream}"
321
+ Log.low "Tee aborting #{Log.fingerprint stream}"
321
322
  raise $!
322
323
  rescue Exception
323
324
  begin
@@ -332,7 +333,7 @@ module Open
332
333
  in_pipes.each do |sin|
333
334
  sin.close unless sin.closed?
334
335
  end
335
- Log.medium "Tee exception #{Log.fingerprint stream}"
336
+ Log.low "Tee exception #{Log.fingerprint stream}"
336
337
  rescue
337
338
  Log.exception $!
338
339
  ensure
@@ -348,7 +349,7 @@ module Open
348
349
  out_pipes.each do |sout|
349
350
  ConcurrentStream.setup sout, :threads => splitter_thread, :filename => filename, :pair => stream
350
351
  end
351
- splitter_thread.wakeup until splitter_thread["name"]
352
+ Thread.pass until splitter_thread["name"]
352
353
 
353
354
  main_pipe = out_pipes.first
354
355
  main_pipe.autojoin = true
@@ -370,4 +371,34 @@ module Open
370
371
  def self.tee_stream(stream)
371
372
  tee_stream_thread(stream)
372
373
  end
374
+
375
+ def self.read_stream(stream, size)
376
+ str = nil
377
+ Thread.pass while IO.select([stream],nil,nil,1).nil?
378
+ while not str = stream.read(size)
379
+ IO.select([stream],nil,nil,1)
380
+ Thread.pass
381
+ raise ClosedStream if stream.eof?
382
+ end
383
+
384
+ while str.length < size
385
+ raise ClosedStream if stream.eof?
386
+ IO.select([stream],nil,nil,1)
387
+ if new = stream.read(size-str.length)
388
+ str << new
389
+ end
390
+ end
391
+ str
392
+ end
393
+
394
+ def self.read_stream(stream, size)
395
+ str = ""
396
+ while str.length < size
397
+ missing = size - str.length
398
+ more = stream.read(missing)
399
+ str << more
400
+ end
401
+ str
402
+ end
403
+
373
404
  end
@@ -0,0 +1,148 @@
1
+ begin
2
+ require 'inline'
3
+ continue = true
4
+ rescue Exception
5
+ Log.warn "The RubyInline gem could not be loaded: semaphore synchronization will not work"
6
+ continue = false
7
+ end
8
+
9
+ if continue
10
+ module ScoutSemaphore
11
+ inline(:C) do |builder|
12
+ builder.prefix <<-EOF
13
+ #include <unistd.h>
14
+ #include <stdio.h>
15
+ #include <stdlib.h>
16
+ #include <semaphore.h>
17
+ #include <time.h>
18
+ #include <assert.h>
19
+ #include <errno.h>
20
+ #include <signal.h>
21
+ #include <fcntl.h>
22
+ EOF
23
+
24
+ builder.c_singleton <<-EOF
25
+ void create_semaphore(char* name, int value){
26
+ sem_open(name, O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO, value);
27
+ }
28
+ EOF
29
+ builder.c_singleton <<-EOF
30
+ void delete_semaphore(char* name){
31
+ sem_unlink(name);
32
+ }
33
+ EOF
34
+
35
+ builder.c_singleton <<-EOF
36
+ int wait_semaphore(char* name){
37
+ int ret;
38
+ sem_t* sem;
39
+ sem = sem_open(name, 0);
40
+ ret = sem_wait(sem);
41
+ sem_close(sem);
42
+ return(ret);
43
+ }
44
+ EOF
45
+
46
+ builder.c_singleton <<-EOF
47
+ void post_semaphore(char* name){
48
+ sem_t* sem;
49
+ sem = sem_open(name, 0);
50
+ sem_post(sem);
51
+ sem_close(sem);
52
+ }
53
+ EOF
54
+ end
55
+
56
+ SEM_MUTEX = Mutex.new
57
+ def self.synchronize(sem)
58
+ ret = ScoutSemaphore.wait_semaphore(sem)
59
+ raise SemaphoreInterrupted if ret == -1
60
+ begin
61
+ yield
62
+ ensure
63
+ ScoutSemaphore.post_semaphore(sem)
64
+ end
65
+ end
66
+
67
+ def self.with_semaphore(size, file = nil)
68
+ if file.nil?
69
+ file = "/" << Misc.digest(rand(1000000000000).to_s) if file.nil?
70
+ else
71
+ file = file.gsub('/', '_') if file
72
+ end
73
+
74
+ begin
75
+ Log.debug "Creating semaphore (#{ size }): #{file}"
76
+ ScoutSemaphore.create_semaphore(file, size)
77
+ yield file
78
+ ensure
79
+ Log.debug "Removing semaphore #{ file }"
80
+ ScoutSemaphore.delete_semaphore(file)
81
+ end
82
+ end
83
+
84
+ def self.fork_each_on_semaphore(elems, size, file = nil)
85
+
86
+ TSV.traverse elems, :cpus => size, :bar => "Fork each on semaphore: #{ Misc.fingerprint elems }", :into => Set.new do |elem|
87
+ elems.annotate elem if elems.respond_to? :annotate
88
+ begin
89
+ yield elem
90
+ rescue Interrupt
91
+ Log.warn "Process #{Process.pid} was aborted"
92
+ end
93
+ nil
94
+ end
95
+ nil
96
+ end
97
+
98
+ def self.thread_each_on_semaphore(elems, size)
99
+ mutex = Mutex.new
100
+ count = 0
101
+ cv = ConditionVariable.new
102
+ wait_mutex = Mutex.new
103
+
104
+ begin
105
+
106
+ threads = []
107
+ wait_mutex.synchronize do
108
+ threads = elems.collect do |elem|
109
+ Thread.new(elem) do |elem|
110
+
111
+ continue = false
112
+ mutex.synchronize do
113
+ while not continue do
114
+ if count < size
115
+ continue = true
116
+ count += 1
117
+ end
118
+ mutex.sleep 1 unless continue
119
+ end
120
+ end
121
+
122
+ begin
123
+ yield elem
124
+ rescue Interrupt
125
+ Log.error "Thread was aborted while processing: #{Misc.fingerprint elem}"
126
+ raise $!
127
+ ensure
128
+ mutex.synchronize do
129
+ count -= 1
130
+ cv.signal if mutex.locked?
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ threads.each do |thread|
138
+ thread.join
139
+ end
140
+ rescue Exception
141
+ Log.exception $!
142
+ Log.info "Ensuring threads are dead: #{threads.length}"
143
+ threads.each do |thread| thread.kill end
144
+ end
145
+ end
146
+ end
147
+ end
148
+