From ae6937530bfe8f0aebea278d1654ff897a8c458c Mon Sep 17 00:00:00 2001 From: Louis Qian Date: Tue, 29 Apr 2025 23:17:42 -0500 Subject: [PATCH 1/4] feat: save and read pid from lock process --- Sources/CoreCommands/SwiftCommandState.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index 3549d6bd99c..256d9a832cd 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -1064,25 +1064,33 @@ public final class SwiftCommandState { // Try a non-blocking lock first so that we can inform the user about an already running SwiftPM. do { try workspaceLock.lock(type: .exclusive, blocking: false) + let pid = ProcessInfo.processInfo.processIdentifier + try String(pid).write(toFile: self.scratchDirectory.appending(".lock").pathString, atomically: true, encoding: .utf8) } catch ProcessLockError.unableToAquireLock(let errno) { if errno == EWOULDBLOCK { + let lockingPID = try? String(contentsOfFile: self.scratchDirectory.appending(".lock").pathString, encoding: .utf8) + let pidInfo = lockingPID.map { "(PID: \($0))" } ?? "" + if self.options.locations.ignoreLock { self.outputStream .write( - "Another instance of SwiftPM is already running using '\(self.scratchDirectory)', but this will be ignored since `--ignore-lock` has been passed" + "Another instance of SwiftPM \(pidInfo) is already running using '\(self.scratchDirectory)', but this will be ignored since `--ignore-lock` has been passed" .utf8 ) self.outputStream.flush() } else { self.outputStream .write( - "Another instance of SwiftPM is already running using '\(self.scratchDirectory)', waiting until that process has finished execution..." + "Another instance of SwiftPM \(pidInfo) is already running using '\(self.scratchDirectory)', waiting until that process has finished execution..." .utf8 ) self.outputStream.flush() // Only if we fail because there's an existing lock we need to acquire again as blocking. try workspaceLock.lock(type: .exclusive, blocking: true) + + let pid = ProcessInfo.processInfo.processIdentifier + try String(pid).write(toFile: self.scratchDirectory.appending(".lock").pathString, atomically: true, encoding: .utf8) } } } From 0985501a92c2e22774851d1d217c8812945cf474 Mon Sep 17 00:00:00 2001 From: Louis Qian Date: Wed, 30 Apr 2025 13:36:29 -0500 Subject: [PATCH 2/4] fix: pid spacer --- Sources/CoreCommands/SwiftCommandState.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index 256d9a832cd..7217a480b04 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -1069,19 +1069,19 @@ public final class SwiftCommandState { } catch ProcessLockError.unableToAquireLock(let errno) { if errno == EWOULDBLOCK { let lockingPID = try? String(contentsOfFile: self.scratchDirectory.appending(".lock").pathString, encoding: .utf8) - let pidInfo = lockingPID.map { "(PID: \($0))" } ?? "" + let pidInfo = lockingPID.map { "(PID: \($0)) " } ?? "" if self.options.locations.ignoreLock { self.outputStream .write( - "Another instance of SwiftPM \(pidInfo) is already running using '\(self.scratchDirectory)', but this will be ignored since `--ignore-lock` has been passed" + "Another instance of SwiftPM \(pidInfo)is already running using '\(self.scratchDirectory)', but this will be ignored since `--ignore-lock` has been passed" .utf8 ) self.outputStream.flush() } else { self.outputStream .write( - "Another instance of SwiftPM \(pidInfo) is already running using '\(self.scratchDirectory)', waiting until that process has finished execution..." + "Another instance of SwiftPM \(pidInfo)is already running using '\(self.scratchDirectory)', waiting until that process has finished execution..." .utf8 ) self.outputStream.flush() From 506ec61cfe761990ab78acd7eaa4dd84dbd5b575 Mon Sep 17 00:00:00 2001 From: Louis Qian Date: Wed, 30 Apr 2025 13:38:34 -0500 Subject: [PATCH 3/4] chore: refactor lockfile as a variable --- Sources/CoreCommands/SwiftCommandState.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index 7217a480b04..762f0946569 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -1060,15 +1060,16 @@ public final class SwiftCommandState { self.workspaceLockState = .locked let workspaceLock = try FileLock.prepareLock(fileToLock: self.scratchDirectory) + let lockFile = self.scratchDirectory.appending(".lock").pathString // Try a non-blocking lock first so that we can inform the user about an already running SwiftPM. do { try workspaceLock.lock(type: .exclusive, blocking: false) let pid = ProcessInfo.processInfo.processIdentifier - try String(pid).write(toFile: self.scratchDirectory.appending(".lock").pathString, atomically: true, encoding: .utf8) + try String(pid).write(toFile: lockFile, atomically: true, encoding: .utf8) } catch ProcessLockError.unableToAquireLock(let errno) { if errno == EWOULDBLOCK { - let lockingPID = try? String(contentsOfFile: self.scratchDirectory.appending(".lock").pathString, encoding: .utf8) + let lockingPID = try? String(contentsOfFile: lockFile, encoding: .utf8) let pidInfo = lockingPID.map { "(PID: \($0)) " } ?? "" if self.options.locations.ignoreLock { @@ -1090,7 +1091,7 @@ public final class SwiftCommandState { try workspaceLock.lock(type: .exclusive, blocking: true) let pid = ProcessInfo.processInfo.processIdentifier - try String(pid).write(toFile: self.scratchDirectory.appending(".lock").pathString, atomically: true, encoding: .utf8) + try String(pid).write(toFile: lockFile, atomically: true, encoding: .utf8) } } } From 9058721cd2d1a85043af346b78ffde353a5ac5d1 Mon Sep 17 00:00:00 2001 From: Louis Qian Date: Wed, 30 Apr 2025 21:34:57 -0500 Subject: [PATCH 4/4] feat: mark pid write as `try?` so it's non-blocking --- Sources/CoreCommands/SwiftCommandState.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index 762f0946569..a62fa3cae40 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -1066,7 +1066,7 @@ public final class SwiftCommandState { do { try workspaceLock.lock(type: .exclusive, blocking: false) let pid = ProcessInfo.processInfo.processIdentifier - try String(pid).write(toFile: lockFile, atomically: true, encoding: .utf8) + try? String(pid).write(toFile: lockFile, atomically: true, encoding: .utf8) } catch ProcessLockError.unableToAquireLock(let errno) { if errno == EWOULDBLOCK { let lockingPID = try? String(contentsOfFile: lockFile, encoding: .utf8) @@ -1091,7 +1091,7 @@ public final class SwiftCommandState { try workspaceLock.lock(type: .exclusive, blocking: true) let pid = ProcessInfo.processInfo.processIdentifier - try String(pid).write(toFile: lockFile, atomically: true, encoding: .utf8) + try? String(pid).write(toFile: lockFile, atomically: true, encoding: .utf8) } } }