1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-08-14 13:30:48 +03:00

0029935: Foundation Classes - introduce OSD_ThreadPool class defining a thread pool

New class OSD_ThreadPool has been introduced to define a Thread Pool for multi-threading algorithm.
Thread Pool assigns a serial number for each thread allowing Multi-Threading algorithm to allocate thread-local storage variables as an array whose size is the same as the number of threads.

OSD_ThreadPool also redirects exceptions to a thread calling parallel execution and consistently initializes FPE exception handling.

New class Standard_Condition provides a platform-independent  tool similar to Event in WinAPI.

A new auxiliary function Standard_Atomic_CompareAndSwap() has been introduced
for performing atomic compare and swap of integer number.
Standard_Atomic_Increment/Standard_Atomic_Decrement fallback implementation
using ASM code for x86 processors for GCC has been dropped;
instead, it is expected that GCC should be properly configured targeting modern x86 architectures.

OSD_Signal now declares fFltExceptions as thread_local variable accessible through OSD::ToCatchFloatingSignals() property.
Standard_THREADLOCAL macro (wrapping thread_local attribute) has been moved to public header Standard_Macro.hxx.

OSD_Parallel::ForEach() has been extended with new optional parameter theNbItems and uses OSD_ThreadPool::DefaultPool().
This commit is contained in:
kgv
2018-07-07 02:27:51 +03:00
committed by bugmaster
parent be3d8cbc02
commit 6f498847fa
17 changed files with 1404 additions and 125 deletions

View File

@@ -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

View File

@@ -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) && ! 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<volatile long*>(theValue));
}
bool Standard_Atomic_CompareAndSwap (volatile int* theValue, int theOldValue, int theNewValue)
{
return _InterlockedCompareExchange (reinterpret_cast<volatile long*>(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

View File

@@ -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 <windows.h>
#else
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>
#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
}

View File

@@ -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 <Standard.hxx>
#ifndef _WIN32
#include <pthread.h>
#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

View File

@@ -58,33 +58,6 @@ static void deallocate_message(Standard_CString aMessage)
}
}
//! @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
// ******************************************************************
// Standard_Failure *
// ******************************************************************

View File

@@ -68,6 +68,33 @@
#define Standard_UNUSED
#endif
//! @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