From 33013c3ec6a7ab9d3187845efd4f6d8260d42ab1 Mon Sep 17 00:00:00 2001 From: Mikhail Zholobov Date: Tue, 3 Nov 2015 18:47:28 +0200 Subject: [PATCH 1/2] Add lock to "forward_ports" action to avoid the parallel execution It much easier and faster just to avoid the concurrency in this place. It guarantees that we will not create some port forwarding rules with the same names --- lib/vagrant-parallels/action/forward_ports.rb | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/vagrant-parallels/action/forward_ports.rb b/lib/vagrant-parallels/action/forward_ports.rb index b40cbcf3..cb130bbc 100644 --- a/lib/vagrant-parallels/action/forward_ports.rb +++ b/lib/vagrant-parallels/action/forward_ports.rb @@ -3,6 +3,7 @@ module Parallels module Action class ForwardPorts include VagrantPlugins::Parallels::Util::CompileForwardedPorts + @@lock = Mutex.new def initialize(app, env) @app = app @@ -21,8 +22,20 @@ def call(env) # Get the ports we're forwarding env[:forwarded_ports] ||= compile_forwarded_ports(env[:machine].config) - env[:ui].output(I18n.t('vagrant.actions.vm.forward_ports.forwarding')) - forward_ports + + # Acquire both of class- and process-level locks so that we don't + # forward ports simultaneousely with someone else. + @@lock.synchronize do + begin + env[:machine].env.lock('forward_ports') do + env[:ui].output(I18n.t('vagrant.actions.vm.forward_ports.forwarding')) + forward_ports + end + rescue Errors::EnvironmentLockedError + sleep 1 + retry + end + end @app.call(env) end From 15e2121ac2cc6790213dbea153c23ccfbfcfaaaa Mon Sep 17 00:00:00 2001 From: Mikhail Zholobov Date: Tue, 3 Nov 2015 18:53:05 +0200 Subject: [PATCH 2/2] Reduce the number of driver calls in "forward_ports" action Driver methods are slow. Since "forward_ports" action is now parallel-safe, we can call driver method "read_forwarded_ports" only once per each machine. --- lib/vagrant-parallels/action/forward_ports.rb | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/lib/vagrant-parallels/action/forward_ports.rb b/lib/vagrant-parallels/action/forward_ports.rb index cb130bbc..f583fb01 100644 --- a/lib/vagrant-parallels/action/forward_ports.rb +++ b/lib/vagrant-parallels/action/forward_ports.rb @@ -23,6 +23,9 @@ def call(env) # Get the ports we're forwarding env[:forwarded_ports] ||= compile_forwarded_ports(env[:machine].config) + # Exit if there are no ports to forward + return @app.call(env) if env[:forwarded_ports].empty? + # Acquire both of class- and process-level locks so that we don't # forward ports simultaneousely with someone else. @@lock.synchronize do @@ -41,6 +44,8 @@ def call(env) end def forward_ports + all_rules = @env[:machine].provider.driver.read_forwarded_ports(true) + names_in_use = all_rules.collect { |r| r[:rule_name] } ports = [] @env[:forwarded_ports].each do |fp| @@ -56,11 +61,22 @@ def forward_ports @env[:ui].detail(I18n.t('vagrant_parallels.actions.vm.forward_ports.forwarding_entry', message_attributes)) + # In Parallels Desktop the scope port forwarding rules is global, + # so we have to keep their names unique. + unique_id = fp.id + # Append random suffix to get the unique rule name + while names_in_use.include?(unique_id) + suffix = (0...4).map { ('a'..'z').to_a[rand(26)] }.join + unique_id = "#{fp.id}_#{suffix}" + end + # Mark this rule name as in use + names_in_use << unique_id + # Add the options to the ports array to send to the driver later ports << { guestport: fp.guest_port, hostport: fp.host_port, - name: get_unique_name(fp.id), + name: unique_id, protocol: fp.protocol } end @@ -70,21 +86,6 @@ def forward_ports @env[:machine].provider.driver.forward_ports(ports) end end - - private - - def get_unique_name(id) - all_rules = @env[:machine].provider.driver.read_forwarded_ports(true) - names_in_use = all_rules.collect { |r| r[:rule_name] } - - # Append random suffix to get unique rule name - while names_in_use.include?(id) - suffix = (0...4).map { ('a'..'z').to_a[rand(26)] }.join - id = "#{id}_#{suffix}" - end - - id - end end end end