]> OCCT Git - occt.git/commitdiff
Data Exchange - Step thread safety improvement #307
authorDmitrii Kulikov <164657232+AtheneNoctuaPt@users.noreply.github.com>
Thu, 30 Jan 2025 17:42:07 +0000 (17:42 +0000)
committerGitHub <noreply@github.com>
Thu, 30 Jan 2025 17:42:07 +0000 (17:42 +0000)
Mutex is added to XSControl_WorkSession to prevent data races
  during reading and writing.
Tests are added to check the behavior of STEP readers/writers in
  multithreading environment.

src/QABugs/QABugs_20.cxx
src/StepFile/StepFile_Read.cxx
src/XSControl/XSControl_WorkSession.cxx
src/XSControl/XSControl_WorkSession.hxx
tests/bugs/step/bug33657_1 [new file with mode: 0644]
tests/bugs/step/bug33657_2 [new file with mode: 0644]
tests/bugs/step/bug33657_3 [new file with mode: 0644]
tests/bugs/step/bug33657_4 [new file with mode: 0644]

index 7985d764749721423b9c3f693fd9ac140126b906..722ae84d62de3da40f4b72fcc1c6c614c827058e 100644 (file)
@@ -66,6 +66,9 @@
 #include <TDataStd_Name.hxx>
 #include <AppCont_Function.hxx>
 #include <math_ComputeKronrodPointsAndWeights.hxx>
+#include <STEPCAFControl_Writer.hxx>
+#include <STEPCAFControl_Controller.hxx>
+#include <ShapeAnalysis_ShapeContents.hxx>
 
 #include <limits>
 
@@ -4923,6 +4926,155 @@ static Standard_Integer OCC33048(Draw_Interpretor&, Standard_Integer, const char
   return 0;
 }
 
+//=================================================================================================
+
+static Standard_Integer OCC33657_1(Draw_Interpretor&, Standard_Integer, const char**)
+{
+  STEPCAFControl_Controller::Init();
+  // Checking constructors working in parallel.
+  OSD_Parallel::For(0, 1000, [](int) {
+    STEPCAFControl_Reader aReader;
+    aReader.SetColorMode(true);
+    STEPCAFControl_Writer aWriter;
+    aWriter.SetDimTolMode(true);
+  });
+
+  return 0;
+}
+
+//=================================================================================================
+
+static Standard_Integer OCC33657_2(Draw_Interpretor& theDI,
+                                   Standard_Integer  theArgC,
+                                   const char**      theArgV)
+{
+  if (theArgC < 2)
+  {
+    theDI << "Use: " << theArgV[0] << " file\n";
+    return 1;
+  }
+
+  STEPCAFControl_Controller::Init();
+  // Checking readers working in parallel.
+  OSD_Parallel::For(0, 100, [&](int) {
+    STEPControl_Reader aReader;
+    aReader.ReadFile(theArgV[1], DESTEP_Parameters{});
+    aReader.TransferRoots();
+  });
+
+  return 0;
+}
+
+//=================================================================================================
+
+static Standard_Integer OCC33657_3(Draw_Interpretor&, Standard_Integer, const char**)
+{
+  STEPCAFControl_Controller::Init();
+  const TopoDS_Shape aShape = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape();
+  // Checking writers working in parallel.
+  OSD_Parallel::For(0, 100, [&](int) {
+    STEPControl_Writer aWriter;
+    aWriter.Transfer(aShape, STEPControl_StepModelType::STEPControl_AsIs, DESTEP_Parameters{});
+    std::ostringstream aStream;
+    aWriter.WriteStream(aStream);
+  });
+
+  return 0;
+}
+
+//=================================================================================================
+
+static Standard_Integer OCC33657_4(Draw_Interpretor& theDI,
+                                   Standard_Integer  theArgC,
+                                   const char**      theArgV)
+{
+  if (theArgC < 2)
+  {
+    theDI << "Use: " << theArgV[0] << " file\n";
+    return 1;
+  }
+
+  STEPCAFControl_Controller::Init();
+
+  // Acquire shape to write/read.
+  STEPControl_Reader aReader;
+  aReader.ReadFile(theArgV[1], DESTEP_Parameters{});
+  aReader.TransferRoots();
+  TopoDS_Shape aSourceShape = aReader.OneShape();
+
+  // Analyzer to compare the shape with the the same shape after write-read sequence.
+  ShapeAnalysis_ShapeContents aSourceAnalyzer;
+  aSourceAnalyzer.Perform(aSourceShape);
+
+  // Flag is set to false if any error is detected.
+  // Reads and writes to the flag are performed exclusively in relaxed memory order
+  // in order to avoid inter-thread syncronization that can potentially omit some problems.
+  std::atomic_bool anErrorOccurred(false);
+
+  OSD_Parallel::For(0, 100, [&](int) {
+    if (anErrorOccurred.load(std::memory_order_relaxed))
+    {
+      return;
+    }
+
+    // Writing.
+    STEPControl_Writer aWriter;
+    aWriter.Transfer(aSourceShape,
+                     STEPControl_StepModelType::STEPControl_AsIs,
+                     DESTEP_Parameters{});
+    std::stringstream aStream;
+    aWriter.WriteStream(aStream);
+
+    // Reading.
+    STEPControl_Reader aReader;
+    aReader.ReadStream("", DESTEP_Parameters{}, aStream);
+    aReader.TransferRoots();
+    const TopoDS_Shape          aResultShape = aReader.OneShape();
+    ShapeAnalysis_ShapeContents aResultAnalyzer;
+    aResultAnalyzer.Perform(aResultShape);
+
+    // Making sure that shape is unchanged.
+    if (aSourceAnalyzer.NbSolids() != aResultAnalyzer.NbSolids())
+    {
+      theDI << "Error: Wrong number of solids in the result shape.\nExpected: "
+            << aSourceAnalyzer.NbSolids() << "\nActual" << aResultAnalyzer.NbSolids() << "\n";
+      anErrorOccurred.store(true, std::memory_order_relaxed);
+    }
+    if (aSourceAnalyzer.NbShells() != aResultAnalyzer.NbShells())
+    {
+      theDI << "Error: Wrong number of shells in the result shape.\nExpected: "
+            << aSourceAnalyzer.NbShells() << "\nActual" << aResultAnalyzer.NbShells() << "\n";
+      anErrorOccurred.store(true, std::memory_order_relaxed);
+    }
+    if (aSourceAnalyzer.NbFaces() != aResultAnalyzer.NbFaces())
+    {
+      theDI << "Error: Wrong number of faces in the result shape.\nExpected: "
+            << aSourceAnalyzer.NbFaces() << "\nActual" << aResultAnalyzer.NbFaces() << "\n";
+      anErrorOccurred.store(true, std::memory_order_relaxed);
+    }
+    if (aSourceAnalyzer.NbWires() != aResultAnalyzer.NbWires())
+    {
+      theDI << "Error: Wrong number of wires in the result shape.\nExpected: "
+            << aSourceAnalyzer.NbWires() << "\nActual" << aResultAnalyzer.NbWires() << "\n";
+      anErrorOccurred.store(true, std::memory_order_relaxed);
+    }
+    if (aSourceAnalyzer.NbEdges() != aResultAnalyzer.NbEdges())
+    {
+      theDI << "Error: Wrong number of edges in the result shape.\nExpected: "
+            << aSourceAnalyzer.NbEdges() << "\nActual" << aResultAnalyzer.NbEdges() << "\n";
+      anErrorOccurred.store(true, std::memory_order_relaxed);
+    }
+    if (aSourceAnalyzer.NbVertices() != aResultAnalyzer.NbVertices())
+    {
+      theDI << "Error: Wrong number of vertices in the result shape.\nExpected: "
+            << aSourceAnalyzer.NbVertices() << "\nActual" << aResultAnalyzer.NbVertices() << "\n";
+      anErrorOccurred.store(true, std::memory_order_relaxed);
+    }
+  });
+
+  return anErrorOccurred;
+}
+
 //=======================================================================
 // function : QACheckBends
 // purpose :
@@ -5283,5 +5435,30 @@ void QABugs::Commands_20(Draw_Interpretor& theCommands)
                   OCC26441,
                   group);
 
+  theCommands.Add(
+    "OCC33657_1",
+    "Check performance of STEPCAFControl_Reader/Writer constructors in multithreading environment.",
+    __FILE__,
+    OCC33657_1,
+    group);
+
+  theCommands.Add("OCC33657_2",
+                  "Check performance of STEPControl_Reader in multithreading environment.",
+                  __FILE__,
+                  OCC33657_2,
+                  group);
+
+  theCommands.Add("OCC33657_3",
+                  "Check performance of STEPControl_Writer in multithreading environment.",
+                  __FILE__,
+                  OCC33657_3,
+                  group);
+
+  theCommands.Add("OCC33657_4",
+                  "Check performance of STEPControl_Reader/Writer in multithreading environment.",
+                  __FILE__,
+                  OCC33657_4,
+                  group);
+
   return;
 }
index 9e00796ea6ad12228ac3d8995af30ff580c21e70..83a7ed9a8a7fd939e416b773832848961d3aabec 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <Standard_ErrorHandler.hxx>
 #include <Standard_Failure.hxx>
+#include <Standard_Mutex.hxx>
 
 #include <Message.hxx>
 #include <Message_Messenger.hxx>
   #define CHRONOMESURE
 #endif
 
+namespace
+{
+static Standard_Mutex THE_GLOBAL_READ_MUTEX;
+}
+
 void StepFile_Interrupt(Standard_CString theErrorMessage, const Standard_Boolean theIsFail)
 {
   if (theErrorMessage == NULL)
@@ -113,7 +119,8 @@ static Standard_Integer StepFile_Read(const char*                            the
 
   sout << "      ...    STEP File   Read    ...\n";
 
-  Standard_Integer nbhead, nbrec, nbpar;
+  Standard_Mutex::Sentry aLocker(THE_GLOBAL_READ_MUTEX);
+  Standard_Integer       nbhead, nbrec, nbpar;
   aFileDataModel.GetFileNbR(&nbhead, &nbrec, &nbpar); // renvoi par lex/yacc
   Handle(StepData_StepReaderData) undirec =
     // clang-format off
index 713074048f5444c392ee734896cb5adcf33d2a24..9436f55ab232c1cf635eb36bbe694470b91ece74 100644 (file)
 
 IMPLEMENT_STANDARD_RTTIEXT(XSControl_WorkSession, IFSelect_WorkSession)
 
+namespace
+{
+static Standard_Mutex WS_GLOBAL_MUTEX; //!< Mutex to prevent data races during reading and writing.
+}
+
 //=================================================================================================
 
 XSControl_WorkSession::XSControl_WorkSession()
@@ -67,6 +72,7 @@ void XSControl_WorkSession::ClearData(const Standard_Integer mode)
 
 Standard_Boolean XSControl_WorkSession::SelectNorm(const Standard_CString normname)
 {
+  const Standard_Mutex::Sentry aMutexLock(WS_GLOBAL_MUTEX);
   // Old norm and results
   myTransferReader->Clear(-1);
   //  ????  En toute rigueur, menage a faire dans XWS : virer les items
@@ -424,6 +430,7 @@ Standard_Integer XSControl_WorkSession::TransferReadRoots(const Message_Progress
 
 Handle(Interface_InterfaceModel) XSControl_WorkSession::NewModel()
 {
+  const Standard_Mutex::Sentry     aMutexLock(WS_GLOBAL_MUTEX);
   Handle(Interface_InterfaceModel) newmod;
   if (myController.IsNull())
     return newmod;
@@ -446,7 +453,8 @@ IFSelect_ReturnStatus XSControl_WorkSession::TransferWriteShape(
   const Standard_Boolean       compgraph,
   const Message_ProgressRange& theProgress)
 {
-  IFSelect_ReturnStatus status;
+  const Standard_Mutex::Sentry aMutexLock(WS_GLOBAL_MUTEX);
+  IFSelect_ReturnStatus        status;
   if (myController.IsNull())
     return IFSelect_RetError;
   const Handle(Interface_InterfaceModel)& model = Model();
index 7681103099272923d0ae56c86f9b156b9b60047f..a992d784d4906186cec2e34d17ba617c9714d0c6 100644 (file)
@@ -196,6 +196,7 @@ private:
   //! Clears binders
   Standard_EXPORT void ClearBinders();
 
+private:
   Handle(XSControl_Controller)     myController;
   Handle(XSControl_TransferReader) myTransferReader;
   Handle(XSControl_TransferWriter) myTransferWriter;
diff --git a/tests/bugs/step/bug33657_1 b/tests/bugs/step/bug33657_1
new file mode 100644 (file)
index 0000000..9e03e23
--- /dev/null
@@ -0,0 +1,4 @@
+# Check performance of STEPCAFControl_Reader/Writer constructors in multithreading environment.
+# If no crash occures, its fine.
+pload QAcommands
+OCC33657_1
diff --git a/tests/bugs/step/bug33657_2 b/tests/bugs/step/bug33657_2
new file mode 100644 (file)
index 0000000..e0c1878
--- /dev/null
@@ -0,0 +1,4 @@
+# Check performance of STEPControl_Reader in multithreading environment.
+# If no crash occures, its fine.
+pload QAcommands
+OCC33657_2 [locate_data_file bug21802_as1-oc-214.stp]
diff --git a/tests/bugs/step/bug33657_3 b/tests/bugs/step/bug33657_3
new file mode 100644 (file)
index 0000000..8013fb1
--- /dev/null
@@ -0,0 +1,4 @@
+# Check performance of STEPControl_Writer in multithreading environment.
+# If no crash occures, its fine.
+pload QAcommands
+OCC33657_1
diff --git a/tests/bugs/step/bug33657_4 b/tests/bugs/step/bug33657_4
new file mode 100644 (file)
index 0000000..6d24511
--- /dev/null
@@ -0,0 +1,3 @@
+# Check performance of STEPControl_Reader/Writer in multithreading environment.
+pload QAcommands
+OCC33657_4 [locate_data_file bug21802_as1-oc-214.stp]