utilrb 3.1.0 → 3.2.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.
@@ -1,5 +1,5 @@
1
- require 'set'
2
- require 'shellwords'
1
+ require "set"
2
+ require "shellwords"
3
3
 
4
4
  module Utilrb
5
5
  # Access to information from pkg-config(1)
@@ -32,7 +32,7 @@ module Utilrb
32
32
  # pkg.static
33
33
  #
34
34
  # Arbitrary variables defined in the .pc file can be accessed with
35
- #
35
+ #
36
36
  # pkg.prefix
37
37
  # pkg.libdir
38
38
  #
@@ -62,7 +62,9 @@ module Utilrb
62
62
 
63
63
  # Returns the pkg-config object that matches the given name, and
64
64
  # optionally a version string
65
- def self.get(name, version_spec = nil, preset_variables = Hash.new, minimal: false, pkg_config_path: self.pkg_config_path)
65
+ def self.get(name, version_spec = nil, preset_variables = Hash.new,
66
+ minimal: false, pkg_config_path: self.pkg_config_path, memo: Hash.new)
67
+
66
68
  paths = find_all_package_files(name, pkg_config_path: pkg_config_path)
67
69
  if paths.empty?
68
70
  raise NotFound.new(name), "cannot find the pkg-config specification for #{name}"
@@ -74,14 +76,15 @@ module Utilrb
74
76
 
75
77
  # Now try to find a matching spec
76
78
  if match = find_matching_version(candidates, version_spec)
77
- match
79
+ memo[[name, version_spec]] = [false, match]
78
80
  else
79
- raise NotFound, "found #{candidates.size} packages for #{name}, but none match the version specification #{version_spec}"
81
+ raise NotFound, "found #{candidates.size} packages for #{name},"\
82
+ " but none match the version specification #{version_spec}"
80
83
  end
81
84
 
82
- if !minimal
83
- match.load_fields
84
- end
85
+ match.load_fields(memo: memo) unless minimal
86
+
87
+ memo[[name, version_spec]] = [true, match]
85
88
  match
86
89
  end
87
90
 
@@ -93,8 +96,8 @@ module Utilrb
93
96
  # ...
94
97
  # @return [PkgConfig] the pkg-config description
95
98
  # @raise [NotFound] if the package is not found
96
- def self.new(name, version_spec = nil, options = Hash.new)
97
- get(name, version_spec, options)
99
+ def self.new(name, version_spec = nil, **options)
100
+ get(name, version_spec, **options)
98
101
  end
99
102
 
100
103
  # Returns the first package in +candidates+ that match the given version
@@ -128,29 +131,30 @@ module Utilrb
128
131
  end
129
132
 
130
133
  # Exception raised when a request pkg-config file is not found
131
- class NotFound < RuntimeError
134
+ class NotFound < RuntimeError
132
135
  # The name of the pkg-config package
133
- attr_reader :name
136
+ attr_reader :name
134
137
 
135
- def initialize(name); @name = name end
136
- end
138
+ def initialize(name); @name = name end
139
+ end
137
140
 
138
141
  # Exception raised when invalid data is found in a pkg-config file
139
142
  class Invalid < RuntimeError
140
143
  # The name of the pkg-config package
141
- attr_reader :name
144
+ attr_reader :name
142
145
 
143
- def initialize(name); @name = name end
146
+ def initialize(name); @name = name end
144
147
  end
145
148
 
146
-
147
149
  attr_reader :path
148
-
149
- # The module name
150
- attr_reader :name
150
+
151
+ # The module name
152
+ attr_reader :name
151
153
  attr_reader :description
154
+
152
155
  # The module version as a string
153
156
  attr_reader :raw_version
157
+
154
158
  # The module version, as an array of integers
155
159
  attr_reader :version
156
160
 
@@ -165,13 +169,13 @@ module Utilrb
165
169
  # @return [Array<PkgConfig>]
166
170
  attr_reader :requires
167
171
 
168
- # Create a PkgConfig object for the package +name+
169
- # Raises PkgConfig::NotFound if the module does not exist
170
- def initialize(name)
171
- @name = name
172
- @fields = Hash.new
173
- @variables = Hash.new
174
- end
172
+ # Create a PkgConfig object for the package +name+
173
+ # Raises PkgConfig::NotFound if the module does not exist
174
+ def initialize(name)
175
+ @name = name
176
+ @fields = Hash.new
177
+ @variables = Hash.new
178
+ end
175
179
 
176
180
  # Helper method that expands ${word} in +value+ using the name to value
177
181
  # map +variables+
@@ -192,31 +196,38 @@ module Utilrb
192
196
  value
193
197
  end
194
198
 
195
- def self.parse_dependencies(string)
196
- if string =~ /,/
197
- packages = string.split(',')
198
- else
199
- packages = []
200
- words = string.split(' ')
201
- while !words.empty?
202
- w = words.shift
203
- if w =~ /[<>=]/
204
- packages[-1] += " #{w} #{words.shift}"
199
+ class DependencyLoop < RuntimeError; end
200
+
201
+ def self.parse_dependencies(string, allow_loops: false, memo: Hash.new)
202
+ string = string.gsub(',', ' ')
203
+ packages = []
204
+ words = string.split(' ')
205
+ while !words.empty?
206
+ w = words.shift
207
+ if w =~ /[<>=]/
208
+ version = words.shift
209
+ if version =~ /[\d\.]+/
210
+ packages[-1][1] = "#{w} #{version}"
205
211
  else
206
- packages << w
212
+ packages << [version, nil]
207
213
  end
214
+ else
215
+ packages << [w, nil]
208
216
  end
209
217
  end
210
-
211
218
  result = packages.map do |dep|
212
- dep = dep.strip
213
- if dep =~ /^(#{PACKAGE_NAME_RX})\s*([=<>]+.*)/
214
- PkgConfig.get($1, $2.strip)
219
+ finished, pkg = memo[dep]
220
+ if pkg
221
+ if allow_loops || finished
222
+ pkg
223
+ else
224
+ raise DependencyLoop, "found a dependency loop"
225
+ end
215
226
  else
216
- PkgConfig.get(dep)
227
+ PkgConfig.get(*dep, memo: memo)
217
228
  end
218
229
  end
219
- result
230
+ result.compact
220
231
  end
221
232
 
222
233
  SHELL_VARS = %w{Cflags Libs Libs.private}
@@ -256,7 +267,6 @@ module Utilrb
256
267
  end
257
268
  end.compact
258
269
 
259
-
260
270
  raw_variables, raw_fields = Hash.new, Hash.new
261
271
  file.each do |line|
262
272
  case line
@@ -271,7 +281,7 @@ module Utilrb
271
281
  end
272
282
  return raw_variables, raw_fields
273
283
  end
274
-
284
+
275
285
  def expand_variables(raw_variables)
276
286
  raw_variables = raw_variables.dup
277
287
 
@@ -288,9 +298,9 @@ module Utilrb
288
298
  end
289
299
  variables
290
300
  end
291
-
301
+
292
302
  def expand_field(name, field)
293
- if SHELL_VARS.include?(name)
303
+ if SHELL_VARS.include?(name)
294
304
  value = Shellwords.shellsplit(field)
295
305
  resolved = Array.new
296
306
  while !value.empty?
@@ -310,17 +320,28 @@ module Utilrb
310
320
  end
311
321
  end
312
322
 
323
+ def builtin_variables(path)
324
+ {
325
+ "pcfiledir" => File.dirname(path),
326
+ "pc_sysrootdir" => sysrootdir
327
+ }
328
+ end
329
+
313
330
  def load_variables(path, preset_variables = Hash.new)
314
331
  raw_variables, raw_fields = parse(path)
315
332
  raw_variables = preset_variables.merge(raw_variables)
316
- expand_variables(raw_variables)
333
+ expand_variables(
334
+ raw_variables.merge(builtin_variables(path))
335
+ )
317
336
  end
318
-
337
+
319
338
  def load_minimal(path, preset_variables = Hash.new)
320
339
  raw_variables, raw_fields = parse(path)
321
340
  raw_variables = preset_variables.merge(raw_variables)
322
341
 
323
- @variables = expand_variables(raw_variables)
342
+ @variables = expand_variables(
343
+ raw_variables.merge(builtin_variables(path))
344
+ )
324
345
  if raw_fields['Version']
325
346
  @raw_version = expand_field('Version', raw_fields['Version'])
326
347
  else
@@ -333,7 +354,7 @@ module Utilrb
333
354
  @path = path
334
355
  end
335
356
 
336
- def load_fields
357
+ def load_fields(memo: Hash.new)
337
358
  fields = Hash.new
338
359
  @raw_fields.each do |name, value|
339
360
  fields[name] = expand_field(name, value)
@@ -344,27 +365,39 @@ module Utilrb
344
365
  @description = (fields['Description'] || '')
345
366
 
346
367
  # Get the requires/conflicts
347
- @requires = PkgConfig.parse_dependencies(fields['Requires'] || '')
348
- @requires_private = PkgConfig.parse_dependencies(fields['Requires.private'] || '')
349
- @conflicts = PkgConfig.parse_dependencies(fields['Conflicts'] || '')
368
+ @requires = PkgConfig.parse_dependencies(
369
+ fields['Requires'] || '', allow_loops: false, memo: memo)
370
+ @requires_private = PkgConfig.parse_dependencies(
371
+ fields['Requires.private'] || '', allow_loops: false, memo: memo)
372
+ @conflicts = PkgConfig.parse_dependencies(
373
+ fields['Conflicts'] || '', allow_loops: true, memo: memo)
350
374
 
351
375
  # And finally resolve the compilation flags
352
- @cflags = fields['Cflags'] || []
376
+ cflags = fields['Cflags'] || []
377
+ cflags.uniq!
378
+ cflags -= self.class.default_search_path_I
379
+ cflags = apply_sysrootdir(cflags, "-I")
353
380
  @requires.each do |pkg|
354
- @cflags.concat(pkg.raw_cflags)
381
+ cflags.concat(pkg.raw_cflags)
355
382
  end
356
383
  @requires_private.each do |pkg|
357
- @cflags.concat(pkg.raw_cflags)
384
+ cflags.concat(pkg.raw_cflags)
358
385
  end
359
- @cflags.uniq!
360
- @cflags.delete('-I/usr/include')
361
- @ldflags = Hash.new
362
- @ldflags[false] = fields['Libs'] || []
363
- @ldflags[false].delete('-L/usr/lib')
364
- @ldflags[false].uniq!
365
- @ldflags[true] = @ldflags[false] + (fields['Libs.private'] || [])
366
- @ldflags[true].delete('-L/usr/lib')
367
- @ldflags[true].uniq!
386
+ @cflags = cflags
387
+
388
+ ldflags_public = fields['Libs'] || []
389
+ ldflags_public.uniq!
390
+ ldflags_private = ldflags_public + (fields['Libs.private'] || [])
391
+ ldflags_private.uniq!
392
+
393
+ ldflags_public -= self.class.default_search_path_L
394
+ ldflags_public = apply_sysrootdir(ldflags_public, "-L")
395
+ ldflags_private -= self.class.default_search_path_L
396
+ ldflags_private = apply_sysrootdir(ldflags_private, "-L")
397
+ @ldflags = {
398
+ false => ldflags_public,
399
+ true => ldflags_private
400
+ }
368
401
 
369
402
  @ldflags_with_requires = {
370
403
  true => @ldflags[true].dup,
@@ -387,7 +420,7 @@ module Utilrb
387
420
  load_fields
388
421
  end
389
422
 
390
- def self.define_pkgconfig_action(action) # :nodoc:
423
+ def self.define_pkgconfig_action(action) # :nodoc:
391
424
  class_eval <<-EOD, __FILE__, __LINE__+1
392
425
  def pkgconfig_#{action.gsub(/-/, '_')}(static = false)
393
426
  if static
@@ -397,8 +430,8 @@ module Utilrb
397
430
  end
398
431
  end
399
432
  EOD
400
- nil
401
- end
433
+ nil
434
+ end
402
435
 
403
436
  def pkgconfig_variable(varname)
404
437
  `pkg-config --variable=#{varname}`.strip
@@ -411,6 +444,7 @@ module Utilrb
411
444
  if result.any?(&:empty?)
412
445
  raise Invalid.new(name), "empty include directory (-I without argument) found in pkg-config package #{name}"
413
446
  end
447
+
414
448
  result
415
449
  end
416
450
 
@@ -421,12 +455,38 @@ module Utilrb
421
455
  if result.any?(&:empty?)
422
456
  raise Invalid.new(name), "empty link directory (-L without argument) found in pkg-config package #{name}"
423
457
  end
458
+
424
459
  result
425
460
  end
426
461
 
427
- ACTIONS = %w{cflags cflags-only-I cflags-only-other
462
+ # A "new root" that should be prepended to -L and -I flags
463
+ def sysrootdir
464
+ ENV["PKG_CONFIG_SYSROOT_DIR"] || "/"
465
+ end
466
+
467
+ # @api private
468
+ #
469
+ # Apply {#sysrootdir} to all the given paths flags (-I or -L)
470
+ def apply_sysrootdir(entries, flag_name)
471
+ sysrootdir = self.sysrootdir
472
+ return entries if sysrootdir == "/"
473
+
474
+ entries.map do |v|
475
+ if v.start_with?(flag_name)
476
+ "#{flag_name}#{sysrootdir}#{v[2..-1]}"
477
+ else
478
+ v
479
+ end
480
+ end
481
+ end
482
+
483
+ ACTIONS = %w{cflags cflags-only-I cflags-only-other
428
484
  libs libs-only-L libs-only-l libs-only-other}
429
- ACTIONS.each { |action| define_pkgconfig_action(action) }
485
+ ACTIONS.each { |action| define_pkgconfig_action(action) }
486
+
487
+ def conflicts
488
+ @conflicts
489
+ end
430
490
 
431
491
  def raw_cflags
432
492
  @cflags
@@ -495,13 +555,13 @@ module Utilrb
495
555
  raw_libs_only_other(static).join(" ")
496
556
  end
497
557
 
498
- def method_missing(varname, *args, &proc) # :nodoc:
499
- if args.empty?
558
+ def method_missing(varname, *args, &proc) # :nodoc:
559
+ if args.empty?
500
560
  variables[varname.to_s]
501
- else
502
- super(varname, *args, &proc)
503
- end
504
- end
561
+ else
562
+ super(varname, *args, &proc)
563
+ end
564
+ end
505
565
 
506
566
  def self.pkg_config_path
507
567
  ENV['PKG_CONFIG_PATH']
@@ -509,9 +569,8 @@ module Utilrb
509
569
 
510
570
  def self.each_pkgconfig_directory(pkg_config_path: self.pkg_config_path, &block)
511
571
  return enum_for(__method__) if !block_given?
512
- if pkg_config_path
513
- pkg_config_path.split(':').each(&block)
514
- end
572
+
573
+ pkg_config_path.split(':').each(&block) if pkg_config_path
515
574
  default_search_path.each(&block)
516
575
  end
517
576
 
@@ -561,34 +620,64 @@ module Utilrb
561
620
  end
562
621
 
563
622
 
564
- FOUND_PATH_RX = /Scanning directory (?:#\d+ )?'(.*\/)((?:lib|lib64|share)\/.*)'$/
565
- NONEXISTENT_PATH_RX = /Cannot open directory (?:#\d+ )?'.*\/((?:lib|lib64|share)\/.*)' in package search path:.*/
623
+ PKGCONFIG_PATH_RX = %r{.*/((?:lib|lib64|share)/.*)}.freeze
566
624
 
567
625
  # Returns the system-wide search path that is embedded in pkg-config
568
626
  def self.default_search_path
569
- if !@default_search_path
570
- output = `LANG=C PKG_CONFIG_PATH= pkg-config --debug 2>&1`.split("\n")
571
- @default_search_path = output.grep(FOUND_PATH_RX).
572
- map { |l| l.gsub(FOUND_PATH_RX, '\1\2') }
573
- end
574
- return @default_search_path
627
+ @default_search_path ||=
628
+ `LANG=C pkg-config --variable pc_path pkg-config`
629
+ .strip
630
+ .split(":")
631
+ .grep(PKGCONFIG_PATH_RX)
632
+ .to_set
575
633
  end
634
+
576
635
  @default_search_path = nil
577
636
 
637
+ def self.arch_dir
638
+ return if @arch_dir == false
639
+
640
+ unless @arch_dir
641
+ suffix_with_arch =
642
+ default_search_suffixes
643
+ .find { |p| %r{^lib/[^/]+/pkgconfig} =~ p }
644
+
645
+ @arch_dir =
646
+ if suffix_with_arch
647
+ suffix_with_arch.split("/")[1]
648
+ else
649
+ false
650
+ end
651
+ end
652
+
653
+ @arch_dir
654
+ end
655
+
656
+ def self.default_search_path_L # rubocop:disable Naming/MethodName
657
+ unless @default_search_path_L
658
+ arch_dir = self.arch_dir
659
+ @default_search_path_L = # rubocop:disable Naming/VariableName
660
+ ["-L/usr/lib", "-L/lib"]
661
+ .flat_map { |p| [p, "#{p}/#{arch_dir}"] }
662
+ end
663
+
664
+ @default_search_path_L
665
+ end
666
+
667
+ def self.default_search_path_I # rubocop:disable Naming/MethodName
668
+ @default_search_path_I ||= ["-I/usr/include"] # rubocop:disable Naming/VariableName
669
+ end
670
+
578
671
  # Returns the system-wide standard suffixes that should be appended to
579
672
  # new prefixes to find pkg-config files
580
673
  def self.default_search_suffixes
581
- if !@default_search_suffixes
582
- output = `LANG=C PKG_CONFIG_PATH= pkg-config --debug 2>&1`.split("\n")
583
- found_paths = output.grep(FOUND_PATH_RX).
584
- map { |l| l.gsub(FOUND_PATH_RX, '\2') }.
585
- to_set
586
- not_found = output.grep(NONEXISTENT_PATH_RX).
587
- map { |l| l.gsub(NONEXISTENT_PATH_RX, '\1') }.
588
- to_set
589
- @default_search_suffixes = found_paths | not_found
590
- end
591
- return @default_search_suffixes
674
+ @default_search_suffixes ||=
675
+ `LANG=C pkg-config --variable pc_path pkg-config`
676
+ .strip
677
+ .split(":")
678
+ .grep(PKGCONFIG_PATH_RX)
679
+ .map { |l| l.gsub(PKGCONFIG_PATH_RX, '\1') }
680
+ .to_set
592
681
  end
593
682
  end
594
683
  end
@@ -9,8 +9,8 @@ module Utilrb
9
9
  #
10
10
  # @example Using a thread pool of 10 threads
11
11
  # pool = ThreadPool.new(10)
12
- # 0.upto(9) do
13
- # pool.process do
12
+ # 0.upto(9) do
13
+ # pool.process do
14
14
  # sleep 1
15
15
  # puts "done"
16
16
  # end
@@ -40,7 +40,7 @@ module Utilrb
40
40
  attr_reader :pool
41
41
 
42
42
  # State of the task
43
- #
43
+ #
44
44
  # @return [:waiting,:running,:stopping,:finished,:terminated,:exception] the state
45
45
  attr_reader :state
46
46
 
@@ -54,7 +54,7 @@ module Utilrb
54
54
  # return [Thread] the thread
55
55
  attr_reader :thread
56
56
 
57
- # The time the task was queued
57
+ # The time the task was queued
58
58
  #
59
59
  # return [Time] the time
60
60
  attr_accessor :queued_at
@@ -115,15 +115,15 @@ module Utilrb
115
115
  def exception?; @state == :exception; end
116
116
 
117
117
  # A new task which can be added to the work queue of a {ThreadPool}.
118
- # If a sync key is given no task having the same key will be
118
+ # If a sync key is given no task having the same key will be
119
119
  # executed in parallel which is useful for instance member calls
120
120
  # which are not thread safe.
121
121
  #
122
122
  # @param [Hash] options The options of the task.
123
123
  # @option options [Object] :sync_key The sync key
124
124
  # @option options [Proc] :callback The callback
125
- # @option options [Object] :default Default value returned when an error ocurred which was handled.
126
- # @param [Array] args The arguments for the code block
125
+ # @option options [Object] :default Default value returned when an error ocurred which was handled.
126
+ # @param [Array] args The arguments for the code block
127
127
  # @param [#call] block The code block
128
128
  def initialize (options = Hash.new,*args, &block)
129
129
  unless block
@@ -162,7 +162,7 @@ module Utilrb
162
162
  # returns true if the task has a default return vale
163
163
  # @return [Boolean]
164
164
  def default?
165
- @mutex.synchronize do
165
+ @mutex.synchronize do
166
166
  @default != nil
167
167
  end
168
168
  end
@@ -170,7 +170,7 @@ module Utilrb
170
170
  #sets all internal state to running
171
171
  #call execute after that.
172
172
  def pre_execute(pool=nil)
173
- @mutex.synchronize do
173
+ @mutex.synchronize do
174
174
  #store current thread to be able to terminate
175
175
  #the thread
176
176
  @pool = pool
@@ -182,7 +182,7 @@ module Utilrb
182
182
 
183
183
  # Executes the task.
184
184
  # Should be called from a worker thread after pre_execute was called.
185
- # After execute returned and the task was deleted
185
+ # After execute returned and the task was deleted
186
186
  # from any internal list finalize must be called
187
187
  # to propagate the task state.
188
188
  def execute()
@@ -200,7 +200,7 @@ module Utilrb
200
200
  end
201
201
  @stopped_at = Time.now
202
202
  end
203
-
203
+
204
204
  # propagates the tasks state
205
205
  # should be called after execute
206
206
  def finalize
@@ -225,7 +225,7 @@ module Utilrb
225
225
  end
226
226
  end
227
227
 
228
- # Called from the worker thread when the work is done
228
+ # Called from the worker thread when the work is done
229
229
  #
230
230
  # @yield [Object,Exception] The callback
231
231
  def callback(&block)
@@ -249,6 +249,10 @@ module Utilrb
249
249
  0
250
250
  end
251
251
  end
252
+
253
+ def to_s
254
+ "#<Utilrb::ThreadPool::Task #{@state} #{@block}>"
255
+ end
252
256
  end
253
257
 
254
258
  # The minimum number of worker threads.
@@ -270,12 +274,12 @@ module Utilrb
270
274
  #
271
275
  # @return [Fixnum]
272
276
  attr_reader :waiting
273
-
277
+
274
278
  # The average execution time of a (running) task.
275
279
  #
276
280
  # @return [Float]
277
281
  attr_reader :avg_run_time
278
-
282
+
279
283
  # The average waiting time of a task before being executed.
280
284
  #
281
285
  # @return [Float]
@@ -300,7 +304,7 @@ module Utilrb
300
304
 
301
305
  @tasks_waiting = [] # tasks waiting for execution
302
306
  @tasks_running = [] # tasks which are currently running
303
-
307
+
304
308
  # Statistics
305
309
  @avg_run_time = 0 # average run time of a task in s [Float]
306
310
  @avg_wait_time = 0 # average time a task has to wait for execution in s [Float]
@@ -370,10 +374,10 @@ module Utilrb
370
374
  end
371
375
 
372
376
  # Number of tasks waiting for execution
373
- #
377
+ #
374
378
  # @return [Fixnum] the number of tasks
375
379
  def backlog
376
- @mutex.synchronize do
380
+ @mutex.synchronize do
377
381
  @tasks_waiting.length
378
382
  end
379
383
  end
@@ -396,7 +400,7 @@ module Utilrb
396
400
  process_with_options(nil,*args,&block)
397
401
  end
398
402
 
399
- # Returns true if a worker thread is currently processing a task
403
+ # Returns true if a worker thread is currently processing a task
400
404
  # and no work is queued
401
405
  #
402
406
  # @return [Boolean]
@@ -426,7 +430,7 @@ module Utilrb
426
430
  # safe.
427
431
  #
428
432
  # @param [Object] sync_key The sync key
429
- # @yield [*args] the code block block
433
+ # @yield [*args] the code block block
430
434
  # @return [Object] The result of the code block
431
435
  def sync(sync_key,*args,&block)
432
436
  raise ArgumentError,"no sync key" unless sync_key
@@ -454,7 +458,7 @@ module Utilrb
454
458
  #
455
459
  # @param [Object] sync_key The sync key
456
460
  # @param [Float] timeout The timeout
457
- # @yield [*args] the code block block
461
+ # @yield [*args] the code block block
458
462
  # @return [Object] The result of the code block
459
463
  def sync_timeout(sync_key,timeout,*args,&block)
460
464
  raise ArgumentError,"no sync key" unless sync_key
@@ -480,14 +484,14 @@ module Utilrb
480
484
  end
481
485
 
482
486
  # Processes the given {Task} as soon as the next thread is available
483
- #
487
+ #
484
488
  # @param [Task] task The task.
485
489
  # @return [Task]
486
490
  def <<(task)
487
491
  raise "cannot add task #{task} it is still running" if task.thread
488
492
  task.reset if task.finished?
489
493
  @mutex.synchronize do
490
- if shutdown?
494
+ if shutdown?
491
495
  raise "unable to add work while shutting down"
492
496
  end
493
497
  task.queued_at = Time.now
@@ -500,7 +504,7 @@ module Utilrb
500
504
  task
501
505
  end
502
506
 
503
- # Trims the number of threads if threads are waiting for work and
507
+ # Trims the number of threads if threads are waiting for work and
504
508
  # the number of spawned threads is higher than the minimum number.
505
509
  #
506
510
  # @param [boolean] force Trim even if no thread is waiting.
@@ -551,12 +555,12 @@ module Utilrb
551
555
 
552
556
  private
553
557
 
554
- #calculates the moving average
558
+ #calculates the moving average
555
559
  def moving_average(current_val,new_val)
556
560
  return new_val if current_val == 0
557
561
  current_val * 0.95 + new_val * 0.05
558
562
  end
559
-
563
+
560
564
  # spawns a worker thread
561
565
  # must be called from a synchronized block
562
566
  def spawn_thread
@@ -1,4 +1,4 @@
1
1
  module Utilrb
2
- VERSION = "3.1.0"
2
+ VERSION = "3.2.0"
3
3
  end
4
4