File tree Expand file tree Collapse file tree 3 files changed +109
-0
lines changed
Sources/Elementary/ServerSupport Expand file tree Collapse file tree 3 files changed +109
-0
lines changed Original file line number Diff line number Diff line change 1+ #if swift(>=6.0) && !hasFeature(Embedded)
2+ import Synchronization
3+
4+ @available ( macOS 15 . 0 , * )
5+ final class SendOnceBox : Sendable , SendOnceBoxing {
6+ // final class SendOnceBox<Value>: Sendable, SendOnceBoxing {
7+ typealias Value = any HTML
8+ // NOTE: generics+Synchronization crashes the compiler ATM
9+ // https://github.com/swiftlang/swift/issues/78048
10+
11+ let mutex : Mutex < Value ? >
12+
13+ init ( _ value: sending Value) {
14+ mutex = Mutex ( value)
15+ }
16+
17+ func tryTake( ) -> sending Value? {
18+ mutex. withLock { value -> sending Value? in
19+ let result = value
20+ value = nil
21+ return result
22+ }
23+ }
24+ }
25+
26+ // NOTE: this is for macOS runtime availability of SendOnceBox and can be removed when macOS 15 is the minimum
27+ protocol SendOnceBoxing < Value> : AnyObject , Sendable {
28+ associatedtype Value
29+ func tryTake( ) -> sending Value?
30+ }
31+ #endif
Original file line number Diff line number Diff line change 1+ #if !hasFeature(Embedded)
2+ /// A wrapper around an `any HTML` value that can be safely sent once.
3+ ///
4+ /// Note: For non-sendable values, this will only allow the value to be taken only once.
5+ /// Sendable values can safely be taken multiple times.
6+ public struct _SendableAnyHTMLBox : Sendable {
7+ var storage : Storage
8+
9+ enum Storage {
10+ case sendable( any HTML & Sendable )
11+ #if swift(>=6.0)
12+ // NOTE: protocol can be removed when macOS 15 is the minimum
13+ case sendOnceBox( any SendOnceBoxing < any HTML > )
14+ #endif
15+ }
16+
17+ public init ( _ html: any HTML & Sendable ) {
18+ storage = . sendable( html)
19+ }
20+
21+ #if swift(>=6.0)
22+ @available ( macOS 15 , * )
23+ public init ( _ html: sending any HTML ) {
24+ storage = . sendOnceBox( SendOnceBox ( html) )
25+ }
26+ #endif
27+
28+ #if swift(>=6.0)
29+ public consuming func tryTake( ) -> sending ( any HTML ) ? {
30+ switch storage {
31+ case let . sendable( html) :
32+ return html
33+ case let . sendOnceBox( box) :
34+ return box. tryTake ( )
35+ }
36+ }
37+ #else
38+ public consuming func tryTake( ) -> ( any HTML & Sendable ) ? {
39+ switch storage {
40+ case let . sendable( html) :
41+ return html
42+ }
43+ }
44+ #endif
45+ }
46+ #endif
Original file line number Diff line number Diff line change 1+ import Elementary
2+ import XCTest
3+
4+ @available ( macOS 15 . 0 , * )
5+ final class SendOnceHTMLValueTests : XCTestCase {
6+ func testHoldsSendableValue( ) {
7+ let html = div { " Hello, World! " }
8+ let box = _SendableAnyHTMLBox ( html)
9+ XCTAssertNotNil ( box. tryTake ( ) )
10+ XCTAssertNotNil ( box. tryTake ( ) )
11+ }
12+
13+ #if swift(>=6.0)
14+ func testHoldsNonSendable( ) {
15+ let html = MyComponent ( )
16+ let box = _SendableAnyHTMLBox ( html)
17+ XCTAssertNotNil ( box. tryTake ( ) )
18+ XCTAssertNil ( box. tryTake ( ) )
19+ }
20+ #endif
21+ }
22+
23+ class NonSendable {
24+ var x : Int = 0
25+ }
26+
27+ struct MyComponent : HTML {
28+ let ns = NonSendable ( )
29+ var content : some HTML {
30+ div { " \( ns. x) " }
31+ }
32+ }
You can’t perform that action at this time.
0 commit comments