class Vagrant::Action::Builtin::Lock

This class creates a multi-process lock using ‘flock`. The lock is active for the remainder of the middleware stack.

Public Class Methods

new(app, env, options=nil) click to toggle source
# File lib/vagrant/action/builtin/lock.rb, line 9
def initialize(app, env, options=nil)
  @app     = app
  @logger  = Log4r::Logger.new("vagrant::action::builtin::lock")
  @options ||= options || {}
  raise ArgumentError, "Please specify a lock path" if !@options[:path]
  raise ArgumentError, "Please specify an exception." if !@options[:exception]
end

Public Instance Methods

call(env) click to toggle source
# File lib/vagrant/action/builtin/lock.rb, line 17
def call(env)
  lock_path = @options[:path]
  lock_path = lock_path.call(env) if lock_path.is_a?(Proc)

  env_key   = "has_lock_#{lock_path}"

  if !env[env_key]
    # If we already have the key in our environment we assume the
    # lock is held by our middleware stack already and we allow
    # nesting.
    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.
      @logger.info("Locking: #{lock_path}")
      if f.flock(File::LOCK_EX | File::LOCK_NB) === false
        exception = @options[:exception]
        exception = exception.call(env) if exception.is_a?(Proc)
        raise exception
      end

      # Set that we gained the lock and call deeper into the
      # middleware, but make sure we UNSET the lock when we leave.
      begin
        env[env_key] = true
        @app.call(env)
      ensure
        @logger.info("Unlocking: #{lock_path}")
        env[env_key] = false
        f.flock(File::LOCK_UN)
      end
    end
  else
    # Just call up the middleware because we already hold the lock
    @app.call(env)
  end
end