scout-gear 5.2.0 → 7.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.
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
+