88using System . Diagnostics ;
99using System . IO ;
1010using System . Linq ;
11+ using System . Runtime . CompilerServices ;
1112using System . Text ;
1213using System . Threading ;
1314using System . Threading . Tasks ;
@@ -45,9 +46,9 @@ namespace Lucene.Net.Store
4546 /// <summary>
4647 /// Tests MMapDirectory's MultiMMapIndexInput
4748 /// <para/>
48- /// Because Java 's ByteBuffer uses an int to address the
49- /// values, it's necessary to access a file >
50- /// Integer.MAX_VALUE in size using multiple byte buffers.
49+ /// Because .NET 's <see cref="Span{T}"/> and <see cref="ReadOnlySpan{T}"/> use an int to address the
50+ /// values, and because we use a similar chunking approach to Lucene, it's necessary to access a file >
51+ /// <see cref="Int32.MaxValue"/> in size using multiple byte buffers.
5152 /// </summary>
5253 [ TestFixture ]
5354 public class TestMultiMMap : LuceneTestCase
@@ -536,14 +537,15 @@ public void TestOpenInputConcurrentFileExtension_Issue1090()
536537 using var mmapDir = new MMapDirectory ( dir ) ;
537538
538539 const long maxFileSize = 64L * 1024 * 1024 ; // 64 MiB safety cap
539- var stop = new ManualResetEventSlim ( false ) ;
540+ using var stop = new ManualResetEventSlim ( false ) ;
540541 Exception writerError = null ;
541542
542543 var writer = new Thread ( ( ) =>
543544 {
544545 var chunk = new byte [ 64 ] ;
545546 try
546547 {
548+ // ReSharper disable once AccessToDisposedClosure - thread joined below
547549 while ( ! stop . IsSet )
548550 {
549551 using var fs = new FileStream ( filePath , FileMode . Open , FileAccess . Write , FileShare . ReadWrite ) ;
@@ -643,14 +645,15 @@ public void TestConcurrentCloneReadVsDispose_Issue1013()
643645 iteration ++ ;
644646
645647 var primary = mmapDir . OpenInput ( name , NewIOContext ( random ) ) ;
646- var start = new ManualResetEventSlim ( false ) ;
648+ using var start = new ManualResetEventSlim ( false ) ;
647649 var threads = new Thread [ readerThreads ] ;
648650 long totalReads = 0 ;
649651
650652 for ( int i = 0 ; i < readerThreads ; i ++ )
651653 {
652654 threads [ i ] = new Thread ( ( ) =>
653655 {
656+ // ReSharper disable once AccessToDisposedClosure - thread joined below
654657 start . Wait ( ) ;
655658 try
656659 {
@@ -659,6 +662,7 @@ public void TestConcurrentCloneReadVsDispose_Issue1013()
659662 IndexInput clone ;
660663 try
661664 {
665+ // ReSharper disable once AccessToDisposedClosure - thread joined below
662666 clone = ( IndexInput ) primary . Clone ( ) ;
663667 }
664668 catch ( Exception e ) when ( e . IsAlreadyClosedException ( ) )
@@ -777,8 +781,8 @@ public void TestSameThreadOwnerDisposeWhileSiblingClonesRead_NoAVE()
777781 // The primary is opened, read, AND disposed all on THIS thread.
778782 var primary = mmapDir . OpenInput ( name , NewIOContext ( random ) ) ;
779783
780- var start = new ManualResetEventSlim ( false ) ;
781- var stop = new ManualResetEventSlim ( false ) ;
784+ using var start = new ManualResetEventSlim ( false ) ;
785+ using var stop = new ManualResetEventSlim ( false ) ;
782786 var readers = new Thread [ siblingReaders ] ;
783787 for ( int i = 0 ; i < readers . Length ; i ++ )
784788 {
@@ -791,16 +795,19 @@ public void TestSameThreadOwnerDisposeWhileSiblingClonesRead_NoAVE()
791795 IndexInput clone ;
792796 try
793797 {
798+ // ReSharper disable once AccessToDisposedClosure - thread joined below
794799 clone = ( IndexInput ) primary . Clone ( ) ;
795800 }
796801 catch ( Exception e ) when ( e . IsAlreadyClosedException ( ) )
797802 {
798803 return ;
799804 }
800805
806+ // ReSharper disable once AccessToDisposedClosure - thread joined below
801807 start . Wait ( ) ;
802808 try
803809 {
810+ // ReSharper disable once AccessToDisposedClosure - thread joined below
804811 while ( ! stop . IsSet )
805812 {
806813 clone . Seek ( 0 ) ;
@@ -904,18 +911,20 @@ public void TestConcurrentCloneVsDispose_RaceScenario()
904911 {
905912 iterations ++ ;
906913 var primary = mmapDir . OpenInput ( name , NewIOContext ( random ) ) ;
907- var start = new ManualResetEventSlim ( false ) ;
914+ using var start = new ManualResetEventSlim ( false ) ;
908915 var cloners = new Thread [ 6 ] ;
909916 for ( int i = 0 ; i < cloners . Length ; i ++ )
910917 {
911918 cloners [ i ] = new Thread ( ( ) =>
912919 {
920+ // ReSharper disable once AccessToDisposedClosure - thread joined below
913921 start . Wait ( ) ;
914922 try
915923 {
916924 while ( true )
917925 {
918926 IndexInput c ;
927+ // ReSharper disable once AccessToDisposedClosure - thread joined below
919928 try { c = ( IndexInput ) primary . Clone ( ) ; }
920929 catch ( Exception e ) when ( e . IsAlreadyClosedException ( ) ) { return ; }
921930 // Touch a byte on the clone — but don't read past dispose to keep the test focused on Clone itself.
@@ -998,7 +1007,7 @@ public void TestConcurrentReadVsSelfDispose_RaceScenario()
9981007 {
9991008 iterations ++ ;
10001009 var input = mmapDir . OpenInput ( name , NewIOContext ( random ) ) ;
1001- var start = new ManualResetEventSlim ( false ) ;
1010+ using var start = new ManualResetEventSlim ( false ) ;
10021011 var readers = new Thread [ 4 ] ;
10031012 for ( int i = 0 ; i < readers . Length ; i ++ )
10041013 {
@@ -1009,9 +1018,11 @@ public void TestConcurrentReadVsSelfDispose_RaceScenario()
10091018 // the reclaimer on the shared mapping, not a single
10101019 // IndexInput.
10111020 IndexInput clone ;
1021+ // ReSharper disable once AccessToDisposedClosure - thread joined below
10121022 try { clone = ( IndexInput ) input . Clone ( ) ; }
10131023 catch ( Exception e ) when ( e . IsAlreadyClosedException ( ) ) { return ; }
10141024
1025+ // ReSharper disable once AccessToDisposedClosure - thread joined below
10151026 start . Wait ( ) ;
10161027 try
10171028 {
@@ -1070,7 +1081,7 @@ public void TestConcurrentSliceReadVsSlicerDispose_RaceScenario()
10701081 {
10711082 iterations ++ ;
10721083 var slicer = mmapDir . CreateSlicer ( name , NewIOContext ( random ) ) ;
1073- var start = new ManualResetEventSlim ( false ) ;
1084+ using var start = new ManualResetEventSlim ( false ) ;
10741085 var readers = new Thread [ 4 ] ;
10751086 for ( int i = 0 ; i < readers . Length ; i ++ )
10761087 {
@@ -1080,10 +1091,12 @@ public void TestConcurrentSliceReadVsSlicerDispose_RaceScenario()
10801091 IndexInput slice ;
10811092 try
10821093 {
1094+ // ReSharper disable once AccessToDisposedClosure - thread joined below
10831095 slice = slicer . OpenSlice ( "slice" + sliceIndex , 0 , fileSize ) ;
10841096 }
10851097 catch ( Exception e ) when ( e . IsAlreadyClosedException ( ) ) { return ; }
10861098
1099+ // ReSharper disable once AccessToDisposedClosure - thread joined below
10871100 start . Wait ( ) ;
10881101 try
10891102 {
@@ -1511,14 +1524,15 @@ public void TestConcurrentClonesReadIdenticalBytes()
15111524 clones [ i ] = ( IndexInput ) root . Clone ( ) ;
15121525 }
15131526
1514- var start = new ManualResetEventSlim ( false ) ;
1527+ using var start = new ManualResetEventSlim ( false ) ;
15151528 var threads = new Thread [ numWorkers ] ;
15161529 for ( int i = 0 ; i < numWorkers ; i ++ )
15171530 {
15181531 int idx = i ;
15191532 threads [ i ] = new Thread ( ( ) =>
15201533 {
15211534 var buf = new byte [ fileSize ] ;
1535+ // ReSharper disable once AccessToDisposedClosure - thread joined below
15221536 start . Wait ( ) ;
15231537 for ( int pass = 0 ; pass < passesPerWorker && errors . IsEmpty ; pass ++ )
15241538 {
@@ -2296,7 +2310,7 @@ public void TestCrossThreadCloneDisposeReleasesReadRefDeterministically()
22962310 // disposes the clone from THIS thread - exercising the cross-thread
22972311 // Dispose path. Static and self-contained so the clone and reader thread
22982312 // are unreachable once this returns.
2299- [ System . Runtime . CompilerServices . MethodImpl ( System . Runtime . CompilerServices . MethodImplOptions . NoInlining ) ]
2313+ [ MethodImpl ( MethodImplOptions . NoInlining ) ]
23002314 private static void DisposeCloneCrossThread ( MMapDirectory . MMapIndexInput parent )
23012315 {
23022316 IndexInput clone = ( IndexInput ) parent . Clone ( ) ;
@@ -2394,11 +2408,17 @@ public void TestCloseWhileCloneReadingBlocksUntilDrainThenUnmaps()
23942408 Assert . IsTrue ( chunks . Length > 1 , "expected a multi-chunk mapping" ) ;
23952409
23962410 var clone = ( MMapDirectory . MMapIndexInput ) root . Clone ( ) ;
2397- var entered = new ManualResetEventSlim ( false ) ;
2398- var resume = new ManualResetEventSlim ( false ) ;
2411+ using var entered = new ManualResetEventSlim ( false ) ;
2412+ using var resume = new ManualResetEventSlim ( false ) ;
23992413 // Park the clone INSIDE its read bracket (admitted, before the load
24002414 // returns) so the owner's close must observe it as an active reader.
2401- clone . SetOnEnterForTest ( ( ) => { entered . Set ( ) ; resume . Wait ( ) ; } ) ;
2415+ clone . SetOnEnterForTest ( ( ) =>
2416+ {
2417+ // ReSharper disable once AccessToDisposedClosure - runs synchronously
2418+ entered . Set ( ) ;
2419+ // ReSharper disable once AccessToDisposedClosure - runs synchronously
2420+ resume . Wait ( ) ;
2421+ } ) ;
24022422
24032423 var reader = new Thread ( ( ) => { clone . Seek ( 0 ) ; clone . ReadByte ( ) ; } )
24042424 { IsBackground = true } ;
0 commit comments