//////////////////////////////////////////////////////////////////////////////// /** \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 //------------------------------------------------------------------------------ /** \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