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

Thread: Re-add <new> include for std::hardware_destructive_interference_size #101004

Merged

Conversation

akien-mga
Copy link
Member

@akien-mga akien-mga commented Jan 1, 2025

Somehow it would still build fine, but would crash when compiled with GCC 12.2 on Debian 12.

Also re-add wrongly removed Mutex include from thread_safe.h, where it's used in macros.

Add IWYU pragma comments to prevent it from mistakenly flagging those as unused.

CC @RandomShaper @hpvb
I still don't know why it would compile OK without <new>, but then crash, but I've confirmed that this patch fixes the crash with Debian 12's GCC 12.2.0.

@akien-mga akien-mga added this to the 4.4 milestone Jan 1, 2025
@akien-mga akien-mga requested a review from a team as a code owner January 1, 2025 20:52
@@ -31,6 +31,8 @@
#ifndef THREAD_SAFE_H
#define THREAD_SAFE_H

#include "core/os/mutex.h" // IWYU pragma: keep // Used in macro.
Copy link
Member Author

Choose a reason for hiding this comment

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

CC @Repiteo, I took this format from #100918.

Does that work for clangd too and not just include-what-you-use?

Copy link
Contributor

Choose a reason for hiding this comment

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

It indeed worked for clangd, so much so that it's part of their guidelines:

https://clangd.llvm.org/guides/include-cleaner#scenarios-and-solutions

@akien-mga akien-mga force-pushed the core-thread-fix-gcc12-crash branch from 10c28f2 to a2b5460 Compare January 1, 2025 20:57
core/os/thread.h Outdated
Comment on lines 52 to 54
#if defined(__cpp_lib_hardware_interference_size) && !defined(ANDROID_ENABLED)
#include <new> // For CACHE_LINE_BYTES.
#endif
Copy link
Member Author

@akien-mga akien-mga Jan 1, 2025

Choose a reason for hiding this comment

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

I'm assuming that adding the #if conditions matching where it's used will prevent tools like iwyu from flagging this as unused when run on platforms which don't fulfill that condition (seems to be GCC specific, possibly supported on Clang, but thus not MSVC most likely).

If not, we should add a pragma comment to prevent its removal, but then I wouldn't trust the judgement of those tools.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok this is what broke my fix, to test __cpp_lib_hardware_interference_size we first need to include <new>.

So pragma comment it will be.

@akien-mga
Copy link
Member Author

akien-mga commented Jan 1, 2025

I went maybe a tiny bit heavy handed on tweaking code style / #ifdefs in a critical bugfix, so to be clear the minimal fix is just this (i.e. a partial revert of #100564):

diff --git a/core/os/thread.h b/core/os/thread.h
index 066c4befa7..1c442b41f6 100644
--- a/core/os/thread.h
+++ b/core/os/thread.h
@@ -42,6 +42,8 @@
 #include "core/templates/safe_refcount.h"
 #include "core/typedefs.h"
 
+#include <new>
+
 #ifdef MINGW_ENABLED
 #define MINGW_STDTHREAD_REDUNDANCY_WARNING
 #include "thirdparty/mingw-std-threads/mingw.thread.h"
diff --git a/core/os/thread_safe.h b/core/os/thread_safe.h
index 316b0c9687..042a0b7d98 100644
--- a/core/os/thread_safe.h
+++ b/core/os/thread_safe.h
@@ -31,6 +31,8 @@
 #ifndef THREAD_SAFE_H
 #define THREAD_SAFE_H
 
+#include "core/os/mutex.h"
+
 #define _THREAD_SAFE_CLASS_ mutable Mutex _thread_safe_;
 #define _THREAD_SAFE_METHOD_ MutexLock _thread_safe_method_(_thread_safe_);
 #define _THREAD_SAFE_LOCK_ _thread_safe_.lock();

This PR should be equivalent to that diff if I didn't mess up.

@akien-mga akien-mga requested review from hpvb and RandomShaper January 1, 2025 21:08
@akien-mga akien-mga marked this pull request as draft January 1, 2025 21:13
@akien-mga
Copy link
Member Author

akien-mga commented Jan 1, 2025

Taking back as draft for a bit, the latest iteration of my PR doesn't seem to fix the crash anymore... Seems like some of the "harmless" refactoring I've done may have caused harm? Or the problem is deeper than I thought.

…rence_size`

Somehow it would still build fine, but would crash when compiled with GCC 12.2
on Debian 12.

Also re-add wrongly removed Mutex include from `thread_safe.h`, where it's used
in macros.

Add IWYU pragma comments to prevent it from mistakenly flagging those as unused.
@akien-mga akien-mga force-pushed the core-thread-fix-gcc12-crash branch from a2b5460 to f2d4dac Compare January 1, 2025 21:44
@akien-mga akien-mga marked this pull request as ready for review January 1, 2025 21:57
@akien-mga
Copy link
Member Author

akien-mga commented Jan 1, 2025

OK I fixed the issue, the include of <new> can't be made conditional on defined(__cpp_lib_hardware_interference_size) because <new> is needed to check that feature.

Which hints to why everything compiled fine despite the removal of <new>. It meant that (at least occasionally) we were falling back to the #else case here:

#if defined(__cpp_lib_hardware_interference_size) && !defined(ANDROID_ENABLED) // This would be OK with NDK >= 26.
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Winterference-size"
#endif
        static constexpr size_t CACHE_LINE_BYTES = std::hardware_destructive_interference_size;
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
#else
        // At a negligible memory cost, we use a conservatively high value.
        static constexpr size_t CACHE_LINE_BYTES = 128;
#endif

Either it doesn't work properly, or we're mixing both branches depending on whether thread.h is included in code that already includes <new> or not.

I made this quick test to confirm (on master with Debian 12's GCC 12.2.0, i.e. without re-adding <new> in thread.h):

diff --git a/core/os/thread.h b/core/os/thread.h
index 066c4be..6d1ac06 100644
--- a/core/os/thread.h
+++ b/core/os/thread.h
@@ -90,11 +90,13 @@ public:
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Winterference-size"
 #endif
+       #warning Good branch.
        static constexpr size_t CACHE_LINE_BYTES = std::hardware_destructive_interference_size;
 #if defined(__GNUC__) && !defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
 #else
+       #warning Bad branch.
        // At a negligible memory cost, we use a conservatively high value.
        static constexpr size_t CACHE_LINE_BYTES = 128;
 #endif

Here are the full build logs:
godot-thread-build.log

There's indeed a few "Bad branches." in there:

$ cat ~/godot-thread-build.log | grep "Good branch" | wc -l
2462
$ cat ~/godot-thread-build.log | grep "Bad branch" | wc -l
44

Including <new> explicitly prevents this, but this hints that we might have more such problems in the codebase where a file is wrongly compiled taking diverging code paths, and the consequences can be disastrous, as seen here. We should really look into this and see if we can detect and prevent this.

@fire
Copy link
Member

fire commented Jan 2, 2025

Somehow it would still build fine, but would crash when compiled with GCC 12.2 on Debian 12.

I think the only way is build with Github actions, and then test with a benchmark server with a gpu, but it might to be too expensive.

@akien-mga akien-mga merged commit 22f4322 into godotengine:master Jan 3, 2025
20 checks passed
@akien-mga akien-mga deleted the core-thread-fix-gcc12-crash branch January 3, 2025 00:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Godot crash on Linux when compiled with GCC 12
4 participants