@@ -13,25 +13,39 @@ extension JSObject {
13
13
/// intended to be shared across threads.
14
14
public struct Transferring : @unchecked Sendable {
15
15
fileprivate struct CriticalState {
16
- var continuation : CheckedContinuation < JSObject , Error > ?
16
+ var continuation : CheckedContinuation < JavaScriptObjectRef , Error > ?
17
17
}
18
18
fileprivate class Storage {
19
- let sourceTid : Int32
20
- let idInSource : JavaScriptObjectRef
19
+ /// The original ``JSObject`` that is transferred.
20
+ ///
21
+ /// Retain it here to prevent it from being released before the transfer is complete.
22
+ let sourceObject : JSObject
21
23
#if compiler(>=6.1) && _runtime(_multithreaded)
22
24
let criticalState : Mutex < CriticalState > = . init( CriticalState ( ) )
23
25
#endif
24
26
25
- init ( sourceTid: Int32 , id: JavaScriptObjectRef ) {
26
- self . sourceTid = sourceTid
27
- self . idInSource = id
27
+ var idInSource : JavaScriptObjectRef {
28
+ sourceObject. id
29
+ }
30
+
31
+ var sourceTid : Int32 {
32
+ #if compiler(>=6.1) && _runtime(_multithreaded)
33
+ sourceObject. ownerTid
34
+ #else
35
+ // On single-threaded runtime, source and destination threads are always the main thread (TID = -1).
36
+ - 1
37
+ #endif
38
+ }
39
+
40
+ init ( sourceObject: JSObject ) {
41
+ self . sourceObject = sourceObject
28
42
}
29
43
}
30
44
31
45
private let storage : Storage
32
46
33
- fileprivate init ( sourceTid : Int32 , id : JavaScriptObjectRef ) {
34
- self . init ( storage: Storage ( sourceTid : sourceTid , id : id ) )
47
+ fileprivate init ( sourceObject : JSObject ) {
48
+ self . init ( storage: Storage ( sourceObject : sourceObject ) )
35
49
}
36
50
37
51
fileprivate init ( storage: Storage ) {
@@ -63,7 +77,7 @@ extension JSObject {
63
77
self . storage. sourceTid,
64
78
Unmanaged . passRetained ( self . storage) . toOpaque ( )
65
79
)
66
- return try await withCheckedThrowingContinuation { continuation in
80
+ let idInDestination = try await withCheckedThrowingContinuation { continuation in
67
81
self . storage. criticalState. withLock { criticalState in
68
82
guard criticalState. continuation == nil else {
69
83
// This is a programming error, `receive` should be called only once.
@@ -72,6 +86,7 @@ extension JSObject {
72
86
criticalState. continuation = continuation
73
87
}
74
88
}
89
+ return JSObject ( id: idInDestination)
75
90
#else
76
91
return JSObject ( id: storage. idInSource)
77
92
#endif
@@ -85,12 +100,7 @@ extension JSObject {
85
100
/// - Parameter object: The ``JSObject`` to be transferred.
86
101
/// - Returns: A ``Transferring`` instance that can be shared across threads.
87
102
public static func transfer( _ object: JSObject ) -> Transferring {
88
- #if compiler(>=6.1) && _runtime(_multithreaded)
89
- Transferring ( sourceTid: object. ownerTid, id: object. id)
90
- #else
91
- // On single-threaded runtime, source and destination threads are always the main thread (TID = -1).
92
- Transferring ( sourceTid: - 1 , id: object. id)
93
- #endif
103
+ return Transferring ( sourceObject: object)
94
104
}
95
105
}
96
106
@@ -110,7 +120,7 @@ func _swjs_receive_object(_ object: JavaScriptObjectRef, _ transferring: UnsafeR
110
120
let storage = Unmanaged < JSObject . Transferring . Storage > . fromOpaque ( transferring) . takeRetainedValue ( )
111
121
storage. criticalState. withLock { criticalState in
112
122
assert ( criticalState. continuation != nil , " JSObject.Transferring object is not yet received!? " )
113
- criticalState. continuation? . resume ( returning: JSObject ( id : object) )
123
+ criticalState. continuation? . resume ( returning: object)
114
124
}
115
125
#endif
116
126
}
0 commit comments