-
Notifications
You must be signed in to change notification settings - Fork 569
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
Replace mem::uninitialized with mem::MaybeUninit #165
Conversation
@@ -391,7 +391,7 @@ impl WndProc for MyWndProc { | |||
if let Ok(mut s) = self.state.try_borrow_mut() { | |||
let s = s.as_mut().unwrap(); | |||
if s.dcomp_state.is_some() { | |||
let mut rect: RECT = mem::uninitialized(); |
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.
This stuff is new to me too, but reading the docs for MaybeUninit
it doesn't sound like assume_init
is what we want. I think we want to just use uninit()
, and then call assum_init
after we've called the external function.
Basically what's happening in all these places is we're passing a memory location to the windows API, which is supposed to write something to that location; so the memory is not safe to access until after that write has happened (and I imagine all of these winapi functions return a flag indicating if there is an error or not, and if there's an error we should also assume the memory is uninitialized...)
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.
Yeah, so the current refactoring initializes the memory to safe values and assumes init, which I guess is only slightly safer than the previous use of mem::uninitialized
- I was just treading the path of least resistance there. The problem then becomes whether passing MaybeUninit
to these external functions is the same as passing the pointers themselves, or whether we need to do some black magic there. 🤔I wasn't able to tell from the docs how MaybeUninit translates externally.
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.
ah okay, I guess the main question I'd have is whether zeros
is valid data for all the types used here? If it is then that works for me!
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.
and yes, passing a pointer to the MaybeUninit
is the same as passing a pointer to the value; all MaybeUninit
does is use the type system to force us to be explicit about initialization on the Rust side.
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.
This is fancier than needed for RECT, you can just do mem::zeroed
instead of mem::uninitialized
. It's a tiny optimization not to zero, but when I wrote the code I was doing things "the C way."
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.
Okay, good to know. Now my only concern is with the implications of GetClientRect
returning false, is that something we want to log and/or return an error?
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.
Yes. This is extremely unlikely, but we want to program defensively in any case.
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.
Thanks for the feedback, friends! It's updated now. 😄
druid-shell/src/windows/win_main.rs
Outdated
@@ -84,7 +84,7 @@ impl RunLoop { | |||
|
|||
// Handle windows messages | |||
loop { | |||
let mut msg = mem::uninitialized(); | |||
let mut msg = mem::MaybeUninit::<MSG>::zeroed().assume_init(); |
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.
@cmyr Am unable to find MSG
, which is supposed to be the type for msg here.
RECT
should be fine to zero, as it just contains four numerical values.
DXGI_ADAPTER_DESC
is confusing to me. The type declaration is as follows:
struct DXGI_ADAPTER_DESC {
Description: [WCHAR; 128],
VendorId: UINT,
DeviceId: UINT,
SubSysId: UINT,
Revision: UINT,
DedicatedVideoMemory: SIZE_T,
DedicatedSystemMemory: SIZE_T,
SharedSystemMemory: SIZE_T,
AdapterLuid: LUID,
}
My search-foo fails me here, am unable to find out what LUID
is. On the other hand, I assume that Rust initializes the vector for Description
within the struct instead of using a pointer to an external vector, which would make zeroing it safe.
@@ -391,7 +391,7 @@ impl WndProc for MyWndProc { | |||
if let Ok(mut s) = self.state.try_borrow_mut() { | |||
let s = s.as_mut().unwrap(); | |||
if s.dcomp_state.is_some() { | |||
let mut rect: RECT = mem::uninitialized(); |
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.
This is fancier than needed for RECT, you can just do mem::zeroed
instead of mem::uninitialized
. It's a tiny optimization not to zero, but when I wrote the code I was doing things "the C way."
druid-shell/src/windows/mod.rs
Outdated
@@ -899,7 +899,7 @@ unsafe fn choose_adapter(factory: *mut IDXGIFactory2) -> *mut IDXGIAdapter { | |||
if !SUCCEEDED((*factory).EnumAdapters(i, &mut adapter)) { | |||
break; | |||
} | |||
let mut desc: DXGI_ADAPTER_DESC = mem::uninitialized(); | |||
let mut desc = mem::MaybeUninit::<DXGI_ADAPTER_DESC>::zeroed().assume_init(); |
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 think the way I'd write this is:
let desc = mem::MaybeUninit::uninit();
let hr = (*adapter).GetDesc(desc.as_mut_ptr());
...check hr...
let desc = desc.assume_init();
I'm noticing that the existing code is not checking the return code. This is UB if the call fails.
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.
See inline about wide string manipulation; maybe a separate issue? Thanks for this!
error!("Failed to get adapter description: {:?}", Error::Hr(hr)); | ||
break; | ||
} | ||
let mut desc: DXGI_ADAPTER_DESC = desc.assume_init(); |
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.
It feels like this doesn't really need to be mut
. The problem is that our "util" module takes a LPWSTR
, which is more idiomatically C. I think the solution is to use *const u16
and the from_wide_ptr_null() method from wio and remove our hand-rolled "util" stuff, but that's arguably a separate issue from the scope of this PR, which is specifically about uninit.
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.
Sounds good. I'll add an issue for it.
closes #164
I ran some examples, and everything seemed to work just the same. Using
::zeroed
, I believe the structs in these cases can handle zero values for all of their fields. I am not familiar with the external functions, however, and don't know if we want toassume_init
after calling them.