Skip to content

Commit a8e6870

Browse files
authored
Lock scratch directory during tool execution (#7269)
1 parent f313486 commit a8e6870

File tree

2 files changed

+22
-8
lines changed

2 files changed

+22
-8
lines changed

Sources/Basics/FileSystem/FileSystem+Extensions.swift

+2-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

Sources/CoreCommands/SwiftTool.swift

+20-6
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import func TSCBasic.exec
4141
import protocol TSCBasic.OutputByteStream
4242
import class TSCBasic.Process
4343
import enum TSCBasic.ProcessEnv
44+
import enum TSCBasic.ProcessLockError
4445
import var TSCBasic.stderrStream
4546
import class TSCBasic.TerminalController
4647
import class TSCBasic.ThreadSafeOutputByteStream
@@ -108,14 +109,27 @@ extension SwiftCommand {
108109
workspaceLoaderProvider: self.workspaceLoaderProvider
109110
)
110111
swiftTool.buildSystemProvider = try buildSystemProvider(swiftTool)
111-
var toolError: Error? = .none
112+
113+
// Try a non-blocking lock first so that we can inform the user about an already running SwiftPM.
112114
do {
113-
try self.run(swiftTool)
114-
if swiftTool.observabilityScope.errorsReported || swiftTool.executionStatus == .failure {
115-
throw ExitCode.failure
115+
try swiftTool.fileSystem.withLock(on: swiftTool.scratchDirectory, type: .exclusive, blocking: false) {}
116+
} catch let ProcessLockError.unableToAquireLock(errno) {
117+
if errno == EWOULDBLOCK {
118+
swiftTool.outputStream.write("Another instance of SwiftPM is already running using '\(swiftTool.scratchDirectory)', waiting until that process has finished execution...".utf8)
119+
swiftTool.outputStream.flush()
120+
}
121+
}
122+
123+
var toolError: Error? = .none
124+
try swiftTool.fileSystem.withLock(on: swiftTool.scratchDirectory, type: .exclusive) {
125+
do {
126+
try self.run(swiftTool)
127+
if swiftTool.observabilityScope.errorsReported || swiftTool.executionStatus == .failure {
128+
throw ExitCode.failure
129+
}
130+
} catch {
131+
toolError = error
116132
}
117-
} catch {
118-
toolError = error
119133
}
120134

121135
// wait for all observability items to process

0 commit comments

Comments
 (0)