@@ -602,6 +602,183 @@ class FileSystemTests: XCTestCase {
602
602
}
603
603
#endif
604
604
}
605
+
606
+ func testInMemoryFileSystemFileLock( ) throws {
607
+ let fs = InMemoryFileSystem ( )
608
+ let path = AbsolutePath ( " / " )
609
+ try fs. createDirectory ( path)
610
+
611
+ let fileA = path. appending ( component: " fileA " )
612
+ let fileB = path. appending ( component: " fileB " )
613
+ let lockFile = path. appending ( component: " lockfile " )
614
+
615
+ let writerThreads = ( 0 ..< 100 ) . map { _ in
616
+ return Thread {
617
+ try ! fs. withLock ( on: lockFile, type: . exclusive) {
618
+ // Get thr current contents of the file if any.
619
+ let valueA : Int
620
+ if fs. exists ( fileA) {
621
+ valueA = Int ( try fs. readFileContents ( fileA) . description) ?? 0
622
+ } else {
623
+ valueA = 0
624
+ }
625
+ // Sum and write back to file.
626
+ try fs. writeFileContents ( fileA, bytes: ByteString ( encodingAsUTF8: String ( valueA + 1 ) ) )
627
+
628
+ Thread . yield ( )
629
+
630
+ // Get thr current contents of the file if any.
631
+ let valueB : Int
632
+ if fs. exists ( fileB) {
633
+ valueB = Int ( try fs. readFileContents ( fileB) . description) ?? 0
634
+ } else {
635
+ valueB = 0
636
+ }
637
+ // Sum and write back to file.
638
+ try fs. writeFileContents ( fileB, bytes: ByteString ( encodingAsUTF8: String ( valueB + 1 ) ) )
639
+ }
640
+ }
641
+ }
642
+
643
+ let readerThreads = ( 0 ..< 20 ) . map { _ in
644
+ return Thread {
645
+ try ! fs. withLock ( on: lockFile, type: . shared) {
646
+ try XCTAssertEqual ( fs. readFileContents ( fileA) , fs. readFileContents ( fileB) )
647
+
648
+ Thread . yield ( )
649
+
650
+ try XCTAssertEqual ( fs. readFileContents ( fileA) , fs. readFileContents ( fileB) )
651
+ }
652
+ }
653
+ }
654
+
655
+ writerThreads. forEach { $0. start ( ) }
656
+ readerThreads. forEach { $0. start ( ) }
657
+ writerThreads. forEach { $0. join ( ) }
658
+ readerThreads. forEach { $0. join ( ) }
659
+
660
+ try XCTAssertEqual ( fs. readFileContents ( fileA) , " 100 " )
661
+ try XCTAssertEqual ( fs. readFileContents ( fileB) , " 100 " )
662
+ }
663
+
664
+ func testLocalFileSystemFileLock( ) throws {
665
+ try withTemporaryDirectory { tempDir in
666
+ let fileA = tempDir. appending ( component: " fileA " )
667
+ let fileB = tempDir. appending ( component: " fileB " )
668
+ let lockFile = tempDir. appending ( component: " lockfile " )
669
+
670
+ let writerThreads = ( 0 ..< 100 ) . map { _ in
671
+ return Thread {
672
+ try ! localFileSystem. withLock ( on: lockFile, type: . exclusive) {
673
+ // Get thr current contents of the file if any.
674
+ let valueA : Int
675
+ if localFileSystem. exists ( fileA) {
676
+ valueA = Int ( try localFileSystem. readFileContents ( fileA) . description) ?? 0
677
+ } else {
678
+ valueA = 0
679
+ }
680
+ // Sum and write back to file.
681
+ try localFileSystem. writeFileContents ( fileA, bytes: ByteString ( encodingAsUTF8: String ( valueA + 1 ) ) )
682
+
683
+ Thread . yield ( )
684
+
685
+ // Get thr current contents of the file if any.
686
+ let valueB : Int
687
+ if localFileSystem. exists ( fileB) {
688
+ valueB = Int ( try localFileSystem. readFileContents ( fileB) . description) ?? 0
689
+ } else {
690
+ valueB = 0
691
+ }
692
+ // Sum and write back to file.
693
+ try localFileSystem. writeFileContents ( fileB, bytes: ByteString ( encodingAsUTF8: String ( valueB + 1 ) ) )
694
+ }
695
+ }
696
+ }
697
+
698
+ let readerThreads = ( 0 ..< 20 ) . map { _ in
699
+ return Thread {
700
+ try ! localFileSystem. withLock ( on: lockFile, type: . shared) {
701
+ try XCTAssertEqual ( localFileSystem. readFileContents ( fileA) , localFileSystem. readFileContents ( fileB) )
702
+
703
+ Thread . yield ( )
704
+
705
+ try XCTAssertEqual ( localFileSystem. readFileContents ( fileA) , localFileSystem. readFileContents ( fileB) )
706
+ }
707
+ }
708
+ }
709
+
710
+ writerThreads. forEach { $0. start ( ) }
711
+ readerThreads. forEach { $0. start ( ) }
712
+ writerThreads. forEach { $0. join ( ) }
713
+ readerThreads. forEach { $0. join ( ) }
714
+
715
+ try XCTAssertEqual ( localFileSystem. readFileContents ( fileA) , " 100 " )
716
+ try XCTAssertEqual ( localFileSystem. readFileContents ( fileB) , " 100 " )
717
+ }
718
+ }
719
+
720
+ func testRerootedFileSystemViewFileLock( ) throws {
721
+ let inMemoryFS = InMemoryFileSystem ( )
722
+ let rootPath = AbsolutePath ( " /tmp " )
723
+ try inMemoryFS. createDirectory ( rootPath)
724
+
725
+ let fs = RerootedFileSystemView ( inMemoryFS, rootedAt: rootPath)
726
+ let path = AbsolutePath ( " / " )
727
+ try fs. createDirectory ( path)
728
+
729
+ let fileA = path. appending ( component: " fileA " )
730
+ let fileB = path. appending ( component: " fileB " )
731
+ let lockFile = path. appending ( component: " lockfile " )
732
+
733
+ let writerThreads = ( 0 ..< 100 ) . map { _ in
734
+ return Thread {
735
+ try ! fs. withLock ( on: lockFile, type: . exclusive) {
736
+ // Get thr current contents of the file if any.
737
+ let valueA : Int
738
+ if fs. exists ( fileA) {
739
+ valueA = Int ( try ! fs. readFileContents ( fileA) . description) ?? 0
740
+ } else {
741
+ valueA = 0
742
+ }
743
+ // Sum and write back to file.
744
+ try ! fs. writeFileContents ( fileA, bytes: ByteString ( encodingAsUTF8: String ( valueA + 1 ) ) )
745
+
746
+ Thread . yield ( )
747
+
748
+ // Get thr current contents of the file if any.
749
+ let valueB : Int
750
+ if fs. exists ( fileB) {
751
+ valueB = Int ( try fs. readFileContents ( fileB) . description) ?? 0
752
+ } else {
753
+ valueB = 0
754
+ }
755
+ // Sum and write back to file.
756
+ try fs. writeFileContents ( fileB, bytes: ByteString ( encodingAsUTF8: String ( valueB + 1 ) ) )
757
+ }
758
+ }
759
+ }
760
+
761
+ let readerThreads = ( 0 ..< 20 ) . map { _ in
762
+ return Thread {
763
+ try ! fs. withLock ( on: lockFile, type: . shared) {
764
+ try XCTAssertEqual ( fs. readFileContents ( fileA) , fs. readFileContents ( fileB) )
765
+
766
+ Thread . yield ( )
767
+
768
+ try XCTAssertEqual ( fs. readFileContents ( fileA) , fs. readFileContents ( fileB) )
769
+ }
770
+ }
771
+ }
772
+
773
+ writerThreads. forEach { $0. start ( ) }
774
+ readerThreads. forEach { $0. start ( ) }
775
+ writerThreads. forEach { $0. join ( ) }
776
+ readerThreads. forEach { $0. join ( ) }
777
+
778
+ try XCTAssertEqual ( fs. readFileContents ( fileA) , " 100 " )
779
+ try XCTAssertEqual ( fs. readFileContents ( fileB) , " 100 " )
780
+ }
781
+
605
782
}
606
783
607
784
/// Helper method to test file tree removal method on the given file system.
0 commit comments