From 85e21f1ae2962e97d1428c173f4a86e3d1e0fddd Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 07:01:23 +0000 Subject: [PATCH] Lock the shared sets of the parallel STEP import With StepImport.Parallel, CreateEntity runs on several threads, but it added mapped items to the HashSet mappedItems and, for assemblies, products to the HashSet products without a lock (the other place that adds products locks it). Concurrent Add calls can corrupt a HashSet, and the enumeration of mappedItems after the creation then met a null entry: a NullReferenceException in ImportStep.Read. StepCyclicReferenceTest from #498 imports a file with mapped items 20 times in parallel and showed this now and then on the 2-core CI runner. With the test process limited to 2 cores, it failed 3 of 30 times without this change and 0 of 60 times with it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01YXvDW91oXpy7RquGHNxgR5 --- CADability/ImportStep.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CADability/ImportStep.cs b/CADability/ImportStep.cs index 955480b2..f3cd3d4f 100644 --- a/CADability/ImportStep.cs +++ b/CADability/ImportStep.cs @@ -4076,7 +4076,7 @@ private object CreateEntity(Item item) child.parameter["_transformation"] = new Item(Item.ItemType.created, transformation); child.parameter["_referred"] = relatedProduct; relatingProduct.parameter["_children"].lval.Add(child); - products.Add(relatingProduct); + lock (products) products.Add(relatingProduct); // the parallel import adds products on several threads } #if DEBUG //System.Diagnostics.Trace.WriteLine(SubTree(item.definingIndex, 5)); @@ -4590,7 +4590,7 @@ private object CreateEntity(Item item) // System.Diagnostics.Trace.WriteLine(up[i]); //} #endif - mappedItems.Add(item); + lock (mappedItems) mappedItems.Add(item); // the parallel import creates mapped items on several threads string name = item.parameter["name"].sval; object mo = CreateEntity(item.parameter["mapping_source"].parameter["mapping_origin"]); object mr = CreateEntity(item.parameter["mapping_source"].parameter["mapped_representation"]);