parallel 1.12.1 → 1.22.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
- SHA1:
3
- metadata.gz: ab1626dc9142c46fd158c149029251516227b016
4
- data.tar.gz: 3c29eb9eefe300e498affd0bb8cdf1a89af3de84
2
+ SHA256:
3
+ metadata.gz: 7efbfe49c3df93ae88464b4bd4ce85d1aaccc9c173e1a81e7cf9100fe182982c
4
+ data.tar.gz: 7e82ff83bd44c96da760d83ab665baed41bc62d53f010987951c805cf41723e4
5
5
  SHA512:
6
- metadata.gz: 7321516475efc14a7423b97ad1072723e1ec1cf6760040b0d3faa97b83da04c4dcd19f4625c616958665bb1f7bde53ffbd3cb4ca0fc3191d74caf407ec4f678b
7
- data.tar.gz: d4572925839f6b2c5852af07ce8e425467544a44bb69f86262eb1617ada97bfb45b41819eb089787bcffb1545df5f48610f98db18ee133610fc4424e4228f76f
6
+ metadata.gz: a81cae96dd4dcfa1d287ce3e5893c830de3cb8e305236db4170db81f94de021f187da0d21462b7df5e99310002c14984fbb00aef818936c13d0ae834c1c1b2e7
7
+ data.tar.gz: 79c099a5f1f8bdad3d9f187829213d242087779605f14fb421f0172c1de1a8f853b4f13045935410a989d845d08e736e9552c8e3b9a6c3763d2dfdd27ede7daf
@@ -1,90 +1,41 @@
1
- if RUBY_VERSION.to_f >= 2.2
2
- require 'etc'
3
- end
4
-
1
+ # frozen_string_literal: true
5
2
  module Parallel
3
+ # TODO: inline this method into parallel.rb and kill physical_processor_count in next major release
6
4
  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
- #
5
+ # Number of processors seen by the OS, used for process scheduling
21
6
  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
7
+ require 'etc'
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
- ppc = case RbConfig::CONFIG["target_os"]
66
- when /darwin1/
67
- IO.popen("/usr/sbin/sysctl -n hw.physicalcpu").read.to_i
68
- when /linux/
69
- cores = {} # unique physical ID / core ID combinations
70
- phy = 0
71
- IO.read("/proc/cpuinfo").scan(/^physical id.*|^core id.*/) do |ln|
72
- if ln.start_with?("physical")
73
- phy = ln[/\d+/]
74
- elsif ln.start_with?("core")
75
- cid = phy + ":" + ln[/\d+/]
76
- cores[cid] = true if not cores[cid]
14
+ ppc =
15
+ case RbConfig::CONFIG["target_os"]
16
+ when /darwin[12]/
17
+ IO.popen("/usr/sbin/sysctl -n hw.physicalcpu").read.to_i
18
+ when /linux/
19
+ cores = {} # unique physical ID / core ID combinations
20
+ phy = 0
21
+ File.read("/proc/cpuinfo").scan(/^physical id.*|^core id.*/) do |ln|
22
+ if ln.start_with?("physical")
23
+ phy = ln[/\d+/]
24
+ elsif ln.start_with?("core")
25
+ cid = "#{phy}:#{ln[/\d+/]}"
26
+ cores[cid] = true unless cores[cid]
27
+ end
77
28
  end
29
+ cores.count
30
+ when /mswin|mingw/
31
+ require 'win32ole'
32
+ result_set = WIN32OLE.connect("winmgmts://").ExecQuery(
33
+ "select NumberOfCores from Win32_Processor"
34
+ )
35
+ result_set.to_enum.collect(&:NumberOfCores).reduce(:+)
36
+ else
37
+ processor_count
78
38
  end
79
- cores.count
80
- when /mswin|mingw/
81
- require 'win32ole'
82
- result_set = WIN32OLE.connect("winmgmts://").ExecQuery(
83
- "select NumberOfCores from Win32_Processor")
84
- result_set.to_enum.collect(&:NumberOfCores).reduce(:+)
85
- else
86
- processor_count
87
- end
88
39
  # fall back to logical count if physical info is invalid
89
40
  ppc > 0 ? ppc : processor_count
90
41
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Parallel
2
- VERSION = Version = '1.12.1'
3
+ VERSION = Version = '1.22.1' # rubocop:disable Naming/ConstantName
3
4
  end
data/lib/parallel.rb CHANGED
@@ -1,31 +1,40 @@
1
+ # frozen_string_literal: true
1
2
  require 'rbconfig'
2
3
  require 'parallel/version'
3
4
  require 'parallel/processor_count'
4
5
 
5
6
  module Parallel
6
- extend Parallel::ProcessorCount
7
+ extend ProcessorCount
8
+
9
+ Stop = Object.new.freeze
7
10
 
8
11
  class DeadWorker < StandardError
9
12
  end
10
13
 
11
14
  class Break < StandardError
15
+ attr_reader :value
16
+
17
+ def initialize(value = nil)
18
+ super()
19
+ @value = value
20
+ end
12
21
  end
13
22
 
14
- class Kill < StandardError
23
+ class Kill < Break
15
24
  end
16
25
 
17
26
  class UndumpableException < StandardError
18
27
  attr_reader :backtrace
28
+
19
29
  def initialize(original)
20
30
  super "#{original.class}: #{original.message}"
21
31
  @backtrace = original.backtrace
22
32
  end
23
33
  end
24
34
 
25
- Stop = Object.new
26
-
27
35
  class ExceptionWrapper
28
36
  attr_reader :exception
37
+
29
38
  def initialize(exception)
30
39
  # Remove the bindings stack added by the better_errors gem,
31
40
  # because it cannot be marshalled
@@ -36,7 +45,7 @@ module Parallel
36
45
  @exception =
37
46
  begin
38
47
  Marshal.dump(exception) && exception
39
- rescue
48
+ rescue StandardError
40
49
  UndumpableException.new(exception)
41
50
  end
42
51
  end
@@ -45,8 +54,11 @@ module Parallel
45
54
  class Worker
46
55
  attr_reader :pid, :read, :write
47
56
  attr_accessor :thread
57
+
48
58
  def initialize(read, write, pid)
49
- @read, @write, @pid = read, write, pid
59
+ @read = read
60
+ @write = write
61
+ @pid = pid
50
62
  end
51
63
 
52
64
  def stop
@@ -73,7 +85,7 @@ module Parallel
73
85
  rescue EOFError
74
86
  raise DeadWorker
75
87
  end
76
- raise result.exception if ExceptionWrapper === result
88
+ raise result.exception if result.is_a?(ExceptionWrapper)
77
89
  result
78
90
  end
79
91
 
@@ -102,7 +114,7 @@ module Parallel
102
114
  item, index = @mutex.synchronize do
103
115
  return if @stopped
104
116
  item = @lambda.call
105
- @stopped = (item == Parallel::Stop)
117
+ @stopped = (item == Stop)
106
118
  return if @stopped
107
119
  [item, @index += 1]
108
120
  end
@@ -140,7 +152,7 @@ module Parallel
140
152
  end
141
153
 
142
154
  def queue_wrapper(array)
143
- array.respond_to?(:num_waiting) && array.respond_to?(:pop) && lambda { array.pop(false) }
155
+ array.respond_to?(:num_waiting) && array.respond_to?(:pop) && -> { array.pop(false) }
144
156
  end
145
157
  end
146
158
 
@@ -156,7 +168,7 @@ module Parallel
156
168
 
157
169
  if @to_be_killed.empty?
158
170
  old_interrupt = trap_interrupt(signal) do
159
- $stderr.puts 'Parallel execution interrupted, exiting ...'
171
+ warn 'Parallel execution interrupted, exiting ...'
160
172
  @to_be_killed.flatten.each { |pid| kill(pid) }
161
173
  end
162
174
  end
@@ -200,46 +212,61 @@ module Parallel
200
212
  end
201
213
 
202
214
  class << self
203
- def in_threads(options={:count => 2})
204
- count, _ = extract_count_from_options(options)
205
- Array.new(count) do |i|
206
- Thread.new { yield(i) }
207
- end.map!(&:value)
215
+ def in_threads(options = { count: 2 })
216
+ threads = []
217
+ count, = extract_count_from_options(options)
218
+
219
+ Thread.handle_interrupt(Exception => :never) do
220
+ Thread.handle_interrupt(Exception => :immediate) do
221
+ count.times do |i|
222
+ threads << Thread.new { yield(i) }
223
+ end
224
+ threads.map(&:value)
225
+ end
226
+ ensure
227
+ threads.each(&:kill)
228
+ end
208
229
  end
209
230
 
210
231
  def in_processes(options = {}, &block)
211
232
  count, options = extract_count_from_options(options)
212
233
  count ||= processor_count
213
- map(0...count, options.merge(:in_processes => count), &block)
234
+ map(0...count, options.merge(in_processes: count), &block)
214
235
  end
215
236
 
216
- def each(array, options={}, &block)
217
- map(array, options.merge(:preserve_results => false), &block)
237
+ def each(array, options = {}, &block)
238
+ map(array, options.merge(preserve_results: false), &block)
218
239
  end
219
240
 
220
241
  def any?(*args, &block)
221
242
  raise "You must provide a block when calling #any?" if block.nil?
222
- !each(*args) { |*a| raise Parallel::Kill if block.call(*a) }
243
+ !each(*args) { |*a| raise Kill if block.call(*a) }
223
244
  end
224
245
 
225
246
  def all?(*args, &block)
226
247
  raise "You must provide a block when calling #all?" if block.nil?
227
- !!each(*args) { |*a| raise Parallel::Kill unless block.call(*a) }
248
+ !!each(*args) { |*a| raise Kill unless block.call(*a) }
228
249
  end
229
250
 
230
- def each_with_index(array, options={}, &block)
231
- each(array, options.merge(:with_index => true), &block)
251
+ def each_with_index(array, options = {}, &block)
252
+ each(array, options.merge(with_index: true), &block)
232
253
  end
233
254
 
234
255
  def map(source, options = {}, &block)
256
+ options = options.dup
235
257
  options[:mutex] = Mutex.new
236
258
 
237
- if RUBY_PLATFORM =~ /java/ and not options[:in_processes]
259
+ if options[:in_processes] && options[:in_threads]
260
+ raise ArgumentError, "Please specify only one of `in_processes` or `in_threads`."
261
+ elsif RUBY_PLATFORM =~ (/java/) && !(options[:in_processes])
238
262
  method = :in_threads
239
263
  size = options[method] || processor_count
240
264
  elsif options[:in_threads]
241
265
  method = :in_threads
242
266
  size = options[method]
267
+ elsif options[:in_ractors]
268
+ method = :in_ractors
269
+ size = options[method]
243
270
  else
244
271
  method = :in_processes
245
272
  if Process.respond_to?(:fork)
@@ -256,26 +283,35 @@ module Parallel
256
283
  options[:return_results] = (options[:preserve_results] != false || !!options[:finish])
257
284
  add_progress_bar!(job_factory, options)
258
285
 
259
- results = if size == 0
260
- work_direct(job_factory, options, &block)
261
- elsif method == :in_threads
262
- work_in_threads(job_factory, options.merge(:count => size), &block)
263
- else
264
- work_in_processes(job_factory, options.merge(:count => size), &block)
265
- end
266
- if results
267
- options[:return_results] ? results : source
268
- end
286
+ result =
287
+ if size == 0
288
+ work_direct(job_factory, options, &block)
289
+ elsif method == :in_threads
290
+ work_in_threads(job_factory, options.merge(count: size), &block)
291
+ elsif method == :in_ractors
292
+ work_in_ractors(job_factory, options.merge(count: size), &block)
293
+ else
294
+ work_in_processes(job_factory, options.merge(count: size), &block)
295
+ end
296
+
297
+ return result.value if result.is_a?(Break)
298
+ raise result if result.is_a?(Exception)
299
+ options[:return_results] ? result : source
300
+ end
301
+
302
+ def map_with_index(array, options = {}, &block)
303
+ map(array, options.merge(with_index: true), &block)
269
304
  end
270
305
 
271
- def map_with_index(array, options={}, &block)
272
- map(array, options.merge(:with_index => true), &block)
306
+ def flat_map(*args, &block)
307
+ map(*args, &block).flatten(1)
273
308
  end
274
309
 
275
310
  def worker_number
276
311
  Thread.current[:parallel_worker_number]
277
312
  end
278
313
 
314
+ # TODO: this does not work when doing threads in forks, so should remove and yield the number instead if needed
279
315
  def worker_number=(worker_num)
280
316
  Thread.current[:parallel_worker_number] = worker_num
281
317
  end
@@ -318,10 +354,10 @@ module Parallel
318
354
  call_with_index(item, index, options, &block)
319
355
  end
320
356
  end
321
- rescue
357
+ rescue StandardError
322
358
  exception = $!
323
359
  end
324
- handle_exception(exception, results)
360
+ exception || results
325
361
  ensure
326
362
  self.worker_number = nil
327
363
  end
@@ -342,21 +378,83 @@ module Parallel
342
378
  call_with_index(item, index, options, &block)
343
379
  end
344
380
  results_mutex.synchronize { results[index] = result }
345
- rescue
381
+ rescue StandardError
346
382
  exception = $!
347
383
  end
348
384
  end
349
385
  end
350
386
 
351
- handle_exception(exception, results)
387
+ exception || results
352
388
  end
353
389
 
354
- def work_in_processes(job_factory, options, &blk)
355
- workers = if options[:isolation]
356
- [] # we create workers per job and not beforehand
357
- else
358
- create_workers(job_factory, options, &blk)
390
+ def work_in_ractors(job_factory, options)
391
+ exception = nil
392
+ results = []
393
+ results_mutex = Mutex.new # arrays are not thread-safe on jRuby
394
+
395
+ callback = options[:ractor]
396
+ if block_given? || !callback
397
+ raise ArgumentError, "pass the code you want to execute as `ractor: [ClassName, :method_name]`"
359
398
  end
399
+
400
+ # build
401
+ ractors = Array.new(options.fetch(:count)) do
402
+ Ractor.new do
403
+ loop do
404
+ got = receive
405
+ (klass, method_name), item, index = got
406
+ break if index == :break
407
+ begin
408
+ Ractor.yield [nil, klass.send(method_name, item), item, index]
409
+ rescue StandardError => e
410
+ Ractor.yield [e, nil, item, index]
411
+ end
412
+ end
413
+ end
414
+ end
415
+
416
+ # start
417
+ ractors.dup.each do |ractor|
418
+ if set = job_factory.next
419
+ item, index = set
420
+ instrument_start item, index, options
421
+ ractor.send [callback, item, index]
422
+ else
423
+ ractor.send([[nil, nil], nil, :break]) # stop the ractor
424
+ ractors.delete ractor
425
+ end
426
+ end
427
+
428
+ # replace with new items
429
+ while set = job_factory.next
430
+ item_next, index_next = set
431
+ done, (exception, result, item, index) = Ractor.select(*ractors)
432
+ if exception
433
+ ractors.delete done
434
+ break
435
+ end
436
+ instrument_finish item, index, result, options
437
+ results_mutex.synchronize { results[index] = (options[:preserve_results] == false ? nil : result) }
438
+
439
+ instrument_start item_next, index_next, options
440
+ done.send([callback, item_next, index_next])
441
+ end
442
+
443
+ # finish
444
+ ractors.each do |ractor|
445
+ (new_exception, result, item, index) = ractor.take
446
+ exception ||= new_exception
447
+ next if new_exception
448
+ instrument_finish item, index, result, options
449
+ results_mutex.synchronize { results[index] = (options[:preserve_results] == false ? nil : result) }
450
+ ractor.send([[nil, nil], nil, :break]) # stop the ractor
451
+ end
452
+
453
+ exception || results
454
+ end
455
+
456
+ def work_in_processes(job_factory, options, &blk)
457
+ workers = create_workers(job_factory, options, &blk)
360
458
  results = []
361
459
  results_mutex = Mutex.new # arrays are not thread-safe
362
460
  exception = nil
@@ -364,6 +462,8 @@ module Parallel
364
462
  UserInterruptHandler.kill_on_ctrl_c(workers.map(&:pid), options) do
365
463
  in_threads(options) do |i|
366
464
  worker = workers[i]
465
+ worker.thread = Thread.current
466
+ worked = false
367
467
 
368
468
  begin
369
469
  loop do
@@ -372,44 +472,44 @@ module Parallel
372
472
  break unless index
373
473
 
374
474
  if options[:isolation]
375
- worker = replace_worker(job_factory, workers, i, options, blk)
475
+ worker = replace_worker(job_factory, workers, i, options, blk) if worked
476
+ worked = true
477
+ worker.thread = Thread.current
376
478
  end
377
479
 
378
- worker.thread = Thread.current
379
-
380
480
  begin
381
481
  result = with_instrumentation item, index, options do
382
482
  worker.work(job_factory.pack(item, index))
383
483
  end
384
484
  results_mutex.synchronize { results[index] = result } # arrays are not threads safe on jRuby
385
- rescue
386
- exception = $!
387
- if Parallel::Kill === exception
485
+ rescue StandardError => e
486
+ exception = e
487
+ if exception.is_a?(Kill)
388
488
  (workers - [worker]).each do |w|
389
- w.thread.kill unless w.thread.nil?
489
+ w.thread&.kill
390
490
  UserInterruptHandler.kill(w.pid)
391
491
  end
392
492
  end
393
493
  end
394
494
  end
395
495
  ensure
396
- worker.stop if worker
496
+ worker.stop
397
497
  end
398
498
  end
399
499
  end
400
500
 
401
- handle_exception(exception, results)
501
+ exception || results
402
502
  end
403
503
 
404
- def replace_worker(job_factory, workers, i, options, blk)
504
+ def replace_worker(job_factory, workers, index, options, blk)
405
505
  options[:mutex].synchronize do
406
506
  # old worker is no longer used ... stop it
407
- worker = workers[i]
408
- worker.stop if worker
507
+ worker = workers[index]
508
+ worker.stop
409
509
 
410
510
  # create a new replacement worker
411
511
  running = workers - [worker]
412
- workers[i] = worker(job_factory, options.merge(started_workers: running, worker_number: i), &blk)
512
+ workers[index] = worker(job_factory, options.merge(started_workers: running, worker_number: index), &blk)
413
513
  end
414
514
  end
415
515
 
@@ -451,21 +551,25 @@ module Parallel
451
551
  until read.eof?
452
552
  data = Marshal.load(read)
453
553
  item, index = job_factory.unpack(data)
454
- result = begin
455
- call_with_index(item, index, options, &block)
456
- rescue
457
- ExceptionWrapper.new($!)
554
+
555
+ result =
556
+ begin
557
+ call_with_index(item, index, options, &block)
558
+ # https://github.com/rspec/rspec-support/blob/673133cdd13b17077b3d88ece8d7380821f8d7dc/lib/rspec/support.rb#L132-L140
559
+ rescue NoMemoryError, SignalException, Interrupt, SystemExit # rubocop:disable Lint/ShadowedException
560
+ raise $!
561
+ rescue Exception # # rubocop:disable Lint/RescueException
562
+ ExceptionWrapper.new($!)
563
+ end
564
+
565
+ begin
566
+ Marshal.dump(result, write)
567
+ rescue Errno::EPIPE
568
+ return # parent thread already dead
458
569
  end
459
- Marshal.dump(result, write)
460
570
  end
461
571
  end
462
572
 
463
- def handle_exception(exception, results)
464
- return nil if [Parallel::Break, Parallel::Kill].include? exception.class
465
- raise exception if exception
466
- results
467
- end
468
-
469
573
  # options is either a Integer or a Hash with :count
470
574
  def extract_count_from_options(options)
471
575
  if options.is_a?(Hash)
@@ -480,21 +584,29 @@ module Parallel
480
584
  def call_with_index(item, index, options, &block)
481
585
  args = [item]
482
586
  args << index if options[:with_index]
587
+ results = block.call(*args)
483
588
  if options[:return_results]
484
- block.call(*args)
589
+ results
485
590
  else
486
- block.call(*args)
487
591
  nil # avoid GC overhead of passing large results around
488
592
  end
489
593
  end
490
594
 
491
595
  def with_instrumentation(item, index, options)
492
- on_start = options[:start]
493
- on_finish = options[:finish]
494
- options[:mutex].synchronize { on_start.call(item, index) } if on_start
596
+ instrument_start(item, index, options)
495
597
  result = yield
496
- options[:mutex].synchronize { on_finish.call(item, index, result) } if on_finish
598
+ instrument_finish(item, index, result, options)
497
599
  result unless options[:preserve_results] == false
498
600
  end
601
+
602
+ def instrument_finish(item, index, result, options)
603
+ return unless on_finish = options[:finish]
604
+ options[:mutex].synchronize { on_finish.call(item, index, result) }
605
+ end
606
+
607
+ def instrument_start(item, index, options)
608
+ return unless on_start = options[:start]
609
+ options[:mutex].synchronize { on_start.call(item, index) }
610
+ end
499
611
  end
500
612
  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.12.1
4
+ version: 1.22.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: 2017-12-16 00:00:00.000000000 Z
11
+ date: 2022-03-25 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.22.1/Readme.md
29
+ source_code_uri: https://github.com/grosser/parallel/tree/v1.22.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.5'
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.5.1
46
+ rubygems_version: 3.1.6
44
47
  signing_key:
45
48
  specification_version: 4
46
49
  summary: Run any kind of code in parallel processes