# class Chef::RunLock

## Chef::RunLock¶ ↑

Provides an interface for acquiring and releasing a system-wide exclusive lock.

Used by Chef::Client to ensure only one instance of chef-client (or solo) is modifying the system at a time.

### Attributes

mutex[R]
runlock[R]
runlock_file[R]

### Public Class Methods

new(lockfile) click to toggle source

Create a new instance of RunLock

### Arguments¶ ↑

- :lockfile:

    the full path to the lockfile.

    # File lib/chef/run_lock.rb, line 42
    def initialize(lockfile)
      @runlock_file = lockfile
      @runlock = nil
      @mutex = nil
    end

### Public Instance Methods

acquire() click to toggle source

Acquire the system-wide lock. Will block indefinitely if another process already has the lock.

Each call to acquire should have a corresponding call to release.

The implementation is based on File#flock (see also: flock(2)).

Either acquire() or test() methods should be called in order to get the ownership of run_lock.

    # File lib/chef/run_lock.rb, line 57
    def acquire
      wait unless test
    end

release() click to toggle source

Release the system-wide lock.

    # File lib/chef/run_lock.rb, line 114
    def release
      if runlock
        if Chef::Platform.windows?
          mutex.release
        else
          runlock.flock(File::LOCK_UN)
        end
        runlock.close
        # Don't unlink the pid file, if another chef-client was waiting, it
        # won't be recreated. Better to leave a "dead" pid file than not have
        # it available if you need to break the lock.
        reset
      end
    end

save_pid() click to toggle source

    # File lib/chef/run_lock.rb, line 104
    def save_pid
      runlock.truncate(0)
      runlock.rewind # truncate doesn't reset position to 0.
      runlock.write(Process.pid.to_s)
      # flush the file fsync flushes the system buffers
      # in addition to ruby buffers
      runlock.fsync
    end

test() click to toggle source

Tests and if successful acquires the system-wide lock. Returns true if the lock is acquired, false otherwise.

Either acquire() or test() methods should be called in order to get the ownership of run_lock.

    # File lib/chef/run_lock.rb, line 67
    def test
      # ensure the runlock_file path exists
      create_path(File.dirname(runlock_file))
      @runlock = File.open(runlock_file,'a+')

      if Chef::Platform.windows?
        acquire_win32_mutex
      else
        # If we support FD_CLOEXEC, then use it.
        # NB: ruby-2.0.0-p195 sets FD_CLOEXEC by default, but not
        # ruby-1.8.7/1.9.3
        if Fcntl.const_defined?('F_SETFD') && Fcntl.const_defined?('FD_CLOEXEC')
          runlock.fcntl(Fcntl::F_SETFD, runlock.fcntl(Fcntl::F_GETFD, 0) | Fcntl::FD_CLOEXEC)
        end
        # Flock will return 0 if it can acquire the lock otherwise it
        # will return false
        if runlock.flock(File::LOCK_NB|File::LOCK_EX) == 0
          true
        else
          false
        end
      end
    end

wait() click to toggle source

Waits until acquiring the system-wide lock.

    # File lib/chef/run_lock.rb, line 94
    def wait
      runpid = runlock.read.strip.chomp
      Chef::Log.warn("Chef client #{runpid} is running, will wait for it to finish and then run.")
      if Chef::Platform.windows?
        mutex.wait
      else
        runlock.flock(File::LOCK_EX)
      end
    end