diff --git a/app/Tests/Tools/HTMLParserConcurrencyTests.cs b/app/Tests/Tools/HTMLParserConcurrencyTests.cs
index d10e89b2..86600910 100644
--- a/app/Tests/Tools/HTMLParserConcurrencyTests.cs
+++ b/app/Tests/Tools/HTMLParserConcurrencyTests.cs
@@ -38,27 +38,16 @@ public sealed class HTMLParserConcurrencyTests
// overlap for this test to mean anything, and only starting them together makes that
// certain.
//
+ // What a thread works with is handed over when it starts rather than captured. The barrier
+ // is disposed at the end of this method, and while the joins below make sure no thread is
+ // still at it by then, that is nothing one can see from inside a lambda.
+ //
using var startSignal = new Barrier(THREAD_COUNT);
var threads = new List(THREAD_COUNT);
for (var threadIndex = 0; threadIndex < THREAD_COUNT; threadIndex++)
{
- var thread = new Thread(() =>
- {
- startSignal.SignalAndWait();
- for (var conversion = 0; conversion < CONVERSIONS_PER_THREAD; conversion++)
- {
- try
- {
- results.Add(HTMLParser.ParseToMarkdown(html));
- }
- catch (Exception exception)
- {
- failures.Add(exception);
- }
- }
- });
-
- thread.Start();
+ var thread = new Thread(ConvertRepeatedly);
+ thread.Start(new ConversionRun(startSignal, html, results, failures));
threads.Add(thread);
}
@@ -75,6 +64,27 @@ public sealed class HTMLParserConcurrencyTests
});
}
+ ///
+ /// Converts the same page over and over, once every thread has arrived at the barrier.
+ ///
+ private static void ConvertRepeatedly(object? state)
+ {
+ var run = (ConversionRun)state!;
+ run.StartSignal.SignalAndWait();
+
+ for (var conversion = 0; conversion < CONVERSIONS_PER_THREAD; conversion++)
+ {
+ try
+ {
+ run.Results.Add(HTMLParser.ParseToMarkdown(run.Html));
+ }
+ catch (Exception exception)
+ {
+ run.Failures.Add(exception);
+ }
+ }
+ }
+
///
/// Builds a page out of the elements the reported stack traces named.
///
@@ -118,4 +128,9 @@ public sealed class HTMLParserConcurrencyTests
return string.Concat(Enumerable.Repeat(BLOCK, 20));
}
+
+ ///
+ /// Everything one thread of this test needs, so that it is passed rather than captured.
+ ///
+ private sealed record ConversionRun(Barrier StartSignal, string Html, ConcurrentBag Results, ConcurrentBag Failures);
}
\ No newline at end of file