Class: Vagrant::Action::Builtin::Lock

Inherits:
Object
  • Object
show all
Defined in:
lib/vagrant/action/builtin/lock.rb

Overview

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

Instance Method Summary collapse

Constructor Details

#initialize(app, env, options = nil) ⇒ Lock

Returns a new instance of Lock.

Raises:

  • (ArgumentError)


9
10
11
12
13
14
15
# 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

Instance Method Details

#call(env) ⇒ Object



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 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