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 async ready support #3221

Merged
merged 6 commits into from
Aug 27, 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
2 changes: 2 additions & 0 deletions .github/workflows/clippy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ jobs:
run: cargo clippy -p test_extensions
- name: Clippy test_futures
run: cargo clippy -p test_futures
- name: Clippy test_futures_impl
run: cargo clippy -p test_futures_impl
- name: Clippy test_handles
run: cargo clippy -p test_handles
- name: Clippy test_helpers
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ jobs:
run: cargo test -p test_extensions --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_futures
run: cargo test -p test_futures --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_futures_impl
run: cargo test -p test_futures_impl --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_handles
run: cargo test -p test_handles --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_helpers
Expand Down Expand Up @@ -253,10 +255,10 @@ jobs:
run: cargo test -p test_return_handle --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_return_struct
run: cargo test -p test_return_struct --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_riddle
run: cargo test -p test_riddle --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Clean
run: cargo clean
- name: Test test_riddle
run: cargo test -p test_riddle --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_standalone
run: cargo test -p test_standalone --target ${{ matrix.target }} ${{ matrix.etc }}
- name: Test test_string_param
Expand Down
9 changes: 9 additions & 0 deletions crates/libs/core/src/param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ where
}
}

impl<T> Param<T> for InterfaceRef<'_, T>
where
T: Type<T>,
{
unsafe fn param(self) -> ParamValue<T> {
ParamValue::Borrowed(transmute_copy(&self))
}
}

impl<T, U> Param<T, InterfaceType> for &U
where
T: TypeKind<TypeKind = InterfaceType> + Clone,
Expand Down
2 changes: 2 additions & 0 deletions crates/libs/windows/src/extensions/Foundation.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
pub mod Async;
#[cfg(feature = "implement")]
pub mod AsyncReady;
#[cfg(feature = "Foundation_Collections")]
pub mod Collections;
#[cfg(feature = "Foundation_Numerics")]
Expand Down
26 changes: 26 additions & 0 deletions crates/libs/windows/src/extensions/Foundation/Async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,15 @@ pub trait Async: Interface {
// The type of value produced on completion.
type Output;

// The type of the delegate use for completion notification.
type CompletedHandler;

// Sets the handler or callback to invoke when execution completes. This handler can only be set once.
fn set_completed<F: Fn() + Send + 'static>(&self, handler: F) -> Result<()>;

// Calls the given handler with the current object and status.
fn invoke_completed(&self, hander: &Self::CompletedHandler, status: AsyncStatus);

// Returns the value produced on completion. This should only be called when execution completes.
fn get_results(&self) -> Result<Self::Output>;
}
Expand Down Expand Up @@ -108,6 +114,7 @@ impl<A: Async> Future for AsyncFuture<A> {

impl Async for IAsyncAction {
type Output = ();
type CompletedHandler = AsyncActionCompletedHandler;

fn set_completed<F: Fn() + Send + 'static>(&self, handler: F) -> Result<()> {
self.SetCompleted(&AsyncActionCompletedHandler::new(move |_, _| {
Expand All @@ -116,13 +123,18 @@ impl Async for IAsyncAction {
}))
}

fn invoke_completed(&self, handler: &Self::CompletedHandler, status: AsyncStatus) {
_ = handler.Invoke(self, status);
}

fn get_results(&self) -> Result<Self::Output> {
self.GetResults()
}
}

impl<T: RuntimeType> Async for IAsyncOperation<T> {
type Output = T;
type CompletedHandler = AsyncOperationCompletedHandler<T>;

fn set_completed<F: Fn() + Send + 'static>(&self, handler: F) -> Result<()> {
self.SetCompleted(&AsyncOperationCompletedHandler::new(move |_, _| {
Expand All @@ -131,13 +143,18 @@ impl<T: RuntimeType> Async for IAsyncOperation<T> {
}))
}

fn invoke_completed(&self, handler: &Self::CompletedHandler, status: AsyncStatus) {
_ = handler.Invoke(self, status);
}

fn get_results(&self) -> Result<Self::Output> {
self.GetResults()
}
}

impl<P: RuntimeType> Async for IAsyncActionWithProgress<P> {
type Output = ();
type CompletedHandler = AsyncActionWithProgressCompletedHandler<P>;

fn set_completed<F: Fn() + Send + 'static>(&self, handler: F) -> Result<()> {
self.SetCompleted(&AsyncActionWithProgressCompletedHandler::new(move |_, _| {
Expand All @@ -146,13 +163,18 @@ impl<P: RuntimeType> Async for IAsyncActionWithProgress<P> {
}))
}

fn invoke_completed(&self, handler: &Self::CompletedHandler, status: AsyncStatus) {
_ = handler.Invoke(self, status);
}

fn get_results(&self) -> Result<Self::Output> {
self.GetResults()
}
}

impl<T: RuntimeType, P: RuntimeType> Async for IAsyncOperationWithProgress<T, P> {
type Output = T;
type CompletedHandler = AsyncOperationWithProgressCompletedHandler<T, P>;

fn set_completed<F: Fn() + Send + 'static>(&self, handler: F) -> Result<()> {
self.SetCompleted(&AsyncOperationWithProgressCompletedHandler::new(move |_, _| {
Expand All @@ -161,6 +183,10 @@ impl<T: RuntimeType, P: RuntimeType> Async for IAsyncOperationWithProgress<T, P>
}))
}

fn invoke_completed(&self, handler: &Self::CompletedHandler, status: AsyncStatus) {
_ = handler.Invoke(self, status);
}

fn get_results(&self) -> Result<Self::Output> {
self.GetResults()
}
Expand Down
219 changes: 219 additions & 0 deletions crates/libs/windows/src/extensions/Foundation/AsyncReady.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
use super::Async::Async;
use crate::{core::*, Foundation::*};
use std::sync::atomic::{AtomicBool, Ordering};

struct ReadyState<T: Async> {
set_completed: AtomicBool,
result: Result<T::Output>,
}

impl<T: Async> ReadyState<T> {
fn new(result: Result<T::Output>) -> Self {
Self { set_completed: AtomicBool::new(false), result }
}

fn status(&self) -> AsyncStatus {
if self.result.is_ok() {
AsyncStatus::Completed
} else {
AsyncStatus::Error
}
}

// The "Ready" implementations don't need to store the handler since the handler is invoked immediately
// but still need to confirm that `SetCompleted` is called at most once.
fn invoke_completed(&self, sender: &T, handler: Option<&T::CompletedHandler>) -> Result<()> {
if self.set_completed.swap(true, Ordering::SeqCst) == false {
if let Some(handler) = handler {
sender.invoke_completed(handler, self.status());
}
Ok(())
} else {
Err(Error::from_hresult(HRESULT(0x80000018u32 as i32))) // E_ILLEGAL_DELEGATE_ASSIGNMENT
}
}

// The `From` implementation is not used here since we don't want to transfer any error object to the calling thread.
// That happens when `GetResults` is called.
fn error_code(&self) -> Result<HRESULT> {
Ok(match &self.result {
Ok(_) => HRESULT(0),
Err(error) => error.code(),
})
}
}

#[implement(IAsyncAction, IAsyncInfo)]
struct ReadyAction(ReadyState<IAsyncAction>);

#[implement(IAsyncOperation<T>, IAsyncInfo)]
struct ReadyOperation<T>(ReadyState<IAsyncOperation<T>>)
where
T: RuntimeType + 'static;

#[implement(IAsyncActionWithProgress<P>, IAsyncInfo)]
struct ReadyActionWithProgress<P>(ReadyState<IAsyncActionWithProgress<P>>)
where
P: RuntimeType + 'static;

#[implement(IAsyncOperationWithProgress<T, P>, IAsyncInfo)]
struct ReadyOperationWithProgress<T, P>(ReadyState<IAsyncOperationWithProgress<T, P>>)
where
T: RuntimeType + 'static,
P: RuntimeType + 'static;

impl IAsyncInfo_Impl for ReadyAction_Impl {
fn Id(&self) -> Result<u32> {
Ok(1)
}
fn Status(&self) -> Result<AsyncStatus> {
Ok(self.0.status())
}
fn ErrorCode(&self) -> Result<HRESULT> {
self.0.error_code()
}
fn Cancel(&self) -> Result<()> {
Ok(())
}
fn Close(&self) -> Result<()> {
Ok(())
}
}

impl<T: RuntimeType> IAsyncInfo_Impl for ReadyOperation_Impl<T> {
fn Id(&self) -> Result<u32> {
Ok(1)
}
fn Status(&self) -> Result<AsyncStatus> {
Ok(self.0.status())
}
fn ErrorCode(&self) -> Result<HRESULT> {
self.0.error_code()
}
fn Cancel(&self) -> Result<()> {
Ok(())
}
fn Close(&self) -> Result<()> {
Ok(())
}
}

impl<P: RuntimeType> IAsyncInfo_Impl for ReadyActionWithProgress_Impl<P> {
fn Id(&self) -> Result<u32> {
Ok(1)
}
fn Status(&self) -> Result<AsyncStatus> {
Ok(self.0.status())
}
fn ErrorCode(&self) -> Result<HRESULT> {
self.0.error_code()
}
fn Cancel(&self) -> Result<()> {
Ok(())
}
fn Close(&self) -> Result<()> {
Ok(())
}
}

impl<T: RuntimeType, P: RuntimeType> IAsyncInfo_Impl for ReadyOperationWithProgress_Impl<T, P> {
fn Id(&self) -> Result<u32> {
Ok(1)
}
fn Status(&self) -> Result<AsyncStatus> {
Ok(self.0.status())
}
fn ErrorCode(&self) -> Result<HRESULT> {
self.0.error_code()
}
fn Cancel(&self) -> Result<()> {
Ok(())
}
fn Close(&self) -> Result<()> {
Ok(())
}
}

impl IAsyncAction_Impl for ReadyAction_Impl {
fn SetCompleted(&self, handler: Option<&AsyncActionCompletedHandler>) -> Result<()> {
self.0.invoke_completed(&self.as_interface(), handler)
}
fn Completed(&self) -> Result<AsyncActionCompletedHandler> {
Err(Error::empty())
}
fn GetResults(&self) -> Result<()> {
self.0.result.clone()
}
}

impl<T: RuntimeType> IAsyncOperation_Impl<T> for ReadyOperation_Impl<T> {
fn SetCompleted(&self, handler: Option<&AsyncOperationCompletedHandler<T>>) -> Result<()> {
self.0.invoke_completed(&self.as_interface(), handler)
}
fn Completed(&self) -> Result<AsyncOperationCompletedHandler<T>> {
Err(Error::empty())
}
fn GetResults(&self) -> Result<T> {
self.0.result.clone()
}
}

impl<P: RuntimeType> IAsyncActionWithProgress_Impl<P> for ReadyActionWithProgress_Impl<P> {
fn SetCompleted(&self, handler: Option<&AsyncActionWithProgressCompletedHandler<P>>) -> Result<()> {
self.0.invoke_completed(&self.as_interface(), handler)
}
fn Completed(&self) -> Result<AsyncActionWithProgressCompletedHandler<P>> {
Err(Error::empty())
}
fn GetResults(&self) -> Result<()> {
self.0.result.clone()
}
fn SetProgress(&self, _: Option<&AsyncActionProgressHandler<P>>) -> Result<()> {
Ok(())
}
fn Progress(&self) -> Result<AsyncActionProgressHandler<P>> {
Err(Error::empty())
}
}

impl<T: RuntimeType, P: RuntimeType> IAsyncOperationWithProgress_Impl<T, P> for ReadyOperationWithProgress_Impl<T, P> {
fn SetCompleted(&self, handler: Option<&AsyncOperationWithProgressCompletedHandler<T, P>>) -> Result<()> {
self.0.invoke_completed(&self.as_interface(), handler)
}
fn Completed(&self) -> Result<AsyncOperationWithProgressCompletedHandler<T, P>> {
Err(Error::empty())
}
fn GetResults(&self) -> Result<T> {
self.0.result.clone()
}
fn SetProgress(&self, _: Option<&AsyncOperationProgressHandler<T, P>>) -> Result<()> {
Ok(())
}
fn Progress(&self) -> Result<AsyncOperationProgressHandler<T, P>> {
Err(Error::empty())
}
}

impl IAsyncAction {
pub fn ready(result: Result<()>) -> Self {
ReadyAction(ReadyState::new(result)).into()
}
}

impl<T: RuntimeType> IAsyncOperation<T> {
pub fn ready(result: Result<T>) -> Self {
ReadyOperation(ReadyState::new(result)).into()
}
}

impl<P: RuntimeType> IAsyncActionWithProgress<P> {
pub fn ready(result: Result<()>) -> Self {
ReadyActionWithProgress(ReadyState::new(result)).into()
}
}

impl<T: RuntimeType, P: RuntimeType> IAsyncOperationWithProgress<T, P> {
pub fn ready(result: Result<T>) -> Self {
ReadyOperationWithProgress(ReadyState::new(result)).into()
}
}
17 changes: 17 additions & 0 deletions crates/tests/futures_impl/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "test_futures_impl"
version = "0.0.0"
edition = "2021"
publish = false

[lib]
doc = false
doctest = false

[dependencies.windows]
path = "../../libs/windows"
features = [
"implement",
"Foundation",
"Win32_Foundation",
]
1 change: 1 addition & 0 deletions crates/tests/futures_impl/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading