From 54fdf026f4ffc1a221de496a74571c9eb0a52445 Mon Sep 17 00:00:00 2001 From: Magne Sjaastad Date: Sun, 20 Jul 2025 11:22:13 +0200 Subject: [PATCH] Addresses race condition in object creation Ensures thread-safe initialization of PDM objects when created in parallel. Avoids a potential race condition during the first instantiation of a PDM object in a multi-threaded environment. This is achieved by creating a dummy object in the main thread to trigger the necessary initialization before parallel creation begins. Adds a unit test to verify the fix. --- .../Summary/RimSummaryAddressCollection.cpp | 8 ++++++ .../cafPdmBasicTest.cpp | 27 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryAddressCollection.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryAddressCollection.cpp index 6276091f2a..7c06b49bcc 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryAddressCollection.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryAddressCollection.cpp @@ -257,6 +257,14 @@ void RimSummaryAddressCollection::updateFolderStructure( const std::set rimAddresses; rimAddresses.resize( sortedAddresses.size() ); + // Make sure that the first object is created in the main thread. This is needed to avoid a race condition related to initialization of + // static data for a PDM object. + // See CreateObjectInMultipleThreads test in cafPdmBasicTest.cpp + // This is a workaround until initialization of PDM objects is thread safe. + { + RimSummaryAddress dummyObject; + } + #pragma omp parallel for for ( int i = 0; i < static_cast( sortedAddresses.size() ); ++i ) { diff --git a/Fwk/AppFwk/cafProjectDataModel/cafProjectDataModel_UnitTests/cafPdmBasicTest.cpp b/Fwk/AppFwk/cafProjectDataModel/cafProjectDataModel_UnitTests/cafPdmBasicTest.cpp index 4cd446a264..d1536bada1 100644 --- a/Fwk/AppFwk/cafProjectDataModel/cafProjectDataModel_UnitTests/cafPdmBasicTest.cpp +++ b/Fwk/AppFwk/cafProjectDataModel/cafProjectDataModel_UnitTests/cafPdmBasicTest.cpp @@ -233,6 +233,33 @@ TEST( BaseTest, Delete ) delete s2; } +TEST( BaseTest, CreateObjectInMultipleThreads ) +{ + // When creating PDM objects in a multi threaded loop, we need to make sure that the AppEnum and Enum objects are + // initialized. This will happen once as part of the initialization when the first object of the type is + // instantiated. Further instantiation of the same type will not cause any initialization, as all other access is + // read-only. + + // At this point in this test file InheritedDemoObj has not yet been initialized. Create a dummy object to ensure the + // PDM system is initialized. When the PDM system is initialized, all access to AppEnum and EnumMapper is read only + InheritedDemoObj dummy; + + const int objectCount = 10000; + std::vector rimAddresses; + rimAddresses.resize( objectCount ); + +#pragma omp parallel for + for ( int i = 0; i < objectCount; ++i ) + { + rimAddresses[i] = new InheritedDemoObj; + } + + for ( int i = 0; i < objectCount; ++i ) + { + delete rimAddresses[i]; + } +} + //-------------------------------------------------------------------------------------------------- /// This is a testbed to try out different aspects, instead of having a main in a prototype program /// To be disabled when everything gets more mature.