Class: LockFile Private

Inherits:
Object show all
Defined in:
lock_file.rb

Overview

This class is part of a private API. This class may only be used in the Homebrew/brew repository. Third parties should avoid using this class if possible, as it may be removed or changed without warning.

A lock file to prevent multiple Homebrew processes from modifying the same path.

Direct Known Subclasses

CaskLock, DownloadLock, FormulaLock

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type, locked_path) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:



14
15
16
17
18
19
# File 'lock_file.rb', line 14

def initialize(type, locked_path)
  @locked_path = locked_path
  lock_name = locked_path.basename.to_s
  @path = HOMEBREW_LOCKS/"#{lock_name}.#{type}.lock"
  @lockfile = nil
end

Instance Attribute Details

#pathObject (readonly)

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.



11
12
13
# File 'lock_file.rb', line 11

def path
  @path
end

Instance Method Details

#lockvoid

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.



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
54
55
56
57
58
59
60
61
62
63
64
# File 'lock_file.rb', line 22

def lock
  ignore_interrupts do
    next if @lockfile.present?

    path.dirname.mkpath

    begin
      lockfile = begin
        path.open(File::RDWR | File::CREAT)
      rescue Errno::EMFILE
        odie "The maximum number of open files on this system has been reached. " \
             "Use `ulimit -n` to increase this limit."
      end
      lockfile.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)

      if lockfile.flock(File::LOCK_EX | File::LOCK_NB)
        # This prevents a race condition in case the file we locked doesn't exist on disk anymore, e.g.:
        #
        # 1. Process A creates and opens the file.
        # 2. Process A locks the file.
        # 3. Process B opens the file.
        # 4. Process A unlinks the file.
        # 5. Process A unlocks the file.
        # 6. Process B locks the file.
        # 7. Process C creates and opens the file.
        # 8. Process C locks the file.
        # 9. Process B and C hold locks to files with different inode numbers. 💥
        if !path.exist? || lockfile.stat.ino != path.stat.ino
          lockfile.close
          raise OpenFileChangedOnDisk
        end

        @lockfile = lockfile
        next
      end
    rescue OpenFileChangedOnDisk
      retry
    end

    lockfile.close
    raise OperationInProgressError, @locked_path
  end
end

#unlock(unlink: false) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.

Parameters:

  • unlink (Boolean) (defaults to: false)


67
68
69
70
71
72
73
74
75
76
# File 'lock_file.rb', line 67

def unlock(unlink: false)
  ignore_interrupts do
    next if @lockfile.nil?

    @path.unlink if unlink
    @lockfile.flock(File::LOCK_UN)
    @lockfile.close
    @lockfile = nil
  end
end

#with_lockObject

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.



78
79
80
81
82
83
# File 'lock_file.rb', line 78

def with_lock
  lock
  yield
ensure
  unlock
end