From df223af7196e8edebac686097a110058252c8ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 7 Nov 2023 10:28:23 +0200 Subject: [PATCH] element: Add `catch_panic_future()` helper function for subclasses This allows wrapping a future in a way that panics are converted to error messages on the object. Part-of: --- gstreamer/Cargo.toml | 1 + gstreamer/src/subclass/element.rs | 52 ++++++++++++++++++++++++++++++- gstreamer/src/subclass/mod.rs | 2 +- 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/gstreamer/Cargo.toml b/gstreamer/Cargo.toml index 73023d6ad..8c6caec23 100644 --- a/gstreamer/Cargo.toml +++ b/gstreamer/Cargo.toml @@ -32,6 +32,7 @@ pretty-hex = "0.3" thiserror = "1" smallvec = { version = "1.0", features = ["write"] } itertools = "0.11" +pin-project-lite = "0.2" [dev-dependencies] ron = "0.8" diff --git a/gstreamer/src/subclass/element.rs b/gstreamer/src/subclass/element.rs index 833a61a13..47b1737ac 100644 --- a/gstreamer/src/subclass/element.rs +++ b/gstreamer/src/subclass/element.rs @@ -1,6 +1,6 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use std::{borrow::Cow, sync::atomic}; +use std::{borrow::Cow, future::Future, sync::atomic}; use glib::{subclass::prelude::*, translate::*}; @@ -294,6 +294,18 @@ pub trait ElementImplExt: sealed::Sealed + ObjectSubclass { panic_to_error!(self, fallback(), { f(self) }) } + fn catch_panic_future R, G: Future>( + &self, + fallback: F, + fut: G, + ) -> CatchPanic { + CatchPanic { + self_: self.ref_counted().downgrade(), + fallback: Some(fallback), + fut, + } + } + fn catch_panic_pad_function R, G: FnOnce() -> R>( parent: Option<&crate::Object>, fallback: G, @@ -316,6 +328,44 @@ pub trait ElementImplExt: sealed::Sealed + ObjectSubclass { impl ElementImplExt for T {} +pin_project_lite::pin_project! { + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct CatchPanic { + self_: glib::subclass::ObjectImplWeakRef, + fallback: Option, + #[pin] + fut: G, + } +} + +impl R, G: Future> Future for CatchPanic { + type Output = R; + + fn poll( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + let this = self.project(); + + let Some(self_) = this.self_.upgrade() else { + return std::task::Poll::Ready((this + .fallback + .take() + .expect("Future polled after resolving"))( + )); + }; + + panic_to_error!( + &*self_, + std::task::Poll::Ready(this.fallback.take().expect("Future polled after resolving")()), + { + let fut = this.fut; + fut.poll(cx) + } + ) + } +} + unsafe impl IsSubclassable for Element { fn class_init(klass: &mut glib::Class) { Self::parent_class_init::(klass); diff --git a/gstreamer/src/subclass/mod.rs b/gstreamer/src/subclass/mod.rs index d4f8de985..5ca789d2a 100644 --- a/gstreamer/src/subclass/mod.rs +++ b/gstreamer/src/subclass/mod.rs @@ -33,7 +33,7 @@ mod uri_handler; pub use self::{ device_provider::DeviceProviderMetadata, - element::ElementMetadata, + element::{CatchPanic, ElementMetadata}, error::{post_panic_error_message, FlowError}, plugin::{MAJOR_VERSION, MINOR_VERSION}, task_pool::TaskPoolFunction,