Using certain parts of wx from a thread other than wxThread results in a memory leak.
For example, using wxSemaphore causes wxLogTrace calls that create a wxThreadSpecificInfo object that then never gets freed. This was tested using wxGTK 3.2.2.1:
`#include <wx/wx.h>
#include
int main(int argc, char **argv) {
wxInitializer initializer;
wxSemaphore sema;
for (;;) {
std::async(std::launch::async, &sema { sema.Post(); });
sema.Wait();
}
return 0;
}
`
This commit tries to work around the problem by only creating the wxThreadSpecificInfo from wxThreads, and otherwise reverting to using common log objects.
Notably in master this would probably be a trivial thing to work around by just creating the wxThreadSpecificInfo thread_local, but I wanted a workaround for 3.2.
Another place which this will affect is wxTranslations::GetUntranslatedString. I switched it to use a common critical section protected hashmap, which should work just find in my understanding. Although I am not 100% sure if the hash map created by WX_DECLARE_HASH_SET keeps the addresses of its items intact when inserting new items. std::unordered_set should be safe and I am assuming this works the same way.
https://github.com/wxWidgets/wxWidgets/pull/23535
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@vadz commented on this pull request.
Thanks for looking into this!
This is definitely a problem which needs to be fixed, but while I've started
writing a reply saying that I didn't see a way to do it in any other way, I've finally deleted it because I think I do see a way, or even a couple of ways, to do it better.
Basically, we just need to ensure that wxThreadSpecificInfo::ThreadCleanUp() is called on any thread exit, not just when wxThread terminates. And it should be possible to do it:
thread_local object whose dtor would be executed -- if my understanding is correct, I didn't test it -- whenever a thread terminates. Then it would be simply a matter of calling ThreadCleanUp() from it. And I think the ordering should be correct, i.e. it should happen after all the rest of the thread code ends running.std::thread and so people using it are much more likely to use wxThread, we could still do it in platform-specific way by registering a function to call on thread exit when creating wxThreadSpecificInfo:Doing this would be much preferable to this PR because it wouldn't (silently!) change the behaviour of the existing code, which is really problematic, especially in 3.2 branch.
It would be great if you could please try re-fixing this problem in this way, so as to preserve the current behaviour and let wxLog etc work as it does now.
TIA!
> - return oldLogger; + wxASSERT_MSG( false, "SetThreadActiveTarget() only works when called from a wxThread" );
This should be a wxCHECK_MSG( wxThreadInfo, ...) above (would also preserve the indent of the existing code).
> }
/* static */
bool wxLog::EnableThreadLogging(bool enable)
{
- const bool wasEnabled = !wxThreadInfo.loggingDisabled;
- wxThreadInfo.loggingDisabled = !enable;
+ const bool wasEnabled = IsThreadLoggingEnabled();
+ if ( wxThreadInfo )
+ wxThreadInfo->loggingDisabled = !enable;
+ else
+ wxASSERT_MSG(enable, "Disabling thread logging only works for wxThreads");
Same remark as above.
In src/common/translation.cpp:
>
/* static */
const wxString& wxTranslations::GetUntranslatedString(const wxString& str)
{
- wxLocaleUntranslatedStrings& strings = wxThreadInfo.untranslatedStrings;
-
- wxLocaleUntranslatedStrings::iterator i = strings.find(str);
- if ( i == strings.end() )
- return *strings.insert(str).first;
-
- return *i;
+ static wxCriticalSection s_cs;
This is only MT-safe in C++11, in C++98 we'd need to make it global. Not sure if it's worth changing this...
In include/wx/private/threadinfo.h:
> + // Initialize the wxThreadSpecificInfo. + // If this is not called from a thread, the wxThreadInfo will remain NULL + // in that thread's context. Use ThreadCleanUp to free when thread ends. + static void ThreadInitialize();
It would make sense to have a wxThreadInfoInitializer class calling this in its ctor and CleanUp() in its dtor now that we have both functions.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Alright, thanks for the feedback. I'll see if I can get the ThreadCleanUp call to work on all threads. I actually have an FLS-based TLS implementation somewhere which might be usable here...
I believe with C++11 thread_local would make the entire ThreadCleanUp obsolete: you could just implement the wxThreadSpecificInfo::Get() like:
thread_local wxThreadSpecificInfo info;
return info;
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Alright, thanks for the feedback. I'll see if I can get the ThreadCleanUp call to work on all threads. I actually have an FLS-based TLS implementation somewhere which might be usable here...
Note that this would only be needed to support non-wxThread uses from C++98 programs. I think this should be quite rare...
I believe with C++11 thread_local would make the entire ThreadCleanUp obsolete: you could just implement the wxThreadSpecificInfo::Get() like:
thread_local wxThreadSpecificInfo info;return info;
Hmm, yes, it could actually indeed be as simple as this. And if so, we should do it -- please don't hesitate making a PR against master simplifying this. TIA!
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Note that this would only be needed to support non-
wxThreaduses from C++98 programs. I think this should be quite rare...
Well, the problem is there regardless of the C++ version in main program as long as wxWidgets itself uses C++98, is it not? Once wxWidgets switches to C++11 (as it has in master?) the workaround becomes obsolete, but meanwhile I'd like to have a workaround for stable 3.2.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@nietsu pushed 1 commit.
—
View it on GitHub or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Does this look more like what you had in mind? (overlooking the stray #include <wx/tls.h> I seem to have left in thread.cpp...)
I'm looking at master now as well. I think the entire threadinfo.h/cpp files might be obsolete. You could just declare thread_local logger and loggingdisabled in log.cpp and thread_local strings in wxTranslations::GetUntranslatedString. There's a comment about wxThreadSpecificInfo being about preserving TLS slots, and I don't think that's really a concern anymore with thread_local?
Alternatively I could just alter the wxThreadSpecificInfo::Get to use thread_local, remove the ThreadCleanUp, and keep the rest unchanged.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@nietsu pushed 1 commit.
—
View it on GitHub or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Just a quick note: apparently old MSVC and MinGW don't have the header, see AppVeyor build logs, e.g.
..\..\src\msw\thread.cpp(44) : fatal error C1083: Cannot open include file: 'fibersapi.h': No such file or directory
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Does this look more like what you had in mind?
Yes, this looks good, thanks! We just need to declare the function and load it dynamically as we can't rely on having it in the headers/import library :-(
I'm looking at master now as well. I think the entire threadinfo.h/cpp files might be obsolete.
Yes, I think you're right.
You could just declare thread_local logger and loggingdisabled in log.cpp and thread_local strings in wxTranslations::GetUntranslatedString. There's a comment about wxThreadSpecificInfo being about preserving TLS slots, and I don't think that's really a concern anymore with thread_local?
I don't know how is thread_local implemented internally. I suspect that they already do the equivalent of what wx code does, i.e. use a single TLS slot to store all thread local variables as otherwise I don't see how could it work. But I'm not 100% sure.
Alternatively I could just alter the wxThreadSpecificInfo::Get to use thread_local, remove the ThreadCleanUp, and keep the rest unchanged.
This would be the simplest/fastest change, so if you don't have time to do the rest, doing just this would be already nice. But I'd welcome getting rid of it entirely too.
Thanks again!
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@nietsu pushed 1 commit.
—
View it on GitHub or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Another day, another version.
There was a note on Fls function documentation that they are not supported until Vista. I added Tls fallbacks as workaround, but they require the cleanup mechanic again, meaning that the leak is still there on Windows XP. Still an improvement I guess.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@nietsu pushed 1 commit.
—
View it on GitHub or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@nietsu pushed 1 commit.
—
View it on GitHub or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Thanks for all this work, looks good to me now! I'm going to merge it soon with just some minor reformatting (to better conform to wx style) and rewording.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Merged now, thanks again!
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Closed #23535.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()