diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index 8164ab2af..59d484675 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -73,7 +73,6 @@ module Vagrant :cwd => nil, :home_path => nil, :local_data_path => nil, - :lock_path => nil, :ui_class => nil, :vagrantfile_name => nil, }.merge(opts || {}) @@ -99,7 +98,6 @@ module Vagrant # Set instance variables for all the configuration parameters. @cwd = opts[:cwd] @home_path = opts[:home_path] - @lock_path = opts[:lock_path] @vagrantfile_name = opts[:vagrantfile_name] @ui = opts[:ui_class].new @ui_class = opts[:ui_class] @@ -108,7 +106,7 @@ module Vagrant # runs at a time from {#batch}. @batch_lock = Mutex.new - @lock_acquired = false + @locks = {} @logger = Log4r::Logger.new("vagrant::environment") @logger.info("Environment initialized (#{self})") @@ -341,38 +339,46 @@ module Vagrant end end - # This returns the path which Vagrant uses to determine the location - # of the file lock. This is specific to each operating system. - def lock_path - @lock_path || tmp_path.join("vagrant.lock") - end - - # This locks Vagrant for the duration of the block passed to this - # method. During this time, any other environment which attempts - # to lock which points to the same lock file will fail. - def lock + # This acquires a process-level lock with the given name. + # + # The lock file is held within the data directory of this environment, + # so make sure that all environments that are locking are sharing + # the same data directory. + # + # This will raise Errors::EnvironmentLockedError if the lock can't + # be obtained. + # + # @param [String] name Name of the lock, since multiple locks can + # be held at one time. + def lock(name="global", **opts) # If we don't have a block, then locking is useless, so ignore it return if !block_given? # This allows multiple locks in the same process to be nested - return yield if @lock_acquired + return yield if @locks[name] + + # The path to this lock + lock_path = data_dir.join("lock.#{name}.lock") File.open(lock_path, "w+") do |f| # The file locking fails only if it returns "false." If it # succeeds it returns a 0, so we must explicitly check for # the proper error case. - raise Errors::EnvironmentLockedError if f.flock(File::LOCK_EX | File::LOCK_NB) === false + if f.flock(File::LOCK_EX | File::LOCK_NB) === false + raise Errors::EnvironmentLockedError, + name: name + end begin # Mark that we have a lock - @lock_acquired = true + @locks[name] = true yield ensure # We need to make sure that no matter what this is always # reset to false so we don't think we have a lock when we # actually don't. - @lock_acquired = false + @locks.delete(name) end end end diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 07ba05167..31c0ff85a 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -658,10 +658,9 @@ en: The download was interrupted by an external signal. It did not complete. environment_locked: |- - An instance of Vagrant is already running. Only one instance of Vagrant - may run at any given time to avoid problems with VirtualBox inconsistencies - occurring. Please wait for the other instance of Vagrant to end and then - try again. + Vagrant attempted to acquire a lock named '%{name}', but this + lock is being held by another instance of Vagrant already. Please + wait and try again. environment_non_existent_cwd: |- The working directory for Vagrant doesn't exist! This is the specified working directory: