0
0
mirror of https://github.com/yse/easy_profiler.git synced 2024-12-27 00:31:02 +08:00

(profiler_core) !! Non-blocking API using Thread-Local-Storage (threal_local or __declspec(thread) or __thread)

This commit is contained in:
Victor Zarubkin 2016-09-06 23:03:05 +03:00
parent 31705d5daf
commit 10bb3da45b
5 changed files with 204 additions and 177 deletions

View File

@ -20,7 +20,23 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
#define EASY_PROFILER____H_______
#ifdef _WIN32
#define __func__ __FUNCTION__
# define __func__ __FUNCTION__
# if defined(_MSC_VER) && _MSC_VER <= 1800
// There is no support for C++11 thread_local keyword prior to Visual Studio 2015. Use __declspec(thread) instead.
# define EASY_THREAD_LOCAL __declspec(thread)
# endif
#elif defined(__GNUC__)
# if (__GNUC__ == 4 && __GNUC_MINOR__ < 8) || (__GNUC__ < 4)
// There is no support for C++11 thread_local keyword prior to gcc 4.8. Use __thread instead.
# define EASY_THREAD_LOCAL __thread
# endif
#endif
// TODO: Check thread_local support for Clang!
#ifndef EASY_THREAD_LOCAL
# define EASY_THREAD_LOCAL thread_local
# define EASY_THREAD_LOCAL_CPP11
#endif
#if defined ( __clang__ )
@ -80,9 +96,9 @@ Block will be automatically completed by destructor.
\ingroup profiler
*/
#define EASY_BLOCK(name, ...)\
static const ::profiler::StaticBlockDescriptor EASY_UNIQUE_DESC(__LINE__)(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\
static const ::profiler::block_id_t EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\
::profiler::BLOCK_TYPE_BLOCK , ## __VA_ARGS__);\
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_BLOCK, EASY_UNIQUE_DESC(__LINE__).id(), EASY_RUNTIME_NAME(name));\
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_BLOCK, EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\
::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable
/** Macro of beginning of block with function name and custom color.
@ -105,9 +121,9 @@ Name of the block automatically created with function name.
\ingroup profiler
*/
#define EASY_FUNCTION(...)\
static const ::profiler::StaticBlockDescriptor EASY_UNIQUE_DESC(__LINE__)(__func__, __FILE__, __LINE__,\
static const ::profiler::block_id_t EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(__func__, __FILE__, __LINE__,\
::profiler::BLOCK_TYPE_BLOCK , ## __VA_ARGS__);\
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_BLOCK, EASY_UNIQUE_DESC(__LINE__).id(), "");\
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_BLOCK, EASY_UNIQUE_DESC(__LINE__), "");\
::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable
/** Macro of completion of last nearest open block.
@ -145,9 +161,9 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION.
\ingroup profiler
*/
#define EASY_EVENT(name, ...)\
static const ::profiler::StaticBlockDescriptor EASY_UNIQUE_DESC(__LINE__)(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\
static const ::profiler::block_id_t EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\
::profiler::BLOCK_TYPE_EVENT , ## __VA_ARGS__);\
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_EVENT, EASY_UNIQUE_DESC(__LINE__).id(), EASY_RUNTIME_NAME(name));\
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_EVENT, EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\
::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable
/** Macro enabling profiler
@ -166,10 +182,15 @@ If this thread has been already named then nothing changes.
\ingroup profiler
*/
#ifdef _WIN32
#define EASY_THREAD(name) ::profiler::setThreadName(name, __FILE__, __func__, __LINE__);
#ifdef EASY_THREAD_LOCAL_CPP11
#define EASY_THREAD(name)\
EASY_THREAD_LOCAL static const char* EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = \
::profiler::setThreadName(name, __FILE__, __func__, __LINE__);
#else
#define EASY_THREAD(name) thread_local static const ::profiler::ThreadNameSetter EASY_TOKEN_CONCATENATE(unique_profiler_thread_name_setter_, __LINE__)(name, __FILE__, __func__, __LINE__);
#define EASY_THREAD(name)\
EASY_THREAD_LOCAL static const char* EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = nullptr;\
if (EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) == nullptr)\
EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = ::profiler::setThreadName(name, __FILE__, __func__, __LINE__);
#endif
/** Macro of naming main thread.
@ -211,20 +232,9 @@ class ThreadStorage;
namespace profiler {
class Block;
class StaticBlockDescriptor;
extern "C"{
void PROFILER_API beginBlock(Block& _block);
void PROFILER_API endBlock();
void PROFILER_API setEnabled(bool isEnable);
uint32_t PROFILER_API dumpBlocksToFile(const char* filename);
void PROFILER_API setThreadName(const char* name, const char* filename, const char* _funcname, int line);
void PROFILER_API setContextSwitchLogFilename(const char* name);
PROFILER_API const char* getContextSwitchLogFilename();
}
typedef uint64_t timestamp_t;
typedef uint32_t thread_id_t;
typedef uint64_t timestamp_t;
typedef uint32_t thread_id_t;
typedef uint32_t block_id_t;
enum BlockType : uint8_t
@ -237,6 +247,17 @@ namespace profiler {
BLOCK_TYPES_NUMBER
};
typedef BlockType block_type_t;
extern "C" {
PROFILER_API block_id_t registerDescription(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color = DefaultBlockColor);
PROFILER_API void beginBlock(Block& _block);
PROFILER_API void endBlock();
PROFILER_API void setEnabled(bool isEnable);
PROFILER_API uint32_t dumpBlocksToFile(const char* filename);
PROFILER_API const char* setThreadName(const char* name, const char* filename, const char* _funcname, int line);
PROFILER_API void setContextSwitchLogFilename(const char* name);
PROFILER_API const char* getContextSwitchLogFilename();
}
#pragma pack(push,1)
class PROFILER_API BaseBlockDescriptor
@ -322,28 +343,6 @@ namespace profiler {
//////////////////////////////////////////////////////////////////////
class PROFILER_API StaticBlockDescriptor final
{
block_id_t m_id;
public:
StaticBlockDescriptor(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color = DefaultBlockColor);
inline block_id_t id() const { return m_id; }
};
#ifndef _WIN32
struct PROFILER_API ThreadNameSetter final
{
ThreadNameSetter(const char* _name, const char* _filename, const char* _funcname, int _line)
{
setThreadName(_name, _filename, _funcname, _line);
}
};
#endif
//////////////////////////////////////////////////////////////////////
} // END of namespace profiler.
#if defined ( __clang__ )

View File

@ -48,9 +48,9 @@ namespace profiler {
auto _contextSwitchEvent = reinterpret_cast<CSwitch*>(_traceEvent->UserData);
const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart);
static const ::profiler::StaticBlockDescriptor desc("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White);
MANAGER._cswitchBeginBlock(time, desc.id(), _contextSwitchEvent->OldThreadId);
MANAGER._cswitchEndBlock(_contextSwitchEvent->NewThreadId, time);
static const auto desc = MANAGER.addBlockDescriptor("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White);
MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, desc);
MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, time);
}
//////////////////////////////////////////////////////////////////////////

View File

@ -10,16 +10,6 @@
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
#define EASY_INTERNAL_BLOCK(name, easyType, CODE) CODE
//#define EASY_INTERNAL_BLOCK(name, easyType, CODE)\
// static const profiler::StaticBlockDescriptor EASY_UNIQUE_DESC(__LINE__)(name, __FILE__, __LINE__, easyType, profiler::colors::White);\
// ::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(easyType, EASY_UNIQUE_DESC(__LINE__).id(), "");\
// CODE\
// EASY_UNIQUE_BLOCK(__LINE__).finish();\
// thread_storage.storeBlock(EASY_UNIQUE_BLOCK(__LINE__))
//////////////////////////////////////////////////////////////////////////
using namespace profiler;
#ifdef _WIN32
@ -29,14 +19,19 @@ extern decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY;
//auto& MANAGER = ProfileManager::instance();
#define MANAGER ProfileManager::instance()
extern "C"{
extern "C" {
void PROFILER_API endBlock()
PROFILER_API block_id_t registerDescription(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
{
return MANAGER.addBlockDescriptor(_name, _filename, _line, _block_type, _color);
}
PROFILER_API void endBlock()
{
MANAGER.endBlock();
}
void PROFILER_API setEnabled(bool isEnable)
PROFILER_API void setEnabled(bool isEnable)
{
MANAGER.setEnabled(isEnable);
#ifdef _WIN32
@ -47,22 +42,22 @@ extern "C"{
#endif
}
void PROFILER_API beginBlock(Block& _block)
PROFILER_API void beginBlock(Block& _block)
{
MANAGER.beginBlock(_block);
}
uint32_t PROFILER_API dumpBlocksToFile(const char* filename)
PROFILER_API uint32_t dumpBlocksToFile(const char* filename)
{
return MANAGER.dumpBlocksToFile(filename);
}
void PROFILER_API setThreadName(const char* name, const char* filename, const char* _funcname, int line)
PROFILER_API const char* setThreadName(const char* name, const char* filename, const char* _funcname, int line)
{
return MANAGER.setThreadName(name, filename, _funcname, line);
}
void PROFILER_API setContextSwitchLogFilename(const char* name)
PROFILER_API void setContextSwitchLogFilename(const char* name)
{
return MANAGER.setContextSwitchLogFilename(name);
}
@ -84,12 +79,6 @@ SerializedBlock::SerializedBlock(const Block& block, uint16_t name_length)
//////////////////////////////////////////////////////////////////////////
StaticBlockDescriptor::StaticBlockDescriptor(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
: m_id(MANAGER.addBlockDescriptor(_name, _filename, _line, _block_type, _color))
{
}
BaseBlockDescriptor::BaseBlockDescriptor(int _line, block_type_t _block_type, color_t _color)
: m_line(_line)
, m_type(_block_type)
@ -136,6 +125,8 @@ void ThreadStorage::clearClosed()
//////////////////////////////////////////////////////////////////////////
EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr;
// #ifdef _WIN32
// LPTOP_LEVEL_EXCEPTION_FILTER PREVIOUS_FILTER = NULL;
// LONG WINAPI easyTopLevelExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo)
@ -175,32 +166,30 @@ void ProfileManager::beginBlock(Block& _block)
if (!m_isEnabled)
return;
EASY_INTERNAL_BLOCK("Easy.Spin", profiler::BLOCK_TYPE_BLOCK,\
auto& thread_storage = threadStorage(getCurrentThreadId());\
);
if (THREAD_STORAGE == nullptr)
THREAD_STORAGE = &threadStorage(getCurrentThreadId());
EASY_INTERNAL_BLOCK("Easy.Insert", profiler::BLOCK_TYPE_BLOCK,\
if (!_block.isFinished())
thread_storage.blocks.openedList.emplace(_block);
else
thread_storage.storeBlock(_block);\
);
if (!_block.isFinished())
THREAD_STORAGE->blocks.openedList.emplace(_block);
else
THREAD_STORAGE->storeBlock(_block);
}
void ProfileManager::_cswitchBeginBlock(profiler::timestamp_t _time, profiler::block_id_t _id, profiler::thread_id_t _thread_id)
void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id)
{
auto thread_storage = _threadStorage(_thread_id);
if (thread_storage != nullptr)
thread_storage->sync.openedList.emplace(_time, profiler::BLOCK_TYPE_CONTEXT_SWITCH, _id, "");
auto ts = findThreadStorage(_thread_id);
if (ts != nullptr)
ts->sync.openedList.emplace(_time, profiler::BLOCK_TYPE_CONTEXT_SWITCH, _id, "");
}
void ProfileManager::_cswitchStoreBlock(profiler::timestamp_t _time, profiler::block_id_t _id, profiler::thread_id_t _thread_id)
void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id)
{
auto thread_storage = _threadStorage(_thread_id);
if (thread_storage != nullptr){
auto ts = findThreadStorage(_thread_id);
if (ts != nullptr)
{
profiler::Block lastBlock(_time, profiler::BLOCK_TYPE_CONTEXT_SWITCH, _id, "");
lastBlock.finish(_time);
thread_storage->storeCSwitch(lastBlock);
ts->storeCSwitch(lastBlock);
}
}
@ -209,29 +198,28 @@ void ProfileManager::endBlock()
if (!m_isEnabled)
return;
auto& thread_storage = threadStorage(getCurrentThreadId());
if (thread_storage.blocks.openedList.empty())
if (THREAD_STORAGE->blocks.openedList.empty())
return;
Block& lastBlock = thread_storage.blocks.openedList.top();
Block& lastBlock = THREAD_STORAGE->blocks.openedList.top();
if (!lastBlock.isFinished())
lastBlock.finish();
thread_storage.storeBlock(lastBlock);
thread_storage.blocks.openedList.pop();
THREAD_STORAGE->storeBlock(lastBlock);
THREAD_STORAGE->blocks.openedList.pop();
}
void ProfileManager::_cswitchEndBlock(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime)
void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime)
{
auto thread_storage = _threadStorage(_thread_id);
if (thread_storage == nullptr || thread_storage->sync.openedList.empty())
auto ts = findThreadStorage(_thread_id);
if (ts == nullptr || ts->sync.openedList.empty())
return;
Block& lastBlock = thread_storage->sync.openedList.top();
Block& lastBlock = ts->sync.openedList.top();
lastBlock.finish(_endtime);
thread_storage->storeCSwitch(lastBlock);
thread_storage->sync.openedList.pop();
ts->storeCSwitch(lastBlock);
ts->sync.openedList.pop();
}
void ProfileManager::setEnabled(bool isEnable)
@ -283,11 +271,11 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* filename)
if(infile.is_open())
{
while (infile >> timestamp >> thread_from >> thread_to )
static const auto desc = addBlockDescriptor("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White);
while (infile >> timestamp >> thread_from >> thread_to)
{
static const ::profiler::StaticBlockDescriptor desc("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White);
_cswitchBeginBlock(timestamp, desc.id(), thread_from);
_cswitchEndBlock(thread_to, timestamp);
beginContextSwitch(thread_from, timestamp, desc);
endContextSwitch(thread_to, timestamp);
}
}
@ -385,15 +373,22 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* filename)
return blocks_number;
}
void ProfileManager::setThreadName(const char* name, const char* filename, const char* _funcname, int line)
const char* ProfileManager::setThreadName(const char* name, const char* filename, const char* _funcname, int line)
{
auto& thread_storage = threadStorage(getCurrentThreadId());
if (thread_storage.named)
return;
if (THREAD_STORAGE == nullptr)
{
THREAD_STORAGE = &threadStorage(getCurrentThreadId());
}
const auto id = addBlockDescriptor(_funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Random);
thread_storage.storeBlock(profiler::Block(profiler::BLOCK_TYPE_THREAD_SIGN, id, name));
thread_storage.named = true;
if (!THREAD_STORAGE->named)
{
const auto id = addBlockDescriptor(_funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Random);
THREAD_STORAGE->storeBlock(profiler::Block(profiler::BLOCK_TYPE_THREAD_SIGN, id, name));
THREAD_STORAGE->name = name;
THREAD_STORAGE->named = true;
}
return THREAD_STORAGE->name.c_str();
}
//////////////////////////////////////////////////////////////////////////

View File

@ -26,8 +26,8 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
#include <stack>
#include <map>
#include <list>
#include <set>
#include <vector>
#include <string>
#include <functional>
//////////////////////////////////////////////////////////////////////////
@ -44,10 +44,10 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
inline uint32_t getCurrentThreadId()
{
#ifdef _WIN32
return (uint32_t)::GetCurrentThreadId();
return (uint32_t)::GetCurrentThreadId();
#else
thread_local static pid_t x = syscall(__NR_gettid);
thread_local static uint32_t _id = (uint32_t)x;//std::hash<std::thread::id>()(std::this_thread::get_id());
EASY_THREAD_LOCAL static const pid_t x = syscall(__NR_gettid);
EASY_THREAD_LOCAL static const uint32_t _id = (uint32_t)x;//std::hash<std::thread::id>()(std::this_thread::get_id());
return _id;
#endif
}
@ -150,6 +150,7 @@ public:
BlocksList<std::reference_wrapper<profiler::Block>, SIZEOF_CSWITCH * (uint16_t)1024U> blocks;
BlocksList<profiler::Block, SIZEOF_CSWITCH * (uint16_t)128U> sync;
std::string name;
bool named = false;
void storeBlock(const profiler::Block& _block);
@ -161,8 +162,6 @@ public:
class ProfileManager final
{
friend profiler::StaticBlockDescriptor;
ProfileManager();
ProfileManager(const ProfileManager& p) = delete;
ProfileManager& operator=(const ProfileManager&) = delete;
@ -185,11 +184,20 @@ public:
static ProfileManager& instance();
~ProfileManager();
template <class ... TArgs>
uint32_t addBlockDescriptor(TArgs ... _args)
{
guard_lock_t lock(m_storedSpin);
const auto id = static_cast<uint32_t>(m_descriptors.size());
m_descriptors.emplace_back(m_usedMemorySize, _args...);
return id;
}
void beginBlock(profiler::Block& _block);
void endBlock();
void setEnabled(bool isEnable);
uint32_t dumpBlocksToFile(const char* filename);
void setThreadName(const char* name, const char* filename, const char* _funcname, int line);
const char* setThreadName(const char* name, const char* filename, const char* _funcname, int line);
void setContextSwitchLogFilename(const char* name)
{
@ -201,28 +209,19 @@ public:
return m_csInfoFilename.c_str();
}
void _cswitchBeginBlock(profiler::timestamp_t _time, profiler::block_id_t _id, profiler::thread_id_t _thread_id);
void _cswitchStoreBlock(profiler::timestamp_t _time, profiler::block_id_t _id, profiler::thread_id_t _thread_id);
void _cswitchEndBlock(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime);
void beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id);
void storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id);
void endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime);
private:
template <class ... TArgs>
uint32_t addBlockDescriptor(TArgs ... _args)
{
guard_lock_t lock(m_storedSpin);
const auto id = static_cast<uint32_t>(m_descriptors.size());
m_descriptors.emplace_back(m_usedMemorySize, _args...);
return id;
}
ThreadStorage& threadStorage(profiler::thread_id_t _thread_id)
{
guard_lock_t lock(m_spin);
return m_threads[_thread_id];
}
ThreadStorage* _threadStorage(profiler::thread_id_t _thread_id)
ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id)
{
guard_lock_t lock(m_spin);
auto it = m_threads.find(_thread_id);

View File

@ -16,53 +16,87 @@ You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef ________SPIN_LOCK__________H______
#define ________SPIN_LOCK__________H______
#ifndef EASY_PROFILER__SPIN_LOCK__________H______
#define EASY_PROFILER__SPIN_LOCK__________H______
#define EASY_USE_CRITICAL_SECTION // Use CRITICAL_SECTION instead of std::atomic_flag
#if defined(_WIN32) && defined(EASY_USE_CRITICAL_SECTION)
#include <Windows.h>
#else
#include <atomic>
namespace profiler{
class spin_lock
{
std::atomic_flag m_lock;
public:
void lock()
{
while (m_lock.test_and_set(std::memory_order_acquire)){}
}
void unlock()
{
m_lock.clear(std::memory_order_release);
}
spin_lock(){
m_lock.clear();
}
};
template<class T>
class guard_lock
{
T& m_mutex;
bool m_isLocked = false;
public:
explicit guard_lock(T& m) :m_mutex(m){
m_mutex.lock();
m_isLocked = true;
}
~guard_lock(){
this->release();
}
inline void release(){
if (m_isLocked){
m_mutex.unlock();
m_isLocked = false;
}
}
};
}
#endif
namespace profiler {
#if defined(_WIN32) && defined(EASY_USE_CRITICAL_SECTION)
// std::atomic_flag on Windows works slower than critical section, so we will use it instead of std::atomic_flag...
// By the way, Windows critical sections are slower than std::atomic_flag on Unix.
class spin_lock final { CRITICAL_SECTION m_lock; public:
void lock() {
EnterCriticalSection(&m_lock);
}
void unlock() {
LeaveCriticalSection(&m_lock);
}
spin_lock() {
InitializeCriticalSection(&m_lock);
}
~spin_lock() {
DeleteCriticalSection(&m_lock);
}
};
#else
// std::atomic_flag on Unix works fine and very fast (almost instant!)
class spin_lock final { ::std::atomic_flag m_lock; public:
void lock() {
while (m_lock.test_and_set(::std::memory_order_acquire));
}
void unlock() {
m_lock.clear(::std::memory_order_release);
}
spin_lock() {
m_lock.clear();
}
};
#endif
template <class T>
class guard_lock final
{
T& m_lock;
bool m_isLocked = false;
public:
explicit guard_lock(T& m) : m_lock(m) {
m_lock.lock();
m_isLocked = true;
}
~guard_lock() {
unlock();
}
inline void unlock() {
if (m_isLocked) {
m_lock.unlock();
m_isLocked = false;
}
}
};
} // END of namespace profiler.
#ifdef EASY_USE_CRITICAL_SECTION
# undef EASY_USE_CRITICAL_SECTION
#endif
#endif // EASY_PROFILER__SPIN_LOCK__________H______