Work around WaitForCommunicator lock race condition

The LXC provider issues the "fetch_ip" action to look up the IP address
of the container as part of its "ssh_info" action.
Vagrant::LXC::Action.action_fetch_ip checks the machine state using
Builtin::IsState, which calls Vagrant::Machine.state, which also updates
the state in the machine index and acquires a machine index entry lock to do that.
A race condition ensues in WaitForCommunicator.call, where ready_thr tries
to acquire the machine index lock while running ssh_info, and states_thr tries
to acquire the same lock doing its own state look up (env[:machine].state.id).
If they both try to acquire the lock at the same time, one will fail, and
an exception will be raised.

Work around this issue by checking for the desired machine state (:running) in
Vagrant::LXC::Provider.ssh_info, which can get the state from
Vagrant::LXC::Provider.state, which in turn does not write out the state into
the index file and does not acquire the index entry lock.
This commit is contained in:
Ray Ruvinskiy 2014-09-29 16:47:29 -04:00
parent e76a18185c
commit dc55c914e4
2 changed files with 5 additions and 10 deletions

View file

@ -197,14 +197,9 @@ module Vagrant
# is expected to be put into the `:machine_ip` key. # is expected to be put into the `:machine_ip` key.
def self.action_fetch_ip def self.action_fetch_ip
Builder.new.tap do |b| Builder.new.tap do |b|
b.use Builtin::ConfigValidate b.use Builtin::Call, Builtin::ConfigValidate do |env, b2|
b.use Builtin::Call, Builtin::IsState, :running do |env, b2| b2.use FetchIpWithLxcAttach if env[:machine].provider.driver.supports_attach?
if env[:result] b2.use FetchIpFromDnsmasqLeases
b2.use FetchIpWithLxcAttach if env[:machine].provider.driver.supports_attach?
b2.use FetchIpFromDnsmasqLeases
else
b2.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_running")
end
end end
end end
end end

View file

@ -64,9 +64,9 @@ module Vagrant
# Returns the SSH info for accessing the Container. # Returns the SSH info for accessing the Container.
def ssh_info def ssh_info
# If the Container is not created then we cannot possibly SSH into it, so # If the Container is not running then we cannot possibly SSH into it, so
# we return nil. # we return nil.
return nil if state == :not_created return nil if state.id != :running
# Run a custom action called "fetch_ip" which does what it says and puts # Run a custom action called "fetch_ip" which does what it says and puts
# the IP found into the `:machine_ip` key in the environment. # the IP found into the `:machine_ip` key in the environment.