-
Notifications
You must be signed in to change notification settings - Fork 910
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 a better strategy on Windows for main thread detection #4006
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO all this is all a bit much for just a debug check to make developing on other platforms easier...
Why, again, isn't this just a warning that can be turned off?
12fd29b
to
849a8df
Compare
It's to make platforms consist. Ideally every platform would act the exact same way to prevent differences in platforms from becoming too large. Not to mention, it can be turned off via the |
let mut slot = mem::MaybeUninit::uninit(); | ||
let result = if self.first_entry { | ||
self.first_entry = false; | ||
unsafe { toolhelp::Thread32First(self.handle.as_raw_handle() as _, slot.as_mut_ptr()) } | ||
} else { | ||
unsafe { toolhelp::Thread32Next(self.handle.as_raw_handle() as _, slot.as_mut_ptr()) } | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to initialize the dwSize
field of THREADENTRY32
, right now this call just errors and no threads get iterated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed!
Yeah this seems to work and is probably watertight enough. Though I have to point out it's possible for it to false negative if you terminate the real main thread by doing something like calling I do still want to point out though that this really should be put behind |
fn new(process_id: u32) -> Result<Self, EventLoopError> { | ||
// Take a snapshot. | ||
let handle = | ||
unsafe { toolhelp::CreateToolhelp32Snapshot(toolhelp::TH32CS_SNAPTHREAD, process_id) }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can pass 0
to CreateToolhelp32Snapshot
, no need to pass the current process ID.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use the process ID in other places, so I figure why not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the docs:
This parameter can be zero to indicate the current process.
So it's not really necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I figure it's slightly clearer to whoever is reading who may not be familiar with this API.
849a8df
to
0767a5f
Compare
This is weird enough and requires enough unsafe code that I'm fine if someone gets around it like this.
Like I said, all platforms should have uniform behavior. Differences between platforms should be considered a bug, save for features not being supported. |
We can get a list of the threads in the process, and determine which thread came first. This thread will be the one who called the main function. So use this instead of the current strategy if it's available. Signed-off-by: John Nunley <dev@notgull.net>
0767a5f
to
9c9dde9
Compare
I understand that, however the only reason "consistency between platforms" is even being enforced here is for development reasons. There is no situation where library consumers ever want this code to be running on a release build. Best case scenario the bomb never goes off. Worst case scenario there there is another scenario that can break our assumption, and it blows up in somebody's face on a shipped game 5 years in the future. Maybe that's triggered by some compat layer like Wine. Maybe some modder is trying to do something funny. Regardless, this check serves no positive purpose once the application leaves the developers' machine, and that is best ensured by removing it. As a developer I myself would not like this kind of code to be present when I am shipping a game. There is no good reason to have this risk factor in a production-grade library. "You can turn it off with Think of this with a thought experiment:
By removing this you're reducing moving parts, you're removing unnecessary work (seriously, why would my game, on startup on any person's PC, seriously need to iterate all threads in the OS?), you're just reducing the chance of things going wrong. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also think keeping the main_thread_id_via_crt
at this point is pointless. I assume we can envision no scenario in which main_thread_id_via_snapshot
will ever fail, but we do know scenarios in which main_thread_id_via_crt
fails. Because of this we should at least remove it, so that if main_thread_id_via_snapshot
fails we do not end back up with the original issue unaddressed.
We can get a list of the threads in the process, and determine which
thread came first. This thread will be the one who called the main
function. So use this instead of the current strategy if it's available.
This aims to resolve #3999. @PJB3005 would you be able to test this in the dylib configuration?
changelog
module if knowledge of this change could be valuable to users