Files
SaraP 86f4bb48be Extern :
- C3d aggiornamento delle librerie ( 117960).
2023-10-12 08:53:50 +02:00

634 lines
32 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/**
\file
\brief \ru Реализация блокировок на базе системных механизмов синхронизации
и OpenMP блокировок.
\en Locks implementation on base of system synchronization mechanisms
and OpenMP locks. \~
details \ru Реализация блокировок (в том числе блокировки в области видимости)
на базе системных механизмов синхронизации и OpenMP блокировок.\n
\en Implementation of locks (including scoped lock) on base of
system synchronization mechanisms and OpenMP locks.\n \~
*/
////////////////////////////////////////////////////////////////////////////////
#ifndef __TOOL_MUTEX_H
#define __TOOL_MUTEX_H
#include <math_define.h>
//------------------------------------------------------------------------------
/**
\brief \ru Режимы многопоточных вычислений.
\en Multithreading modes. \~
\details \ru Режимы многопоточных вычислений. Режим многопоточности ядра управляет механизмом потокобезопасности объектов ядра и определяет, какие операции ядра будут распараллеливаться. \n
\en Multithreading modes. Multithreading mode of the kernel manages the mechanism of thread-safity of the kernel objects and defines which \n \~
\ingroup Data_Structures
*/
//---
enum MbeMultithreadedMode
{
///< \ru Многопоточность ядра полностью отключена. Не разрешено использовать интерфейсы ядра в многопоточных вычислениях.
///< \en Kernel multithreading is completely disabled. Using kernel interfaces in parallel computing is prohibited.
mtm_Off = 0,
///< \ru Разрешена многопоточная обработка только для объектов, не имеющих общих данных (независимых объектов). Включены внутренние многопоточные вычисления только для независимых объектов.
///< \en Allowed parallel processing of objects that do not have common data (independent objects). Enabled internal multi-threaded calculations only for independent objects.
mtm_Standard = 1,
///< \ru Обеспечивается потокобезопасность интерфейсов ядра. Разрешено использовать все интерфейсы ядра в многопоточных вычислениях. При этом включены внутренние многопоточные вычисления только для независимых объектов.
///< \en Ensured thread safety of kernel interfaces. Allowed using all kernel interfaces in parallel computing. Enabled internal multithreaded calculations only for independent objects.
mtm_SafeItems = 2,
///< \ru Обеспечивается потокобезопасность объектов ядра. Разрешено использовать все интерфейсы ядра в многопоточных вычислениях. Полностью включены внутренние многопоточные вычисления.
///< \en Ensured thread-safety of kernel interfaces. Allowed using all kernel interfaces in parallel computing. Enabled all internal parallel calculations in the kernel.
mtm_Items = 3,
///< \ru Включена максимальная многопоточность ядра. Можно использовать интерфейсы ядра в параллельных вычислениях. Полностью включены внутренние многопоточные вычисления.
///< \en Maximal kernel multithreading is ON. Allowed using all kernel interfaces in parallel computing. Enabled all internal parallel calculations in the kernel.
mtm_Max = 31
};
////////////////////////////////////////////////////////////////////////////////
//
// \ru Поддержка многопоточности в пользовательском приложении.
//
// \ru Перед запуском параллельных вычислений, необходимо проверить, что установленный режим
// многопоточности ядра, позволяет использовать параллельные вычисления.
// \ru Чтобы использовать интерфейсы ядра в нескольких потоках,
// \ru в ядре должен быть установлен режим многопоточных вычислений не ниже mtm_SafeItems.
// \ru По умолчанию в ядре установлен режим максимальной многопоточности.
//
// \ru При использовании параллельных вычислений необходимо нотифицировать ядро
// о входе в параллельный регион и выходе из него.
// \ru Для этого могут быть использованы:
// либо класс ParallelRegionGuard (защитник параллельного региона в области видимости),
// либо парные функции EnterParallelRegion и ExitParallelRegion,
// либо парные макросы ENTER_PARALLEL и EXIT_PARALLEL.
//
// \ru Примеры:
//
// // Использование класса ParallelRegionGuard.
// if( Math::CheckMultithreadedMode( mtm_Items ) ){
// ParallelRegionGuard l;
// std::thread t1( function1 );
// std::thread t2( function2 );
// t1.join();
// t2.join();
// }
//
// // Использование функций.
// if( Math::CheckMultithreadedMode( mtm_Items ) ){
// EnterParallelRegion();
// std::thread t1( function1 );
// std::thread t2( function2 );
// t1.join();
// t2.join();
// ExitParallelRegion();
// }
//
// // Использование макросов.
// bool useParallel = Math::CheckMultithreadedMode( mtm_Items );
// ENTER_PARALLEL( useParallel );
// std::thread t1( function1 );
// std::thread t2( function2 );
// t1.join();
// t2.join();
// EXIT_PARALLEL( useParallel );
//
//
// \en Support of multithreading in user application.
//
// \en Before running parallel calculations, you need to check that the kernel multithreading mode
// \en allows parallel computing.
// \en For using the kernel interfaces in several threads, the multithreading mode mtm_SafeItems
// \en or higher should be defined in the kernel.
// \en By default, the maximal multithreading mode is set in the kernel.
//
// \en While using parallel calculations, you need to notify the kernel about entering
// \en and exiting a parallel region.
// \en For that, you can use one of the ways:
// the class ParallelRegionGuard (a scoped guard of parallel region),
// \en or the pair of the functions EnterParallelRegion and ExitParallelRegion,
// or the pair of the macros ENTER_PARALLEL and EXIT_PARALLEL.
//
// \en Examples:
//
// // Using the ParallelRegionGuard class.
// if( Math::CheckMultithreadedMode( mtm_Items ) )
// {
// ParallelRegionGuard l;
// std::thread t1( function1 );
// std::thread t2( function2 );
// t1.join();
// t2.join();
// }
//
// // Using the pair of the functions.
// if( Math::CheckMultithreadedMode( mtm_Items ) )
// {
// EnterParallelRegion();
// std::thread t1( function1 );
// std::thread t2( function2 );
// t1.join();
// t2.join();
// ExitParallelRegion();
// }
//
// // Using the pair of the macros.
// bool useParallel = Math::CheckMultithreadedMode( mtm_Items );
// ENTER_PARALLEL( useParallel );
// std::thread t1( function1 );
// std::thread t2( function2 );
// t1.join();
// t2.join();
// EXIT_PARALLEL( useParallel );
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// \ru Управление блокировками.
// \en Locks management. \~
//
// \ru Переменная C3D_NATIVE_LOCK включает использование блокировок на базе
// \ru системных механизмов синхронизации вместо OpenMP,
// \ru что позволяет использовать механизмы распараллеливания, отличные от OpenMP.
//
// \en The variable C3D_NATIVE_LOCK enables using locks on base of system
// \en synchronization mechanisms instead of OpenMP, that allows
// \en use of parallelization frameworks, other than OpenMP. \~
//
////////////////////////////////////////////////////////////////////////////////
#define C3D_NATIVE_LOCK
#ifdef C3D_NATIVE_LOCK
class ToolLock;
//------------------------------------------------------------------------------
/** \brief \ru Класс блокировки. \en Lock class. \~
\details \ru Класс блокировки (реализация на базе системных механизмов синхронизации).
\en Lock class (implementation on base of system synchronization mechanisms). \~
\ingroup Base_Tools
*/
// ---
class MATH_CLASS CommonMutex
{
ToolLock* m_lock;
public:
CommonMutex();
~CommonMutex();
/** \brief \ru Установить блокировку. \en Set a lock. \~
*/
void Lock();
/** \brief \ru Снять блокировку. \en Unset a lock. \~
*/
void Unlock();
private:
// \ru Запрет копирования. \en Copy forbidden.
CommonMutex ( const CommonMutex& );
CommonMutex& operator = ( const CommonMutex& );
};
//------------------------------------------------------------------------------
/** \brief \ru Одинаковая реализация CommonMutex и CommonRecursiveMutex.
\en Same implementation of CommonMutex and CommonRecursiveMutex. \~
\ingroup Base_Tools
*/
#define CommonRecursiveMutex CommonMutex
#else // C3D_NATIVE_LOCK
//------------------------------------------------------------------------------
/** \brief \ru Класс блокировки.
\en Lock class. \~
\details \ru Класс блокировки на базе OpenMP lock.
\en Lock class on base of OpenMP lock. \~
\ingroup Base_Tools
*/
// ---
class MATH_CLASS CommonMutex
{
omp_lock_t m_lock;
public:
// For correct work, CommonMutex implementation should be encapsulated in cpp.
CommonMutex();
~CommonMutex();
void lock();
void unlock();
};
//------------------------------------------------------------------------------
/** \brief \ru Класс блокировки на базе вложенного OpenMP lock.
\en Wrapper for nested OpenMP lock. \~
\ingroup Base_Tools
*/
// ---
class MATH_CLASS CommonRecursiveMutex
{
omp_nest_lock_t m_lock;
public:
// For correct work, CommonRecursiveMutex implementation should be encapsulated in cpp.
CommonRecursiveMutex();
~CommonRecursiveMutex();
void lock();
void unlock();
};
#endif // C3D_NATIVE_LOCK
//------------------------------------------------------------------------------
/** \brief \ru Защитник параллельного региона в области видимости.
\en Scoped guard of parallel region. \~
\details \ru Класс защищает регион кода, выполняющийся параллельно. Работает в области видимости.
Должен использоваться для защиты параллельного кода.
Пример использования:
if( Math::CheckMultithreadedMode( mtm_Items ) )
{
ParallelRegionGuard l;
std::thread t1( function1 );
std::thread t2( function2 );
t1.join();
t2.join();
}
\en The class guards a code region running in parallel. Works in scope.
Should be used to protect parallel code.
Example of use:
if( Math::CheckMultithreadedMode( mtm_Items ) )
{
ParallelRegionGuard l;
std::thread t1( function1 );
std::thread t2( function2 );
t1.join();
t2.join();
}
\ingroup Base_Tools
*/
// ---
class MATH_CLASS ParallelRegionGuard
{
public:
ParallelRegionGuard();
~ParallelRegionGuard();
};
//------------------------------------------------------------------------------
/** \brief \ru Функция нотифицирует ядро о входе в параллельный блок кода.
Вызов функции должен стоять перед началом параллельного блока.
\en The function notifies the kernel about entering a parallel region.
The function call should be placed before the start of a parallel block
*/
// ---
MATH_FUNC( void ) EnterParallelRegion();
//------------------------------------------------------------------------------
/** \brief \ru Функция нотифицирует ядро о выходе из параллельного блока кода.
Вызов функции должен стоять после окончания параллельного блока.
\en The function notifies the kernel about exiting a parallel region.
The function call should be placed after the end of the parallel block.
*/
// ---
MATH_FUNC( void ) ExitParallelRegion();
//------------------------------------------------------------------------------
// \ru Макросы для нотификации о входе и выходе из параллельного цикла.
// Вызов макросов при использовании OpenMP не обязателен, однако значительно ускоряет
// выполнение параллельного цикла.
// Вызов ENTER_PARALLEL должен стоять перед началом параллельного блока.
// Вызов EXIT_PARALLEL должен стоять после окончания параллельного блока.
// Пример использования:
// bool useParallel = Math::CheckMultithreadedMode( mtm_Items );
// ENTER_PARALLEL( useParallel );
// #pragma omp parallel for
// for ( ptrdiff_t i = 0; i < count; ++i ) {
// /* Cycle body */
// }
// EXIT_PARALLEL( useParallel );
//
/// \ru Macros for notification of entering and exiting a parallel block.
// Calling the macros when using OpenMP is not required, but significantly speeds up
// the execution of parallel cycle.
// The call ENTER_PARALLEL should be placed before the start of a parallel block.
// The call EXIT_PARALLEL should be placed after the end of the parallel block.
// Example of use:
// bool useParallel = Math::CheckMultithreadedMode( mtm_Items );
// ENTER_PARALLEL( useParallel );
// #pragma omp parallel for
// for ( ptrdiff_t i = 0; i < count; ++i ) {
// /* Cycle body */
// }
// EXIT_PARALLEL( useParallel );
// ---
//------------------------------------------------------------------------------
/** \brief \ru Если useParallel == true, нотифицирует ядро о входе в параллельный блок кода.
\en If useParallel == true, notifies the kernel about entering a parallel region.
\details \ru Если useParallel == true, нотифицирует ядро о входе в параллельный блок кода.
Вызов должен стоять перед началом параллельного блока (перед прагмой OpenMP).
Использование макроса значительно ускоряет параллельные циклы OpenMP.
\en If useParallel == true, notifies the kernel about entering a parallel region.
The call should be placed before the start of a parallel block (before OpenMP pragma).
Using a macro speeds up parallel OpenMP cycles significantly.
*/
// ---
#define ENTER_PARALLEL(useParallel) if ( useParallel ) EnterParallelRegion();
//------------------------------------------------------------------------------
/** \brief \ru Если useParallel == true, нотифицирует ядро о выходе из параллельного блока кода.
\en If useParallel == true, notifies the kernel about exiting a parallel region.
\details \ru Если useParallel == true, нотифицирует ядро о выходе из параллельного блока кода.
Вызов должен стоять после окончания параллельного блока.
Использование макроса значительно ускоряет параллельные циклы OpenMP.
\en If useParallel == true, notifies the kernel about exiting a parallel region.
The call should be placed after the end of the parallel block.
Using a macro speeds up parallel OpenMP cycles significantly.
*/
// ---
#define EXIT_PARALLEL(useParallel) if ( useParallel ) ExitParallelRegion();
//------------------------------------------------------------------------------
/** \brief \ru Нотифицирует ядро о входе в параллельный блок кода.
\en Notifies the kernel about entering a parallel region.
\details \ru Нотифицирует ядро о входе в параллельный блок кода.
Вызов должен стоять перед началом параллельного блока (перед прагмой OpenMP).
Использование макроса значительно ускоряет параллельные циклы OpenMP.
\en Notifies the kernel about entering a parallel region.
The call should be placed before the start of a parallel block (before OpenMP pragma).
Using a macro speeds up parallel OpenMP cycles significantly.
*/
// ---
#define ENTER_PARALLEL_FORCED EnterParallelRegion();
//------------------------------------------------------------------------------
/** \brief \ru Нотифицирует ядро о выходе из параллельного блока кода.
\en Notifiesotifies the kernel about exiting a parallel region.
\details \ru Нотифицирует ядро о выходе из параллельного блока кода.
Вызов должен стоять после окончания параллельного блока.
Использование макроса значительно ускоряет параллельные циклы OpenMP.
\en Notifies the kernel about exiting a parallel region.
The call should be placed after the end of the parallel block.
Using a macro speeds up parallel OpenMP cycles significantly.
*/
// ---
#define EXIT_PARALLEL_FORCED ExitParallelRegion();
//------------------------------------------------------------------------------
/** \brief \ru Функция определяет, выполняется ли код параллельно.
\en The function determines whether the code is executed in parallel.
*/
// ---
MATH_FUNC( bool ) IsInParallel();
//------------------------------------------------------------------------------
/** \brief \ru Функция определяет, разрешена ли очистка кэшей.
\en The function determines whether the caches cleanup is allowed.
*/
// ---
MATH_FUNC( bool ) CacheCleanupAllowed();
////////////////////////////////////////////////////////////////////////////////
//
// \ru Блокировки и другие средства синхронизации.
// \en Locks and other synchronization objects. \~
// \ru В качестве блокировок должны использоваться CommonMutex и CommonRecursiveMutex
// \ru (OpenMP lock не должны использоваться напрямую).
//
// \en CommonMutex and CommonRecursiveMutex should be used as locks
// \en (OpenMP locks should not be used directly).
//
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
/** \brief \ru Блокировка в области видимости. Может принимать нулевой указатель на мьютекс.
Блокировка происходит, если указатель на мьютекс ненулевой и код выполняется параллельно.
\en Scoped lock. Can accept a null pointer to a mutex.
Locking occurs if the pointer to the mutex is nonzero and the code runs in parallel. \~
\ingroup Base_Tools
*/
// ---
class MATH_CLASS ScopedLock
{
CommonMutex* m_mutex;
public:
ScopedLock( CommonMutex* mtx, bool parallelCheck = true );
~ScopedLock();
/** \brief \ru Выполнена ли реальная блокировка. \en Whether a real locking performed. \~
*/
bool IsLocked();
private:
ScopedLock();
ScopedLock ( const ScopedLock& );
ScopedLock& operator = ( const ScopedLock& );
};
//------------------------------------------------------------------------------
/** \brief \ru Рекурсивная блокировка в области видимости. Может принимать нулевой указатель на мьютекс.
Блокировка происходит, если указатель на мьютекс ненулевой и код выполняется параллельно.
\en Recursive scoped lock. Can accept a null pointer to a mutex.
Locking occurs if the pointer to the mutex is nonzero and the code runs in parallel. \~
\ingroup Base_Tools
*/
// ---
class MATH_CLASS ScopedRecursiveLock
{
CommonRecursiveMutex* m_mutex;
public:
ScopedRecursiveLock( CommonRecursiveMutex* mtx, bool parallelCheck = true );
~ScopedRecursiveLock();
/** \brief \ru Выполнена ли реальная блокировка. \en Whether a real locking performed. \~
*/
bool IsLocked();
private:
ScopedRecursiveLock();
ScopedRecursiveLock ( const ScopedRecursiveLock& );
ScopedRecursiveLock& operator = ( const ScopedRecursiveLock& );
};
//------------------------------------------------------------------------------
/** \brief \ru Базовый объект синхронизации с отложенной инициализацией.
\en Base synchronization object with lazy initialization. \~
\details \ru Базовый объект, предоставляющий средства синхронизации и создающий блокировку при необходимости. \n
\en Base object which provides means of synchronization and creates a lock when needed. \n \~
\ingroup Base_Tools
*/
// ---
class MATH_CLASS MbSyncItem {
protected:
mutable CommonMutex * m_comLock; // \ru Критическая секция для монопольного доступа к объекту. \en The critical section for exclusive access to the object.
mutable int m_locked;
public:
MbSyncItem();
virtual ~MbSyncItem();
/** \brief \ru Включить блокировку (блокировка происходит только при наличии параллельности).
\en Switch lock on (locking happens only in parallel region).
*/
void Lock() const;
/** \brief \ru Снять блокировку, если она была установлена.
\en Switch lock off if locking has been set.
*/
void Unlock() const;
// \ru Выдать указатель на объект мьютекса. Возращает nullptr, если параллельности нет. Для использования в ScopedLock.
// \en Get a pointer to the mutex object. Return nullptr if no parallelism. For use in ScopedLock.
CommonMutex * GetLock() const;
};
//------------------------------------------------------------------------------
/** \brief \ru Базовый объект синхронизации с отложенной инициализацией, поддерживающий множественные блокировки.
\en Base synchronization object with lazy initialization which supports nested locks. \~
\details \ru Базовый объект синхронизации, поддерживающий множественные блокировки и создающий блокировку при необходимости. \n
\en Base synchronization object with support of nested locks which creates a lock if necessary. \n \~
\ingroup Base_Tools
*/
// ---
class MATH_CLASS MbNestSyncItem {
protected:
mutable CommonRecursiveMutex * m_comLock; // \ru Критическая секция для монопольного доступа к объекту. \en The critical section for exclusive access to the object.
mutable int m_locked;
public:
MbNestSyncItem();
virtual ~MbNestSyncItem();
/** \brief \ru Включить блокировку (блокировка происходит только при наличии параллельности).
\en Switch lock on (locking happens only in parallel region).
*/
void Lock() const;
/** \brief \ru Снять блокировку, если она была установлена.
\en Switch lock off if locking has been set.
*/
void Unlock() const;
/** \brief \ru Выдать указатель на объект мьютекса. Возращает nullptr, если параллельности нет. Для использования в ScopedLock.
\en Get a pointer to the mutex object. Return nullptr if no parallelism. For use in ScopedLock.
*/
CommonRecursiveMutex * GetLock() const;
};
//------------------------------------------------------------------------------
/** \brief \ru Базовый объект, предоставляющий средства синхронизации.
\en Base object providing means of synchronization. \~
\details \ru Базовый объект, предоставляющий средства синхронизации. \n
\en Base object providing means of synchronization. \n \~
\ingroup Base_Tools
*/
// ---
class MATH_CLASS MbPersistentSyncItem {
protected:
mutable CommonMutex m_comLock; // \ru Критическая секция для монопольного доступа к объекту. \en The critical section for exclusive access to the object.
mutable int m_locked;
public:
MbPersistentSyncItem();
virtual ~MbPersistentSyncItem();
/** \brief \ru Включить блокировку.
\en Switch lock on.
*/
void Lock() const;
/** \brief \ru Снять блокировку, если она была установлена.
\en Switch lock off if locking has been set.
*/
void Unlock() const;
/** \brief \ru Выдать указатель на объект мьютекса.
\en Get a pointer to the mutex object.
*/
CommonMutex * GetLock() const;
protected:
MbPersistentSyncItem( const MbPersistentSyncItem & );
MbPersistentSyncItem & operator = ( const MbPersistentSyncItem & );
};
//------------------------------------------------------------------------------
/** \brief \ru Базовый объект синхронизации, поддерживающий множественные блокировки.
\en Base synchronization object with support of nested locks. \~
\details \ru Базовый объект синхронизации, поддерживающий множественные блокировки. \n
\en Base synchronization object with support of nested locks. \n \~
\ingroup Base_Tools
*/
// ---
class MATH_CLASS MbPersistentNestSyncItem {
protected:
mutable CommonRecursiveMutex m_comLock; // \ru Критическая секция для монопольного доступа к объекту. \en The critical section for exclusive access to the object.
mutable int m_locked;
public:
MbPersistentNestSyncItem();
virtual ~MbPersistentNestSyncItem();
/** \brief \ru Включить блокировку. \en Switch lock on.
*/
void Lock() const;
/** \brief \ru Снять блокировку, если она была установлена.
\en Switch lock off if locking has been set.
*/
void Unlock() const;
/** \brief \ru Выдать указатель на объект мьютекса.
\en Get a pointer to the mutex object.
*/
CommonRecursiveMutex * GetLock() const;
protected:
MbPersistentNestSyncItem( const MbPersistentNestSyncItem & );
MbPersistentNestSyncItem & operator = ( const MbPersistentNestSyncItem & );
};
//------------------------------------------------------------------------------
/** \brief \ru Установлен ли режим безопасной многопоточности (используется в CacheManager).
\en Whether is enabled a safe multithreading mode (used in CacheManager). \~
\ingroup Base_Tools
*/
MATH_FUNC(bool) IsSafeMultithreading();
//------------------------------------------------------------------------------
/** \brief \ru Включены ли блокировки (режим многопоточности >= mtm_Standard и код выполняется параллельно).
\en Whether locks are enabled (multithreading is on and code is executed in parallel). \~
\ingroup Base_Tools
*/
MATH_FUNC( bool ) LocksEnabled();
//------------------------------------------------------------------------------
/** \brief \ru Получить псевдо-идентификатор текущего потока.
\en Get a current thread pseudo-identifier.
*/
MATH_FUNC( unsigned int ) GetThreadKey();
//------------------------------------------------------------------------------
/** \brief \ru Получить указатель на глобальный мьютекс (используется в CacheManager).
\en Get a pointer to the global mutex (used in CacheManager).
*/
MATH_FUNC( CommonMutex* ) GetGlobalLock();
//------------------------------------------------------------------------------
/** \brief \ru Получить указатель на глобальный рекурсивный мьютекс (используется для операций выделения и освобождения памяти).
\en Get a pointer to the global recursive mutex (used for memory allocation and deallocation operations).
*/
MATH_FUNC( CommonRecursiveMutex* ) GetGlobalRecursiveLock();
//------------------------------------------------------------------------------
/** \brief \ru Установить блокировку в области видимости для операций выделения и освобождения памяти.
\en Set scoped lock for memory allocation and deallocation operations.
*/
#define SET_MEMORY_SCOPED_LOCK ScopedRecursiveLock memScopedLock( GetGlobalRecursiveLock() );
#endif // __TOOL_MUTEX_H