@@ -490,6 +490,120 @@ public virtual void ConcurrentIndexAccessThrowsWithoutSynchronizedStaleFiles()
490490 }
491491 }
492492
493+ // LUCENENET specific: Regression test for #1283. A read handle held open by SimpleFSDirectory or
494+ // NIOFSDirectory must not block deletion of the underlying file. On Windows this only works because the
495+ // read FileStream is opened with FileShare.Delete (the delete becomes a deferred/pending delete); on
496+ // POSIX, delete-while-open always works. After the delete, the still-open handle must keep returning the
497+ // original bytes without corruption. The assertion only has teeth on Windows (POSIX ignores share modes
498+ // and would pass regardless), but the behavior asserted is cross-platform, so the test is not gated.
499+ // MMapDirectory is intentionally excluded: its read handle still lacks FileShare.Delete in master and is
500+ // addressed separately in #1267.
501+ [ Test ]
502+ [ LuceneNetSpecific ]
503+ public virtual void TestDeleteWhileReadHandleOpen ( )
504+ {
505+ DirectoryInfo tempDir = CreateTempDir ( GetType ( ) . Name ) ;
506+ var factories = new Func < Directory > [ ]
507+ {
508+ ( ) => new SimpleFSDirectory ( tempDir ) ,
509+ ( ) => new NIOFSDirectory ( tempDir ) ,
510+ } ;
511+
512+ byte [ ] expected = new byte [ 128 ] ;
513+ for ( int i = 0 ; i < expected . Length ; i ++ )
514+ {
515+ expected [ i ] = ( byte ) i ;
516+ }
517+
518+ int n = 0 ;
519+ foreach ( var factory in factories )
520+ {
521+ using Directory dir = factory ( ) ;
522+ string name = "delete-while-read-" + n ++ ;
523+
524+ using ( IndexOutput output = dir . CreateOutput ( name , NewIOContext ( Random ) ) )
525+ {
526+ output . WriteBytes ( expected , expected . Length ) ;
527+ }
528+
529+ IndexInput input = dir . OpenInput ( name , NewIOContext ( Random ) ) ;
530+ try
531+ {
532+ // Read the first byte so the handle is genuinely active.
533+ Assert . AreEqual ( expected [ 0 ] , input . ReadByte ( ) ) ;
534+
535+ // The crux: deleting while a read handle is open must not throw.
536+ try
537+ {
538+ dir . DeleteFile ( name ) ;
539+ }
540+ catch ( Exception e )
541+ {
542+ Assert . Fail ( "DeleteFile threw while a read handle was open (missing FileShare.Delete?) for "
543+ + dir . GetType ( ) . Name + ": " + e ) ;
544+ }
545+
546+ // The still-open handle must keep returning the original bytes (no corruption, handle still valid).
547+ byte [ ] actual = new byte [ expected . Length - 1 ] ;
548+ input . ReadBytes ( actual , 0 , actual . Length ) ;
549+ for ( int i = 0 ; i < actual . Length ; i ++ )
550+ {
551+ Assert . AreEqual ( expected [ i + 1 ] , actual [ i ] ) ;
552+ }
553+ }
554+ finally
555+ {
556+ input . Dispose ( ) ;
557+ }
558+ }
559+ }
560+
561+ // LUCENENET specific: Regression test for #1283. The shared FSIndexOutput write handle (used by every
562+ // FSDirectory subclass) is opened with FileShare.Delete, so deleting a file must succeed even while its
563+ // write handle is still open. This makes the "if Lucene decides a file is deletable, the delete succeeds"
564+ // guarantee hold unconditionally on Windows regardless of which of our own handles are open.
565+ [ Test ]
566+ [ LuceneNetSpecific ]
567+ public virtual void TestDeleteWhileWriteHandleOpen ( )
568+ {
569+ DirectoryInfo tempDir = CreateTempDir ( GetType ( ) . Name ) ;
570+ var factories = new Func < Directory > [ ]
571+ {
572+ ( ) => new SimpleFSDirectory ( tempDir ) ,
573+ ( ) => new NIOFSDirectory ( tempDir ) ,
574+ ( ) => new MMapDirectory ( tempDir ) ,
575+ } ;
576+
577+ int n = 0 ;
578+ foreach ( var factory in factories )
579+ {
580+ using Directory dir = factory ( ) ;
581+ string name = "delete-while-write-" + n ++ ;
582+
583+ IndexOutput output = dir . CreateOutput ( name , NewIOContext ( Random ) ) ;
584+ try
585+ {
586+ output . WriteByte ( ( byte ) 0x2A ) ;
587+ output . Flush ( ) ;
588+
589+ // Deleting while the write handle is open must not throw.
590+ try
591+ {
592+ dir . DeleteFile ( name ) ;
593+ }
594+ catch ( Exception e )
595+ {
596+ Assert . Fail ( "DeleteFile threw while a write handle was open (missing FileShare.Delete?) for "
597+ + dir . GetType ( ) . Name + ": " + e ) ;
598+ }
599+ }
600+ finally
601+ {
602+ output . Dispose ( ) ;
603+ }
604+ }
605+ }
606+
493607
494608#pragma warning disable 612 , 618
495609 [ Test ]
0 commit comments