@@ -488,6 +488,9 @@ private class LocalFileSystem: FileSystem {
488
488
//
489
489
/// Concrete FileSystem implementation which simulates an empty disk.
490
490
public class InMemoryFileSystem : FileSystem {
491
+
492
+ /// Private internal representation of a file system node.
493
+ /// Not threadsafe.
491
494
private class Node {
492
495
/// The actual node data.
493
496
let contents : NodeContents
@@ -501,6 +504,9 @@ public class InMemoryFileSystem: FileSystem {
501
504
return Node ( contents. copy ( ) )
502
505
}
503
506
}
507
+
508
+ /// Private internal representation the contents of a file system node.
509
+ /// Not threadsafe.
504
510
private enum NodeContents {
505
511
case file( ByteString )
506
512
case directory( DirectoryContents )
@@ -518,6 +524,9 @@ public class InMemoryFileSystem: FileSystem {
518
524
}
519
525
}
520
526
}
527
+
528
+ /// Private internal representation the contents of a directory.
529
+ /// Not threadsafe.
521
530
private class DirectoryContents {
522
531
var entries : [ String : Node ]
523
532
@@ -535,9 +544,16 @@ public class InMemoryFileSystem: FileSystem {
535
544
}
536
545
}
537
546
538
- /// The root filesytem.
547
+ /// The root node of the filesytem.
539
548
private var root : Node
540
- /// Used to access lockFiles in a thread safe manner.
549
+
550
+ /// Protects `root` and everything underneath it.
551
+ /// FIXME: Using a single lock for this is a performance problem, but in
552
+ /// reality, the only practical use for InMemoryFileSystem is for unit
553
+ /// tests.
554
+ private let lock = Lock ( )
555
+
556
+ /// Exclusive file system lock vended to clients through `withLock()`.
541
557
private let lockFilesLock = Lock ( )
542
558
543
559
public init ( ) {
@@ -546,12 +562,15 @@ public class InMemoryFileSystem: FileSystem {
546
562
547
563
/// Creates deep copy of the object.
548
564
public func copy( ) -> InMemoryFileSystem {
549
- let fs = InMemoryFileSystem ( )
550
- fs. root = root. copy ( )
551
- return fs
565
+ return lock. withLock {
566
+ let fs = InMemoryFileSystem ( )
567
+ fs. root = root. copy ( )
568
+ return fs
569
+ }
552
570
}
553
571
554
- /// Get the node corresponding to the given path.
572
+ /// Private function to look up the node corresponding to a path.
573
+ /// Not threadsafe.
555
574
private func getNode( _ path: AbsolutePath , followSymlink: Bool = true ) throws -> Node ? {
556
575
func getNodeInternal( _ path: AbsolutePath ) throws -> Node ? {
557
576
// If this is the root node, return it.
@@ -590,46 +609,54 @@ public class InMemoryFileSystem: FileSystem {
590
609
// MARK: FileSystem Implementation
591
610
592
611
public func exists( _ path: AbsolutePath , followSymlink: Bool ) -> Bool {
593
- do {
594
- switch try getNode ( path, followSymlink: followSymlink) ? . contents {
595
- case . file, . directory, . symlink: return true
596
- case . none: return false
612
+ return lock. withLock {
613
+ do {
614
+ switch try getNode ( path, followSymlink: followSymlink) ? . contents {
615
+ case . file, . directory, . symlink: return true
616
+ case . none: return false
617
+ }
618
+ } catch {
619
+ return false
597
620
}
598
- } catch {
599
- return false
600
621
}
601
622
}
602
623
603
624
public func isDirectory( _ path: AbsolutePath ) -> Bool {
604
- do {
605
- if case . directory? = try getNode ( path) ? . contents {
606
- return true
625
+ return lock. withLock {
626
+ do {
627
+ if case . directory? = try getNode ( path) ? . contents {
628
+ return true
629
+ }
630
+ return false
631
+ } catch {
632
+ return false
607
633
}
608
- return false
609
- } catch {
610
- return false
611
634
}
612
635
}
613
636
614
637
public func isFile( _ path: AbsolutePath ) -> Bool {
615
- do {
616
- if case . file? = try getNode ( path) ? . contents {
617
- return true
638
+ return lock. withLock {
639
+ do {
640
+ if case . file? = try getNode ( path) ? . contents {
641
+ return true
642
+ }
643
+ return false
644
+ } catch {
645
+ return false
618
646
}
619
- return false
620
- } catch {
621
- return false
622
647
}
623
648
}
624
649
625
650
public func isSymlink( _ path: AbsolutePath ) -> Bool {
626
- do {
627
- if case . symlink? = try getNode ( path, followSymlink: false ) ? . contents {
628
- return true
651
+ return lock. withLock {
652
+ do {
653
+ if case . symlink? = try getNode ( path, followSymlink: false ) ? . contents {
654
+ return true
655
+ }
656
+ return false
657
+ } catch {
658
+ return false
629
659
}
630
- return false
631
- } catch {
632
- return false
633
660
}
634
661
}
635
662
@@ -658,18 +685,21 @@ public class InMemoryFileSystem: FileSystem {
658
685
}
659
686
660
687
public func getDirectoryContents( _ path: AbsolutePath ) throws -> [ String ] {
661
- guard let node = try getNode ( path) else {
662
- throw FileSystemError . noEntry
663
- }
664
- guard case . directory( let contents) = node. contents else {
665
- throw FileSystemError . notDirectory
666
- }
688
+ return try lock. withLock {
689
+ guard let node = try getNode ( path) else {
690
+ throw FileSystemError . noEntry
691
+ }
692
+ guard case . directory( let contents) = node. contents else {
693
+ throw FileSystemError . notDirectory
694
+ }
667
695
668
- // FIXME: Perhaps we should change the protocol to allow lazy behavior.
669
- return [ String] ( contents. entries. keys)
696
+ // FIXME: Perhaps we should change the protocol to allow lazy behavior.
697
+ return [ String] ( contents. entries. keys)
698
+ }
670
699
}
671
-
672
- public func createDirectory( _ path: AbsolutePath , recursive: Bool ) throws {
700
+
701
+ /// Not threadsafe.
702
+ private func _createDirectory( _ path: AbsolutePath , recursive: Bool ) throws {
673
703
// Ignore if client passes root.
674
704
guard !path. isRoot else {
675
705
return
@@ -681,10 +711,10 @@ public class InMemoryFileSystem: FileSystem {
681
711
// to create the parent and retry.
682
712
if recursive && path != parentPath {
683
713
// Attempt to create the parent.
684
- try createDirectory ( parentPath, recursive: true )
714
+ try _createDirectory ( parentPath, recursive: true )
685
715
686
716
// Re-attempt creation, non-recursively.
687
- return try createDirectory ( path, recursive: false )
717
+ return try _createDirectory ( path, recursive: false )
688
718
} else {
689
719
// Otherwise, we failed.
690
720
throw FileSystemError . noEntry
@@ -713,71 +743,83 @@ public class InMemoryFileSystem: FileSystem {
713
743
contents. entries [ path. basename] = Node ( . directory( DirectoryContents ( ) ) )
714
744
}
715
745
716
- public func createSymbolicLink( _ path: AbsolutePath , pointingAt destination: AbsolutePath , relative: Bool ) throws {
717
- // Create directory to destination parent.
718
- guard let destinationParent = try getNode ( path. parentDirectory) else {
719
- throw FileSystemError . noEntry
746
+ public func createDirectory( _ path: AbsolutePath , recursive: Bool ) throws {
747
+ return try lock. withLock {
748
+ try _createDirectory ( path, recursive: recursive)
720
749
}
750
+ }
721
751
722
- // Check that the parent is a directory.
723
- guard case . directory( let contents) = destinationParent. contents else {
724
- throw FileSystemError . notDirectory
725
- }
752
+ public func createSymbolicLink( _ path: AbsolutePath , pointingAt destination: AbsolutePath , relative: Bool ) throws {
753
+ return try lock. withLock {
754
+ // Create directory to destination parent.
755
+ guard let destinationParent = try getNode ( path. parentDirectory) else {
756
+ throw FileSystemError . noEntry
757
+ }
726
758
727
- guard contents. entries [ path. basename] == nil else {
728
- throw FileSystemError . alreadyExistsAtDestination
729
- }
759
+ // Check that the parent is a directory.
760
+ guard case . directory( let contents) = destinationParent. contents else {
761
+ throw FileSystemError . notDirectory
762
+ }
763
+
764
+ guard contents. entries [ path. basename] == nil else {
765
+ throw FileSystemError . alreadyExistsAtDestination
766
+ }
730
767
731
- let destination = relative ? destination. relative ( to: path. parentDirectory) . pathString : destination. pathString
768
+ let destination = relative ? destination. relative ( to: path. parentDirectory) . pathString : destination. pathString
732
769
733
- contents. entries [ path. basename] = Node ( . symlink( destination) )
770
+ contents. entries [ path. basename] = Node ( . symlink( destination) )
771
+ }
734
772
}
735
773
736
774
public func readFileContents( _ path: AbsolutePath ) throws -> ByteString {
737
- // Get the node.
738
- guard let node = try getNode ( path) else {
739
- throw FileSystemError . noEntry
740
- }
775
+ return try lock. withLock {
776
+ // Get the node.
777
+ guard let node = try getNode ( path) else {
778
+ throw FileSystemError . noEntry
779
+ }
741
780
742
- // Check that the node is a file.
743
- guard case . file( let contents) = node. contents else {
744
- // The path is a directory, this is an error.
745
- throw FileSystemError . isDirectory
746
- }
781
+ // Check that the node is a file.
782
+ guard case . file( let contents) = node. contents else {
783
+ // The path is a directory, this is an error.
784
+ throw FileSystemError . isDirectory
785
+ }
747
786
748
- // Return the file contents.
749
- return contents
787
+ // Return the file contents.
788
+ return contents
789
+ }
750
790
}
751
791
752
792
public func writeFileContents( _ path: AbsolutePath , bytes: ByteString ) throws {
753
- // It is an error if this is the root node.
754
- let parentPath = path. parentDirectory
755
- guard path != parentPath else {
756
- throw FileSystemError . isDirectory
757
- }
793
+ return try lock. withLock {
794
+ // It is an error if this is the root node.
795
+ let parentPath = path. parentDirectory
796
+ guard path != parentPath else {
797
+ throw FileSystemError . isDirectory
798
+ }
758
799
759
- // Get the parent node.
760
- guard let parent = try getNode ( parentPath) else {
761
- throw FileSystemError . noEntry
762
- }
800
+ // Get the parent node.
801
+ guard let parent = try getNode ( parentPath) else {
802
+ throw FileSystemError . noEntry
803
+ }
763
804
764
- // Check that the parent is a directory.
765
- guard case . directory( let contents) = parent. contents else {
766
- // The parent isn't a directory, this is an error.
767
- throw FileSystemError . notDirectory
768
- }
805
+ // Check that the parent is a directory.
806
+ guard case . directory( let contents) = parent. contents else {
807
+ // The parent isn't a directory, this is an error.
808
+ throw FileSystemError . notDirectory
809
+ }
769
810
770
- // Check if the node exists.
771
- if let node = contents. entries [ path. basename] {
772
- // Verify it is a file.
773
- guard case . file = node. contents else {
774
- // The path is a directory, this is an error.
775
- throw FileSystemError . isDirectory
811
+ // Check if the node exists.
812
+ if let node = contents. entries [ path. basename] {
813
+ // Verify it is a file.
814
+ guard case . file = node. contents else {
815
+ // The path is a directory, this is an error.
816
+ throw FileSystemError . isDirectory
817
+ }
776
818
}
777
- }
778
819
779
- // Write the file.
780
- contents. entries [ path. basename] = Node ( . file( bytes) )
820
+ // Write the file.
821
+ contents. entries [ path. basename] = Node ( . file( bytes) )
822
+ }
781
823
}
782
824
783
825
public func writeFileContents( _ path: AbsolutePath , bytes: ByteString , atomically: Bool ) throws {
@@ -787,21 +829,25 @@ public class InMemoryFileSystem: FileSystem {
787
829
}
788
830
789
831
public func removeFileTree( _ path: AbsolutePath ) throws {
790
- // Ignore root and get the parent node's content if its a directory.
791
- guard !path. isRoot,
792
- let parent = try ? getNode ( path. parentDirectory) ,
793
- case . directory( let contents) = parent. contents else {
794
- return
832
+ return lock. withLock {
833
+ // Ignore root and get the parent node's content if its a directory.
834
+ guard !path. isRoot,
835
+ let parent = try ? getNode ( path. parentDirectory) ,
836
+ case . directory( let contents) = parent. contents else {
837
+ return
838
+ }
839
+ // Set it to nil to release the contents.
840
+ contents. entries [ path. basename] = nil
795
841
}
796
- // Set it to nil to release the contents.
797
- contents. entries [ path. basename] = nil
798
842
}
799
843
800
844
public func chmod( _ mode: FileMode , path: AbsolutePath , options: Set < FileMode . Option > ) throws {
801
845
// FIXME: We don't have these semantics in InMemoryFileSystem.
802
846
}
803
-
804
- public func copy( from sourcePath: AbsolutePath , to destinationPath: AbsolutePath ) throws {
847
+
848
+ /// Private implementation of core copying function.
849
+ /// Not threadsafe.
850
+ private func _copy( from sourcePath: AbsolutePath , to destinationPath: AbsolutePath ) throws {
805
851
// Get the source node.
806
852
guard let source = try getNode ( sourcePath) else {
807
853
throw FileSystemError . noEntry
@@ -824,20 +870,28 @@ public class InMemoryFileSystem: FileSystem {
824
870
contents. entries [ destinationPath. basename] = source
825
871
}
826
872
827
- public func move( from sourcePath: AbsolutePath , to destinationPath: AbsolutePath ) throws {
828
- // Get the source parent node.
829
- guard let sourceParent = try getNode ( sourcePath. parentDirectory) else {
830
- throw FileSystemError . noEntry
873
+ public func copy( from sourcePath: AbsolutePath , to destinationPath: AbsolutePath ) throws {
874
+ return try lock. withLock {
875
+ try _copy ( from: sourcePath, to: destinationPath)
831
876
}
877
+ }
832
878
833
- // Check that the parent is a directory.
834
- guard case . directory( let contents) = sourceParent. contents else {
835
- throw FileSystemError . notDirectory
836
- }
879
+ public func move( from sourcePath: AbsolutePath , to destinationPath: AbsolutePath ) throws {
880
+ return try lock. withLock {
881
+ // Get the source parent node.
882
+ guard let sourceParent = try getNode ( sourcePath. parentDirectory) else {
883
+ throw FileSystemError . noEntry
884
+ }
885
+
886
+ // Check that the parent is a directory.
887
+ guard case . directory( let contents) = sourceParent. contents else {
888
+ throw FileSystemError . notDirectory
889
+ }
837
890
838
- try copy ( from: sourcePath, to: destinationPath)
891
+ try _copy ( from: sourcePath, to: destinationPath)
839
892
840
- contents. entries [ sourcePath. basename] = nil
893
+ contents. entries [ sourcePath. basename] = nil
894
+ }
841
895
}
842
896
843
897
public func withLock< T> ( on path: AbsolutePath , type: FileLock . LockType = . exclusive, _ body: ( ) throws -> T ) throws -> T {
0 commit comments