mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-28 09:21:05 -04:00
ColorScheme.Default and LayoutPart kept single static pens, brushes and a font that every plate view and drawing preview painted with. GDI+ objects are not thread-safe, so two views painting at once on different UI threads (parallel WinForms test collections) failed in PlateRenderer.DrawPlate with "Object is currently in use elsewhere" and crashed the test host. The scheme now builds its pens and brushes per thread and rebuilds them after a color change; LayoutPart's selection, lead-in and label objects are thread-static. A regression test paints previews on four threads at once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
69 lines
2.4 KiB
C#
69 lines
2.4 KiB
C#
using System.Drawing;
|
|
using System.Windows.Forms;
|
|
using OpenNest.CNC;
|
|
using OpenNest.Controls;
|
|
|
|
namespace OpenNest.WinForms.Tests.Controls;
|
|
|
|
public class ShapePreviewConcurrencyTests
|
|
{
|
|
private static Program Square(double size)
|
|
{
|
|
var program = new Program();
|
|
program.Codes.Add(new RapidMove(0, 0));
|
|
program.Codes.Add(new LinearMove(size, 0));
|
|
program.Codes.Add(new LinearMove(size, size));
|
|
program.Codes.Add(new LinearMove(0, size));
|
|
program.Codes.Add(new LinearMove(0, 0));
|
|
return program;
|
|
}
|
|
|
|
// Parallel test collections paint previews on separate STA threads at once.
|
|
// GDI+ objects are not thread-safe, so any pen, brush or font shared between
|
|
// views fails with "Object is currently in use elsewhere".
|
|
[Fact]
|
|
public void PreviewsPaintAtTheSameTimeOnSeparateThreads()
|
|
{
|
|
const int threadCount = 4;
|
|
const int paints = 200;
|
|
var start = new Barrier(threadCount);
|
|
var failures = new List<Exception>();
|
|
var threads = new List<Thread>();
|
|
|
|
for (var t = 0; t < threadCount; t++)
|
|
{
|
|
var thread = new Thread(() =>
|
|
{
|
|
try
|
|
{
|
|
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
|
|
using var preview = new ShapePreviewControl { Size = new Size(200, 200) };
|
|
preview.ShowDrawing(new Drawing("square", Square(10)));
|
|
preview.SetInfo("10 x 10", "1 part");
|
|
using var bitmap = new Bitmap(200, 200);
|
|
|
|
start.SignalAndWait(TimeSpan.FromSeconds(10));
|
|
for (var i = 0; i < paints; i++)
|
|
preview.DrawToBitmap(bitmap, new Rectangle(0, 0, 200, 200));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
lock (failures)
|
|
failures.Add(ex);
|
|
}
|
|
})
|
|
{
|
|
IsBackground = true,
|
|
};
|
|
thread.SetApartmentState(ApartmentState.STA);
|
|
threads.Add(thread);
|
|
thread.Start();
|
|
}
|
|
|
|
foreach (var thread in threads)
|
|
Assert.True(thread.Join(TimeSpan.FromMinutes(1)), "A preview thread did not finish.");
|
|
|
|
Assert.True(failures.Count == 0, string.Join(Environment.NewLine, failures));
|
|
}
|
|
}
|