Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use thread_local instead of static in EASY_LOCAL_STATIC_PTR. #130

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from

Conversation

madevgeny
Copy link

Hi, please look at my changes, without them it's impossible to profile functions called from multiple threads.

…ut it it's impossible to profile functions called from multiple threads.
@cas4ey
Copy link
Collaborator

cas4ey commented Oct 2, 2018

Hi
Which compiler do you use?
The branch you have changed should fire only for compilers with support of c++11 thread-safe statics.

@madevgeny
Copy link
Author

Hi
Which compiler do you use?
The branch you have changed should fire only for compilers with support of c++11 thread-safe statics.

I use visual studio 2015.

I have the following case:
there is method

void ed::PoolTask::run()
{
EASY_BLOCK(id.c_str()); // it's unique for each object.
func();
EASY_END_BLOCK;
}

Which is called from many threads. So if you use static variable it'll be changed in the middle of profiling. It appears under relatively heavy load.

@madevgeny
Copy link
Author

I think this issue isn't compiler specific.

@cas4ey
Copy link
Collaborator

cas4ey commented Oct 3, 2018

C++11 standard guarantees that local static initialization is thread-safe. And this static in EASY_BLOCK doesn't change after it is initialized (and additionally it's initialization is performed with a thread-safe method).

I think your problem is related to the usage of id.c_str() - you must ensure that it will exist and will not change during EASY_BLOCK execution. Try to replace id.c_str() with a compie-time string and if it is OK, then check again that id does not change during block execution.

@cas4ey
Copy link
Collaborator

cas4ey commented Oct 3, 2018

For example:

void ed::PoolTask::run()
{
    const std::string local_id = id; // try to use temporary buffer
    EASY_BLOCK(local_id.c_str());
    func();
    // EASY_END_BLOCK; // this is not necessary because EASY_BLOCK using RAII
}

@cas4ey
Copy link
Collaborator

cas4ey commented Oct 4, 2018

Hi @madevgeny
Any news?

@madevgeny
Copy link
Author

madevgeny commented Oct 4, 2018

Hi, it appears that we use /Zc:threadSafeInit- flag which disables thread safe statics. We need it for our protection system and unfortunately we will not be able to change it in near future.

I attached simple example to reproduce crash. But with my patch it works without crash.

Another thought. EASY_BLOCK unrolls to something like this:

static const ::profiler::BaseBlockDescriptor* desc = ::profiler::registerDescription("unique arguments");
... other code

if we call the same function from multiple threads but with different "unique arguments" we will write to desc pointers to different objects. So there is no guarantee that after writing pointer to desc we will have read the same pointer from it. As static variable desc can be changed from other threads. tread_local solves this problem too.

easy_crash.zip

@madevgeny
Copy link
Author

For example:

void ed::PoolTask::run()
{
    const std::string local_id = id; // try to use temporary buffer
    EASY_BLOCK(local_id.c_str());
    func();
    // EASY_END_BLOCK; // this is not necessary because EASY_BLOCK using RAII
}

No, id is member of PoolTask class.

@cas4ey
Copy link
Collaborator

cas4ey commented Oct 4, 2018

Hi, it appears that we use /Zc:threadSafeInit- flag which disables thread safe statics. We need it for our protection system and unfortunately we will not be able to change it in near future.

Well, this explains the problem :-)

If this flag could be checked at compile-time in source code then the fix will be:
(I don't know is there _MSC_ZC_THREAD_SAFE_INIT or something like this or not)

#ifndef EASY_LOCAL_STATIC_PTR
# if defined(_MSC_VER) && defined(_MSC_ZC_THREAD_SAFE_INIT) // Something like this
#  define EASY_LOCAL_STATIC_PTR(VarType, VarName, VarInitializer) thread_local static VarType ...
# else // This is existing branch:
#  define EASY_LOCAL_STATIC_PTR(VarType, VarName, VarInitializer) static VarType ...
#  define EASY_MAGIC_STATIC_AVAILABLE
#endif

If it's impossible to check the flag in source code then you will have to add new CMake option into easy_profiler_core/CMakeLists.txt and check it in the same way (the code would be exactly the same like in example above).

@cas4ey
Copy link
Collaborator

cas4ey commented Oct 4, 2018

It would be great if you could fix it :-)

…As without it it's impossible to profile functions called from multiple threads."

This reverts commit 3ee2067.
@madevgeny
Copy link
Author

There is no macros to detect /Zc:threadSafeInit- at compile time.

I'll add option to easy_profiler_core/CMakeLists.txt.

evgeny added 2 commits October 5, 2018 17:33
…safe initialization of static local variables.
…in project using profiler.

EASY_OPTION_THREAD_SAFE_INIT doesn't require setting it in project using profiler.
@madevgeny
Copy link
Author

Hi, I added EASY_OPTION_THREAD_SAFE_INIT option and generation of config header.

# define EASY_MAGIC_STATIC_AVAILABLE
# if defined(_MSC_VER) && EASY_DISABLE_THREAD_SAFE_INIT != 0
# define EASY_LOCAL_STATIC_PTR(VarType, VarName, VarInitializer) thread_local static VarType VarName = VarInitializer
# define EASY_MAGIC_STATIC_AVAILABLE
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EASY_MAGIC_STATIC_AVAILABLE should not be defined if thread-safe static init is disabled

set(EASY_DISABLE_THREAD_SAFE_INIT 1)
endif ()

configure_file (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you, please, use existing easy_define_target_option cmake command instead of generating new file?

@dmikushin
Copy link

Is it true that this PR addresses a critical multuthreading usability issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants