Skip to content

Commit 9604d2a

Browse files
authored
Merge pull request #165 from tgymnich/in-memory-file-system-filelock
Restore the Ability to do per-File Locking
2 parents 7fa1973 + 83e7635 commit 9604d2a

File tree

1 file changed

+31
-3
lines changed

1 file changed

+31
-3
lines changed

Sources/TSCBasic/FileSystem.swift

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -552,9 +552,20 @@ public class InMemoryFileSystem: FileSystem {
552552
/// reality, the only practical use for InMemoryFileSystem is for unit
553553
/// tests.
554554
private let lock = Lock()
555+
/// A map that keeps weak references to all locked files.
556+
private var lockFiles = Dictionary<AbsolutePath, WeakReference<DispatchQueue>>()
557+
/// Used to access lockFiles in a thread safe manner.
558+
private let lockFilesLock = Lock()
555559

556560
/// Exclusive file system lock vended to clients through `withLock()`.
557-
private let lockFilesLock = Lock()
561+
// Used to ensure that DispatchQueues are releassed when they are no longer in use.
562+
private struct WeakReference<Value: AnyObject> {
563+
weak var reference: Value?
564+
565+
init(_ value: Value?) {
566+
self.reference = value
567+
}
568+
}
558569

559570
public init() {
560571
root = Node(.directory(DirectoryContents()))
@@ -895,8 +906,25 @@ public class InMemoryFileSystem: FileSystem {
895906
}
896907

897908
public func withLock<T>(on path: AbsolutePath, type: FileLock.LockType = .exclusive, _ body: () throws -> T) throws -> T {
898-
// FIXME: Lock individual files once resolving symlinks is thread-safe.
899-
return try lockFilesLock.withLock(body)
909+
let resolvedPath: AbsolutePath = try lock.withLock {
910+
if case let .symlink(destination) = try getNode(path)?.contents {
911+
return AbsolutePath(destination, relativeTo: path.parentDirectory)
912+
} else {
913+
return path
914+
}
915+
}
916+
917+
let fileQueue: DispatchQueue = lockFilesLock.withLock {
918+
if let queueReference = lockFiles[resolvedPath], let queue = queueReference.reference {
919+
return queue
920+
} else {
921+
let queue = DispatchQueue(label: "org.swift.swiftpm.in-memory-file-system.file-queue", attributes: .concurrent)
922+
lockFiles[resolvedPath] = WeakReference(queue)
923+
return queue
924+
}
925+
}
926+
927+
return try fileQueue.sync(flags: type == .exclusive ? .barrier : .init() , execute: body)
900928
}
901929
}
902930

0 commit comments

Comments
 (0)