mirror of
https://github.com/Open-Cascade-SAS/OCCT.git
synced 2026-09-02 09:26:11 +08:00
Foundation Classes - Fix thread-safety of Strtod by making Bigint pool thread-local (#1309)
- The Strtod() function used a shared static Bigint memory pool (private_mem, pmem_next, TI0) without synchronization. - When multiple threads called Strtod() concurrently (e.g. parallel BRep reading), the pool was corrupted causing random mis-parses of numeric values. - Fix: declare private_mem, pmem_next and TI0 as thread_local so each thread gets its own independent memory pool and freelist. - Added Standard_Strtod_Test.cxx with parallel correctness tests using OSD_Parallel::For. - Added BRepTools_Test.cxx with integration tests for parallel BRep file reading.
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
// Copyright (c) 2026 OPEN CASCADE SAS
|
||||
//
|
||||
// This file is part of Open CASCADE Technology software library.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it under
|
||||
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||
// by the Free Software Foundation, with special exception defined in the file
|
||||
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||
// distribution for complete text of the license and disclaimer of any warranty.
|
||||
//
|
||||
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||
// commercial license or contractual agreement.
|
||||
|
||||
#include <BRep_Builder.hxx>
|
||||
#include <BRepGProp.hxx>
|
||||
#include <BRepPrimAPI_MakeBox.hxx>
|
||||
#include <BRepTools.hxx>
|
||||
#include <GProp_GProps.hxx>
|
||||
#include <NCollection_Array1.hxx>
|
||||
#include <OSD_Parallel.hxx>
|
||||
#include <TopoDS_Shape.hxx>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Write a box to a BRep string once, to be reused by all threads.
|
||||
std::string MakeBoxBrepString()
|
||||
{
|
||||
BRepPrimAPI_MakeBox aBox(10.0, 20.0, 30.0);
|
||||
EXPECT_FALSE(aBox.Shape().IsNull());
|
||||
|
||||
std::ostringstream anOss;
|
||||
BRepTools::Write(aBox.Shape(), anOss);
|
||||
EXPECT_FALSE(anOss.str().empty());
|
||||
return anOss.str();
|
||||
}
|
||||
|
||||
// Read a shape from a BRep buffer. Each call uses its own stream.
|
||||
bool ReadFromBuffer(const std::string& theData, TopoDS_Shape& theShape)
|
||||
{
|
||||
std::istringstream anIss(theData);
|
||||
BRep_Builder aBuilder;
|
||||
BRepTools::Read(theShape, anIss, aBuilder);
|
||||
return !theShape.IsNull();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Parallel BRep reading must produce shapes with identical volume properties.
|
||||
// The underlying bug was that Strtod() shared a single Bigint memory pool
|
||||
// across all threads, causing random mis-parses of numeric values in .brep
|
||||
// files when read concurrently.
|
||||
TEST(BRepTools_Test, ParallelRead_IdenticalMass)
|
||||
{
|
||||
const std::string aBrepData = MakeBoxBrepString();
|
||||
|
||||
// Single-threaded reference
|
||||
TopoDS_Shape aRefShape;
|
||||
ASSERT_TRUE(ReadFromBuffer(aBrepData, aRefShape));
|
||||
GProp_GProps aRefProps;
|
||||
BRepGProp::VolumeProperties(aRefShape, aRefProps);
|
||||
const double aRefMass = std::abs(aRefProps.Mass());
|
||||
ASSERT_GT(aRefMass, 0.0);
|
||||
|
||||
constexpr int aNbReads = 200;
|
||||
|
||||
NCollection_Array1<TopoDS_Shape> aShapes(0, aNbReads - 1);
|
||||
std::atomic<int> aFailCount{0};
|
||||
|
||||
OSD_Parallel::For(0, aNbReads, [&](int theIndex) {
|
||||
TopoDS_Shape aShape;
|
||||
if (!ReadFromBuffer(aBrepData, aShape))
|
||||
{
|
||||
aFailCount.fetch_add(1, std::memory_order_relaxed);
|
||||
return;
|
||||
}
|
||||
aShapes(theIndex) = aShape;
|
||||
});
|
||||
|
||||
EXPECT_EQ(aFailCount.load(), 0) << "Some parallel reads failed";
|
||||
|
||||
constexpr double aEps = 1.0e-6;
|
||||
for (int i = 0; i < aNbReads; ++i)
|
||||
{
|
||||
ASSERT_FALSE(aShapes(i).IsNull()) << "Shape " << i << " is null";
|
||||
GProp_GProps aProps;
|
||||
BRepGProp::VolumeProperties(aShapes(i), aProps);
|
||||
const double aMass = std::abs(aProps.Mass());
|
||||
EXPECT_NEAR(aMass, aRefMass, aEps)
|
||||
<< "Shape " << i << " mass " << aMass << " != reference " << aRefMass;
|
||||
}
|
||||
}
|
||||
|
||||
// Repeated parallel runs to catch intermittent failures.
|
||||
TEST(BRepTools_Test, ParallelRead_RepeatedRuns)
|
||||
{
|
||||
const std::string aBrepData = MakeBoxBrepString();
|
||||
|
||||
TopoDS_Shape aRefShape;
|
||||
ASSERT_TRUE(ReadFromBuffer(aBrepData, aRefShape));
|
||||
GProp_GProps aRefProps;
|
||||
BRepGProp::VolumeProperties(aRefShape, aRefProps);
|
||||
const double aRefMass = std::abs(aRefProps.Mass());
|
||||
|
||||
constexpr int aNbReads = 100;
|
||||
constexpr int aNbRuns = 5;
|
||||
constexpr double aEps = 1.0e-6;
|
||||
|
||||
for (int aRun = 0; aRun < aNbRuns; ++aRun)
|
||||
{
|
||||
NCollection_Array1<TopoDS_Shape> aShapes(0, aNbReads - 1);
|
||||
std::atomic<int> aFailCount{0};
|
||||
|
||||
OSD_Parallel::For(0, aNbReads, [&](int theIndex) {
|
||||
TopoDS_Shape aShape;
|
||||
if (!ReadFromBuffer(aBrepData, aShape))
|
||||
{
|
||||
aFailCount.fetch_add(1, std::memory_order_relaxed);
|
||||
return;
|
||||
}
|
||||
aShapes(theIndex) = aShape;
|
||||
});
|
||||
|
||||
EXPECT_EQ(aFailCount.load(), 0) << "Run " << aRun << ": some reads failed";
|
||||
|
||||
for (int i = 0; i < aNbReads; ++i)
|
||||
{
|
||||
ASSERT_FALSE(aShapes(i).IsNull()) << "Run " << aRun << ", shape " << i << " is null";
|
||||
GProp_GProps aProps;
|
||||
BRepGProp::VolumeProperties(aShapes(i), aProps);
|
||||
const double aMass = std::abs(aProps.Mass());
|
||||
EXPECT_NEAR(aMass, aRefMass, aEps) << "Run " << aRun << ", shape " << i << " mass mismatch";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,7 @@ set(OCCT_TKBRep_GTests_FILES
|
||||
BRepGraph_SparseModel_Test.cxx
|
||||
BRepGraph_Deduplicate_Test.cxx
|
||||
BRepTools_ReShape_Test.cxx
|
||||
BRepTools_Test.cxx
|
||||
TopExp_Test.cxx
|
||||
TopoDS_Builder_Test.cxx
|
||||
TopoDS_Edge_Test.cxx
|
||||
|
||||
Reference in New Issue
Block a user