summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndre Arko <andre@arko.net>2015-04-07 21:03:05 -0700
committerAndre Arko <andre@arko.net>2015-04-07 21:03:05 -0700
commit8b58accd0e60b7d3c0317ed2336f30e37a5b0d04 (patch)
tree27093fa8ece5c124332a2859627e6d76b09e01e9
parent13f44d1241ca7a7ce435bd43790a26a0a140126b (diff)
downloadbundler-8b58accd0e60b7d3c0317ed2336f30e37a5b0d04.tar.gz
Revert 'Clean up parallel installer'
It seems like some really bad build instability started with these changes. This isn't a straight revert, because I accidentally mixed changes to outdated into the original commit, but this reverts all the parallel installer changes. Reverts some of 'a467f7d1f4ded46a519c1f211a75de70c21bc647'.
-rw-r--r--lib/bundler/installer.rb57
-rw-r--r--lib/bundler/installer/parallel_installer.rb94
2 files changed, 53 insertions, 98 deletions
diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb
index cd073bde50..10526115a4 100644
--- a/lib/bundler/installer.rb
+++ b/lib/bundler/installer.rb
@@ -1,6 +1,6 @@
require 'erb'
require 'rubygems/dependency_installer'
-require 'bundler/installer/parallel_installer'
+require 'bundler/worker'
module Bundler
class Installer < Environment
@@ -85,7 +85,6 @@ module Bundler
# installation is just SO MUCH FASTER. so we let people opt in.
jobs = [Bundler.settings[:jobs].to_i-1, 1].max
if jobs > 1 && can_install_in_parallel?
- require 'bundler/installer/parallel_installer'
install_in_parallel jobs, options[:standalone]
else
install_sequentially options[:standalone]
@@ -261,7 +260,7 @@ module Bundler
def install_sequentially(standalone)
specs.each do |spec|
- message = install_gem_from_spec spec, standalone
+ message = install_gem_from_spec spec, standalone, 0
if message
Installer.post_install_messages[spec.name] = message
end
@@ -269,9 +268,59 @@ module Bundler
end
def install_in_parallel(size, standalone)
- ParallelInstaller.call(self, specs, size, standalone)
+ name2spec = {}
+ remains = {}
+ enqueued = {}
+ specs.each do |spec|
+ name2spec[spec.name] = spec
+ remains[spec.name] = true
+ end
+
+ worker_pool = Worker.new size, lambda { |name, worker_num|
+ spec = name2spec[name]
+ message = install_gem_from_spec spec, standalone, worker_num
+ { :name => spec.name, :post_install => message }
+ }
+
+ # Keys in the remains hash represent uninstalled gems specs.
+ # We enqueue all gem specs that do not have any dependencies.
+ # Later we call this lambda again to install specs that depended on
+ # previously installed specifications. We continue until all specs
+ # are installed.
+ enqueue_remaining_specs = lambda do
+ remains.keys.each do |name|
+ next if enqueued[name]
+ spec = name2spec[name]
+ if ready_to_install?(spec, remains)
+ worker_pool.enq name
+ enqueued[name] = true
+ end
+ end
+ end
+ enqueue_remaining_specs.call
+
+ until remains.empty?
+ message = worker_pool.deq
+ remains.delete message[:name]
+ if message[:post_install]
+ Installer.post_install_messages[message[:name]] = message[:post_install]
+ end
+ enqueue_remaining_specs.call
+ end
+ message
+ ensure
+ worker_pool && worker_pool.stop
end
+ # We only want to install a gem spec if all its dependencies are met.
+ # If the dependency is no longer in the `remains` hash then it has been met.
+ # If a dependency is only development or is self referential it can be ignored.
+ def ready_to_install?(spec, remains)
+ spec.dependencies.none? do |dep|
+ next if dep.type == :development || dep.name == spec.name
+ remains[dep.name]
+ end
+ end
def create_bundle_path
Bundler.mkdir_p(Bundler.bundle_path.to_s) unless Bundler.bundle_path.exist?
diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb
deleted file mode 100644
index ced75b47b3..0000000000
--- a/lib/bundler/installer/parallel_installer.rb
+++ /dev/null
@@ -1,94 +0,0 @@
-require 'bundler/worker'
-
-
-class ParallelInstaller
-
- class SpecInstallation
- attr_accessor :installed, :spec, :name, :post_install_message, :enqueued
- def initialize(spec)
- @spec, @name = spec, spec.name
- @installed = false
- @enqueued = false
- @post_install_message = ""
- end
-
- def installed?
- !!installed
- end
-
- def has_post_install_message?
- post_install_message.empty?
- end
-
- def enqueued?
- !!enqueued
- end
-
- def installing?
- !installed? && enqueued?
- end
-
- def ready_to_install?(specs)
- @spec.dependencies.none? do |dep|
- next if dep.type == :development || dep.name == @name
- specs.reject(&:installed?).map(&:name).include? dep.name
- end
- end
- end
-
- def self.call(*options)
- new(*options).call
- end
-
- def self.max_threads
- @@max_threads ||= [Bundler.settings[:jobs].to_i-1, 1].max
- end
-
- def initialize(installer, all_specs, size, standalone)
- @installer = installer
- @size = size
- @standalone = standalone
- @specs = all_specs.map { |s| SpecInstallation.new(s) }
- end
-
- def call
- enqueue_specs
- process_specs until @specs.all?(&:installed?)
- ensure
- worker_pool && worker_pool.stop
- end
-
- def worker_pool
- @worker_pool ||= Bundler::Worker.new @size, lambda { |spec_install, worker_num|
- message = @installer.install_gem_from_spec spec_install.spec, @standalone, worker_num
- spec_install.post_install_message = message unless message.nil?
- spec_install.installed = true
- spec_install
- }
- end
-
- def process_specs
- spec = worker_pool.deq
- spec.enqueued = false
- collect_post_install_message spec if spec.has_post_install_message?
- enqueue_specs
- end
-
- def collect_post_install_message(spec)
- Bundler::Installer.post_install_messages[spec.name] = spec.post_install_message
- end
-
- # Keys in the remains hash represent uninstalled gems specs.
- # We enqueue all gem specs that do not have any dependencies.
- # Later we call this lambda again to install specs that depended on
- # previously installed specifications. We continue until all specs
- # are installed.
- def enqueue_specs
- @specs.reject(&:installing?).each do |spec|
- if spec.ready_to_install? @specs
- worker_pool.enq spec
- spec.enqueued = true
- end
- end
- end
-end