Loading files
3d097a032This file is pinned to the selected release. Download the original to open it in your design tool.
// Created by: Kirill Gavrilov
// Copyright (c) 2017-2019 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 <OSD_ThreadPool.hxx>
#include <OSD.hxx>
#include <OSD_Parallel.hxx>
#include <Standard_ErrorHandler.hxx>
#include <TCollection_AsciiString.hxx>
IMPLEMENT_STANDARD_RTTIEXT(OSD_ThreadPool, Standard_Transient)
//=================================================================================================
bool OSD_ThreadPool::EnumeratedThread::Lock()
{
return myUsageCounter.exchange(1) == 0;
}
//=================================================================================================
void OSD_ThreadPool::EnumeratedThread::Free()
{
myUsageCounter.store(0);
}
//=================================================================================================
void OSD_ThreadPool::EnumeratedThread::WakeUp(JobInterface* theJob, bool theToCatchFpe)
{
myJob = theJob;
myToCatchFpe = theToCatchFpe;
if (myIsSelfThread)
{
if (theJob != nullptr)
{
OSD_ThreadPool::performJob(myFailure, myJob, myThreadIndex);
}
return;
}
myWakeEvent.Set();
if (theJob != nullptr && !myIsStarted)
{
myIsStarted = true;
Run(this);
}
}
//=================================================================================================
void OSD_ThreadPool::EnumeratedThread::WaitIdle()
{
if (!myIsSelfThread)
{
myIdleEvent.Wait();
myIdleEvent.Reset();
}
}
//=================================================================================================
const occ::handle<OSD_ThreadPool>& OSD_ThreadPool::DefaultPool(int theNbThreads)
{
static const occ::handle<OSD_ThreadPool> THE_GLOBAL_POOL = new OSD_ThreadPool(theNbThreads);
return THE_GLOBAL_POOL;
}
//=================================================================================================
OSD_ThreadPool::OSD_ThreadPool(int theNbThreads)
: myNbDefThreads(0),
myShutDown(false)
{
Init(theNbThreads);
myNbDefThreads = NbThreads();
}
//=================================================================================================
bool OSD_ThreadPool::IsInUse()
{
for (NCollection_Array1<EnumeratedThread>::Iterator aThreadIter(myThreads); aThreadIter.More();
aThreadIter.Next())
{
EnumeratedThread& aThread = aThreadIter.ChangeValue();
if (!aThread.Lock())
{
return true;
}
aThread.Free();
}
return false;
}
//=================================================================================================
void OSD_ThreadPool::Init(int theNbThreads)
{
const int aNbThreads =
std::max(0, (theNbThreads > 0 ? theNbThreads : OSD_Parallel::NbLogicalProcessors()) - 1);
if (myThreads.Length() == aNbThreads)
{
return;
}
// release old threads
if (!myThreads.IsEmpty())
{
NCollection_Array1<EnumeratedThread*> aLockThreads(myThreads.Lower(), myThreads.Upper());
aLockThreads.Init(nullptr);
int aThreadIndex = myThreads.Lower();
for (NCollection_Array1<EnumeratedThread>::Iterator aThreadIter(myThreads); aThreadIter.More();
aThreadIter.Next())
{
EnumeratedThread& aThread = aThreadIter.ChangeValue();
if (!aThread.Lock())
{
for (NCollection_Array1<EnumeratedThread*>::Iterator aLockThreadIter(aLockThreads);
aLockThreadIter.More() && aLockThreadIter.Value() != nullptr;
aLockThreadIter.Next())
{
aLockThreadIter.ChangeValue()->Free();
}
throw Standard_ProgramError("Error: active ThreadPool is reinitialized");
}
aLockThreads.SetValue(aThreadIndex++, &aThread);
}
}
release();
myShutDown = false;
if (aNbThreads > 0)
{
myThreads.Resize(0, aNbThreads - 1, false);
int aLastThreadIndex = 0;
for (NCollection_Array1<EnumeratedThread>::Iterator aThreadIter(myThreads); aThreadIter.More();
aThreadIter.Next())
{
EnumeratedThread& aThread = aThreadIter.ChangeValue();
aThread.myPool = this;
aThread.myThreadIndex = aLastThreadIndex++;
aThread.SetFunction(&OSD_ThreadPool::EnumeratedThread::runThread);
}
}
else
{
NCollection_Array1<EnumeratedThread> anEmpty;
myThreads.Move(anEmpty);
}
}
//=================================================================================================
OSD_ThreadPool::~OSD_ThreadPool()
{
release();
}
//=================================================================================================
void OSD_ThreadPool::release()
{
if (myThreads.IsEmpty())
{
return;
}
myShutDown = true;
for (NCollection_Array1<EnumeratedThread>::Iterator aThreadIter(myThreads); aThreadIter.More();
aThreadIter.Next())
{
aThreadIter.ChangeValue().WakeUp(nullptr, false);
aThreadIter.ChangeValue().Wait();
}
}
//=================================================================================================
void OSD_ThreadPool::Launcher::perform(JobInterface& theJob)
{
run(theJob);
wait();
}
//=================================================================================================
void OSD_ThreadPool::Launcher::run(JobInterface& theJob)
{
bool toCatchFpe = OSD::ToCatchFloatingSignals();
for (NCollection_Array1<EnumeratedThread*>::Iterator aThreadIter(myThreads);
aThreadIter.More() && aThreadIter.Value() != nullptr;
aThreadIter.Next())
{
aThreadIter.ChangeValue()->WakeUp(&theJob, toCatchFpe);
}
}
//=================================================================================================
void OSD_ThreadPool::Launcher::wait()
{
int aNbFailures = 0;
for (NCollection_Array1<EnumeratedThread*>::Iterator aThreadIter(myThreads);
aThreadIter.More() && aThreadIter.Value() != nullptr;
aThreadIter.Next())
{
aThreadIter.ChangeValue()->WaitIdle();
if (aThreadIter.Value()->myFailure)
{
++aNbFailures;
}
}
if (aNbFailures == 0)
{
return;
}
TCollection_AsciiString aFailures;
for (NCollection_Array1<EnumeratedThread*>::Iterator aThreadIter(myThreads);
aThreadIter.More() && aThreadIter.Value() != nullptr;
aThreadIter.Next())
{
if (aThreadIter.Value()->myFailure)
{
if (aNbFailures == 1)
{
// Re-throw the single exception directly
throw *aThreadIter.Value()->myFailure;
}
if (!aFailures.IsEmpty())
{
aFailures += "\n";
}
aFailures += aThreadIter.Value()->myFailure->what();
}
}
aFailures = TCollection_AsciiString("Multiple exceptions:\n") + aFailures;
throw Standard_ProgramError(aFailures.ToCString(), nullptr);
}
//=================================================================================================
void OSD_ThreadPool::performJob(std::optional<Standard_ProgramError>& theFailure,
OSD_ThreadPool::JobInterface* theJob,
int theThreadIndex)
{
try
{
OCC_CATCH_SIGNALS
theJob->Perform(theThreadIndex);
}
catch (Standard_Failure const& aFailure)
{
TCollection_AsciiString aMsg =
TCollection_AsciiString(aFailure.ExceptionType()) + ": " + aFailure.what();
theFailure.emplace(aMsg.ToCString(), aFailure.GetStackString());
}
catch (std::exception& anStdException)
{
TCollection_AsciiString aMsg =
TCollection_AsciiString(typeid(anStdException).name()) + ": " + anStdException.what();
theFailure.emplace(aMsg.ToCString(), nullptr);
}
catch (...)
{
theFailure.emplace("Error: Unknown exception", nullptr);
}
}
//=================================================================================================
void OSD_ThreadPool::EnumeratedThread::performThread()
{
OSD::SetThreadLocalSignal(OSD::SignalMode(), false);
for (;;)
{
myWakeEvent.Wait();
myWakeEvent.Reset();
if (myPool->myShutDown)
{
return;
}
myFailure.reset();
if (myJob != nullptr)
{
OSD::SetThreadLocalSignal(OSD::SignalMode(), myToCatchFpe);
OSD_ThreadPool::performJob(myFailure, myJob, myThreadIndex);
myJob = nullptr;
}
myIdleEvent.Set();
}
}
//=================================================================================================
void* OSD_ThreadPool::EnumeratedThread::runThread(void* theTask)
{
EnumeratedThread* aThread = static_cast<EnumeratedThread*>(theTask);
aThread->performThread();
return nullptr;
}
//=================================================================================================
OSD_ThreadPool::Launcher::Launcher(OSD_ThreadPool& thePool, int theMaxThreads)
: mySelfThread(true),
myNbThreads(0)
{
const int aNbThreads =
theMaxThreads > 0 ? std::min(theMaxThreads, thePool.NbThreads())
: (theMaxThreads < 0 ? std::max(thePool.NbDefaultThreadsToLaunch(), 1) : 1);
myThreads.Resize(0, aNbThreads - 1, false);
myThreads.Init(nullptr);
if (aNbThreads > 1)
{
for (NCollection_Array1<EnumeratedThread>::Iterator aThreadIter(thePool.myThreads);
aThreadIter.More();
aThreadIter.Next())
{
if (aThreadIter.ChangeValue().Lock())
{
myThreads.SetValue(myNbThreads, &aThreadIter.ChangeValue());
// make thread index to fit into myThreads range
aThreadIter.ChangeValue().myThreadIndex = myNbThreads;
if (++myNbThreads == aNbThreads - 1)
{
break;
}
}
}
}
// self thread should be executed last
myThreads.SetValue(myNbThreads, &mySelfThread);
mySelfThread.myThreadIndex = myNbThreads;
++myNbThreads;
}
//=================================================================================================
void OSD_ThreadPool::Launcher::Release()
{
for (NCollection_Array1<EnumeratedThread*>::Iterator aThreadIter(myThreads);
aThreadIter.More() && aThreadIter.Value() != nullptr;
aThreadIter.Next())
{
if (aThreadIter.Value() != &mySelfThread)
{
aThreadIter.Value()->Free();
}
}
NCollection_Array1<EnumeratedThread*> anEmpty;
myThreads.Move(anEmpty);
myNbThreads = 0;
}