Skip to content

feat: Make lots of types Sendable#1009

Merged
dayaffe merged 34 commits intomainfrom
day/make-everything-sendable
Feb 3, 2026
Merged

feat: Make lots of types Sendable#1009
dayaffe merged 34 commits intomainfrom
day/make-everything-sendable

Conversation

@dayaffe
Copy link
Contributor

@dayaffe dayaffe commented Dec 11, 2025

Issue #

Description of changes

Scope

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@dayaffe dayaffe requested a review from jbelkins December 12, 2025 20:51
fun render(serviceSymbol: Symbol) {
writer.openBlock(
"${ctx.settings.visibility} class \$L: \$N {",
"${ctx.settings.visibility} final class \$L: \$N, Sendable {",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my note on the other PR, rather than mark Sendable here (and on every generated client) I think we can just specify that ClientRuntime.Client conforms to Sendable


writer.openBlock(
"public class \$LConfiguration: \$L {",
"public final class \$LConfiguration: \$L, @unchecked Sendable {",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also see my note on the other PR, even after applying final this type is not Sendable (checked or unchecked)

Comment on lines 70 to 80
override fun getMethods(ctx: ProtocolGenerator.GenerationContext): Set<Function> =
setOf(
Function(
name = "addInterceptorProvider",
renderBody = { writer -> writer.write("self.httpInterceptorProviders.append(provider)") },
parameters =
listOf(
FunctionParameter.NoLabel("provider", ClientRuntimeTypes.Core.HttpInterceptorProvider),
),
),
)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for this and similar changes is because the newly updated structs now have only immutable properties so we can't append interceptors after initialization. This would have be provided at initialization. Will need to double check how this fits in with SRA

@dayaffe dayaffe requested a review from jbelkins December 29, 2025 20:23
Comment on lines 34 to 37
throw ClientBuilderError.incompatibleConfigurationType(
expected: String(describing: ClientType.Config.self),
received: String(describing: type(of: configuredClient))
)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can change this to be more similar to our current errors

parameters =
listOf(
FunctionParameter.NoLabel("provider", ClientRuntimeTypes.Core.InterceptorProvider),
software.amazon.smithy.swift.codegen.lang.FunctionParameter.NoLabel(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better to import this type above than use the full identifier here

parameters =
listOf(
FunctionParameter.NoLabel("provider", ClientRuntimeTypes.Core.HttpInterceptorProvider),
software.amazon.smithy.swift.codegen.lang.FunctionParameter.NoLabel(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better to import this type above than use the full identifier here

visibility: String,
) {
writer.openBlock("$visibility protocol \$N {", "}", EndpointTypes.EndpointResolver) {
writer.openBlock("$visibility protocol \$N: Sendable {", "}", EndpointTypes.EndpointResolver) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use the Sendable symbol in SwiftTypes rather than a literal string


writer.openBlock(
"public class \$LConfiguration: \$L {",
"public struct \$LConfiguration: \$L, Sendable {",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above use SwiftTypes.Sendable

properties: List<ConfigProperty>,
) {
// Only render if there are async properties
if (properties.none { it.default?.isAsync == true }) return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we potentially removing an initializer that customers are using by doing this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That init was customer facing so potentially -- I'll revert this change back so we dont break anyone just in case

interceptorProviders: [ClientRuntime.InterceptorProvider]? = nil,
httpInterceptorProviders: [ClientRuntime.HttpInterceptorProvider]? = nil
httpInterceptorProviders: [ClientRuntime.HttpInterceptorProvider]? = nil,
bearerTokenIdentityResolver: (any SmithyIdentity.BearerTokenIdentityResolver)? = nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes to order of init parameters is breaking; we need to preserve the current order.

///
/// Default options are used if none are set.
var retryStrategyOptions: RetryStrategyOptions { get set }
var retryStrategyOptions: RetryStrategyOptions { get }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per discussion, revert these to { get set }

self.authSchemes = authSchemes
}

public func configureClient(clientConfiguration: ClientConfiguration) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per discussion, param should be clientConfiguration: inout ClientConfiguration

@dayaffe dayaffe requested a review from jbelkins January 12, 2026 20:48
val OrchestratorBuilder = runtimeSymbol("OrchestratorBuilder", SwiftDeclaration.CLASS)
val OrchestratorTelemetry = runtimeSymbol("OrchestratorTelemetry", SwiftDeclaration.CLASS)
val OrchestratorBuilder = runtimeSymbol("OrchestratorBuilder", null)
val OrchestratorTelemetry = runtimeSymbol("OrchestratorTelemetry", null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do all of these no longer have types assigned?

val ClientError = runtimeSymbol("ClientError", SwiftDeclaration.ENUM)
val Context = runtimeSymbol("Context", SwiftDeclaration.CLASS)
val ContextBuilder = runtimeSymbol("ContextBuilder", SwiftDeclaration.CLASS)
val ContextBuilder = runtimeSymbol("ContextBuilder", null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as in ClientRuntimeTypes

@dayaffe dayaffe requested a review from jbelkins January 15, 2026 16:04
/// Note: Uses `@unchecked Sendable` because the wrapper is designed to safely encapsulate
/// non-Sendable providers for use in concurrent contexts. The safety is ensured by the
/// immutability of the stored provider reference.
public struct SendableInterceptorProviderBox: InterceptorProvider, @unchecked Sendable {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will be able to remove these wrapper types once we deprecate the class based config. The purpose of these wrappers is to allow the new struct based config to fully support being marked as sendable.

(token is String && !(token as! String).isEmpty) ||
(token is [String: Any] && !(token as! [String: Any]).isEmpty) {
self.input = input.usingPaginationToken(token)
if let token = token {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed for readability when solving a bug, allows us to remove swiftlint disable

/// Note: Uses `@unchecked Sendable` because the wrapper is designed to safely encapsulate
/// non-Sendable providers for use in concurrent contexts. The safety is ensured by the
/// immutability of the stored provider reference.
public struct SendableHttpInterceptorProviderBox: HttpInterceptorProvider, @unchecked Sendable {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as below, see comment on SendableInterceptorProviderBox

@dayaffe dayaffe merged commit 025065f into main Feb 3, 2026
55 of 57 checks passed
@dayaffe dayaffe deleted the day/make-everything-sendable branch February 3, 2026 18:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants