Class: Textus::Ports::BuildLock

Inherits:
Object
  • Object
show all
Defined in:
lib/textus/ports/build_lock.rb

Overview

Cross-process build lock: a pid/host-stamped lockfile under the store root that serializes reconcile’s produce/sweep. An instantiable class — it holds the root and lock state; ‘self.with(root:)` is a convenience that constructs one and runs the block under the held lock. It already satisfied ADR 0109’s single-shape rule (every port is an instantiable class) before that ADR’s Clock/Publisher conversions, so it was unchanged by them.

Constant Summary collapse

MAX_HOLDER_BYTES =
512

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root:) ⇒ BuildLock

Returns a new instance of BuildLock.



20
21
22
23
# File 'lib/textus/ports/build_lock.rb', line 20

def initialize(root:)
  @path = Textus::Layout.build_lock(root)
  @file = nil
end

Class Method Details

.with(root:) ⇒ Object



16
17
18
# File 'lib/textus/ports/build_lock.rb', line 16

def self.with(root:, &)
  new(root: root).acquire_or_raise(&)
end

Instance Method Details

#acquire_or_raiseObject



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/textus/ports/build_lock.rb', line 25

def acquire_or_raise
  FileUtils.mkdir_p(File.dirname(@path))
  @file = File.open(@path, File::RDWR | File::CREAT, 0o644)
  @file.close_on_exec = true

  unless @file.flock(File::LOCK_EX | File::LOCK_NB)
    holder = read_holder_safely
    @file.close
    @file = nil
    raise Textus::BuildInProgress.new(holder)
  end

  @file.truncate(0)
  @file.write("pid=#{Process.pid} started=#{Time.now.utc.iso8601} host=#{Socket.gethostname}\n")
  @file.flush

  yield
ensure
  release
end