Skip to content

Commit ea9d4f9

Browse files
paulirwinclaude
andcommitted
Migrate test CountdownEvent usage to Java-style CountDownLatch, #1284
Replaces System.Threading.CountdownEvent with the new Lucene.Net.Support.Threading.CountDownLatch across 22 test files where the upstream Java equivalent uses java.util.concurrent.CountDownLatch. Mechanical changes: - type rename: CountdownEvent -> CountDownLatch - .Signal() -> .CountDown() - .Wait() / .Wait(TimeSpan) -> .Await() / .Await(TimeSpan) - .CurrentCount -> .Count - .IsSet -> .Count == 0 - added using Lucene.Net.Support.Threading where needed Workarounds for CountdownEvent.Signal()'s "throws past zero" behavior that are now redundant (CountDown is idempotent at zero, matching Java): - TestConcurrentMergeScheduler.TestMaxMergeCount: removed the CurrentCount > 0 guard. This was the trigger for the CI failure on Windows .NET Framework and Ubuntu .NET 8 after the LUCENE-5871 port made Shutdown(false) flush more aggressively. - TestIndexWriterWithThreads: removed the same pre-existing guard at iwConstructed. - TestDocumentsWriterStallControl: removed if (!updateJoin.IsSet) guard around CountDown(); upstream calls countDown() unconditionally. - TestControlledRealTimeReopenThread: replaced two signal.Reset(Count - 1)/latch.Reset(Count - 1) hacks with plain CountDown(), matching upstream signal.countDown()/latch.countDown(). Other adjustments: - BaseDocValuesFormatTestCase: dropped `using` keyword on two latches. CountdownEvent was IDisposable; CountDownLatch isn't (Java's isn't either), so the latches now go out of scope without explicit cleanup. - TestControlledRealTimeReopenThread.LatchedIndexWriter: ctor changed from public to internal so CountDownLatch (internal) is reachable from a public class member. Files touched: 22 test files plus 2 in TestFramework. All ported sites now read the same as the upstream Java; no LUCENENET notes were added because the code is now equivalent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 35e248f commit ea9d4f9

22 files changed

Lines changed: 203 additions & 195 deletions

src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using Attribute = Lucene.Net.Util.Attribute;
1818
using Directory = Lucene.Net.Store.Directory;
1919
using JCG = J2N.Collections.Generic;
20+
using Lucene.Net.Support.Threading;
2021

2122
namespace Lucene.Net.Analysis
2223
{
@@ -629,15 +630,15 @@ internal class AnalysisThread : ThreadJob
629630
internal readonly bool simple;
630631
internal readonly bool offsetsAreCorrect;
631632
internal readonly RandomIndexWriter iw;
632-
private readonly CountdownEvent latch;
633+
private readonly CountDownLatch latch;
633634

634635
// NOTE: not volatile because we don't want the tests to
635636
// add memory barriers (ie alter how threads
636637
// interact)... so this is just "best effort":
637638
public bool Failed { get; set; }
638639
public Exception FirstException { get; set; } = null;
639640

640-
internal AnalysisThread(long seed, CountdownEvent latch, Analyzer a, int iterations, int maxWordLength,
641+
internal AnalysisThread(long seed, CountDownLatch latch, Analyzer a, int iterations, int maxWordLength,
641642
bool useCharFilter, bool simple, bool offsetsAreCorrect, RandomIndexWriter iw)
642643
{
643644
this.seed = seed;
@@ -656,7 +657,7 @@ public override void Run()
656657
bool success = false;
657658
try
658659
{
659-
if (latch != null) latch.Wait();
660+
if (latch != null) latch.Await();
660661
// see the part in checkRandomData where it replays the same text again
661662
// to verify reproducability/reuse: hopefully this would catch thread hazards.
662663
CheckRandomData(new J2N.Randomizer(seed), a, iterations, maxWordLength, useCharFilter, simple, offsetsAreCorrect, iw);
@@ -710,7 +711,7 @@ public static void CheckRandomData(Random random, Analyzer a, int iterations, in
710711
// now test with multiple threads: note we do the EXACT same thing we did before in each thread,
711712
// so this should only really fail from another thread if its an actual thread problem
712713
int numThreads = TestUtil.NextInt32(random, 2, 4);
713-
var startingGun = new CountdownEvent(1);
714+
var startingGun = new CountDownLatch(1);
714715
var threads = new AnalysisThread[numThreads];
715716
for (int i = 0; i < threads.Length; i++)
716717
{
@@ -722,7 +723,7 @@ public static void CheckRandomData(Random random, Analyzer a, int iterations, in
722723
thread.Start();
723724
}
724725

725-
startingGun.Signal();
726+
startingGun.CountDown();
726727
foreach (var t in threads)
727728
{
728729
try

src/Lucene.Net.TestFramework/Index/BaseDocValuesFormatTestCase.cs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using Assert = Lucene.Net.TestFramework.Assert;
1919
using JCG = J2N.Collections.Generic;
2020
using Test = NUnit.Framework.TestAttribute;
21+
using Lucene.Net.Support.Threading;
2122

2223
namespace Lucene.Net.Index
2324
{
@@ -3243,13 +3244,13 @@ public virtual void TestThreads()
32433244
using DirectoryReader ir = DirectoryReader.Open(dir);
32443245
int numThreads = TestUtil.NextInt32(Random, 2, 7);
32453246
ThreadJob[] threads = new ThreadJob[numThreads];
3246-
using CountdownEvent startingGun = new CountdownEvent(1);
3247+
CountDownLatch startingGun = new CountDownLatch(1);
32473248
for (int i = 0; i < threads.Length; i++)
32483249
{
32493250
threads[i] = new ThreadAnonymousClass(ir, startingGun);
32503251
threads[i].Start();
32513252
}
3252-
startingGun.Signal();
3253+
startingGun.CountDown();
32533254
foreach (ThreadJob t in threads)
32543255
{
32553256
t.Join();
@@ -3259,9 +3260,9 @@ public virtual void TestThreads()
32593260
private sealed class ThreadAnonymousClass : ThreadJob
32603261
{
32613262
private readonly DirectoryReader ir;
3262-
private readonly CountdownEvent startingGun;
3263+
private readonly CountDownLatch startingGun;
32633264

3264-
public ThreadAnonymousClass(DirectoryReader ir, CountdownEvent startingGun)
3265+
public ThreadAnonymousClass(DirectoryReader ir, CountDownLatch startingGun)
32653266
{
32663267
this.ir = ir;
32673268
this.startingGun = startingGun;
@@ -3271,7 +3272,7 @@ public override void Run()
32713272
{
32723273
try
32733274
{
3274-
startingGun.Wait();
3275+
startingGun.Await();
32753276
BytesRef scratch = new BytesRef(); // LUCENENET: Moved outside of the loop for performance
32763277
foreach (AtomicReaderContext context in ir.Leaves)
32773278
{
@@ -3379,13 +3380,13 @@ public virtual void TestThreads2()
33793380
using DirectoryReader ir = DirectoryReader.Open(dir);
33803381
int numThreads = TestUtil.NextInt32(Random, 2, 7);
33813382
ThreadJob[] threads = new ThreadJob[numThreads];
3382-
using CountdownEvent startingGun = new CountdownEvent(1);
3383+
CountDownLatch startingGun = new CountDownLatch(1);
33833384
for (int i = 0; i < threads.Length; i++)
33843385
{
33853386
threads[i] = new ThreadAnonymousClass2(ir, startingGun);
33863387
threads[i].Start();
33873388
}
3388-
startingGun.Signal();
3389+
startingGun.CountDown();
33893390
foreach (ThreadJob t in threads)
33903391
{
33913392
t.Join();
@@ -3395,9 +3396,9 @@ public virtual void TestThreads2()
33953396
private sealed class ThreadAnonymousClass2 : ThreadJob
33963397
{
33973398
private readonly DirectoryReader ir;
3398-
private readonly CountdownEvent startingGun;
3399+
private readonly CountDownLatch startingGun;
33993400

3400-
public ThreadAnonymousClass2(DirectoryReader ir, CountdownEvent startingGun)
3401+
public ThreadAnonymousClass2(DirectoryReader ir, CountDownLatch startingGun)
34013402
{
34023403
this.ir = ir;
34033404
this.startingGun = startingGun;
@@ -3407,7 +3408,7 @@ public override void Run()
34073408
{
34083409
try
34093410
{
3410-
startingGun.Wait();
3411+
startingGun.Await();
34113412
foreach (AtomicReaderContext context in ir.Leaves)
34123413
{
34133414
AtomicReader r = context.AtomicReader;

src/Lucene.Net.Tests/Index/TestBagOfPositions.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public virtual void Test()
117117
// else just positions
118118

119119
ThreadJob[] threads = new ThreadJob[threadCount];
120-
CountdownEvent startingGun = new CountdownEvent(1);
120+
CountDownLatch startingGun = new CountDownLatch(1);
121121

122122
for (int threadID = 0; threadID < threadCount; threadID++)
123123
{
@@ -128,7 +128,7 @@ public virtual void Test()
128128
threads[threadID] = new ThreadAnonymousClass(maxTermsPerDoc, postings, iw, startingGun, threadRandom, document, field);
129129
threads[threadID].Start();
130130
}
131-
startingGun.Signal();
131+
startingGun.CountDown();
132132
foreach (ThreadJob t in threads)
133133
{
134134
t.Join();
@@ -160,12 +160,12 @@ private sealed class ThreadAnonymousClass : ThreadJob
160160
private readonly int maxTermsPerDoc;
161161
private readonly ConcurrentQueue<string> postings;
162162
private readonly RandomIndexWriter iw;
163-
private readonly CountdownEvent startingGun;
163+
private readonly CountDownLatch startingGun;
164164
private readonly Random threadRandom;
165165
private readonly Document document;
166166
private readonly Field field;
167167

168-
public ThreadAnonymousClass(int maxTermsPerDoc, ConcurrentQueue<string> postings, RandomIndexWriter iw, CountdownEvent startingGun, Random threadRandom, Document document, Field field)
168+
public ThreadAnonymousClass(int maxTermsPerDoc, ConcurrentQueue<string> postings, RandomIndexWriter iw, CountDownLatch startingGun, Random threadRandom, Document document, Field field)
169169
{
170170
this.maxTermsPerDoc = maxTermsPerDoc;
171171
this.postings = postings;
@@ -180,7 +180,7 @@ public override void Run()
180180
{
181181
try
182182
{
183-
startingGun.Wait();
183+
startingGun.Await();
184184
while (!postings.IsEmpty)
185185
{
186186
StringBuilder text = new StringBuilder();

src/Lucene.Net.Tests/Index/TestBagOfPostings.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,14 @@ public virtual void Test()
9696
}
9797

9898
ThreadJob[] threads = new ThreadJob[threadCount];
99-
CountdownEvent startingGun = new CountdownEvent(1);
99+
CountDownLatch startingGun = new CountDownLatch(1);
100100

101101
for (int threadID = 0; threadID < threadCount; threadID++)
102102
{
103103
threads[threadID] = new ThreadAnonymousClass(maxTermsPerDoc, postings, iw, startingGun);
104104
threads[threadID].Start();
105105
}
106-
startingGun.Signal();
106+
startingGun.CountDown();
107107
foreach (ThreadJob t in threads)
108108
{
109109
t.Join();
@@ -141,9 +141,9 @@ private sealed class ThreadAnonymousClass : ThreadJob
141141
private readonly int maxTermsPerDoc;
142142
private readonly ConcurrentQueue<string> postings;
143143
private readonly RandomIndexWriter iw;
144-
private readonly CountdownEvent startingGun;
144+
private readonly CountDownLatch startingGun;
145145

146-
public ThreadAnonymousClass(int maxTermsPerDoc, ConcurrentQueue<string> postings, RandomIndexWriter iw, CountdownEvent startingGun)
146+
public ThreadAnonymousClass(int maxTermsPerDoc, ConcurrentQueue<string> postings, RandomIndexWriter iw, CountDownLatch startingGun)
147147
{
148148
this.maxTermsPerDoc = maxTermsPerDoc;
149149
this.postings = postings;
@@ -158,7 +158,7 @@ public override void Run()
158158
Document document = new Document();
159159
Field field = NewTextField("field", "", Field.Store.NO);
160160
document.Add(field);
161-
startingGun.Wait();
161+
startingGun.Await();
162162
while (!postings.IsEmpty)
163163
{
164164
StringBuilder text = new StringBuilder();

src/Lucene.Net.Tests/Index/TestBinaryDocValuesUpdates.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using System.Threading;
1919
using Assert = Lucene.Net.TestFramework.Assert;
2020
using JCG = J2N.Collections.Generic;
21+
using Lucene.Net.Support.Threading;
2122

2223
namespace Lucene.Net.Index
2324
{
@@ -1200,7 +1201,7 @@ public virtual void TestStressMultiThreading()
12001201
writer.AddDocument(doc);
12011202
}
12021203

1203-
CountdownEvent done = new CountdownEvent(numThreads);
1204+
CountDownLatch done = new CountDownLatch(numThreads);
12041205
AtomicInt32 numUpdates = new AtomicInt32(AtLeast(100));
12051206

12061207
// same thread updates a field as well as reopens
@@ -1216,7 +1217,7 @@ public virtual void TestStressMultiThreading()
12161217
{
12171218
t.Start();
12181219
}
1219-
done.Wait();
1220+
done.Await();
12201221
writer.Dispose();
12211222

12221223
DirectoryReader reader = DirectoryReader.Open(dir);
@@ -1253,12 +1254,12 @@ private sealed class ThreadAnonymousClass : ThreadJob
12531254
{
12541255
private readonly IndexWriter writer;
12551256
private readonly int numDocs;
1256-
private readonly CountdownEvent done;
1257+
private readonly CountDownLatch done;
12571258
private readonly AtomicInt32 numUpdates;
12581259
private readonly string f;
12591260
private readonly string cf;
12601261

1261-
public ThreadAnonymousClass(string str, IndexWriter writer, int numDocs, CountdownEvent done, AtomicInt32 numUpdates, string f, string cf)
1262+
public ThreadAnonymousClass(string str, IndexWriter writer, int numDocs, CountDownLatch done, AtomicInt32 numUpdates, string f, string cf)
12621263
: base(str)
12631264
{
12641265
this.writer = writer;
@@ -1365,7 +1366,7 @@ public override void Run()
13651366
}
13661367
}
13671368
}
1368-
done.Signal();
1369+
done.CountDown();
13691370
}
13701371
}
13711372
}

src/Lucene.Net.Tests/Index/TestConcurrentMergeScheduler.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Lucene.Net.Documents;
44
using Lucene.Net.Index.Extensions;
55
using Lucene.Net.Store;
6+
using Lucene.Net.Support.Threading;
67
using Lucene.Net.Util;
78
using NUnit.Framework;
89
using RandomizedTesting.Generators;
@@ -286,7 +287,7 @@ public virtual void TestMaxMergeCount()
286287

287288
int maxMergeCount = TestUtil.NextInt32(Random, 1, 5);
288289
int maxMergeThreads = TestUtil.NextInt32(Random, 1, maxMergeCount);
289-
CountdownEvent enoughMergesWaiting = new CountdownEvent(maxMergeCount);
290+
CountDownLatch enoughMergesWaiting = new CountDownLatch(maxMergeCount);
290291
AtomicInt32 runningMergeCount = new AtomicInt32(0);
291292
AtomicBoolean failed = new AtomicBoolean();
292293

@@ -308,7 +309,7 @@ public virtual void TestMaxMergeCount()
308309
IndexWriter w = new IndexWriter(dir, iwc);
309310
Document doc = new Document();
310311
doc.Add(NewField("field", "field", TextField.TYPE_NOT_STORED));
311-
while (enoughMergesWaiting.CurrentCount != 0 && !failed)
312+
while (enoughMergesWaiting.Count != 0 && !failed)
312313
{
313314
for (int i = 0; i < 10; i++)
314315
{
@@ -322,11 +323,11 @@ public virtual void TestMaxMergeCount()
322323
private sealed class ConcurrentMergeSchedulerAnonymousClass : ConcurrentMergeScheduler
323324
{
324325
private readonly int maxMergeCount;
325-
private readonly CountdownEvent enoughMergesWaiting;
326+
private readonly CountDownLatch enoughMergesWaiting;
326327
private readonly AtomicInt32 runningMergeCount;
327328
private readonly AtomicBoolean failed;
328329

329-
public ConcurrentMergeSchedulerAnonymousClass(int maxMergeCount, CountdownEvent enoughMergesWaiting, AtomicInt32 runningMergeCount, AtomicBoolean failed)
330+
public ConcurrentMergeSchedulerAnonymousClass(int maxMergeCount, CountDownLatch enoughMergesWaiting, AtomicInt32 runningMergeCount, AtomicBoolean failed)
330331
{
331332
this.maxMergeCount = maxMergeCount;
332333
this.enoughMergesWaiting = enoughMergesWaiting;
@@ -344,14 +345,14 @@ protected internal override void DoMerge(MergePolicy.OneMerge merge)
344345
try
345346
{
346347
Assert.IsTrue(count <= maxMergeCount, "count=" + count + " vs maxMergeCount=" + maxMergeCount);
347-
enoughMergesWaiting.Signal();
348+
enoughMergesWaiting.CountDown();
348349

349350
// Stall this merge until we see exactly
350351
// maxMergeCount merges waiting
351352
while (true)
352353
{
353354
// wait for 10 milliseconds
354-
if (enoughMergesWaiting.Wait(new TimeSpan(0, 0, 0, 0, 10)) || failed)
355+
if (enoughMergesWaiting.Await(new TimeSpan(0, 0, 0, 0, 10)) || failed)
355356
{
356357
break;
357358
}

src/Lucene.Net.Tests/Index/TestDocValuesIndexing.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System;
88
using System.Threading;
99
using Assert = Lucene.Net.TestFramework.Assert;
10+
using Lucene.Net.Support.Threading;
1011

1112
namespace Lucene.Net.Index
1213
{
@@ -513,7 +514,7 @@ public virtual void TestMixedTypesDifferentThreads()
513514
Directory dir = NewDirectory();
514515
IndexWriter w = new IndexWriter(dir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random)));
515516

516-
CountdownEvent startingGun = new CountdownEvent(1);
517+
CountDownLatch startingGun = new CountDownLatch(1);
517518
AtomicBoolean hitExc = new AtomicBoolean();
518519
ThreadJob[] threads = new ThreadJob[3];
519520
for (int i = 0; i < 3; i++)
@@ -538,7 +539,7 @@ public virtual void TestMixedTypesDifferentThreads()
538539
threads[i].Start();
539540
}
540541

541-
startingGun.Signal();
542+
startingGun.CountDown();
542543

543544
foreach (ThreadJob t in threads)
544545
{
@@ -552,11 +553,11 @@ public virtual void TestMixedTypesDifferentThreads()
552553
private sealed class ThreadAnonymousClass : ThreadJob
553554
{
554555
private readonly IndexWriter w;
555-
private readonly CountdownEvent startingGun;
556+
private readonly CountDownLatch startingGun;
556557
private readonly AtomicBoolean hitExc;
557558
private readonly Document doc;
558559

559-
public ThreadAnonymousClass(IndexWriter w, CountdownEvent startingGun, AtomicBoolean hitExc, Document doc)
560+
public ThreadAnonymousClass(IndexWriter w, CountDownLatch startingGun, AtomicBoolean hitExc, Document doc)
560561
{
561562
this.w = w;
562563
this.startingGun = startingGun;
@@ -568,7 +569,7 @@ public override void Run()
568569
{
569570
try
570571
{
571-
startingGun.Wait();
572+
startingGun.Await();
572573
w.AddDocument(doc);
573574
}
574575
catch (Exception iae) when (iae.IsIllegalArgumentException())

0 commit comments

Comments
 (0)