Skip to content

Commit

Permalink
Add support for freeing handles automatically (#3013)
Browse files Browse the repository at this point in the history
  • Loading branch information
kennykerr committed May 1, 2024
1 parent 4fcb8d7 commit 52352c0
Show file tree
Hide file tree
Showing 32 changed files with 520 additions and 31 deletions.
58 changes: 55 additions & 3 deletions crates/libs/bindgen/src/rust/handles.rs
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! {}
};

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
@@ -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
/// 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.
#[repr(transparent)]
#[derive(Clone, PartialEq, Eq, Default, Debug)]
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
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
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
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
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
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
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
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

0 comments on commit 52352c0

Please sign in to comment.