parallel 1.13.0 → 1.20.1

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.
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