diff --git a/Cargo.lock b/Cargo.lock index 0ce25a785b..3e797f1056 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1503,6 +1503,7 @@ dependencies = [ "serde", "serde_json", "serial_test", + "static_assertions", "tempfile", "test-case", "thiserror 2.0.18", diff --git a/src/auth/Cargo.toml b/src/auth/Cargo.toml index 63e7c37d42..556fe5a815 100644 --- a/src/auth/Cargo.toml +++ b/src/auth/Cargo.toml @@ -68,6 +68,7 @@ tokio = { workspace = true, features = ["macros", "rt-multi-thre tokio-test.workspace = true url.workspace = true mutants.workspace = true +static_assertions = { workspace = true } [features] default = ["default-idtoken-backend", "default-rustls-provider"] diff --git a/src/auth/src/retry.rs b/src/auth/src/retry.rs index 6fbd2a5c51..dbc6534d12 100644 --- a/src/auth/src/retry.rs +++ b/src/auth/src/retry.rs @@ -23,6 +23,7 @@ use gax::retry_loop_internal::retry_loop; use gax::retry_policy::{AlwaysRetry, RetryPolicy, RetryPolicyArg, RetryPolicyExt}; use gax::retry_throttler::{AdaptiveThrottler, RetryThrottlerArg, SharedRetryThrottler}; use std::error::Error; +use std::panic::{RefUnwindSafe, UnwindSafe}; use std::sync::{Arc, Mutex}; #[derive(Debug)] @@ -40,6 +41,17 @@ pub(crate) struct Builder { retry_throttler: Option, } +// This is necessary because adding [Builder] to existing, released auth features +// caused a breaking change. The containing structs would lose their automatically derived +// `RefUnwindSafe` implementation because the dynamic trait objects (like `dyn RetryPolicy`) +// are not `RefUnwindSafe` nor `UnwindSafe` by default. +// +// This is safe because in the builder, we never call or use the retry policies. We just hold them +// until we create the client. There is no opportunity for a panic to leave them in an inconsistent +// state. +impl RefUnwindSafe for Builder {} +impl UnwindSafe for Builder {} + impl Builder { pub(crate) fn with_retry_policy(mut self, retry_policy: RetryPolicyArg) -> Self { self.retry_policy = Some(retry_policy); @@ -142,6 +154,7 @@ mod tests { use gax::retry_state::RetryState; use gax::retry_throttler::RetryThrottler; use mockall::{Sequence, mock}; + use static_assertions::assert_impl_all; use std::error::Error; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; @@ -479,4 +492,9 @@ mod tests { original_error_string ); } + + #[test] + fn test_unwind_safe() { + assert_impl_all!(Builder: std::panic::UnwindSafe, std::panic::RefUnwindSafe); + } }