parallel 1.13.0 → 1.20.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 89d859597b00942e642e88797290b1407a87615c0bb9756b8a73a3c976410aac
4
- data.tar.gz: 40537d9251999345344d79a6ef06946257301af2ddbe5484e1c97e6dea28d356
3
+ metadata.gz: fa334cb0d83b8049f260a5e6724d2fd0e65768f85afd0968a2af05d55dda4e0e
4
+ data.tar.gz: 7ba3ade3a7af4bcfe5944b8d7313b5ec6e4074d27755a83f95e4f8a01d77261e
5
5
  SHA512:
6
- metadata.gz: 45a8ae3ecf5ded1f1c53aaf98cc5bb45b6e1c60e51997143a2420433f4a94d0de4e339a6a9be8f276432f8bdff359fdeb10fdc5e8aa10e34874973c5c84384c4
7
- data.tar.gz: 9b2a309db6cd24a199fa2fd99279b4015df984c09a65896e6a56a11f080d691fceabb38684606f2c2e3291794ea50cac764c82fbb93d7c32a22720b93960c2e4
6
+ metadata.gz: d470562bd90ab80c66f69b9c1ab016f737bf52e091ac3027c51ea7482eaecd3bd3c186daab756c5d4276b9fb54f0e80f149a8071dc17c31a321ff4fc4d369600
7
+ data.tar.gz: f01bf1c3a052579c11c3097b02b02a4a30c8e881dbb4916c3ceba6f122aab3a40c276a1de82f7ea7d3790eeff2f994bd20283361151439a3d57b025bdd24dfc8
data/lib/parallel.rb CHANGED
@@ -3,15 +3,21 @@ require 'parallel/version'
3
3
  require 'parallel/processor_count'
4
4
 
5
5
  module Parallel
6
- extend Parallel::ProcessorCount
6
+ extend ProcessorCount
7
+
8
+ Stop = Object.new.freeze
7
9
 
8
10
  class DeadWorker < StandardError
9
11
  end
10
12
 
11
13
  class Break < StandardError
14
+ attr_reader :value
15
+ def initialize(value = nil)
16
+ @value = value
17
+ end
12
18
  end
13
19
 
14
- class Kill < StandardError
20
+ class Kill < Break
15
21
  end
16
22
 
17
23
  class UndumpableException < StandardError
@@ -22,8 +28,6 @@ module Parallel
22
28
  end
23
29
  end
24
30
 
25
- Stop = Object.new
26
-
27
31
  class ExceptionWrapper
28
32
  attr_reader :exception
29
33
  def initialize(exception)
@@ -102,7 +106,7 @@ module Parallel
102
106
  item, index = @mutex.synchronize do
103
107
  return if @stopped
104
108
  item = @lambda.call
105
- @stopped = (item == Parallel::Stop)
109
+ @stopped = (item == Stop)
106
110
  return if @stopped
107
111
  [item, @index += 1]
108
112
  end
@@ -201,10 +205,21 @@ module Parallel
201
205
 
202
206
  class << self
203
207
  def in_threads(options={:count => 2})
208
+ threads = []
204
209
  count, _ = extract_count_from_options(options)
205
- Array.new(count) do |i|
206
- Thread.new { yield(i) }
207
- end.map!(&:value)
210
+
211
+ Thread.handle_interrupt(Exception => :never) do
212
+ begin
213
+ Thread.handle_interrupt(Exception => :immediate) do
214
+ count.times do |i|
215
+ threads << Thread.new { yield(i) }
216
+ end
217
+ threads.map(&:value)
218
+ end
219
+ ensure
220
+ threads.each(&:kill)
221
+ end
222
+ end
208
223
  end
209
224
 
210
225
  def in_processes(options = {}, &block)
@@ -219,12 +234,12 @@ module Parallel
219
234
 
220
235
  def any?(*args, &block)
221
236
  raise "You must provide a block when calling #any?" if block.nil?
222
- !each(*args) { |*a| raise Parallel::Kill if block.call(*a) }
237
+ !each(*args) { |*a| raise Kill if block.call(*a) }
223
238
  end
224
239
 
225
240
  def all?(*args, &block)
226
241
  raise "You must provide a block when calling #all?" if block.nil?
227
- !!each(*args) { |*a| raise Parallel::Kill unless block.call(*a) }
242
+ !!each(*args) { |*a| raise Kill unless block.call(*a) }
228
243
  end
229
244
 
230
245
  def each_with_index(array, options={}, &block)
@@ -259,26 +274,33 @@ module Parallel
259
274
  options[:return_results] = (options[:preserve_results] != false || !!options[:finish])
260
275
  add_progress_bar!(job_factory, options)
261
276
 
262
- results = if size == 0
263
- work_direct(job_factory, options, &block)
264
- elsif method == :in_threads
265
- work_in_threads(job_factory, options.merge(:count => size), &block)
266
- else
267
- work_in_processes(job_factory, options.merge(:count => size), &block)
268
- end
269
- if results
270
- options[:return_results] ? results : source
271
- end
277
+ result =
278
+ if size == 0
279
+ work_direct(job_factory, options, &block)
280
+ elsif method == :in_threads
281
+ work_in_threads(job_factory, options.merge(:count => size), &block)
282
+ else
283
+ work_in_processes(job_factory, options.merge(:count => size), &block)
284
+ end
285
+
286
+ return result.value if result.is_a?(Break)
287
+ raise result if result.is_a?(Exception)
288
+ options[:return_results] ? result : source
272
289
  end
273
290
 
274
291
  def map_with_index(array, options={}, &block)
275
292
  map(array, options.merge(:with_index => true), &block)
276
293
  end
277
294
 
295
+ def flat_map(*args, &block)
296
+ map(*args, &block).flatten(1)
297
+ end
298
+
278
299
  def worker_number
279
300
  Thread.current[:parallel_worker_number]
280
301
  end
281
302
 
303
+ # TODO: this does not work when doing threads in forks, so should remove and yield the number instead if needed
282
304
  def worker_number=(worker_num)
283
305
  Thread.current[:parallel_worker_number] = worker_num
284
306
  end
@@ -324,7 +346,7 @@ module Parallel
324
346
  rescue
325
347
  exception = $!
326
348
  end
327
- handle_exception(exception, results)
349
+ exception || results
328
350
  ensure
329
351
  self.worker_number = nil
330
352
  end
@@ -351,15 +373,11 @@ module Parallel
351
373
  end
352
374
  end
353
375
 
354
- handle_exception(exception, results)
376
+ exception || results
355
377
  end
356
378
 
357
379
  def work_in_processes(job_factory, options, &blk)
358
- workers = if options[:isolation]
359
- [] # we create workers per job and not beforehand
360
- else
361
- create_workers(job_factory, options, &blk)
362
- end
380
+ workers = create_workers(job_factory, options, &blk)
363
381
  results = []
364
382
  results_mutex = Mutex.new # arrays are not thread-safe
365
383
  exception = nil
@@ -367,6 +385,8 @@ module Parallel
367
385
  UserInterruptHandler.kill_on_ctrl_c(workers.map(&:pid), options) do
368
386
  in_threads(options) do |i|
369
387
  worker = workers[i]
388
+ worker.thread = Thread.current
389
+ worked = false
370
390
 
371
391
  begin
372
392
  loop do
@@ -375,40 +395,39 @@ module Parallel
375
395
  break unless index
376
396
 
377
397
  if options[:isolation]
378
- worker = replace_worker(job_factory, workers, i, options, blk)
398
+ worker = replace_worker(job_factory, workers, i, options, blk) if worked
399
+ worked = true
400
+ worker.thread = Thread.current
379
401
  end
380
402
 
381
- worker.thread = Thread.current
382
-
383
403
  begin
384
404
  result = with_instrumentation item, index, options do
385
405
  worker.work(job_factory.pack(item, index))
386
406
  end
387
407
  results_mutex.synchronize { results[index] = result } # arrays are not threads safe on jRuby
388
- rescue
389
- exception = $!
390
- if Parallel::Kill === exception
408
+ rescue StandardError => e
409
+ exception = e
410
+ if Kill === exception
391
411
  (workers - [worker]).each do |w|
392
- w.thread.kill unless w.thread.nil?
412
+ w.thread.kill if w.thread
393
413
  UserInterruptHandler.kill(w.pid)
394
414
  end
395
415
  end
396
416
  end
397
417
  end
398
418
  ensure
399
- worker.stop if worker
419
+ worker.stop
400
420
  end
401
421
  end
402
422
  end
403
-
404
- handle_exception(exception, results)
423
+ exception || results
405
424
  end
406
425
 
407
426
  def replace_worker(job_factory, workers, i, options, blk)
408
427
  options[:mutex].synchronize do
409
428
  # old worker is no longer used ... stop it
410
429
  worker = workers[i]
411
- worker.stop if worker
430
+ worker.stop
412
431
 
413
432
  # create a new replacement worker
414
433
  running = workers - [worker]
@@ -456,19 +475,20 @@ module Parallel
456
475
  item, index = job_factory.unpack(data)
457
476
  result = begin
458
477
  call_with_index(item, index, options, &block)
459
- rescue
478
+ # https://github.com/rspec/rspec-support/blob/673133cdd13b17077b3d88ece8d7380821f8d7dc/lib/rspec/support.rb#L132-L140
479
+ rescue NoMemoryError, SignalException, Interrupt, SystemExit
480
+ raise $!
481
+ rescue Exception
460
482
  ExceptionWrapper.new($!)
461
483
  end
462
- Marshal.dump(result, write)
484
+ begin
485
+ Marshal.dump(result, write)
486
+ rescue Errno::EPIPE
487
+ return # parent thread already dead
488
+ end
463
489
  end
464
490
  end
465
491
 
466
- def handle_exception(exception, results)
467
- return nil if [Parallel::Break, Parallel::Kill].include? exception.class
468
- raise exception if exception
469
- results
470
- end
471
-
472
492
  # options is either a Integer or a Hash with :count
473
493
  def extract_count_from_options(options)
474
494
  if options.is_a?(Hash)
@@ -1,65 +1,14 @@
1
- if RUBY_VERSION.to_f >= 2.2
2
- require 'etc'
3
- end
1
+ require 'etc'
4
2
 
5
3
  module Parallel
4
+ # TODO: inline this method into parallel.rb and kill physical_processor_count in next major release
6
5
  module ProcessorCount
7
- # Number of processors seen by the OS and used for process scheduling.
8
- #
9
- # * AIX: /usr/sbin/pmcycles (AIX 5+), /usr/sbin/lsdev
10
- # * BSD: /sbin/sysctl
11
- # * Cygwin: /proc/cpuinfo
12
- # * Darwin: /usr/bin/hwprefs, /usr/sbin/sysctl
13
- # * HP-UX: /usr/sbin/ioscan
14
- # * IRIX: /usr/sbin/sysconf
15
- # * Linux: /proc/cpuinfo
16
- # * Minix 3+: /proc/cpuinfo
17
- # * Solaris: /usr/sbin/psrinfo
18
- # * Tru64 UNIX: /usr/sbin/psrinfo
19
- # * UnixWare: /usr/sbin/psrinfo
20
- #
6
+ # Number of processors seen by the OS, used for process scheduling
21
7
  def processor_count
22
- @processor_count ||= begin
23
- if defined?(Etc) && Etc.respond_to?(:nprocessors)
24
- Etc.nprocessors
25
- else
26
- os_name = RbConfig::CONFIG["target_os"]
27
- if os_name =~ /mingw|mswin/
28
- require 'win32ole'
29
- result = WIN32OLE.connect("winmgmts://").ExecQuery(
30
- "select NumberOfLogicalProcessors from Win32_Processor")
31
- result.to_enum.collect(&:NumberOfLogicalProcessors).reduce(:+)
32
- elsif File.readable?("/proc/cpuinfo")
33
- IO.read("/proc/cpuinfo").scan(/^processor/).size
34
- elsif File.executable?("/usr/bin/hwprefs")
35
- IO.popen("/usr/bin/hwprefs thread_count").read.to_i
36
- elsif File.executable?("/usr/sbin/psrinfo")
37
- IO.popen("/usr/sbin/psrinfo").read.scan(/^.*on-*line/).size
38
- elsif File.executable?("/usr/sbin/ioscan")
39
- IO.popen("/usr/sbin/ioscan -kC processor") do |out|
40
- out.read.scan(/^.*processor/).size
41
- end
42
- elsif File.executable?("/usr/sbin/pmcycles")
43
- IO.popen("/usr/sbin/pmcycles -m").read.count("\n")
44
- elsif File.executable?("/usr/sbin/lsdev")
45
- IO.popen("/usr/sbin/lsdev -Cc processor -S 1").read.count("\n")
46
- elsif File.executable?("/usr/sbin/sysconf") and os_name =~ /irix/i
47
- IO.popen("/usr/sbin/sysconf NPROC_ONLN").read.to_i
48
- elsif File.executable?("/usr/sbin/sysctl")
49
- IO.popen("/usr/sbin/sysctl -n hw.ncpu").read.to_i
50
- elsif File.executable?("/sbin/sysctl")
51
- IO.popen("/sbin/sysctl -n hw.ncpu").read.to_i
52
- else
53
- $stderr.puts "Unknown platform: " + RbConfig::CONFIG["target_os"]
54
- $stderr.puts "Assuming 1 processor."
55
- 1
56
- end
57
- end
58
- end
8
+ @processor_count ||= Integer(ENV['PARALLEL_PROCESSOR_COUNT'] || Etc.nprocessors)
59
9
  end
60
10
 
61
11
  # Number of physical processor cores on the current system.
62
- #
63
12
  def physical_processor_count
64
13
  @physical_processor_count ||= begin
65
14
  ppc = case RbConfig::CONFIG["target_os"]
@@ -1,3 +1,3 @@
1
1
  module Parallel
2
- VERSION = Version = '1.13.0'
2
+ VERSION = Version = '1.20.1'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parallel
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.13.0
4
+ version: 1.20.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Grosser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-17 00:00:00.000000000 Z
11
+ date: 2020-11-22 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: michael@grosser.it
@@ -23,7 +23,11 @@ files:
23
23
  homepage: https://github.com/grosser/parallel
24
24
  licenses:
25
25
  - MIT
26
- metadata: {}
26
+ metadata:
27
+ bug_tracker_uri: https://github.com/grosser/parallel/issues
28
+ documentation_uri: https://github.com/grosser/parallel/blob/v1.20.1/Readme.md
29
+ source_code_uri: https://github.com/grosser/parallel/tree/v1.20.1
30
+ wiki_uri: https://github.com/grosser/parallel/wiki
27
31
  post_install_message:
28
32
  rdoc_options: []
29
33
  require_paths:
@@ -32,15 +36,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
32
36
  requirements:
33
37
  - - ">="
34
38
  - !ruby/object:Gem::Version
35
- version: 1.9.3
39
+ version: '2.4'
36
40
  required_rubygems_version: !ruby/object:Gem::Requirement
37
41
  requirements:
38
42
  - - ">="
39
43
  - !ruby/object:Gem::Version
40
44
  version: '0'
41
45
  requirements: []
42
- rubyforge_project:
43
- rubygems_version: 2.7.6
46
+ rubygems_version: 3.1.3
44
47
  signing_key:
45
48
  specification_version: 4
46
49
  summary: Run any kind of code in parallel processes