Skip to content

Commit 17ede9e

Browse files
committed
Bring back "Lock scratch directory during tool execution"
This reverts commit e92df2d and brings back the changes from #7269. resolves #6643 rdar://113964179
1 parent 3e8328e commit 17ede9e

File tree

2 files changed

+49
-2
lines changed

2 files changed

+49
-2
lines changed

Sources/Basics/FileSystem/FileSystem+Extensions.swift

+13-2
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,8 @@ extension FileSystem {
196196
}
197197

198198
/// Execute the given block while holding the lock.
199-
public func withLock<T>(on path: AbsolutePath, type: FileLock.LockType, _ body: () throws -> T) throws -> T {
200-
try self.withLock(on: path.underlying, type: type, body)
199+
public func withLock<T>(on path: AbsolutePath, type: FileLock.LockType, blocking: Bool = true, _ body: () throws -> T) throws -> T {
200+
try self.withLock(on: path.underlying, type: type, blocking: blocking, body)
201201
}
202202

203203
/// Returns any known item replacement directories for a given path. These may be used by platform-specific
@@ -616,3 +616,14 @@ extension FileSystem {
616616
try self.removeFileTree(tempDirectory)
617617
}
618618
}
619+
620+
// MARK: - Locking
621+
622+
extension FileLock {
623+
public static func prepareLock(
624+
fileToLock: AbsolutePath,
625+
at lockFilesDirectory: AbsolutePath? = nil
626+
) throws -> FileLock {
627+
return try Self.prepareLock(fileToLock: fileToLock.underlying, at: lockFilesDirectory?.underlying)
628+
}
629+
}

Sources/CoreCommands/SwiftTool.swift

+36
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,11 @@ import Musl
3939
#endif
4040

4141
import func TSCBasic.exec
42+
import class TSCBasic.FileLock
4243
import protocol TSCBasic.OutputByteStream
4344
import class TSCBasic.Process
4445
import enum TSCBasic.ProcessEnv
46+
import enum TSCBasic.ProcessLockError
4547
import var TSCBasic.stderrStream
4648
import class TSCBasic.TerminalController
4749
import class TSCBasic.ThreadSafeOutputByteStream
@@ -109,6 +111,7 @@ extension SwiftCommand {
109111
workspaceLoaderProvider: self.workspaceLoaderProvider
110112
)
111113
swiftTool.buildSystemProvider = try buildSystemProvider(swiftTool)
114+
112115
var toolError: Error? = .none
113116
do {
114117
try self.run(swiftTool)
@@ -118,6 +121,7 @@ extension SwiftCommand {
118121
} catch {
119122
toolError = error
120123
}
124+
try swiftTool.releaseLockIfNeeded()
121125

122126
// wait for all observability items to process
123127
swiftTool.waitForObservabilityEvents(timeout: .now() + 5)
@@ -396,6 +400,9 @@ public final class SwiftTool {
396400
return workspace
397401
}
398402

403+
// Before creating the workspace, we need to acquire a lock on the build directory.
404+
try self.acquireLockIfNeeded()
405+
399406
if options.resolver.skipDependencyUpdate {
400407
self.observabilityScope.emit(warning: "'--skip-update' option is deprecated and will be removed in a future release")
401408
}
@@ -866,6 +873,35 @@ public final class SwiftTool {
866873
case success
867874
case failure
868875
}
876+
877+
// MARK: - Locking
878+
879+
private var workspaceLock: FileLock?
880+
881+
fileprivate func acquireLockIfNeeded() throws {
882+
guard workspaceLock == nil else {
883+
fatalError()
884+
}
885+
let workspaceLock = try FileLock.prepareLock(fileToLock: self.scratchDirectory)
886+
887+
// Try a non-blocking lock first so that we can inform the user about an already running SwiftPM.
888+
do {
889+
try workspaceLock.lock(type: .exclusive, blocking: false)
890+
} catch let ProcessLockError.unableToAquireLock(errno) {
891+
if errno == EWOULDBLOCK {
892+
self.outputStream.write("Another instance of SwiftPM is already running using '\(self.scratchDirectory)', waiting until that process has finished execution...".utf8)
893+
self.outputStream.flush()
894+
}
895+
}
896+
897+
let interactive = isatty(STDOUT_FILENO) == 1
898+
try workspaceLock.lock(type: .exclusive, blocking: interactive ? true : false)
899+
self.workspaceLock = workspaceLock
900+
}
901+
902+
fileprivate func releaseLockIfNeeded() {
903+
workspaceLock?.unlock()
904+
}
869905
}
870906

871907
/// Returns path of the nearest directory containing the manifest file w.r.t

0 commit comments

Comments
 (0)