9
9
import Dispatch
10
10
import CAtomics
11
11
12
- private let hptrOffset = 0
13
- private let tptrOffset = MemoryLayout< AtomicMutableRawPointer> . stride
14
- private let fptrOffset = MemoryLayout< AtomicMutableRawPointer> . stride*2
15
-
16
12
open class PostBox < Value> : EventStream < Value >
17
13
{
18
14
private typealias Node = BufferNode < Event < Value > >
19
15
20
- private let s = UnsafeMutableRawPointer . allocate ( byteCount: MemoryLayout< AtomicMutableRawPointer> . stride*3,
21
- alignment: MemoryLayout< AtomicMutableRawPointer> . alignment)
22
- private var hptr : UnsafeMutablePointer < AtomicMutableRawPointer > {
23
- return ( s+ hptrOffset) . assumingMemoryBound ( to: AtomicMutableRawPointer . self)
24
- }
25
- private var head : Node {
26
- get { return Node ( storage: CAtomicsLoad ( hptr, . relaxed) ) }
27
- set { CAtomicsStore ( hptr, newValue. storage, . relaxed) }
16
+ private let s = UnsafeMutableRawPointer . allocate ( byteCount: MemoryLayout< PostBoxState> . size,
17
+ alignment: MemoryLayout< PostBoxState> . alignment)
18
+ private var head : UnsafeMutablePointer < AtomicMutableRawPointer > {
19
+ return ( s+ headOffset) . assumingMemoryBound ( to: AtomicMutableRawPointer . self)
28
20
}
29
- private var tptr : UnsafeMutablePointer < AtomicMutableRawPointer > {
30
- return ( s+ tptrOffset ) . assumingMemoryBound ( to: AtomicMutableRawPointer . self)
21
+ private var tail : UnsafeMutablePointer < AtomicMutableRawPointer > {
22
+ return ( s+ tailOffset ) . assumingMemoryBound ( to: AtomicMutableRawPointer . self)
31
23
}
32
- private var fptr : UnsafeMutablePointer < AtomicOptionalMutableRawPointer > {
33
- return ( s+ fptrOffset ) . assumingMemoryBound ( to: AtomicOptionalMutableRawPointer . self)
24
+ private var last : UnsafeMutablePointer < AtomicOptionalMutableRawPointer > {
25
+ return ( s+ lastOffset ) . assumingMemoryBound ( to: AtomicOptionalMutableRawPointer . self)
34
26
}
35
27
36
28
override init ( validated: ValidatedQueue )
@@ -39,47 +31,46 @@ open class PostBox<Value>: EventStream<Value>
39
31
40
32
// set up an initial dummy node
41
33
let node = Node . dummy
42
- ( s+ hptrOffset ) . bindMemory ( to: AtomicMutableRawPointer . self, capacity: 2 )
43
- CAtomicsInitialize ( hptr , node. storage)
44
- CAtomicsInitialize ( tptr , node. storage)
45
- ( s+ fptrOffset ) . bindMemory ( to: AtomicOptionalMutableRawPointer . self, capacity: 1 )
46
- CAtomicsInitialize ( fptr , nil )
34
+ ( s+ headOffset ) . bindMemory ( to: AtomicMutableRawPointer . self, capacity: 2 )
35
+ CAtomicsInitialize ( head , node. storage)
36
+ CAtomicsInitialize ( tail , node. storage)
37
+ ( s+ lastOffset ) . bindMemory ( to: AtomicOptionalMutableRawPointer . self, capacity: 1 )
38
+ CAtomicsInitialize ( last , nil )
47
39
}
48
40
49
41
deinit {
50
42
// empty the queue
51
- let head = self . head
52
- var next = head. next
43
+ let head = Node ( storage : CAtomicsLoad ( self . head, . relaxed ) )
44
+ var next = Node ( storage : CAtomicsLoad ( head. next, . relaxed ) )
53
45
while let node = next
54
46
{
55
- next = node. next
47
+ next = Node ( storage : CAtomicsLoad ( node. next, . relaxed ) )
56
48
node. deinitialize ( )
57
49
node. deallocate ( )
58
50
}
59
51
60
52
s. deallocate ( )
61
53
}
62
54
63
- final public var isEmpty : Bool { return CAtomicsLoad ( hptr , . relaxed) == CAtomicsLoad ( tptr , . relaxed) }
55
+ final public var isEmpty : Bool { return CAtomicsLoad ( head , . relaxed) == CAtomicsLoad ( tail , . relaxed) }
64
56
65
57
final public func post( _ event: Event < Value > )
66
58
{
67
- guard completed == false , CAtomicsLoad ( fptr , . relaxed) == nil else { return }
59
+ guard completed == false , CAtomicsLoad ( last , . relaxed) == nil else { return }
68
60
69
61
let node = Node ( initializedWith: event)
70
62
if event. isError
71
63
{
72
- guard CAtomicsCompareAndExchange ( fptr , nil , node. storage, . strong, . relaxed) else { return }
64
+ guard CAtomicsCompareAndExchange ( last , nil , node. storage, . strong, . relaxed) else { return }
73
65
}
74
66
75
67
// events posted "simultaneously" synchronize with each other here
76
- let previousTailPointer = CAtomicsExchange ( tptr, node. storage, . acqrel)
77
- let previousTail = Node ( storage: previousTailPointer)
68
+ let previousTail = Node ( storage: CAtomicsExchange ( tail, node. storage, . acqrel) )
78
69
79
70
// publish the new node to processing loop here
80
- CAtomicsStore ( previousTail. nptr , node. storage, . release)
71
+ CAtomicsStore ( previousTail. next , node. storage, . release)
81
72
82
- if previousTailPointer == CAtomicsLoad ( hptr , . relaxed)
73
+ if previousTail . storage == CAtomicsLoad ( head , . relaxed)
83
74
{ // the queue had been empty or blocked
84
75
// resume processing enqueued events
85
76
queue. async ( execute: self . processNext)
@@ -110,17 +101,21 @@ open class PostBox<Value>: EventStream<Value>
110
101
}
111
102
#endif
112
103
104
+ let requested = self . requested
105
+ if requested <= 0 && CAtomicsLoad ( last, . relaxed) == nil { return }
106
+
113
107
// try to dequeue the next event
114
- let oldHead = head
115
- let next = CAtomicsLoad ( oldHead . nptr , . acquire)
108
+ let head = Node ( storage : CAtomicsLoad ( self . head, . acquire ) )
109
+ let next = CAtomicsLoad ( head . next , . acquire)
116
110
117
- if requested <= 0 && CAtomicsLoad ( fptr , . relaxed) != next { return }
111
+ if requested <= 0 && CAtomicsLoad ( last , . relaxed) != next { return }
118
112
119
- if let next = Node ( storage : next)
113
+ if let next = next
120
114
{
121
- let event = next. move ( )
122
- head = next
123
- oldHead. deallocate ( )
115
+ let node = Node ( storage: next)
116
+ let event = node. move ( )
117
+ CAtomicsStore ( self . head, next, . release)
118
+ head. deallocate ( )
124
119
125
120
dispatch ( event)
126
121
queue. async ( execute: self . processNext)
@@ -140,6 +135,16 @@ open class PostBox<Value>: EventStream<Value>
140
135
}
141
136
}
142
137
138
+ private struct PostBoxState
139
+ {
140
+ var head : AtomicMutableRawPointer
141
+ var tail : AtomicMutableRawPointer
142
+ var last : AtomicOptionalMutableRawPointer
143
+ }
144
+ private let headOffset = MemoryLayout . offset ( of: \PostBoxState . head) !
145
+ private let tailOffset = MemoryLayout . offset ( of: \PostBoxState . tail) !
146
+ private let lastOffset = MemoryLayout . offset ( of: \PostBoxState . last) !
147
+
143
148
private let nextOffset = 0
144
149
private let dataOffset = ( MemoryLayout < AtomicOptionalMutableRawPointer > . stride + 15 ) & ~ 15
145
150
@@ -160,10 +165,10 @@ private struct BufferNode<Element>: Equatable
160
165
161
166
private init ( )
162
167
{
163
- let size = dataOffset + MemoryLayout< Element> . stride
168
+ let size = dataOffset + MemoryLayout< Element> . size
164
169
storage = UnsafeMutableRawPointer . allocate ( byteCount: size, alignment: 16 )
165
170
( storage+ nextOffset) . bindMemory ( to: AtomicOptionalMutableRawPointer . self, capacity: 1 )
166
- CAtomicsInitialize ( nptr , nil )
171
+ CAtomicsInitialize ( next , nil )
167
172
( storage+ dataOffset) . bindMemory ( to: Element . self, capacity: 1 )
168
173
}
169
174
@@ -180,14 +185,8 @@ private struct BufferNode<Element>: Equatable
180
185
storage. deallocate ( )
181
186
}
182
187
183
- var nptr : UnsafeMutablePointer < AtomicOptionalMutableRawPointer > {
184
- get {
185
- return ( storage+ nextOffset) . assumingMemoryBound ( to: AtomicOptionalMutableRawPointer . self)
186
- }
187
- }
188
-
189
- var next : BufferNode ? {
190
- get { return BufferNode ( storage: CAtomicsLoad ( nptr, . acquire) ) }
188
+ var next : UnsafeMutablePointer < AtomicOptionalMutableRawPointer > {
189
+ return ( storage+ nextOffset) . assumingMemoryBound ( to: AtomicOptionalMutableRawPointer . self)
191
190
}
192
191
193
192
private var data : UnsafeMutablePointer < Element > {
0 commit comments