From fdbfdb571a07db709d8897d4343e3de5819dfa53 Mon Sep 17 00:00:00 2001 From: nbv Date: Wed, 20 Feb 2019 16:52:22 +0300 Subject: [PATCH] 0029935: Foundation Classes - introduce OSD_ThreadPool class defining a thread pool --- src/BRepMesh/BRepMesh_FastDiscret.cxx | 2 +- src/BRepMesh/BRepMesh_IncrementalMesh.cxx | 2 +- src/OSD/FILES | 4 + src/OSD/OSD.hxx | 5 +- src/OSD/OSD_Parallel.hxx | 444 ++++++++++++---------- src/OSD/OSD_Parallel_TBB.cxx | 48 +++ src/OSD/OSD_Parallel_Threads.cxx | 159 ++++++++ src/OSD/OSD_ThreadPool.cxx | 401 +++++++++++++++++++ src/OSD/OSD_ThreadPool.hxx | 301 +++++++++++++++ src/OSD/OSD_signal.cxx | 125 +++--- src/Standard/FILES | 7 +- src/Standard/Standard_Atomic.hxx | 66 ++-- src/Standard/Standard_Condition.cxx | 207 ++++++++++ src/Standard/Standard_Condition.hxx | 80 ++++ src/Standard/Standard_Failure.cxx | 17 - src/Standard/Standard_Macro.hxx | 121 ++++-- 16 files changed, 1644 insertions(+), 345 deletions(-) create mode 100644 src/OSD/OSD_Parallel_TBB.cxx create mode 100644 src/OSD/OSD_Parallel_Threads.cxx create mode 100644 src/OSD/OSD_ThreadPool.cxx create mode 100644 src/OSD/OSD_ThreadPool.hxx create mode 100644 src/Standard/Standard_Condition.cxx create mode 100644 src/Standard/Standard_Condition.hxx diff --git a/src/BRepMesh/BRepMesh_FastDiscret.cxx b/src/BRepMesh/BRepMesh_FastDiscret.cxx index 1d453897ff..56e7b5f711 100644 --- a/src/BRepMesh/BRepMesh_FastDiscret.cxx +++ b/src/BRepMesh/BRepMesh_FastDiscret.cxx @@ -120,7 +120,7 @@ void BRepMesh_FastDiscret::Perform(const TopoDS_Shape& theShape) aFaces.push_back(aFace); } - OSD_Parallel::ForEach(aFaces.begin(), aFaces.end(), *this, !myParameters.InParallel); + OSD_Parallel::ForEach(aFaces.begin(), aFaces.end(), *this, !myParameters.InParallel, (Standard_Integer )aFaces.size()); } diff --git a/src/BRepMesh/BRepMesh_IncrementalMesh.cxx b/src/BRepMesh/BRepMesh_IncrementalMesh.cxx index 70278be259..09bd970a61 100644 --- a/src/BRepMesh/BRepMesh_IncrementalMesh.cxx +++ b/src/BRepMesh/BRepMesh_IncrementalMesh.cxx @@ -234,7 +234,7 @@ void BRepMesh_IncrementalMesh::update() update(aFaceIt.Value()); // Mesh faces - OSD_Parallel::ForEach(myFaces.begin(), myFaces.end(), *myMesh, !myParameters.InParallel); + OSD_Parallel::ForEach(myFaces.begin(), myFaces.end(), *myMesh, !myParameters.InParallel, myFaces.Size()); commit(); clear(); diff --git a/src/OSD/FILES b/src/OSD/FILES index c9e53811a0..2c33e882b9 100755 --- a/src/OSD/FILES +++ b/src/OSD/FILES @@ -57,6 +57,8 @@ OSD_OpenMode.hxx OSD_OSDError.hxx OSD_Parallel.cxx OSD_Parallel.hxx +OSD_Parallel_TBB.cxx +OSD_Parallel_Threads.cxx OSD_Path.cxx OSD_Path.hxx OSD_PerfMeter.cxx @@ -83,6 +85,8 @@ OSD_SingleProtection.hxx OSD_SysType.hxx OSD_Thread.cxx OSD_Thread.hxx +OSD_ThreadPool.cxx +OSD_ThreadPool.hxx OSD_ThreadFunction.hxx OSD_Timer.cxx OSD_Timer.hxx diff --git a/src/OSD/OSD.hxx b/src/OSD/OSD.hxx index 4418a11766..dd80e028b0 100644 --- a/src/OSD/OSD.hxx +++ b/src/OSD/OSD.hxx @@ -104,7 +104,10 @@ public: //! user's code. Refer to Foundation Classes User's Guide for further details. //! Standard_EXPORT static void SetSignal (const Standard_Boolean theFloatingSignal = Standard_True); - + + //! Return floating signal catching value previously set by SetSignal(). + Standard_EXPORT static Standard_Boolean ToCatchFloatingSignals(); + //! Commands the process to sleep for a number of seconds. Standard_EXPORT static void SecSleep (const Standard_Integer aDelay); diff --git a/src/OSD/OSD_Parallel.hxx b/src/OSD/OSD_Parallel.hxx index 3f34408b74..d39e9d7b47 100644 --- a/src/OSD/OSD_Parallel.hxx +++ b/src/OSD/OSD_Parallel.hxx @@ -14,25 +14,25 @@ #ifndef OSD_Parallel_HeaderFile #define OSD_Parallel_HeaderFile -#include -#include -#include -#include -#include - -#ifdef HAVE_TBB -#include -#include -#include -#endif +#include +#include +#include -//! @class OSD_Parallel -//! @brief Simplifies code parallelization. +//! @brief Simple tool for code parallelization. +//! +//! OSD_Parallel class provides simple interface for parallel processing of +//! tasks that can be formulated in terms of "for" or "foreach" loops. +//! +//! To use this tool it is necessary to: +//! - organize the data to be processed in a collection accessible by +//! iteration (usually array or vector); +//! - implement a functor class providing operator () accepting iterator +//! (or index in array) that does the job; +//! - call either For() or ForEach() providing begin and end iterators and +//! a functor object. //! -//! The Class provides an interface of parallel processing "for" and "foreach" loops. -//! These primitives encapsulates complete logic for creating and managing parallel context of loops. -//! Moreover the primitives may be a wrapper for some primitives from 3rd-party library - TBB. -//! To use it is necessary to implement TBB like interface which is based on functors. +//! Iterators should satisfy requirements of STL forward iterator. +//! Functor //! //! @code //! class Functor @@ -45,268 +45,290 @@ //! }; //! @endcode //! -//! In the body of the operator () should be implemented thread-safe logic of computations that can be performed in parallel context. -//! If parallelized loop iterates on the collections with direct access by index (such as Vector, Array), -//! it is more efficient to use the primitive ParallelFor (because it has no critical section). +//! The operator () should be implemented in a thread-safe way so that +//! the same functor object can process different data items in parallel threads. +//! +//! Iteration by index (For) is expected to be more efficient than using iterators +//! (ForEach). +//! +//! Implementation uses TBB if OCCT is built with support of TBB; otherwise it +//! uses ad-hoc parallelization tool. In general, if TBB is available, it is +//! more efficient to use it directly instead of using OSD_Parallel. + class OSD_Parallel { - //! Auxiliary class which ensures exclusive - //! access to iterators of processed data pool. - template - class Range +private: + + //! Interface class defining API for polymorphic wrappers over iterators. + //! Intended to add polymorphic behaviour to For and ForEach functionality + //! for arbitrary objects and eliminate dependency on template parameters. + class IteratorInterface { - public: //! @name public methods + public: + virtual ~IteratorInterface() {} - typedef Value Iterator; + //! Returns true if iterators wrapped by this and theOther are equal + virtual bool IsEqual (const IteratorInterface& theOther) const = 0; - //! Constructor - Range(const Value& theBegin, const Value& theEnd) - : myBegin(theBegin), - myEnd (theEnd), - myIt (theBegin) - { - } + //! Increments wrapped iterator + virtual void Increment () = 0; + + //! Returns new instance of the wrapper containing copy + //! of the wrapped iterator. + virtual IteratorInterface* Clone() const = 0; + }; - //! Returns const link on the first element. - inline const Value& Begin() const + //! Implementation of polymorphic iterator wrapper suitable for basic + //! types as well as for std iterators. + //! Wraps instance of actual iterator type Type. + template + class IteratorWrapper : public IteratorInterface + { + public: + IteratorWrapper() {} + IteratorWrapper(const Type& theValue) : myValue(theValue) {} + + virtual bool IsEqual (const IteratorInterface& theOther) const Standard_OVERRIDE { - return myBegin; + return myValue == dynamic_cast&>(theOther).myValue; } - //! Returns const link on the last element. - inline const Value& End() const + virtual void Increment () Standard_OVERRIDE { - return myEnd; + ++myValue; } - //! Returns first non processed element or end. - //! Thread-safe method. - inline Iterator It() const + virtual IteratorInterface* Clone() const Standard_OVERRIDE { - Standard_Mutex::Sentry aMutex( myMutex ); - return ( myIt != myEnd ) ? myIt++ : myEnd; + return new IteratorWrapper(myValue); } - private: //! @name private methods - - //! Empty copy constructor - Range(const Range& theCopy); + const Type& Value() const { return myValue; } - //! Empty copy operator. - Range& operator=(const Range& theCopy); - - private: //! @name private fields - - const Value& myBegin; //!< Fisrt element of range. - const Value& myEnd; //!< Last element of range. - mutable Value myIt; //!< First non processed element of range. - mutable Standard_Mutex myMutex; //!< Access controller for the first non processed element. + private: + Type myValue; }; - //! Auxiliary wrapper class for thread function. - template - class Task +protected: + // Note: UniversalIterator and FunctorInterface are made protected to be + // accessible from specialization using threads (non-TBB). + + //! Fixed-type iterator, implementing STL forward iterator interface, used for + //! iteration over objects subject to parallel processing. + //! It stores pointer to instance of polymorphic iterator inheriting from + //! IteratorInterface, which contains actual type-specific iterator. + class UniversalIterator : + // Note that TBB requires that value_type of iterator be copyable, + // thus we use its own type for that + public std::iterator { - public: //! @name public methods + public: + UniversalIterator() {} - //! Constructor. - Task(const Functor& thePerformer, Range& theRange) - : myPerformer(thePerformer), - myRange (theRange) + UniversalIterator(IteratorInterface* theOther) + : myPtr(theOther) { } - //! Method is executed in the context of thread, - //! so this method defines the main calculations. - static Standard_Address RunWithIterator(Standard_Address theTask) + UniversalIterator(const UniversalIterator& theOther) + : myPtr (theOther.myPtr->Clone()) { - Task& aTask = - *( static_cast< Task* >(theTask) ); + } - const Range& aData( aTask.myRange ); - typename Range::Iterator i = aData.It(); + UniversalIterator& operator= (const UniversalIterator& theOther) + { + myPtr.reset (theOther.myPtr->Clone()); + return *this; + } - for ( ; i != aData.End(); i = aData.It() ) - { - aTask.myPerformer(*i); - } + bool operator!= (const UniversalIterator& theOther) const + { + return ! myPtr->IsEqual (*theOther.myPtr); + } - return NULL; + bool operator== (const UniversalIterator& theOther) const + { + return myPtr->IsEqual (*theOther.myPtr); } - //! Method is executed in the context of thread, - //! so this method defines the main calculations. - static Standard_Address RunWithIndex(Standard_Address theTask) + UniversalIterator& operator++() { - Task& aTask = - *( static_cast< Task* >(theTask) ); + myPtr->Increment(); + return *this; + } + + UniversalIterator operator++(int) + { + UniversalIterator aValue(*this); + myPtr->Increment(); + return aValue; + } - const Range& aData( aTask.myRange ); - Standard_Integer i = aData.It(); + const UniversalIterator& operator* () const { return *this; } + UniversalIterator& operator* () { return *this; } - for ( ; i < aData.End(); i = aData.It()) - { - aTask.myPerformer(i); - } + const UniversalIterator* operator->() const { return this; } + UniversalIterator* operator->() { return this; } - return NULL; + // type cast to actual iterator + template + const Iterator& DownCast () const + { + return dynamic_cast*>(myPtr.get())->Value(); } - private: //! @name private methods + private: +#if (defined(_MSC_VER) && (_MSC_VER < 1600)) + std::auto_ptr myPtr; +#else + std::unique_ptr myPtr; +#endif + }; - //! Empty copy constructor. - Task(const Task& theCopy); + //! Interface class representing functor object. + //! Intended to add polymorphic behavour to For and ForEach functionality + //! enabling execution of arbitrary function in parallel mode. + class FunctorInterface + { + public: + virtual ~FunctorInterface() {} - //! Empty copy operator. - Task& operator=(const Task& theCopy); + virtual void operator () (UniversalIterator& theIterator) const = 0; + }; - private: //! @name private fields +private: - const Functor& myPerformer; //!< Link on functor. - const Range& myRange; //!< Link on processed data block. + //! Wrapper for functors manipulating on std iterators. + template + class FunctorWrapperIter : public FunctorInterface + { + public: + FunctorWrapperIter (const Functor& theFunctor) + : myFunctor(theFunctor) + { + } + + virtual void operator() (UniversalIterator& theIterator) const Standard_OVERRIDE + { + const Iterator& anIt = theIterator.DownCast(); + myFunctor(*anIt); + } + + private: + FunctorWrapperIter (const FunctorWrapperIter&); + void operator = (const FunctorWrapperIter&); + const Functor& myFunctor; }; -public: //! @name public methods + //! Wrapper for functors manipulating on integer index. + template + class FunctorWrapperInt : public FunctorInterface + { + public: + FunctorWrapperInt (const Functor& theFunctor) + : myFunctor(theFunctor) + { + } - //! Returns number of logical proccesrs. - Standard_EXPORT static Standard_Integer NbLogicalProcessors(); + virtual void operator() (UniversalIterator& theIterator) const Standard_OVERRIDE + { + Standard_Integer anIndex = theIterator.DownCast(); + myFunctor(anIndex); + } + + private: + FunctorWrapperInt (const FunctorWrapperInt&); + void operator = (const FunctorWrapperInt&); + const Functor& myFunctor; + }; + +private: //! Simple primitive for parallelization of "foreach" loops, e.g.: //! @code //! for (std::iterator anIter = theBegin; anIter != theEnd; ++anIter) {} //! @endcode + //! Implementation of framework-dependent functionality should be provided by + //! forEach_impl function defined in opencascade::parallel namespace. //! @param theBegin the first index (incusive) //! @param theEnd the last index (exclusive) - //! @param theFunctor functor providing an interface "void operator(InputIterator theIter){}" performing task for specified iterator position - //! @param isForceSingleThreadExecution if true, then no threads will be created - template - static void ForEach( InputIterator theBegin, - InputIterator theEnd, - const Functor& theFunctor, - const Standard_Boolean isForceSingleThreadExecution - = Standard_False ); + //! @param theFunctor functor providing an interface "void operator(InputIterator theIter){}" + //! performing task for the specified iterator position + //! @param theNbItems number of items passed by iterator, -1 if unknown + Standard_EXPORT static void forEach (UniversalIterator& theBegin, + UniversalIterator& theEnd, + const FunctorInterface& theFunctor, + Standard_Integer theNbItems); + +public: //! @name public methods + + //! Returns number of logical proccesrs. + Standard_EXPORT static Standard_Integer NbLogicalProcessors(); - //! Simple primitive for parallelization of "for" loops, e.g.: + //! Simple primitive for parallelization of "foreach" loops, equivalent to: //! @code - //! for (int anIter = theBegin; anIter < theEnd; ++anIter) {} + //! for (auto anIter = theBegin; anIter != theEnd; ++anIter) { + //! theFunctor(*anIter); + //! } //! @endcode //! @param theBegin the first index (incusive) //! @param theEnd the last index (exclusive) - //! @param theFunctor functor providing an interface "void operator(int theIndex){}" performing task for specified index + //! @param theFunctor functor providing an interface "void operator(InputIterator theIter){}" + //! performing task for specified iterator position //! @param isForceSingleThreadExecution if true, then no threads will be created - template - static void For( const Standard_Integer theBegin, - const Standard_Integer theEnd, - const Functor& theFunctor, - const Standard_Boolean isForceSingleThreadExecution - = Standard_False ); -}; - -//======================================================================= -//function : OSD_Parallel::Range::It -//purpose : Template concretization. -//======================================================================= -template<> inline Standard_Integer OSD_Parallel::Range::It() const -{ - return Standard_Atomic_Increment( reinterpret_cast(&myIt) ) - 1; -} - -//======================================================================= -//function : ParallelForEach -//purpose : -//======================================================================= -template -void OSD_Parallel::ForEach( InputIterator theBegin, - InputIterator theEnd, - const Functor& theFunctor, - const Standard_Boolean isForceSingleThreadExecution ) -{ - if ( isForceSingleThreadExecution ) - { - for ( InputIterator it(theBegin); it != theEnd; ++it ) - theFunctor(*it); - - return; - } - #ifdef HAVE_TBB + //! @param theNbItems number of items passed by iterator, -1 if unknown + template + static void ForEach(InputIterator theBegin, + InputIterator theEnd, + const Functor& theFunctor, + const Standard_Boolean isForceSingleThreadExecution = Standard_False, + Standard_Integer theNbItems = -1) { - try + if (isForceSingleThreadExecution || theNbItems == 1) { - tbb::parallel_for_each(theBegin, theEnd, theFunctor); + for (InputIterator it(theBegin); it != theEnd; ++it) + theFunctor(*it); } - catch ( tbb::captured_exception& anException ) + else { - throw Standard_NotImplemented(anException.what()); + UniversalIterator aBegin(new IteratorWrapper(theBegin)); + UniversalIterator aEnd (new IteratorWrapper(theEnd)); + FunctorWrapperIter aFunctor (theFunctor); + forEach(aBegin, aEnd, aFunctor, theNbItems); } } - #else - { - Range aData(theBegin, theEnd); - Task aTask(theFunctor, aData); - - const Standard_Integer aNbThreads = OSD_Parallel::NbLogicalProcessors(); - NCollection_Array1 aThreads(0, aNbThreads - 1); - for ( Standard_Integer i = 0; i < aNbThreads; ++i ) - { - OSD_Thread& aThread = aThreads(i); - aThread.SetFunction(&Task::RunWithIterator); - aThread.Run(&aTask); - } - - for ( Standard_Integer i = 0; i < aNbThreads; ++i ) - aThreads(i).Wait(); - } - #endif -} - -//======================================================================= -//function : ParallelFor -//purpose : -//======================================================================= -template -void OSD_Parallel::For( const Standard_Integer theBegin, - const Standard_Integer theEnd, - const Functor& theFunctor, - const Standard_Boolean isForceSingleThreadExecution ) -{ - if ( isForceSingleThreadExecution ) - { - for ( Standard_Integer i = theBegin; i < theEnd; ++i ) - theFunctor(i); - - return; - } - #ifdef HAVE_TBB + //! Simple primitive for parallelization of "for" loops, equivalent to: + //! @code + //! for (int anIter = theBegin; anIter != theEnd; ++anIter) { + //! theFunctor(anIter); + //! } + //! @endcode + //! @param theBegin the first index (incusive) + //! @param theEnd the last index (exclusive) + //! @param theFunctor functor providing an interface "void operator(int theIndex){}" + //! performing task for specified index + //! @param isForceSingleThreadExecution if true, then no threads will be created + template + static void For(const Standard_Integer theBegin, + const Standard_Integer theEnd, + const Functor& theFunctor, + const Standard_Boolean isForceSingleThreadExecution = Standard_False) { - try + if (isForceSingleThreadExecution || (theEnd - theBegin) == 1) { - tbb::parallel_for( theBegin, theEnd, theFunctor ); + for (Standard_Integer it (theBegin); it != theEnd; ++it) + theFunctor(it); } - catch ( tbb::captured_exception& anException ) + else { - throw Standard_NotImplemented(anException.what()); + UniversalIterator aBegin(new IteratorWrapper(theBegin)); + UniversalIterator aEnd (new IteratorWrapper(theEnd)); + FunctorWrapperInt aFunctor (theFunctor); + forEach(aBegin, aEnd, aFunctor, theEnd - theBegin); } } - #else - { - Range aData(theBegin, theEnd); - Task aTask(theFunctor, aData); - - const Standard_Integer aNbThreads = OSD_Parallel::NbLogicalProcessors(); - NCollection_Array1 aThreads(0, aNbThreads - 1); - for ( Standard_Integer i = 0; i < aNbThreads; ++i ) - { - OSD_Thread& aThread = aThreads(i); - aThread.SetFunction(&Task::RunWithIndex); - aThread.Run(&aTask); - } - - for ( Standard_Integer i = 0; i < aNbThreads; ++i ) - aThreads(i).Wait(); - } - #endif -} +}; #endif diff --git a/src/OSD/OSD_Parallel_TBB.cxx b/src/OSD/OSD_Parallel_TBB.cxx new file mode 100644 index 0000000000..508119f393 --- /dev/null +++ b/src/OSD/OSD_Parallel_TBB.cxx @@ -0,0 +1,48 @@ +// Created on: 2014-08-19 +// Created by: Alexander Zaikin +// Copyright (c) 1996-1999 Matra Datavision +// Copyright (c) 2013-2014 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. + +// Version of parallel executor used when TBB is available +#ifdef HAVE_TBB + +#include +#include + +#include +#include +#include + +//======================================================================= +//function : forEach +//purpose : +//======================================================================= + +void OSD_Parallel::forEach (UniversalIterator& theBegin, + UniversalIterator& theEnd, + const FunctorInterface& theFunctor, + Standard_Integer theNbItems) +{ + (void )theNbItems; + try + { + tbb::parallel_for_each(theBegin, theEnd, theFunctor); + } + catch (tbb::captured_exception& anException) + { + throw Standard_ProgramError(anException.what()); + } +} + +#endif /* HAVE_TBB */ \ No newline at end of file diff --git a/src/OSD/OSD_Parallel_Threads.cxx b/src/OSD/OSD_Parallel_Threads.cxx new file mode 100644 index 0000000000..0ffb38fa71 --- /dev/null +++ b/src/OSD/OSD_Parallel_Threads.cxx @@ -0,0 +1,159 @@ +// Created on: 2014-08-19 +// Created by: Alexander Zaikin +// Copyright (c) 1996-1999 Matra Datavision +// Copyright (c) 2013-2014 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. + +// Version of parallel executor used when TBB is not available +#ifndef HAVE_TBB + +#include + +#include + +#include +#include +#include + +namespace +{ + //! Class implementing tools for parallel processing + //! using threads (when TBB is not available); + //! it is derived from OSD_Parallel to get access to + //! Iterator and FunctorInterface nested types. + class OSD_Parallel_Threads : public OSD_ThreadPool, public OSD_Parallel + { + public: + //! Auxiliary class which ensures exclusive + //! access to iterators of processed data pool. + class Range + { + public: //! @name public methods + + //! Constructor + Range(const OSD_Parallel::UniversalIterator& theBegin, + const OSD_Parallel::UniversalIterator& theEnd) + : myBegin(theBegin), + myEnd(theEnd), + myIt(theBegin) + { + } + + //! Returns const link on the first element. + inline const OSD_Parallel::UniversalIterator& Begin() const + { + return myBegin; + } + + //! Returns const link on the last element. + inline const OSD_Parallel::UniversalIterator& End() const + { + return myEnd; + } + + //! Returns first non processed element or end. + //! Thread-safe method. + inline OSD_Parallel::UniversalIterator It() const + { + Standard_Mutex::Sentry aMutex(myMutex); + return (myIt != myEnd) ? myIt++ : myEnd; + } + + private: //! @name private methods + + //! Empty copy constructor + Range(const Range& theCopy); + + //! Empty copy operator. + Range& operator=(const Range& theCopy); + + private: //! @name private fields + + const OSD_Parallel::UniversalIterator& myBegin; //!< Fisrt element of range. + const OSD_Parallel::UniversalIterator& myEnd; //!< Last element of range. + mutable OSD_Parallel::UniversalIterator myIt; //!< First non processed element of range. + mutable Standard_Mutex myMutex; //!< Access controller for the first non processed element. + }; + + //! Auxiliary wrapper class for thread function. + class Task : public JobInterface + { + public: //! @name public methods + + //! Constructor. + Task(const OSD_Parallel::FunctorInterface& thePerformer, Range& theRange) + : myPerformer(thePerformer), + myRange(theRange) + { + } + + //! Method is executed in the context of thread, + //! so this method defines the main calculations. + virtual void Perform (int ) Standard_OVERRIDE + { + for (OSD_Parallel::UniversalIterator anIter = myRange.It(); anIter != myRange.End(); anIter = myRange.It()) + { + myPerformer (anIter); + } + } + + private: //! @name private methods + + //! Empty copy constructor. + Task(const Task& theCopy); + + //! Empty copy operator. + Task& operator=(const Task& theCopy); + + private: //! @name private fields + const FunctorInterface& myPerformer; //!< Link on functor + const Range& myRange; //!< Link on processed data block + }; + + //! Launcher specialization. + class UniversalLauncher : public Launcher + { + public: + //! Constructor. + UniversalLauncher (OSD_ThreadPool& thePool, int theMaxThreads = -1) + : Launcher (thePool, theMaxThreads) {} + + //! Primitive for parallelization of "for" loops. + void Perform (OSD_Parallel::UniversalIterator& theBegin, + OSD_Parallel::UniversalIterator& theEnd, + const OSD_Parallel::FunctorInterface& theFunctor) + { + Range aData (theBegin, theEnd); + Task aJob (theFunctor, aData); + perform (aJob); + } + }; + }; +} + +//======================================================================= +//function : forEach +//purpose : +//======================================================================= +void OSD_Parallel::forEach (UniversalIterator& theBegin, + UniversalIterator& theEnd, + const FunctorInterface& theFunctor, + Standard_Integer theNbItems) +{ + const Handle(OSD_ThreadPool)& aThreadPool = OSD_ThreadPool::DefaultPool(); + const Standard_Integer aNbThreads = theNbItems != -1 ? Min (theNbItems, aThreadPool->NbDefaultThreadsToLaunch()) : -1; + OSD_Parallel_Threads::UniversalLauncher aLauncher (*aThreadPool, aNbThreads); + aLauncher.Perform (theBegin, theEnd, theFunctor); +} + +#endif /* ! HAVE_TBB */ diff --git a/src/OSD/OSD_ThreadPool.cxx b/src/OSD/OSD_ThreadPool.cxx new file mode 100644 index 0000000000..70ad50b31a --- /dev/null +++ b/src/OSD/OSD_ThreadPool.cxx @@ -0,0 +1,401 @@ +// Created by: Kirill Gavrilov +// Copyright (c) 2017 OPEN CASCADE SAS +// +// This file is part of commercial software by OPEN CASCADE SAS. +// +// This software is furnished in accordance with the terms and conditions +// of the contract and with the inclusion of this copyright notice. +// This software or any other copy thereof may not be provided or otherwise +// be made available to any third party. +// No ownership title to the software is transferred hereby. +// +// OPEN CASCADE SAS makes no representation or warranties with respect to the +// performance of this software, and specifically disclaims any responsibility +// for any damages, special or consequential, connected with its use. + +#include + +#include +#include +#include + +IMPLEMENT_STANDARD_RTTIEXT(OSD_ThreadPool, Standard_Transient) + +// ======================================================================= +// function : Lock +// purpose : +// ======================================================================= +bool OSD_ThreadPool::EnumeratedThread::Lock() +{ + return Standard_Atomic_CompareAndSwap (&myUsageCounter, 0, 1); +} + +// ======================================================================= +// function : Free +// purpose : +// ======================================================================= +void OSD_ThreadPool::EnumeratedThread::Free() +{ + Standard_Atomic_CompareAndSwap (&myUsageCounter, 1, 0); +} + +// ======================================================================= +// function : WakeUp +// purpose : +// ======================================================================= +void OSD_ThreadPool::EnumeratedThread::WakeUp (JobInterface* theJob, bool theToCatchFpe) +{ + myJob = theJob; + myToCatchFpe = theToCatchFpe; + if (myIsSelfThread) + { + if (theJob != NULL) + { + OSD_ThreadPool::performJob (myFailure, myJob, myThreadIndex); + } + return; + } + + myWakeEvent.Set(); + if (theJob != NULL && !myIsStarted) + { + myIsStarted = true; + Run (this); + } +} + +// ======================================================================= +// function : WaitIdle +// purpose : +// ======================================================================= +void OSD_ThreadPool::EnumeratedThread::WaitIdle() +{ + if (!myIsSelfThread) + { + myIdleEvent.Wait(); + myIdleEvent.Reset(); + } +} + +// ======================================================================= +// function : DefaultPool +// purpose : +// ======================================================================= +const Handle(OSD_ThreadPool)& OSD_ThreadPool::DefaultPool (int theNbThreads) +{ + static const Handle(OSD_ThreadPool) THE_GLOBAL_POOL = new OSD_ThreadPool (theNbThreads); + return THE_GLOBAL_POOL; +} + +// ======================================================================= +// function : OSD_ThreadPool +// purpose : +// ======================================================================= +OSD_ThreadPool::OSD_ThreadPool (int theNbThreads) +: myNbDefThreads (0), + myShutDown (false) +{ + Init (theNbThreads); + myNbDefThreads = NbThreads(); +} + +// ======================================================================= +// function : IsInUse +// purpose : +// ======================================================================= +bool OSD_ThreadPool::IsInUse() +{ + for (NCollection_Array1::Iterator aThreadIter (myThreads); + aThreadIter.More(); aThreadIter.Next()) + { + EnumeratedThread& aThread = aThreadIter.ChangeValue(); + if (!aThread.Lock()) + { + return true; + } + aThread.Free(); + } + return false; +} + +// ======================================================================= +// function : Init +// purpose : +// ======================================================================= +void OSD_ThreadPool::Init (int theNbThreads) +{ + const int aNbThreads = Max (0, (theNbThreads > 0 ? theNbThreads : OSD_Parallel::NbLogicalProcessors()) - 1); + if (myThreads.Size() == aNbThreads) + { + return; + } + + // release old threads + if (!myThreads.IsEmpty()) + { + NCollection_Array1 aLockThreads (myThreads.Lower(), myThreads.Upper()); + aLockThreads.Init (NULL); + int aThreadIndex = myThreads.Lower(); + for (NCollection_Array1::Iterator aThreadIter (myThreads); + aThreadIter.More(); aThreadIter.Next()) + { + EnumeratedThread& aThread = aThreadIter.ChangeValue(); + if (!aThread.Lock()) + { + for (NCollection_Array1::Iterator aLockThreadIter (aLockThreads); + aLockThreadIter.More() && aLockThreadIter.Value() != NULL; 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::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 anEmpty; + myThreads.Move (anEmpty); + } +} + +// ======================================================================= +// function : ~OSD_ThreadPool +// purpose : +// ======================================================================= +OSD_ThreadPool::~OSD_ThreadPool() +{ + release(); +} + +// ======================================================================= +// function : release +// purpose : +// ======================================================================= +void OSD_ThreadPool::release() +{ + if (myThreads.IsEmpty()) + { + return; + } + + myShutDown = true; + for (NCollection_Array1::Iterator aThreadIter (myThreads); + aThreadIter.More(); aThreadIter.Next()) + { + aThreadIter.ChangeValue().WakeUp (NULL, false); + aThreadIter.ChangeValue().Wait(); + } +} + +// ======================================================================= +// function : perform +// purpose : +// ======================================================================= +void OSD_ThreadPool::Launcher::perform (JobInterface& theJob) +{ + run (theJob); + wait(); +} + +// ======================================================================= +// function : run +// purpose : +// ======================================================================= +void OSD_ThreadPool::Launcher::run (JobInterface& theJob) +{ + bool toCatchFpe = OSD::ToCatchFloatingSignals(); + for (NCollection_Array1::Iterator aThreadIter (myThreads); + aThreadIter.More() && aThreadIter.Value() != NULL; aThreadIter.Next()) + { + aThreadIter.ChangeValue()->WakeUp (&theJob, toCatchFpe); + } +} + +// ======================================================================= +// function : wait +// purpose : +// ======================================================================= +void OSD_ThreadPool::Launcher::wait() +{ + int aNbFailures = 0; + for (NCollection_Array1::Iterator aThreadIter (myThreads); + aThreadIter.More() && aThreadIter.Value() != NULL; aThreadIter.Next()) + { + aThreadIter.ChangeValue()->WaitIdle(); + if (!aThreadIter.Value()->myFailure.IsNull()) + { + ++aNbFailures; + } + } + if (aNbFailures == 0) + { + return; + } + + TCollection_AsciiString aFailures; + for (NCollection_Array1::Iterator aThreadIter (myThreads); + aThreadIter.More() && aThreadIter.Value() != NULL; aThreadIter.Next()) + { + if (!aThreadIter.Value()->myFailure.IsNull()) + { + if (aNbFailures == 1) + { + aThreadIter.Value()->myFailure->Reraise(); + } + + if (!aFailures.IsEmpty()) + { + aFailures += "\n"; + } + aFailures += aThreadIter.Value()->myFailure->GetMessageString(); + } + } + + aFailures = TCollection_AsciiString("Multiple exceptions:\n") + aFailures; + throw Standard_ProgramError (aFailures.ToCString()); +} + +// ======================================================================= +// function : performJob +// purpose : +// ======================================================================= +void OSD_ThreadPool::performJob (Handle(Standard_Failure)& 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.DynamicType()->Name()) + + ": " + aFailure.GetMessageString(); + theFailure = new Standard_ProgramError (aMsg.ToCString()); + } + catch (std::exception& anStdException) + { + TCollection_AsciiString aMsg = TCollection_AsciiString (typeid(anStdException).name()) + + ": " + anStdException.what(); + theFailure = new Standard_ProgramError (aMsg.ToCString()); + } + catch (...) + { + theFailure = new Standard_ProgramError ("Error: Unknown exception"); + } +} + +// ======================================================================= +// function : performThread +// purpose : +// ======================================================================= +void OSD_ThreadPool::EnumeratedThread::performThread() +{ + OSD::SetSignal (false); + for (;;) + { + myWakeEvent.Wait(); + myWakeEvent.Reset(); + if (myPool->myShutDown) + { + return; + } + + myFailure.Nullify(); + if (myJob != NULL) + { + OSD::SetSignal (myToCatchFpe); + OSD_ThreadPool::performJob (myFailure, myJob, myThreadIndex); + myJob = NULL; + } + myIdleEvent.Set(); + } +} + +// ======================================================================= +// function : runThread +// purpose : +// ======================================================================= +Standard_Address OSD_ThreadPool::EnumeratedThread::runThread (Standard_Address theTask) +{ + EnumeratedThread* aThread = static_cast(theTask); + aThread->performThread(); + return NULL; +} + +// ======================================================================= +// function : Launcher +// purpose : +// ======================================================================= +OSD_ThreadPool::Launcher::Launcher (OSD_ThreadPool& thePool, Standard_Integer theMaxThreads) +: mySelfThread (true), + myNbThreads (0) +{ + const int aNbThreads = theMaxThreads > 0 + ? Min (theMaxThreads, thePool.NbThreads()) + : (theMaxThreads < 0 + ? Max (thePool.NbDefaultThreadsToLaunch(), 1) + : 1); + myThreads.Resize (0, aNbThreads - 1, false); + myThreads.Init (NULL); + if (aNbThreads > 1) + { + for (NCollection_Array1::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; +} + +// ======================================================================= +// function : Release +// purpose : +// ======================================================================= +void OSD_ThreadPool::Launcher::Release() +{ + for (NCollection_Array1::Iterator aThreadIter (myThreads); + aThreadIter.More() && aThreadIter.Value() != NULL; aThreadIter.Next()) + { + if (aThreadIter.Value() != &mySelfThread) + { + aThreadIter.Value()->Free(); + } + } + + NCollection_Array1 anEmpty; + myThreads.Move (anEmpty); + myNbThreads = 0; +} diff --git a/src/OSD/OSD_ThreadPool.hxx b/src/OSD/OSD_ThreadPool.hxx new file mode 100644 index 0000000000..8b6dab6ea3 --- /dev/null +++ b/src/OSD/OSD_ThreadPool.hxx @@ -0,0 +1,301 @@ +// Created by: Kirill Gavrilov +// Copyright (c) 2017 OPEN CASCADE SAS +// +// This file is part of commercial software by OPEN CASCADE SAS. +// +// This software is furnished in accordance with the terms and conditions +// of the contract and with the inclusion of this copyright notice. +// This software or any other copy thereof may not be provided or otherwise +// be made available to any third party. +// No ownership title to the software is transferred hereby. +// +// OPEN CASCADE SAS makes no representation or warranties with respect to the +// performance of this software, and specifically disclaims any responsibility +// for any damages, special or consequential, connected with its use. + +#ifndef _OSD_ThreadPool_HeaderFile +#define _OSD_ThreadPool_HeaderFile + +#include +#include +#include +#include +#include +#include + +//! Class defining a thread pool for executing algorithms in multi-threaded mode. +//! Thread pool allocates requested amount of threads and keep them alive +//! (in sleep mode when unused) during thread pool lifetime. +//! The same pool can be used by multiple consumers, +//! including nested multi-threading algorithms and concurrent threads: +//! - Thread pool can be used either by multi-threaded algorithm by creating OSD_ThreadPool::Launcher. +//! The functor performing a job takes two parameters - Thread Index and Data Index: +//! void operator(int theThreadIndex, int theDataIndex){} +//! Multi-threaded algorithm may rely on Thread Index for allocating thread-local variables in array form, +//! since the Thread Index is guaranteed to be within range OSD_ThreadPool::Lower() and OSD_ThreadPool::Upper(). +//! - Default thread pool (OSD_ThreadPool::DefaultPool()) can be used in general case, +//! but application may prefer creating a dedicated pool for better control. +//! - Default thread pool allocates the amount of threads considering concurrency +//! level of the system (amount of logical processors). +//! This can be overridden during OSD_ThreadPool construction or by calling OSD_ThreadPool::Init() +//! (the pool should not be used!). +//! - OSD_ThreadPool::Launcher reserves specific amount of threads from the pool for executing multi-threaded Job. +//! Normally, single Launcher instance will occupy all threads available in thread pool, +//! so that nested multi-threaded algorithms (within the same thread) +//! and concurrent threads trying to use the same thread pool will run sequentially. +//! This behavior is affected by OSD_ThreadPool::NbDefaultThreadsToLaunch() parameter +//! and Launcher constructor, so that single Launcher instance will occupy not all threads +//! in the pool allowing other threads to be used concurrently. +//! - OSD_ThreadPool::Launcher locks thread one-by-one from thread pool in a thread-safe way. +//! - Each working thread catches exceptions occurred during job execution, and Launcher will +//! throw Standard_Failure in a caller thread on completed execution. +class OSD_ThreadPool : public Standard_Transient +{ + DEFINE_STANDARD_RTTIEXT(OSD_ThreadPool, Standard_Transient) +public: + + //! Return (or create) a default thread pool. + //! Number of threads argument will be considered only when called first time. + Standard_EXPORT static const Handle(OSD_ThreadPool)& DefaultPool (int theNbThreads = -1); + +public: + + //! Main constructor. + //! Application may consider specifying more threads than actually + //! available (OSD_Parallel::NbLogicalProcessors()) and set up NbDefaultThreadsToLaunch() to a smaller value + //! so that concurrent threads will be able using single Thread Pool instance more efficiently. + //! @param theNbThreads threads number to be created by pool + //! (if -1 is specified then OSD_Parallel::NbLogicalProcessors() will be used) + Standard_EXPORT OSD_ThreadPool (int theNbThreads = -1); + + //! Destructor. + Standard_EXPORT virtual ~OSD_ThreadPool(); + + //! Return TRUE if at least 2 threads are available (including self-thread). + bool HasThreads() const { return NbThreads() >= 2; } + + //! Return the lower thread index. + int LowerThreadIndex() const { return 0; } + + //! Return the upper thread index (last index is reserved for self-thread). + int UpperThreadIndex() const { return LowerThreadIndex() + myThreads.Size(); } + + //! Return the number of threads; >= 1. + int NbThreads() const { return myThreads.Size() + 1; } + + //! Return maximum number of threads to be locked by a single Launcher object by default; + //! the entire thread pool size is returned by default. + int NbDefaultThreadsToLaunch() const { return myNbDefThreads; } + + //! Set maximum number of threads to be locked by a single Launcher object by default. + //! Should be set BEFORE first usage. + void SetNbDefaultThreadsToLaunch (int theNbThreads) { myNbDefThreads = theNbThreads; } + + //! Checks if thread pools has active consumers. + Standard_EXPORT bool IsInUse(); + + //! Reinitialize the thread pool with a different number of threads. + //! Should be called only with no active jobs, or exception Standard_ProgramError will be thrown! + Standard_EXPORT void Init (int theNbThreads); + +protected: + + //! Thread function interface. + class JobInterface + { + public: + virtual void Perform (int theThreadIndex) = 0; + }; + + //! Thread with back reference to thread pool and thread index in it. + class EnumeratedThread : public OSD_Thread + { + friend class OSD_ThreadPool; + public: + EnumeratedThread (bool theIsSelfThread = false) + : myPool (NULL), myJob (NULL), myWakeEvent (false), + myIdleEvent (false), myThreadIndex (0), myUsageCounter(0), + myIsStarted (false), myToCatchFpe (false), + myIsSelfThread (theIsSelfThread) {} + + //! Occupy this thread for thread pool launcher. + //! @return TRUE on success, or FALSE if thread has been already occupied + Standard_EXPORT bool Lock(); + + //! Release this thread for thread pool launcher; should be called only after successful OccupyThread(). + Standard_EXPORT void Free(); + + //! Wake up the thread. + Standard_EXPORT void WakeUp (JobInterface* theJob, bool theToCatchFpe); + + //! Wait the thread going into Idle state (finished jobs). + Standard_EXPORT void WaitIdle(); + + private: + + //! Method is executed in the context of thread. + void performThread(); + + //! Method is executed in the context of thread. + static Standard_Address runThread (Standard_Address theTask); + + private: + OSD_ThreadPool* myPool; + JobInterface* myJob; + Handle(Standard_Failure) myFailure; + Standard_Condition myWakeEvent; + Standard_Condition myIdleEvent; + int myThreadIndex; + volatile int myUsageCounter; + bool myIsStarted; + bool myToCatchFpe; + bool myIsSelfThread; + }; + +public: + + //! Launcher object locking a subset of threads (or all threads) + //! in a thread pool to perform parallel execution of the job. + class Launcher + { + public: + //! Lock specified number of threads from the thread pool. + //! If thread pool is already locked by another user, + //! Launcher will lock as many threads as possible + //! (if none will be locked, then single threaded execution will be done). + //! @param thePool thread pool to lock the threads + //! @param theMaxThreads number of threads to lock; + //! -1 specifies that default number of threads + //! to be used OSD_ThreadPool::NbDefaultThreadsToLaunch() + Standard_EXPORT Launcher (OSD_ThreadPool& thePool, int theMaxThreads = -1); + + //! Release threads. + ~Launcher() { Release(); } + + //! Return TRUE if at least 2 threads have been locked for parallel execution (including self-thread); + //! otherwise, the functor will be executed within the caller thread. + bool HasThreads() const { return myNbThreads >= 2; } + + //! Return amount of locked threads; >= 1. + int NbThreads() const { return myNbThreads; } + + //! Return the lower thread index. + int LowerThreadIndex() const { return 0; } + + //! Return the upper thread index (last index is reserved for the self-thread). + int UpperThreadIndex() const { return LowerThreadIndex() + myNbThreads - 1; } + + //! Simple primitive for parallelization of "for" loops, e.g.: + //! @code + //! for (int anIter = theBegin; anIter < theEnd; ++anIter) {} + //! @endcode + //! @param theBegin the first data index (inclusive) + //! @param theEnd the last data index (exclusive) + //! @param theFunctor functor providing an interface + //! "void operator(int theThreadIndex, int theDataIndex){}" performing task for specified index + template + void Perform (int theBegin, int theEnd, const Functor& theFunctor) + { + JobRange aData (theBegin, theEnd); + Job aJob (theFunctor, aData); + perform (aJob); + } + + //! Release threads before Launcher destruction. + Standard_EXPORT void Release(); + + protected: + + //! Execute job. + Standard_EXPORT void perform (JobInterface& theJob); + + //! Initialize job and start threads. + Standard_EXPORT void run (JobInterface& theJob); + + //! Wait threads execution. + Standard_EXPORT void wait(); + + private: + Launcher (const Launcher& theCopy); + Launcher& operator=(const Launcher& theCopy); + + private: + NCollection_Array1 myThreads; //!< array of locked threads (including self-thread) + EnumeratedThread mySelfThread; + int myNbThreads; //!< amount of locked threads + }; + +protected: + + //! Auxiliary class which ensures exclusive access to iterators of processed data pool. + class JobRange + { + public: + + //! Constructor + JobRange (const int& theBegin, const int& theEnd) : myBegin(theBegin), myEnd (theEnd), myIt (theBegin) {} + + //! Returns const link on the first element. + const int& Begin() const { return myBegin; } + + //! Returns const link on the last element. + const int& End() const { return myEnd; } + + //! Returns first non processed element or end. + //! Thread-safe method. + int It() const { return Standard_Atomic_Increment (reinterpret_cast(&myIt)) - 1; } + + private: + JobRange (const JobRange& theCopy); + JobRange& operator=(const JobRange& theCopy); + + private: + const int& myBegin; //!< First element of range + const int& myEnd; //!< Last element of range + mutable int myIt; //!< First non processed element of range + }; + + //! Auxiliary wrapper class for thread function. + template class Job : public JobInterface + { + public: + + //! Constructor. + Job (const FunctorT& thePerformer, JobRange& theRange) + : myPerformer (thePerformer), myRange (theRange) {} + + //! Method is executed in the context of thread. + virtual void Perform (int theThreadIndex) Standard_OVERRIDE + { + for (Standard_Integer anIter = myRange.It(); anIter < myRange.End(); anIter = myRange.It()) + { + myPerformer (theThreadIndex, anIter); + } + } + + private: + Job (const Job& theCopy); + Job& operator=(const Job& theCopy); + + private: //! @name private fields + const FunctorT& myPerformer; //!< Link on functor + const JobRange& myRange; //!< Link on processed data block + }; + + //! Release threads. + void release(); + + //! Perform the job and catch exceptions. + static void performJob (Handle(Standard_Failure)& theFailure, + OSD_ThreadPool::JobInterface* theJob, + int theThreadIndex); + +private: + + NCollection_Array1 myThreads; //!< array of defined threads (excluding self-thread) + int myNbDefThreads; //!< maximum number of threads to be locked by a single Launcher by default + bool myShutDown; //!< flag to shut down (destroy) the thread pool + +}; + +#endif // _OSD_ThreadPool_HeaderFile diff --git a/src/OSD/OSD_signal.cxx b/src/OSD/OSD_signal.cxx index 10a9c425ab..06b6394039 100644 --- a/src/OSD/OSD_signal.cxx +++ b/src/OSD/OSD_signal.cxx @@ -17,6 +17,17 @@ #include #include +static Standard_THREADLOCAL Standard_Boolean fFltExceptions = Standard_False; + +//======================================================================= +//function : ToCatchFloatingSignals +//purpose : +//======================================================================= +Standard_Boolean OSD::ToCatchFloatingSignals() +{ + return fFltExceptions; +} + #ifdef _WIN32 //---------------------------- Windows NT System -------------------------------- @@ -53,7 +64,7 @@ #include #include -#include +#include #ifdef _MSC_VER #include @@ -65,10 +76,8 @@ #include static Standard_Boolean fCtrlBrk; -#if !defined(__CYGWIN32__) && !defined(__MINGW32__) + static Standard_Boolean fMsgBox; -static Standard_Boolean fFltExceptions; -static Standard_Boolean fDbgLoaded; // used to forbid simultaneous execution of setting / executing handlers static Standard_Mutex THE_SIGNAL_MUTEX; @@ -76,13 +85,20 @@ static Standard_Mutex THE_SIGNAL_MUTEX; static LONG __fastcall _osd_raise ( DWORD, LPSTR ); static BOOL WINAPI _osd_ctrl_break_handler ( DWORD ); -#ifndef OCCT_UWP +#if ! defined(OCCT_UWP) && !defined(__MINGW32__) && !defined(__CYGWIN32__) +static Standard_Boolean fDbgLoaded; static LONG _osd_debug ( void ); #endif //# define _OSD_FPX ( _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW ) # define _OSD_FPX ( _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW ) +#ifdef OCC_CONVERT_SIGNALS +#define THROW_OR_JUMP(Type,Message) Type::NewInstance(Message)->Jump() +#else +#define THROW_OR_JUMP(Type,Message) throw Type(Message) +#endif + //======================================================================= //function : CallHandler //purpose : @@ -148,7 +164,8 @@ static LONG CallHandler (DWORD dwExceptionCode, break ; case STATUS_NO_MEMORY: // cout << "CallHandler : STATUS_NO_MEMORY:" << endl ; - throw OSD_Exception_STATUS_NO_MEMORY ( "MEMORY ALLOCATION ERROR ( no room in the process heap )" ); + THROW_OR_JUMP (OSD_Exception_STATUS_NO_MEMORY, "MEMORY ALLOCATION ERROR ( no room in the process heap )"); + break; case EXCEPTION_ACCESS_VIOLATION: // cout << "CallHandler : EXCEPTION_ACCESS_VIOLATION:" << endl ; StringCchPrintfW (buffer, _countof(buffer), L"%s%s%s0x%.8p%s%s%s", L"ACCESS VIOLATION", @@ -227,7 +244,7 @@ static LONG CallHandler (DWORD dwExceptionCode, _fpreset(); _clearfp(); -#ifndef OCCT_UWP +#if ! defined(OCCT_UWP) && !defined(__MINGW32__) && !defined(__CYGWIN32__) MessageBeep ( MB_ICONHAND ); int aChoice = ::MessageBoxW (0, buffer, L"OCCT Exception Handler", MB_ABORTRETRYIGNORE | MB_ICONSTOP); if (aChoice == IDRETRY) @@ -287,7 +304,7 @@ static void SIGWntHandler (int signum, int sub_code) break ; default: cout << "SIGWntHandler(default) -> throw Standard_NumericError(\"Floating Point Error\");" << endl; - throw Standard_NumericError("Floating Point Error"); + THROW_OR_JUMP (Standard_NumericError, "Floating Point Error"); break ; } break ; @@ -309,7 +326,6 @@ static void SIGWntHandler (int signum, int sub_code) DebugBreak (); #endif } -#endif //======================================================================= //function : TranslateSE @@ -342,7 +358,6 @@ static void TranslateSE( unsigned int theCode, EXCEPTION_POINTERS* theExcPtr ) // option and unless user sets his own exception handler with // ::SetUnhandledExceptionFilter(). //======================================================================= -#if !defined(__CYGWIN32__) && !defined(__MINGW32__) static LONG WINAPI WntHandler (EXCEPTION_POINTERS *lpXP) { DWORD dwExceptionCode = lpXP->ExceptionRecord->ExceptionCode; @@ -351,7 +366,6 @@ static LONG WINAPI WntHandler (EXCEPTION_POINTERS *lpXP) lpXP->ExceptionRecord->ExceptionInformation[1], lpXP->ExceptionRecord->ExceptionInformation[0]); } -#endif //======================================================================= //function : SetSignal @@ -359,11 +373,8 @@ static LONG WINAPI WntHandler (EXCEPTION_POINTERS *lpXP) //======================================================================= void OSD::SetSignal (const Standard_Boolean theFloatingSignal) { -#if !defined(__CYGWIN32__) && !defined(__MINGW32__) Standard_Mutex::Sentry aSentry (THE_SIGNAL_MUTEX); // lock the mutex to prevent simultaneous handling #if !defined(OCCT_UWP) || defined(NTDDI_WIN10_TH2) - LPTOP_LEVEL_EXCEPTION_FILTER aPreviousFilter; - OSD_Environment env ("CSF_DEBUG_MODE"); TCollection_AsciiString val = env.Value(); if (!env.Failed()) @@ -380,7 +391,7 @@ void OSD::SetSignal (const Standard_Boolean theFloatingSignal) // when user's code is compiled with /EHs // Replaces the existing top-level exception filter for all existing and all future threads // in the calling process - aPreviousFilter = ::SetUnhandledExceptionFilter (/*(LPTOP_LEVEL_EXCEPTION_FILTER)*/ WntHandler); + ::SetUnhandledExceptionFilter (/*(LPTOP_LEVEL_EXCEPTION_FILTER)*/ WntHandler); #endif // NTDDI_WIN10_TH2 // Signal handlers will only be used when the method ::raise() will be used @@ -410,7 +421,6 @@ void OSD::SetSignal (const Standard_Boolean theFloatingSignal) else { _controlfp (_OSD_FPX, _OSD_FPX); // JR add : } -#endif } // end OSD :: SetSignal //============================================================================ @@ -422,7 +432,7 @@ void OSD::ControlBreak () { throw OSD_Exception_CTRL_BREAK ( "*** INTERRUPT ***" ); } } // end OSD :: ControlBreak -#if !defined(__MINGW32__) && !defined(__CYGWIN32__) + #ifndef OCCT_UWP //============================================================================ //==== _osd_ctrl_break_handler @@ -437,6 +447,7 @@ static BOOL WINAPI _osd_ctrl_break_handler ( DWORD dwCode ) { return TRUE; } // end _osd_ctrl_break_handler #endif + //============================================================================ //==== _osd_raise //============================================================================ @@ -447,54 +458,54 @@ static LONG __fastcall _osd_raise ( DWORD dwCode, LPSTR msg ) switch (dwCode) { case EXCEPTION_ACCESS_VIOLATION: - throw OSD_Exception_ACCESS_VIOLATION(msg); + THROW_OR_JUMP (OSD_Exception_ACCESS_VIOLATION, msg); break; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - throw OSD_Exception_ARRAY_BOUNDS_EXCEEDED(msg); + THROW_OR_JUMP (OSD_Exception_ARRAY_BOUNDS_EXCEEDED, msg); break; case EXCEPTION_DATATYPE_MISALIGNMENT: - throw Standard_ProgramError(msg); + THROW_OR_JUMP (Standard_ProgramError, msg); break; case EXCEPTION_ILLEGAL_INSTRUCTION: - throw OSD_Exception_ILLEGAL_INSTRUCTION(msg); + THROW_OR_JUMP (OSD_Exception_ILLEGAL_INSTRUCTION, msg); break; case EXCEPTION_IN_PAGE_ERROR: - throw OSD_Exception_IN_PAGE_ERROR(msg); + THROW_OR_JUMP (OSD_Exception_IN_PAGE_ERROR, msg); break; case EXCEPTION_INT_DIVIDE_BY_ZERO: - throw Standard_DivideByZero(msg); + THROW_OR_JUMP (Standard_DivideByZero, msg); break; case EXCEPTION_INT_OVERFLOW: - throw OSD_Exception_INT_OVERFLOW(msg); + THROW_OR_JUMP (OSD_Exception_INT_OVERFLOW, msg); break; case EXCEPTION_INVALID_DISPOSITION: - throw OSD_Exception_INVALID_DISPOSITION(msg); + THROW_OR_JUMP (OSD_Exception_INVALID_DISPOSITION, msg); break; case EXCEPTION_NONCONTINUABLE_EXCEPTION: - throw OSD_Exception_NONCONTINUABLE_EXCEPTION(msg); + THROW_OR_JUMP (OSD_Exception_NONCONTINUABLE_EXCEPTION, msg); break; case EXCEPTION_PRIV_INSTRUCTION: - throw OSD_Exception_PRIV_INSTRUCTION(msg); + THROW_OR_JUMP (OSD_Exception_PRIV_INSTRUCTION, msg); break; case EXCEPTION_STACK_OVERFLOW: - throw OSD_Exception_STACK_OVERFLOW(msg); + THROW_OR_JUMP (OSD_Exception_STACK_OVERFLOW, msg); break; case EXCEPTION_FLT_DIVIDE_BY_ZERO: - throw Standard_DivideByZero(msg); + THROW_OR_JUMP (Standard_DivideByZero, msg); break; case EXCEPTION_FLT_STACK_CHECK: case EXCEPTION_FLT_OVERFLOW: - throw Standard_Overflow(msg); + THROW_OR_JUMP (Standard_Overflow, msg); break; case EXCEPTION_FLT_UNDERFLOW: - throw Standard_Underflow(msg); + THROW_OR_JUMP (Standard_Underflow, msg); break; case EXCEPTION_FLT_INVALID_OPERATION: case EXCEPTION_FLT_DENORMAL_OPERAND: case EXCEPTION_FLT_INEXACT_RESULT: case STATUS_FLOAT_MULTIPLE_TRAPS: case STATUS_FLOAT_MULTIPLE_FAULTS: - throw Standard_NumericError(msg); + THROW_OR_JUMP (Standard_NumericError, msg); break; default: break; @@ -502,10 +513,10 @@ static LONG __fastcall _osd_raise ( DWORD dwCode, LPSTR msg ) return EXCEPTION_EXECUTE_HANDLER; } // end _osd_raise +#if ! defined(OCCT_UWP) && !defined(__MINGW32__) && !defined(__CYGWIN32__) //============================================================================ //==== _osd_debug //============================================================================ -#ifndef OCCT_UWP LONG _osd_debug ( void ) { LONG action ; @@ -588,10 +599,9 @@ LONG _osd_debug ( void ) { return action ; } // end _osd_debug +#endif /* ! OCCT_UWP && ! __CYGWIN__ && ! __MINGW32__ */ -#endif -#endif -#else +#else /* ! _WIN32 */ //---------- All Systems except Windows NT : ---------------------------------- @@ -616,7 +626,6 @@ LONG _osd_debug ( void ) { #ifdef __linux__ #include //#include -static Standard_Boolean fFltExceptions = Standard_False; #endif // variable signalling that Control-C has been pressed (SIGINT signal) @@ -635,7 +644,7 @@ typedef void (* SIG_PFV) (int); # include # include #else -# ifdef SA_SIGINFO +# ifdef SA_SIGINFO # ifndef _AIX # include # endif @@ -866,11 +875,11 @@ static void SegvHandler(const int theSignal, #endif //============================================================================ -//==== SetSignal +//==== SetSignal //==== Set the differents signals: //============================================================================ -void OSD::SetSignal(const Standard_Boolean aFloatingSignal) +void OSD::SetSignal(const Standard_Boolean aFloatingSignal) { struct sigaction act, oact; int stat = 0; @@ -932,7 +941,7 @@ void OSD::SetSignal(const Standard_Boolean aFloatingSignal) #endif //==== Always detected the signal "SIGFPE" ================================= - stat = sigaction(SIGFPE,&act,&oact); // ...... floating point exception + stat = sigaction(SIGFPE,&act,&oact); // ...... floating point exception if (stat) { #ifdef OCCT_DEBUG cerr << "sigaction does not work !!! KO " << endl; @@ -941,38 +950,38 @@ void OSD::SetSignal(const Standard_Boolean aFloatingSignal) } //==== Detected the only the "free" signals ================================ - sigaction(SIGHUP,&act,&oact); // ...... hangup + sigaction(SIGHUP,&act,&oact); // ...... hangup #ifdef OBJS - if(oact.sa_handler) + if(oact.sa_handler) sigaction(SIGHUP,&oact,&oact); #endif - sigaction(SIGINT,&act,&oact); // ...... interrupt + sigaction(SIGINT,&act,&oact); // ...... interrupt #ifdef OBJS - if(oact.sa_handler) + if(oact.sa_handler) sigaction(SIGINT,&oact,&oact); #endif - + sigaction(SIGQUIT,&act,&oact); // ...... quit #ifdef OBJS - if(oact.sa_handler) + if(oact.sa_handler) sigaction(SIGQUIT,&oact,&oact); #endif - sigaction(SIGILL,&act,&oact); // ...... illegal instruction + sigaction(SIGILL,&act,&oact); // ...... illegal instruction #ifdef OBJS - if(oact.sa_handler) + if(oact.sa_handler) sigaction(SIGILL,&oact,&oact); #endif - sigaction(SIGBUS,&act,&oact); // ...... bus error + sigaction(SIGBUS,&act,&oact); // ...... bus error #ifdef OBJS - if(oact.sa_handler) + if(oact.sa_handler) sigaction(SIGBUS,&oact,&oact); #endif @@ -980,7 +989,7 @@ void OSD::SetSignal(const Standard_Boolean aFloatingSignal) sigaction(SIGSYS,&act,&oact); // ...... bad argument to system call # ifdef OBJS - if(oact.sa_handler) + if(oact.sa_handler) sigaction(SIGSYS,&oact,&oact); # endif #endif @@ -989,7 +998,7 @@ void OSD::SetSignal(const Standard_Boolean aFloatingSignal) sigaction(SIGTRAP,&act,&oact); // Integer Divide By Zero (IRIX) # ifdef OBJS - if(oact.sa_handler) + if(oact.sa_handler) sigaction(SIGTRAP,&oact,&oact); # endif #endif @@ -1004,7 +1013,7 @@ void OSD::SetSignal(const Standard_Boolean aFloatingSignal) perror("OSD::SetSignal sigaction( SIGSEGV , &act , &oact ) ") ; #ifdef OBJS - if(oact.sa_handler) + if(oact.sa_handler) sigaction(SIGSEGV,&oact,&oact); #endif #if defined(__osf__) || defined(DECOSF1) @@ -1012,7 +1021,7 @@ void OSD::SetSignal(const Standard_Boolean aFloatingSignal) action.sa_handler = SIG_IGN; action.sa_mask = 0; action.sa_flags = 0; - + if (sigaction (SIGFPE, &action, &prev_action) == -1) { perror ("sigaction"); exit (1); @@ -1022,14 +1031,14 @@ void OSD::SetSignal(const Standard_Boolean aFloatingSignal) } //============================================================================ -//==== ControlBreak +//==== ControlBreak //============================================================================ -void OSD :: ControlBreak () +void OSD :: ControlBreak () { if ( fCtrlBrk ) { fCtrlBrk = Standard_False; - throw OSD_Exception_CTRL_BREAK("*** INTERRUPT ***"); + throw OSD_Exception_CTRL_BREAK ("*** INTERRUPT ***"); } } diff --git a/src/Standard/FILES b/src/Standard/FILES index da33f5a0e9..1d752bb89f 100755 --- a/src/Standard/FILES +++ b/src/Standard/FILES @@ -11,6 +11,8 @@ Standard_Byte.hxx Standard_Character.hxx Standard_CLocaleSentry.cxx Standard_CLocaleSentry.hxx +Standard_Condition.cxx +Standard_Condition.hxx Standard_ConstructionError.hxx Standard_Copy.tcl Standard_CString.cxx @@ -79,9 +81,9 @@ Standard_Real.hxx Standard_ShortReal.cxx Standard_ShortReal.hxx Standard_Size.hxx -Standard_SStream.cxx Standard_SStream.hxx Standard_Stream.hxx +Standard_Strtod.cxx Standard_ThreadId.hxx Standard_Time.hxx Standard_TooManyUsers.hxx @@ -92,7 +94,8 @@ Standard_Type.hxx Standard_TypeDef.hxx Standard_TypeMismatch.hxx Standard_Underflow.hxx -Standard_UUID.cxx Standard_UUID.hxx Standard_values.h Standard_Version.hxx +Standard_WarningsDisable.hxx +Standard_WarningsRestore.hxx \ No newline at end of file diff --git a/src/Standard/Standard_Atomic.hxx b/src/Standard/Standard_Atomic.hxx index a5b5944031..678e211dc2 100644 --- a/src/Standard/Standard_Atomic.hxx +++ b/src/Standard/Standard_Atomic.hxx @@ -35,6 +35,14 @@ inline int Standard_Atomic_Increment (volatile int* theValue); //! and returns resulting decremented value. inline int Standard_Atomic_Decrement (volatile int* theValue); +//! Perform an atomic compare and swap. +//! That is, if the current value of *theValue is theOldValue, then write theNewValue into *theValue. +//! @param theValue pointer to variable to modify +//! @param theOldValue expected value to perform modification +//! @param theNewValue new value to set in case if *theValue was equal to theOldValue +//! @return TRUE if theNewValue has been set to *theValue +inline bool Standard_Atomic_CompareAndSwap (volatile int* theValue, int theOldValue, int theNewValue); + // Platform-dependent implementation #if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) // gcc explicitly defines the macros __GCC_HAVE_SYNC_COMPARE_AND_SWAP_* @@ -55,16 +63,23 @@ int Standard_Atomic_Decrement (volatile int* theValue) return __sync_sub_and_fetch (theValue, 1); } +bool Standard_Atomic_CompareAndSwap (volatile int* theValue, int theOldValue, int theNewValue) +{ + return __sync_val_compare_and_swap (theValue, theOldValue, theNewValue) == theOldValue; +} + #elif defined(_WIN32) extern "C" { long _InterlockedIncrement (volatile long* lpAddend); long _InterlockedDecrement (volatile long* lpAddend); + long _InterlockedCompareExchange (long volatile* Destination, long Exchange, long Comparand); } -#if defined(_MSC_VER) +#if defined(_MSC_VER) && ! defined(__INTEL_COMPILER) // force intrinsic instead of WinAPI calls #pragma intrinsic (_InterlockedIncrement) #pragma intrinsic (_InterlockedDecrement) + #pragma intrinsic (_InterlockedCompareExchange) #endif // WinAPI function or MSVC intrinsic @@ -80,6 +95,11 @@ int Standard_Atomic_Decrement (volatile int* theValue) return _InterlockedDecrement (reinterpret_cast(theValue)); } +bool Standard_Atomic_CompareAndSwap (volatile int* theValue, int theOldValue, int theNewValue) +{ + return _InterlockedCompareExchange (reinterpret_cast(theValue), theNewValue, theOldValue) == theOldValue; +} + #elif defined(__APPLE__) // use atomic operations provided by MacOS @@ -95,6 +115,11 @@ int Standard_Atomic_Decrement (volatile int* theValue) return OSAtomicDecrement32Barrier (theValue); } +bool Standard_Atomic_CompareAndSwap (volatile int* theValue, int theOldValue, int theNewValue) +{ + return OSAtomicCompareAndSwapInt (theOldValue, theNewValue, theValue); +} + #elif defined(__ANDROID__) // Atomic operations that were exported by the C library didn't @@ -114,34 +139,9 @@ int Standard_Atomic_Decrement (volatile int* theValue) return __atomic_dec (theValue) - 1; // analog of __sync_fetch_and_sub } -#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64)) -// use x86 / x86_64 inline assembly (compatibility with alien compilers / old GCC) - -inline int Standard_Atomic_Add (volatile int* theValue, int theVal) +bool Standard_Atomic_CompareAndSwap (volatile int* theValue, int theOldValue, int theNewValue) { - // C equivalent: - // *theValue += theVal; - // return *theValue; - - int previous; - __asm__ __volatile__ - ( - "lock xadd %0,%1" - : "=q"(previous), "=m"(*theValue) //output - : "0"(theVal), "m"(*theValue) //input - : "memory" //clobbers - ); - return previous + theVal; -} - -int Standard_Atomic_Increment (volatile int* theValue) -{ - return Standard_Atomic_Add (theValue, 1); -} - -int Standard_Atomic_Decrement (volatile int* theValue) -{ - return Standard_Atomic_Add (theValue, -1); + return __atomic_cmpxchg (theOldValue, theNewValue, theValue) == 0; } #else @@ -159,6 +159,16 @@ int Standard_Atomic_Decrement (volatile int* theValue) return --(*theValue); } +bool Standard_Atomic_CompareAndSwap (volatile int* theValue, int theOldValue, int theNewValue) +{ + if (*theValue == theOldValue) + { + *theValue = theNewValue; + return true; + } + return false; +} + #endif #endif //_Standard_Atomic_HeaderFile diff --git a/src/Standard/Standard_Condition.cxx b/src/Standard/Standard_Condition.cxx new file mode 100644 index 0000000000..232ae2b2ec --- /dev/null +++ b/src/Standard/Standard_Condition.cxx @@ -0,0 +1,207 @@ +// Created by: Kirill Gavrilov +// Copyright (c) 2018 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. + +#ifdef _WIN32 + #include +#else + #include + #include + #include + #include +#endif + +#include "Standard_Condition.hxx" + +namespace +{ +#ifndef _WIN32 + //! clock_gettime() wrapper. + static void conditionGetRealTime (struct timespec& theTime) + { + #if defined(__APPLE__) + struct timeval aTime; + gettimeofday (&aTime, NULL); + theTime.tv_sec = aTime.tv_sec; + theTime.tv_nsec = aTime.tv_usec * 1000; + #else + clock_gettime (CLOCK_REALTIME, &theTime); + #endif + } +#endif +} + +// ======================================================================= +// function : Standard_Condition +// purpose : +// ======================================================================= +Standard_Condition::Standard_Condition (bool theIsSet) +#ifdef _WIN32 +: myEvent((void* )::CreateEvent (0, true, theIsSet, NULL)) +#else +: myFlag (theIsSet) +#endif +{ +#ifndef _WIN32 + pthread_mutex_init(&myMutex, 0); + pthread_cond_init (&myCond, 0); +#endif +} + +// ======================================================================= +// function : ~Standard_Condition +// purpose : +// ======================================================================= +Standard_Condition::~Standard_Condition() +{ +#ifdef _WIN32 + ::CloseHandle ((HANDLE )myEvent); +#else + pthread_mutex_destroy(&myMutex); + pthread_cond_destroy (&myCond); +#endif +} + +// ======================================================================= +// function : Set +// purpose : +// ======================================================================= +void Standard_Condition::Set() +{ +#ifdef _WIN32 + ::SetEvent ((HANDLE )myEvent); +#else + pthread_mutex_lock(&myMutex); + myFlag = true; + pthread_cond_broadcast(&myCond); + pthread_mutex_unlock (&myMutex); +#endif +} + +// ======================================================================= +// function : Reset +// purpose : +// ======================================================================= +void Standard_Condition::Reset() +{ +#ifdef _WIN32 + ::ResetEvent ((HANDLE )myEvent); +#else + pthread_mutex_lock (&myMutex); + myFlag = false; + pthread_mutex_unlock (&myMutex); +#endif +} + +// ======================================================================= +// function : Wait +// purpose : +// ======================================================================= +void Standard_Condition::Wait() +{ +#ifdef _WIN32 + ::WaitForSingleObject ((HANDLE )myEvent, INFINITE); +#else + pthread_mutex_lock (&myMutex); + if (!myFlag) + { + pthread_cond_wait (&myCond, &myMutex); + } + pthread_mutex_unlock (&myMutex); +#endif +} + +// ======================================================================= +// function : Wait +// purpose : +// ======================================================================= +bool Standard_Condition::Wait (int theTimeMilliseconds) +{ +#ifdef _WIN32 + return (::WaitForSingleObject ((HANDLE )myEvent, (DWORD )theTimeMilliseconds) != WAIT_TIMEOUT); +#else + bool isSignalled = true; + pthread_mutex_lock (&myMutex); + if (!myFlag) + { + struct timespec aNow; + struct timespec aTimeout; + conditionGetRealTime (aNow); + aTimeout.tv_sec = (theTimeMilliseconds / 1000); + aTimeout.tv_nsec = (theTimeMilliseconds - aTimeout.tv_sec * 1000) * 1000000; + if (aTimeout.tv_nsec > 1000000000) + { + aTimeout.tv_sec += 1; + aTimeout.tv_nsec -= 1000000000; + } + aTimeout.tv_sec += aNow.tv_sec; + aTimeout.tv_nsec += aNow.tv_nsec; + isSignalled = (pthread_cond_timedwait (&myCond, &myMutex, &aTimeout) != ETIMEDOUT); + } + pthread_mutex_unlock (&myMutex); + return isSignalled; +#endif +} + +// ======================================================================= +// function : Check +// purpose : +// ======================================================================= +bool Standard_Condition::Check() +{ +#ifdef _WIN32 + return (::WaitForSingleObject ((HANDLE )myEvent, (DWORD )0) != WAIT_TIMEOUT); +#else + bool isSignalled = true; + pthread_mutex_lock (&myMutex); + if (!myFlag) + { + struct timespec aNow; + struct timespec aTimeout; + conditionGetRealTime (aNow); + aTimeout.tv_sec = aNow.tv_sec; + aTimeout.tv_nsec = aNow.tv_nsec + 100; + isSignalled = (pthread_cond_timedwait (&myCond, &myMutex, &aTimeout) != ETIMEDOUT); + } + pthread_mutex_unlock (&myMutex); + return isSignalled; +#endif +} + +// ======================================================================= +// function : CheckReset +// purpose : +// ======================================================================= +bool Standard_Condition::CheckReset() +{ +#ifdef _WIN32 + const bool wasSignalled = (::WaitForSingleObject ((HANDLE )myEvent, (DWORD )0) != WAIT_TIMEOUT); + ::ResetEvent ((HANDLE )myEvent); + return wasSignalled; +#else + pthread_mutex_lock (&myMutex); + bool wasSignalled = myFlag; + if (!myFlag) + { + struct timespec aNow; + struct timespec aTimeout; + conditionGetRealTime (aNow); + aTimeout.tv_sec = aNow.tv_sec; + aTimeout.tv_nsec = aNow.tv_nsec + 100; + wasSignalled = (pthread_cond_timedwait (&myCond, &myMutex, &aTimeout) != ETIMEDOUT); + } + myFlag = false; + pthread_mutex_unlock (&myMutex); + return wasSignalled; +#endif +} diff --git a/src/Standard/Standard_Condition.hxx b/src/Standard/Standard_Condition.hxx new file mode 100644 index 0000000000..c2f33dcdcd --- /dev/null +++ b/src/Standard/Standard_Condition.hxx @@ -0,0 +1,80 @@ +// Created by: Kirill Gavrilov +// Copyright (c) 2018 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. + +#ifndef _Standard_Condition_HeaderFile +#define _Standard_Condition_HeaderFile + +#include + +#ifndef _WIN32 + #include +#endif + +//! This is boolean flag intended for communication between threads. +//! One thread sets this flag to TRUE to indicate some event happened +//! and another thread either waits this event or checks periodically its state to perform job. +//! +//! This class provides interface similar to WinAPI Event objects. +class Standard_Condition +{ +public: + + //! Default constructor. + //! @param theIsSet Initial flag state + Standard_EXPORT Standard_Condition (bool theIsSet); + + //! Destructor. + Standard_EXPORT ~Standard_Condition(); + + //! Set event into signaling state. + Standard_EXPORT void Set(); + + //! Reset event (unset signaling state) + Standard_EXPORT void Reset(); + + //! Wait for Event (infinity). + Standard_EXPORT void Wait(); + + //! Wait for signal requested time. + //! @param theTimeMilliseconds wait limit in milliseconds + //! @return true if get event + Standard_EXPORT bool Wait (int theTimeMilliseconds); + + //! Do not wait for signal - just test it state. + //! @return true if get event + Standard_EXPORT bool Check(); + + //! Method perform two steps at-once - reset the event object + //! and returns true if it was in signaling state. + //! @return true if event object was in signaling state. + Standard_EXPORT bool CheckReset(); + +#ifdef _WIN32 + //! Access native HANDLE to Event object. + void* getHandle() const { return myEvent; } +#endif + +private: + +#ifdef _WIN32 + void* myEvent; +#else + pthread_mutex_t myMutex; + pthread_cond_t myCond; + bool myFlag; +#endif + +}; + +#endif // _Standard_Condition_HeaderFile diff --git a/src/Standard/Standard_Failure.cxx b/src/Standard/Standard_Failure.cxx index b7a4d147b6..b0b1540998 100644 --- a/src/Standard/Standard_Failure.cxx +++ b/src/Standard/Standard_Failure.cxx @@ -58,23 +58,6 @@ static void deallocate_message(Standard_CString aMessage) } } -// Define Standard_THREADLOCAL modifier as C++11 thread_local keyword where it is available. -#if defined(__clang__) - // CLang version: standard CLang > 3.3 or XCode >= 8 (but excluding 32-bit ARM) - // Note: this has to be in separate #if to avoid failure of preprocessor on other platforms - #if __has_feature(cxx_thread_local) - #define Standard_THREADLOCAL thread_local - #else - #define Standard_THREADLOCAL - #endif -#elif (defined(__INTEL_COMPILER) && __INTEL_COMPILER > 1400) || \ - (defined(_MSC_VER) && _MSC_VER >= 1900) /* MSVC++ >= 14 */ || \ - (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) /* GCC >= 4.8 */ - #define Standard_THREADLOCAL thread_local -#else - #define Standard_THREADLOCAL -#endif - // ****************************************************************** // Standard_Failure * // ****************************************************************** diff --git a/src/Standard/Standard_Macro.hxx b/src/Standard/Standard_Macro.hxx index fd04320593..ebcd0f329e 100644 --- a/src/Standard/Standard_Macro.hxx +++ b/src/Standard/Standard_Macro.hxx @@ -12,13 +12,21 @@ // Alternatively, this file may be used under the terms of Open CASCADE // commercial license or contractual agreement. -// Purpose: This file is intended to be the first file #included to any -// Open CASCADE source. It defines platform-specific pre-processor -// macros necessary for correct compilation of Open CASCADE code +//! @file +//! This file is intended to be the first file included to any +//! Open CASCADE source. It defines platform-specific pre-processor +//! macros necessary for correct compilation of Open CASCADE code. #ifndef _Standard_Macro_HeaderFile # define _Standard_Macro_HeaderFile +//! @def Standard_OVERRIDE +//! Should be used in declarations of virtual methods overriden in the +//! derived classes, to cause compilation error in the case if that virtual +//! function disappears or changes its signature in the base class. +//! +//! Expands to C++11 keyword "override" on compilers that are known to +//! suppot it; empty in other cases. #if defined(__cplusplus) && (__cplusplus >= 201100L) // part of C++11 standard #define Standard_OVERRIDE override @@ -29,16 +37,69 @@ #define Standard_OVERRIDE #endif -// Macro for marking variables / functions as possibly unused -// so that compiler will not emit redundant "unused" warnings. +//! @def Standard_FALLTHROUGH +//! Should be used in a switch statement immediately before a case label, +//! if code associated with the previous case label may fall through to that +//! next label (i.e. does not end with "break" or "return" etc.). +//! This macro indicates that the fall through is intentional and should not be +//! diagnosed by a compiler that warns on fallthrough. +//! +//! Expands to C++17 attribute statement "[[fallthrough]];" on compilers that +//! declare support of C++17, or to "__attribute__((fallthrough));" on +//! GCC 7+. +#if defined(__cplusplus) && (__cplusplus >= 201703L) + // part of C++17 standard + #define Standard_FALLTHROUGH [[fallthrough]]; +#elif defined(__GNUC__) && (__GNUC__ >= 7) + // gcc 7+ + #define Standard_FALLTHROUGH __attribute__((fallthrough)); +#else + #define Standard_FALLTHROUGH +#endif + +//! @def Standard_UNUSED +//! Macro for marking variables / functions as possibly unused +//! so that compiler will not emit redundant "unused" warnings. +//! +//! Expands to "__attribute__((unused))" on GCC and CLang. #if defined(__GNUC__) || defined(__clang__) #define Standard_UNUSED __attribute__((unused)) #else #define Standard_UNUSED #endif -// Macro Standard_DEPRECATED("message") can be used to declare a method deprecated. -// If OCCT_NO_DEPRECATED is defined, Standard_DEPRECATED is defined empty. +//! @def Standard_THREADLOCAL +//! Define Standard_THREADLOCAL modifier as C++11 thread_local keyword where it is available. +#if defined(__clang__) + // CLang version: standard CLang > 3.3 or XCode >= 8 (but excluding 32-bit ARM) + // Note: this has to be in separate #if to avoid failure of preprocessor on other platforms + #if __has_feature(cxx_thread_local) + #define Standard_THREADLOCAL thread_local + #endif +#elif defined(__INTEL_COMPILER) + #if (defined(_MSC_VER) && _MSC_VER >= 1900 && __INTEL_COMPILER > 1400) + // requires msvcrt vc14+ (Visual Studio 2015+) + #define Standard_THREADLOCAL thread_local + #elif (!defined(_MSC_VER) && __INTEL_COMPILER > 1500) + #define Standard_THREADLOCAL thread_local + #endif +#elif (defined(_MSC_VER) && _MSC_VER >= 1900) + // msvcrt coming with vc14+ (VS2015+) + #define Standard_THREADLOCAL thread_local +#elif (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + // GCC >= 4.8 + #define Standard_THREADLOCAL thread_local +#endif + +#ifndef Standard_THREADLOCAL + #define Standard_THREADLOCAL +#endif + +//! @def Standard_DEPRECATED("message") +//! Can be used in declaration of a method or a class to mark it as deprecated. +//! Use of such method or class will cause compiler warning (if supported by +//! compiler and unless disabled). +//! If macro OCCT_NO_DEPRECATED is defined, Standard_DEPRECATED is defined empty. #ifdef OCCT_NO_DEPRECATED #define Standard_DEPRECATED(theMsg) #else @@ -53,9 +114,14 @@ #endif #endif -// Disable warnings about deprecated features. -// This is useful for sections of code kept for backward compatibility and scheduled for removal. - +//! @def Standard_DISABLE_DEPRECATION_WARNINGS +//! Disables warnings on use of deprecated features (see Standard_DEPRECATED), +//! from the current point till appearance of Standard_ENABLE_DEPRECATION_WARNINGS macro. +//! This is useful for sections of code kept for backward compatibility and scheduled for removal. +//! +//! @def Standard_ENABLE_DEPRECATION_WARNINGS +//! Enables warnings on use of deprecated features previously disabled by +//! Standard_DISABLE_DEPRECATION_WARNINGS. #if defined(__ICL) || defined (__INTEL_COMPILER) #define Standard_DISABLE_DEPRECATION_WARNINGS __pragma(warning(push)) __pragma(warning(disable:1478)) #define Standard_ENABLE_DEPRECATION_WARNINGS __pragma(warning(pop)) @@ -72,6 +138,13 @@ #define Standard_ENABLE_DEPRECATION_WARNINGS #endif +//! @def OCCT_NO_RVALUE_REFERENCE +//! Disables methods and constructors that use rvalue references +//! (C++11 move semantics) not supported by obsolete compilers. +#if (defined(_MSC_VER) && (_MSC_VER < 1600)) + #define OCCT_NO_RVALUE_REFERENCE +#endif + # ifdef _WIN32 // We must be careful including windows.h: it is really poisonous stuff! @@ -118,7 +191,15 @@ #endif -# if defined(_WIN32) && !defined(HAVE_NO_DLL) +//! @def Standard_EXPORT +//! This macro should be used in declarations of public methods +//! to ensure that they are exported from DLL on Windows and thus +//! can be called from other (dependent) libraries or applications. +//! +//! If macro OCCT_STATIC_BUILD is defined, then Standard_EXPORT +//! is set to empty. + +# if defined(_WIN32) && !defined(OCCT_STATIC_BUILD) && !defined(HAVE_NO_DLL) //====================================================== // Windows-specific definitions @@ -167,21 +248,9 @@ # endif /* _WIN32 */ -//====================================================== -// Other -//====================================================== - -# ifndef __Standard_API -# if !defined(_WIN32) || defined(__Standard_DLL) || defined(__FSD_DLL) || defined(__MMgt_DLL) || defined(__OSD_DLL) || defined(__Plugin_DLL) || defined(__Quantity_DLL) || defined(__Resource_DLL) || defined(__SortTools_DLL) || defined(__StdFail_DLL) || defined(__Storage_DLL) || defined(__TColStd_DLL) || defined(__TCollection_DLL) || defined(__TShort_DLL) || defined(__Units_DLL) || defined(__UnitsAPI_DLL) || defined(__Dico_DLL) -# define __Standard_API Standard_EXPORT -# define __Standard_APIEXTERN Standard_EXPORTEXTERN -# else -# define __Standard_API Standard_IMPORT -# define __Standard_APIEXTERN Standard_IMPORT -# endif // __Standard_DLL -# endif // __Standard_API - -// Support of Universal Windows Platform +//! @def OCCT_UWP +//! This macro is defined on Windows platform in the case if the code +//! is being compiled for UWP (Universal Windows Platform). #if defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_APP #define OCCT_UWP #else -- 2.39.5