@@ -200,6 +200,9 @@ public protocol FileSystem: class {
200
200
201
201
/// Move a file or directory.
202
202
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
203
206
}
204
207
205
208
/// Convenience implementations (default arguments aren't permitted in protocol
@@ -240,6 +243,10 @@ public extension FileSystem {
240
243
func getFileInfo( _ path: AbsolutePath ) throws -> FileInfo {
241
244
throw FileSystemError . unsupported
242
245
}
246
+
247
+ func withLock< T> ( on path: AbsolutePath , type: LockType , _ body: ( ) throws -> T ) throws -> T {
248
+ throw FileSystemError . unsupported
249
+ }
243
250
}
244
251
245
252
/// Concrete FileSystem implementation which communicates with the local file system.
@@ -450,6 +457,11 @@ private class LocalFileSystem: FileSystem {
450
457
guard !exists( destinationPath) else { throw FileSystemError . alreadyExistsAtDestination }
451
458
try FileManager . default. moveItem ( at: sourcePath. asURL, to: destinationPath. asURL)
452
459
}
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
+ }
453
465
}
454
466
455
467
// FIXME: This class does not yet support concurrent mutation safely.
@@ -502,6 +514,8 @@ public class InMemoryFileSystem: FileSystem {
502
514
503
515
/// The root filesytem.
504
516
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)
505
519
506
520
public init ( ) {
507
521
root = Node ( . directory( DirectoryContents ( ) ) )
@@ -760,6 +774,22 @@ public class InMemoryFileSystem: FileSystem {
760
774
761
775
contents. entries [ sourcePath. basename] = nil
762
776
}
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
+ }
763
793
}
764
794
765
795
/// A rerooted view on an existing FileSystem.
@@ -864,6 +894,10 @@ public class RerootedFileSystemView: FileSystem {
864
894
public func move( from sourcePath: AbsolutePath , to destinationPath: AbsolutePath ) throws {
865
895
try underlyingFileSystem. move ( from: formUnderlyingPath ( sourcePath) , to: formUnderlyingPath ( sourcePath) )
866
896
}
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
+ }
867
901
}
868
902
869
903
/// Public access to the local FS proxy.
0 commit comments