Skip to content

Commit

Permalink
Add async ready support (#3221)
Browse files Browse the repository at this point in the history
  • Loading branch information
kennykerr authored Aug 27, 2024
1 parent 4239043 commit 0cce46b
Show file tree
Hide file tree
Showing 13 changed files with 600 additions and 2 deletions.
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

0 comments on commit 0cce46b

Please sign in to comment.