Skip to content

Commit c44e43a

Browse files
committed
added withLock to FileSystem
1 parent 3712505 commit c44e43a

File tree

2 files changed

+44
-2
lines changed

2 files changed

+44
-2
lines changed

Sources/TSCBasic/FileSystem.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@ public protocol FileSystem: class {
200200

201201
/// Move a file or directory.
202202
func move(from sourcePath: AbsolutePath, to destinationPath: AbsolutePath) throws
203+
204+
/// Execute the given block while holding the lock.
205+
func withLock<T>(on path: AbsolutePath, type: LockType, _ body: () throws -> T) throws -> T
203206
}
204207

205208
/// Convenience implementations (default arguments aren't permitted in protocol
@@ -240,6 +243,10 @@ public extension FileSystem {
240243
func getFileInfo(_ path: AbsolutePath) throws -> FileInfo {
241244
throw FileSystemError.unsupported
242245
}
246+
247+
func withLock<T>(on path: AbsolutePath, type: LockType, _ body: () throws -> T) throws -> T {
248+
throw FileSystemError.unsupported
249+
}
243250
}
244251

245252
/// Concrete FileSystem implementation which communicates with the local file system.
@@ -450,6 +457,11 @@ private class LocalFileSystem: FileSystem {
450457
guard !exists(destinationPath) else { throw FileSystemError.alreadyExistsAtDestination }
451458
try FileManager.default.moveItem(at: sourcePath.asURL, to: destinationPath.asURL)
452459
}
460+
461+
func withLock<T>(on path: AbsolutePath, type: LockType = .exclusive, _ body: () throws -> T) throws -> T {
462+
let lock = FileLock(name: path.basename, cachePath: path.parentDirectory)
463+
return try lock.withLock(type: type, body)
464+
}
453465
}
454466

455467
// FIXME: This class does not yet support concurrent mutation safely.
@@ -502,6 +514,8 @@ public class InMemoryFileSystem: FileSystem {
502514

503515
/// The root filesytem.
504516
private var root: Node
517+
/// A map that keeps weak references to all locked files.
518+
private let lockMap = NSMapTable<NSString, ReadWriteLock>(keyOptions: .copyIn, valueOptions: .weakMemory)
505519

506520
public init() {
507521
root = Node(.directory(DirectoryContents()))
@@ -760,6 +774,22 @@ public class InMemoryFileSystem: FileSystem {
760774

761775
contents.entries[sourcePath.basename] = nil
762776
}
777+
778+
public func withLock<T>(on path: AbsolutePath, type: LockType = .exclusive, _ body: () throws -> T) throws -> T {
779+
let lock = Lock()
780+
var fileLock: ReadWriteLock
781+
782+
lock.lock()
783+
if let lock = lockMap.object(forKey: path.pathString as NSString) {
784+
fileLock = lock
785+
} else {
786+
fileLock = ReadWriteLock()
787+
lockMap.setObject(fileLock, forKey: path.pathString as NSString)
788+
}
789+
lock.unlock()
790+
791+
return try fileLock.withLock(type: type, body)
792+
}
763793
}
764794

765795
/// A rerooted view on an existing FileSystem.
@@ -864,6 +894,10 @@ public class RerootedFileSystemView: FileSystem {
864894
public func move(from sourcePath: AbsolutePath, to destinationPath: AbsolutePath) throws {
865895
try underlyingFileSystem.move(from: formUnderlyingPath(sourcePath), to: formUnderlyingPath(sourcePath))
866896
}
897+
898+
public func withLock<T>(on path: AbsolutePath, type: LockType = .exclusive, _ body: () throws -> T) throws -> T {
899+
return try underlyingFileSystem.withLock(on: formUnderlyingPath(path), type: type, body)
900+
}
867901
}
868902

869903
/// Public access to the local FS proxy.

Sources/TSCBasic/Lock.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,18 @@ public struct Lock {
2424
public init() {
2525
}
2626

27+
func lock() {
28+
_lock.lock()
29+
}
30+
31+
func unlock() {
32+
_lock.unlock()
33+
}
34+
2735
/// Execute the given block while holding the lock.
2836
public func withLock<T> (_ body: () throws -> T) rethrows -> T {
29-
_lock.lock()
30-
defer { _lock.unlock() }
37+
lock()
38+
defer { unlock() }
3139
return try body()
3240
}
3341
}

0 commit comments

Comments
 (0)