@@ -488,6 +488,9 @@ private class LocalFileSystem: FileSystem {
488488//
489489/// Concrete FileSystem implementation which simulates an empty disk.
490490public class InMemoryFileSystem : FileSystem {
491+
492+ /// Private internal representation of a file system node.
493+ /// Not threadsafe.
491494 private class Node {
492495 /// The actual node data.
493496 let contents : NodeContents
@@ -501,6 +504,9 @@ public class InMemoryFileSystem: FileSystem {
501504 return Node ( contents. copy ( ) )
502505 }
503506 }
507+
508+ /// Private internal representation the contents of a file system node.
509+ /// Not threadsafe.
504510 private enum NodeContents {
505511 case file( ByteString )
506512 case directory( DirectoryContents )
@@ -518,6 +524,9 @@ public class InMemoryFileSystem: FileSystem {
518524 }
519525 }
520526 }
527+
528+ /// Private internal representation the contents of a directory.
529+ /// Not threadsafe.
521530 private class DirectoryContents {
522531 var entries : [ String : Node ]
523532
@@ -535,9 +544,16 @@ public class InMemoryFileSystem: FileSystem {
535544 }
536545 }
537546
538- /// The root filesytem.
547+ /// The root node of the filesytem.
539548 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()`.
541557 private let lockFilesLock = Lock ( )
542558
543559 public init ( ) {
@@ -546,12 +562,15 @@ public class InMemoryFileSystem: FileSystem {
546562
547563 /// Creates deep copy of the object.
548564 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+ }
552570 }
553571
554- /// Get the node corresponding to the given path.
572+ /// Private function to look up the node corresponding to a path.
573+ /// Not threadsafe.
555574 private func getNode( _ path: AbsolutePath , followSymlink: Bool = true ) throws -> Node ? {
556575 func getNodeInternal( _ path: AbsolutePath ) throws -> Node ? {
557576 // If this is the root node, return it.
@@ -590,46 +609,54 @@ public class InMemoryFileSystem: FileSystem {
590609 // MARK: FileSystem Implementation
591610
592611 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
597620 }
598- } catch {
599- return false
600621 }
601622 }
602623
603624 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
607633 }
608- return false
609- } catch {
610- return false
611634 }
612635 }
613636
614637 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
618646 }
619- return false
620- } catch {
621- return false
622647 }
623648 }
624649
625650 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
629659 }
630- return false
631- } catch {
632- return false
633660 }
634661 }
635662
@@ -658,18 +685,21 @@ public class InMemoryFileSystem: FileSystem {
658685 }
659686
660687 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+ }
667695
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+ }
670699 }
671-
672- public func createDirectory( _ path: AbsolutePath , recursive: Bool ) throws {
700+
701+ /// Not threadsafe.
702+ private func _createDirectory( _ path: AbsolutePath , recursive: Bool ) throws {
673703 // Ignore if client passes root.
674704 guard !path. isRoot else {
675705 return
@@ -681,10 +711,10 @@ public class InMemoryFileSystem: FileSystem {
681711 // to create the parent and retry.
682712 if recursive && path != parentPath {
683713 // Attempt to create the parent.
684- try createDirectory ( parentPath, recursive: true )
714+ try _createDirectory ( parentPath, recursive: true )
685715
686716 // Re-attempt creation, non-recursively.
687- return try createDirectory ( path, recursive: false )
717+ return try _createDirectory ( path, recursive: false )
688718 } else {
689719 // Otherwise, we failed.
690720 throw FileSystemError . noEntry
@@ -713,71 +743,83 @@ public class InMemoryFileSystem: FileSystem {
713743 contents. entries [ path. basename] = Node ( . directory( DirectoryContents ( ) ) )
714744 }
715745
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)
720749 }
750+ }
721751
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+ }
726758
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+ }
730767
731- let destination = relative ? destination. relative ( to: path. parentDirectory) . pathString : destination. pathString
768+ let destination = relative ? destination. relative ( to: path. parentDirectory) . pathString : destination. pathString
732769
733- contents. entries [ path. basename] = Node ( . symlink( destination) )
770+ contents. entries [ path. basename] = Node ( . symlink( destination) )
771+ }
734772 }
735773
736774 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+ }
741780
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+ }
747786
748- // Return the file contents.
749- return contents
787+ // Return the file contents.
788+ return contents
789+ }
750790 }
751791
752792 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+ }
758799
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+ }
763804
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+ }
769810
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+ }
776818 }
777- }
778819
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+ }
781823 }
782824
783825 public func writeFileContents( _ path: AbsolutePath , bytes: ByteString , atomically: Bool ) throws {
@@ -787,21 +829,25 @@ public class InMemoryFileSystem: FileSystem {
787829 }
788830
789831 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
795841 }
796- // Set it to nil to release the contents.
797- contents. entries [ path. basename] = nil
798842 }
799843
800844 public func chmod( _ mode: FileMode , path: AbsolutePath , options: Set < FileMode . Option > ) throws {
801845 // FIXME: We don't have these semantics in InMemoryFileSystem.
802846 }
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 {
805851 // Get the source node.
806852 guard let source = try getNode ( sourcePath) else {
807853 throw FileSystemError . noEntry
@@ -824,20 +870,28 @@ public class InMemoryFileSystem: FileSystem {
824870 contents. entries [ destinationPath. basename] = source
825871 }
826872
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)
831876 }
877+ }
832878
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+ }
837890
838- try copy ( from: sourcePath, to: destinationPath)
891+ try _copy ( from: sourcePath, to: destinationPath)
839892
840- contents. entries [ sourcePath. basename] = nil
893+ contents. entries [ sourcePath. basename] = nil
894+ }
841895 }
842896
843897 public func withLock< T> ( on path: AbsolutePath , type: FileLock . LockType = . exclusive, _ body: ( ) throws -> T ) throws -> T {
0 commit comments