Skip to content

Commit f7a5082

Browse files
authored
Mark Sendable types as @threadsafe (#341)
1 parent 2d89c61 commit f7a5082

File tree

5 files changed

+120
-0
lines changed

5 files changed

+120
-0
lines changed

Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,9 @@ extension FFMSwift2JavaGenerator {
278278
parentProtocol = "SwiftValue"
279279
}
280280

281+
if decl.swiftNominal.isSendable {
282+
printer.print("@ThreadSafe // Sendable")
283+
}
281284
printer.printBraceBlock("public final class \(decl.swiftNominal.name) extends FFMSwiftInstance implements \(parentProtocol)") {
282285
printer in
283286
// Constants

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,9 @@ extension JNISwift2JavaGenerator {
171171
private func printNominal(
172172
_ printer: inout CodePrinter, _ decl: ImportedNominalType, body: (inout CodePrinter) -> Void
173173
) {
174+
if decl.swiftNominal.isSendable {
175+
printer.print("@ThreadSafe // Sendable")
176+
}
174177
printer.printBraceBlock("public final class \(decl.swiftNominal.name) extends JNISwiftInstance") { printer in
175178
body(&printer)
176179
}

Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,22 @@ package class SwiftNominalTypeDeclaration: SwiftTypeDeclaration {
8585
super.init(moduleName: moduleName, name: node.name.text)
8686
}
8787

88+
/// Returns true if this type conforms to `Sendable` and therefore is "threadsafe".
89+
lazy var isSendable: Bool = {
90+
// Check if Sendable is in the inheritance list
91+
guard let inheritanceClause = self.syntax?.inheritanceClause else {
92+
return false
93+
}
94+
95+
for inheritedType in inheritanceClause.inheritedTypes {
96+
if inheritedType.type.trimmedDescription == "Sendable" {
97+
return true
98+
}
99+
}
100+
101+
return false
102+
}()
103+
88104
/// Determine the known standard library type for this nominal type
89105
/// declaration.
90106
private func computeKnownStandardLibraryType() -> SwiftKnownTypeDeclKind? {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
package org.swift.swiftkit.core.annotations;
16+
17+
import jdk.jfr.Description;
18+
import jdk.jfr.Label;
19+
20+
import java.lang.annotation.Documented;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
import static java.lang.annotation.ElementType.*;
26+
27+
/**
28+
* Used to mark a type as thread-safe, i.e. no additional synchronization is necessary when accessing it
29+
* from multiple threads.
30+
*
31+
* <p> In SwiftJava specifically, this attribute is applied when an extracted Swift type conforms to the Swift
32+
* {@code Sendable} protocol, which is a compiler enforced mechanism to enforce thread-safety in Swift.
33+
*
34+
* @see <a href="https://developer.apple.com/documentation/Swift/Sendable">Swift Sendable API documentation</a>.
35+
*/
36+
@Documented
37+
@Label("Thread-safe")
38+
@Description("Value should be interpreted as safe to be shared across threads.")
39+
@Target({TYPE_USE})
40+
@Retention(RetentionPolicy.RUNTIME)
41+
public @interface ThreadSafe {
42+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import JExtractSwiftLib
16+
import Testing
17+
18+
final class SendableTests {
19+
let source =
20+
"""
21+
public struct SendableStruct: Sendable {}
22+
"""
23+
24+
25+
@Test("Import: Sendable struct (ffm)")
26+
func sendableStruct_ffm() throws {
27+
28+
try assertOutput(
29+
input: source, .ffm, .java,
30+
expectedChunks: [
31+
"""
32+
@ThreadSafe // Sendable
33+
public final class SendableStruct extends FFMSwiftInstance implements SwiftValue {
34+
static final String LIB_NAME = "SwiftModule";
35+
static final Arena LIBRARY_ARENA = Arena.ofAuto();
36+
""",
37+
]
38+
)
39+
}
40+
41+
@Test("Import: Sendable struct (jni)")
42+
func sendableStruct_jni() throws {
43+
44+
try assertOutput(
45+
input: source, .jni, .java,
46+
expectedChunks: [
47+
"""
48+
@ThreadSafe // Sendable
49+
public final class SendableStruct extends JNISwiftInstance {
50+
static final String LIB_NAME = "SwiftModule";
51+
""",
52+
]
53+
)
54+
}
55+
56+
}

0 commit comments

Comments
 (0)