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

Add support for freeing handles automatically #3013

Merged
merged 4 commits into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 55 additions & 3 deletions crates/libs/bindgen/src/rust/handles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub fn gen_win_handle(writer: &Writer, def: metadata::TypeDef) -> TokenStream {
let ident = to_ident(name);
let underlying_type = def.underlying_type();
let signature = writer.type_default_name(&underlying_type);
let check = if underlying_type.is_pointer() {
let is_invalid = if underlying_type.is_pointer() {
quote! {
impl #ident {
pub fn is_invalid(&self) -> bool {
Expand Down Expand Up @@ -63,12 +63,47 @@ pub fn gen_win_handle(writer: &Writer, def: metadata::TypeDef) -> TokenStream {
}
};

let free = if let Some(function) = free_function(def) {
if is_invalid.is_empty() {
// TODO: https://github.com/microsoft/win32metadata/issues/1891
quote! {}
} else {
let name = to_ident(function.name());
let signature = metadata::method_def_signature(def.namespace(), function, &[]);

// BCryptCloseAlgorithmProvider has an unused trailing parameter.
let tail = if signature.params.len() > 1 {
quote! { , 0 }
} else {
quote! {}
};
kennykerr marked this conversation as resolved.
Show resolved Hide resolved

let result = if signature.return_type == metadata::Type::Void {
quote! {}
} else {
quote! { _ = }
};

quote! {
impl windows_core::Free for #ident {
unsafe fn free(&mut self) {
if !self.is_invalid() {
#result #name(*self #tail);
}
}
}
}
}
} else {
quote! {}
};

let mut tokens = quote! {
#[repr(transparent)]
// Unfortunately, Rust requires these to be derived to allow constant patterns.
#[derive(PartialEq, Eq)]
pub struct #ident(pub #signature);
#check
#is_invalid
#free
impl Default for #ident {
fn default() -> Self {
unsafe { core::mem::zeroed() }
Expand Down Expand Up @@ -116,3 +151,20 @@ fn type_def_usable_for(row: metadata::TypeDef) -> Option<metadata::TypeDef> {
}
None
}

fn free_function(def: metadata::TypeDef) -> Option<metadata::MethodDef> {
if let Some(attribute) = def.find_attribute("RAIIFreeAttribute") {
// TODO: https://github.com/microsoft/win32metadata/issues/1892
if matches!(def.name(), "COMPRESSOR_HANDLE" | "WSAEVENT") {
return None;
}

if let Some((_, metadata::Value::String(name))) = attribute.args().first() {
if let Some((method, _)) = def.reader().get_method_def(def.namespace(), name.as_str()).next() {
return Some(method);
}
}
}

None
}
45 changes: 45 additions & 0 deletions crates/libs/core/src/handles.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/// Custom code to free a handle.
///
/// This is similar to the `Drop` trait, and may be used to implement `Drop`, but allows handles
kennykerr marked this conversation as resolved.
Show resolved Hide resolved
/// to be dropped depending on context.
pub trait Free {
/// Calls the handle's free function.
///
/// # Safety
/// The handle must be owned by the caller and safe to free.
unsafe fn free(&mut self);
}

/// A wrapper to provide ownership for handles to automatically drop via the handle's `Free` trait.
kennykerr marked this conversation as resolved.
Show resolved Hide resolved
#[repr(transparent)]
#[derive(Clone, PartialEq, Eq, Default, Debug)]
kennykerr marked this conversation as resolved.
Show resolved Hide resolved
pub struct Owned<T: Free>(T);

impl<T: Free> Owned<T> {
/// Takes ownership of the handle.
///
/// # Safety
/// The handle must be owned by the caller and safe to free.
pub unsafe fn new(x: T) -> Self {
Self(x)
}
}

impl<T: Free> Drop for Owned<T> {
fn drop(&mut self) {
unsafe { self.0.free() };
}
}

impl<T: Free> std::ops::Deref for Owned<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<T: Free> std::ops::DerefMut for Owned<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
2 changes: 2 additions & 0 deletions crates/libs/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod array;
mod as_impl;
mod event;
mod guid;
mod handles;
mod inspectable;
mod interface;
mod param;
Expand All @@ -33,6 +34,7 @@ pub use array::*;
pub use as_impl::*;
pub use event::*;
pub use guid::*;
pub use handles::*;
pub use inspectable::*;
pub use interface::*;
pub use param::*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,13 @@ impl ORHKEY {
self.0 == -1 || self.0 == 0
}
}
impl windows_core::Free for ORHKEY {
unsafe fn free(&mut self) {
if !self.is_invalid() {
_ = ORCloseKey(*self);
}
}
}
impl Default for ORHKEY {
fn default() -> Self {
unsafe { core::mem::zeroed() }
Expand Down
14 changes: 14 additions & 0 deletions crates/libs/windows/src/Windows/Win32/Devices/Bluetooth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2465,6 +2465,13 @@ impl HBLUETOOTH_DEVICE_FIND {
self.0 == -1 || self.0 == 0
}
}
impl windows_core::Free for HBLUETOOTH_DEVICE_FIND {
unsafe fn free(&mut self) {
if !self.is_invalid() {
_ = BluetoothFindDeviceClose(*self);
}
}
}
impl Default for HBLUETOOTH_DEVICE_FIND {
fn default() -> Self {
unsafe { core::mem::zeroed() }
Expand Down Expand Up @@ -2492,6 +2499,13 @@ impl HBLUETOOTH_RADIO_FIND {
self.0 == -1 || self.0 == 0
}
}
impl windows_core::Free for HBLUETOOTH_RADIO_FIND {
unsafe fn free(&mut self) {
if !self.is_invalid() {
_ = BluetoothFindRadioClose(*self);
}
}
}
impl Default for HBLUETOOTH_RADIO_FIND {
fn default() -> Self {
unsafe { core::mem::zeroed() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8392,6 +8392,13 @@ impl HDEVINFO {
self.0 == -1 || self.0 == 0
}
}
impl windows_core::Free for HDEVINFO {
unsafe fn free(&mut self) {
if !self.is_invalid() {
_ = SetupDiDestroyDeviceInfoList(*self);
}
}
}
impl Default for HDEVINFO {
fn default() -> Self {
unsafe { core::mem::zeroed() }
Expand Down
7 changes: 7 additions & 0 deletions crates/libs/windows/src/Windows/Win32/Devices/Display/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6245,6 +6245,13 @@ impl HSEMAPHORE {
self.0 == -1 || self.0 == 0
}
}
impl windows_core::Free for HSEMAPHORE {
unsafe fn free(&mut self) {
if !self.is_invalid() {
EngDeleteSemaphore(*self);
}
}
}
impl Default for HSEMAPHORE {
fn default() -> Self {
unsafe { core::mem::zeroed() }
Expand Down
7 changes: 7 additions & 0 deletions crates/libs/windows/src/Windows/Win32/Devices/Usb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5968,6 +5968,13 @@ impl WINUSB_INTERFACE_HANDLE {
self.0 == 0
}
}
impl windows_core::Free for WINUSB_INTERFACE_HANDLE {
unsafe fn free(&mut self) {
if !self.is_invalid() {
_ = WinUsb_Free(*self);
}
}
}
impl Default for WINUSB_INTERFACE_HANDLE {
fn default() -> Self {
unsafe { core::mem::zeroed() }
Expand Down
35 changes: 35 additions & 0 deletions crates/libs/windows/src/Windows/Win32/Foundation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10785,6 +10785,13 @@ impl HANDLE {
self.0 == -1 || self.0 == 0
}
}
impl windows_core::Free for HANDLE {
unsafe fn free(&mut self) {
if !self.is_invalid() {
_ = CloseHandle(*self);
}
}
}
impl Default for HANDLE {
fn default() -> Self {
unsafe { core::mem::zeroed() }
Expand Down Expand Up @@ -10834,6 +10841,13 @@ impl HGLOBAL {
self.0.is_null()
}
}
impl windows_core::Free for HGLOBAL {
unsafe fn free(&mut self) {
if !self.is_invalid() {
_ = GlobalFree(*self);
}
}
}
impl Default for HGLOBAL {
fn default() -> Self {
unsafe { core::mem::zeroed() }
Expand Down Expand Up @@ -10861,6 +10875,13 @@ impl HINSTANCE {
self.0 == 0
}
}
impl windows_core::Free for HINSTANCE {
unsafe fn free(&mut self) {
if !self.is_invalid() {
_ = FreeLibrary(*self);
}
}
}
impl Default for HINSTANCE {
fn default() -> Self {
unsafe { core::mem::zeroed() }
Expand Down Expand Up @@ -10894,6 +10915,13 @@ impl HLOCAL {
self.0.is_null()
}
}
impl windows_core::Free for HLOCAL {
unsafe fn free(&mut self) {
if !self.is_invalid() {
_ = LocalFree(*self);
}
}
}
impl Default for HLOCAL {
fn default() -> Self {
unsafe { core::mem::zeroed() }
Expand Down Expand Up @@ -10943,6 +10971,13 @@ impl HMODULE {
self.0 == 0
}
}
impl windows_core::Free for HMODULE {
unsafe fn free(&mut self) {
if !self.is_invalid() {
_ = FreeLibrary(*self);
}
}
}
impl Default for HMODULE {
fn default() -> Self {
unsafe { core::mem::zeroed() }
Expand Down
Loading