parallel 1.12.1 → 1.22.0

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
- SHA1:
3
- metadata.gz: ab1626dc9142c46fd158c149029251516227b016
4
- data.tar.gz: 3c29eb9eefe300e498affd0bb8cdf1a89af3de84
2
+ SHA256:
3
+ metadata.gz: 1b517593727e82ec4dd9a897612cfca6ed731f788b84bceee4166a455bbc339d
4
+ data.tar.gz: a47a4fbac65ebe5ecad57c3d2c78df64f261e3770637cacbb2063b91369538bf
5
5
  SHA512:
6
- metadata.gz: 7321516475efc14a7423b97ad1072723e1ec1cf6760040b0d3faa97b83da04c4dcd19f4625c616958665bb1f7bde53ffbd3cb4ca0fc3191d74caf407ec4f678b
7
- data.tar.gz: d4572925839f6b2c5852af07ce8e425467544a44bb69f86262eb1617ada97bfb45b41819eb089787bcffb1545df5f48610f98db18ee133610fc4424e4228f76f
6
+ metadata.gz: a4e9dbbbf9c0cbff88e12750fd521ee3f65bd1c0de2ab5a18e1a9b7e96c413cd12cce0de985aef9bd4ca0eb8c5e438cf83ac59c88a74e67e38ef73cc7ac8fac9
7
+ data.tar.gz: 37ddce10ac73b9d722452693914bb889f2d638cbe52fe36ffe37673d6bba9d01601656568408eac29155619df8208892c51861e9033888e549bcaa467ce1f912
@@ -1,90 +1,42 @@
1
- if RUBY_VERSION.to_f >= 2.2
2
- require 'etc'
3
- end
1
+ # frozen_string_literal: true
2
+ require 'etc'
4
3
 
5
4
  module Parallel
5
+ # TODO: inline this method into parallel.rb and kill physical_processor_count in next major release
6
6
  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
- #
7
+ # Number of processors seen by the OS, used for process scheduling
21
8
  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
9
+ @processor_count ||= Integer(ENV['PARALLEL_PROCESSOR_COUNT'] || Etc.nprocessors)
59
10
  end
60
11
 
61
12
  # Number of physical processor cores on the current system.
62
- #
63
13
  def physical_processor_count
64
14
  @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]
15
+ ppc =
16
+ case RbConfig::CONFIG["target_os"]
17
+ when /darwin[12]/
18
+ IO.popen("/usr/sbin/sysctl -n hw.physicalcpu").read.to_i
19
+ when /linux/
20
+ cores = {} # unique physical ID / core ID combinations
21
+ phy = 0
22
+ File.read("/proc/cpuinfo").scan(/^physical id.*|^core id.*/) do |ln|
23
+ if ln.start_with?("physical")
24
+ phy = ln[/\d+/]
25
+ elsif ln.start_with?("core")
26
+ cid = "#{phy}:#{ln[/\d+/]}"
27
+ cores[cid] = true unless cores[cid]
28
+ end
77
29
  end
30
+ cores.count
31
+ when /mswin|mingw/
32
+ require 'win32ole'
33
+ result_set = WIN32OLE.connect("winmgmts://").ExecQuery(
34
+ "select NumberOfCores from Win32_Processor"
35
+ )
36
+ result_set.to_enum.collect(&:NumberOfCores).reduce(:+)
37
+ else
38
+ processor_count
78
39
  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
40
  # fall back to logical count if physical info is invalid
89
41
  ppc > 0 ? ppc : processor_count
90
42
  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.0' # 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,16 +1,16 @@
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.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Grosser
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-16 00:00:00.000000000 Z
11
+ date: 2022-03-21 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description:
13
+ description:
14
14
  email: michael@grosser.it
15
15
  executables: []
16
16
  extensions: []
@@ -23,8 +23,12 @@ files:
23
23
  homepage: https://github.com/grosser/parallel
24
24
  licenses:
25
25
  - MIT
26
- metadata: {}
27
- post_install_message:
26
+ metadata:
27
+ bug_tracker_uri: https://github.com/grosser/parallel/issues
28
+ documentation_uri: https://github.com/grosser/parallel/blob/v1.22.0/Readme.md
29
+ source_code_uri: https://github.com/grosser/parallel/tree/v1.22.0
30
+ wiki_uri: https://github.com/grosser/parallel/wiki
31
+ post_install_message:
28
32
  rdoc_options: []
29
33
  require_paths:
30
34
  - lib
@@ -32,16 +36,15 @@ 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
44
- signing_key:
46
+ rubygems_version: 3.3.3
47
+ signing_key:
45
48
  specification_version: 4
46
49
  summary: Run any kind of code in parallel processes
47
50
  test_files: []