1
+ # ##
2
+ # ## ReadIndicator (single counter)
3
+ # ##
4
+
1
5
# TODO : maybe use "NO_WAITERS_MASK" so that depart can use `iszero`
2
6
const HAS_WAITER_MASK = ~ (typemax (UInt) >> 1 )
3
7
@@ -102,37 +106,65 @@ end
102
106
# used for establishing the happens-before edge required for non-atomic store/load of
103
107
# `ind.waiter` field.
104
108
109
+ # ##
110
+ # ## ShardedReadIndicator
111
+ # ##
112
+
113
+ struct ShardedReadIndicator
114
+ indicators:: Vector{ReadIndicator}
115
+ end
116
+
117
+ ShardedReadIndicator () =
118
+ ShardedReadIndicator ([ReadIndicator () for _ in 1 : Threads. nthreads ()])
119
+
120
+ arrive! (ind:: ShardedReadIndicator ) = arrive! (ind. indicators[Threads. threadid ()])
121
+
122
+ function wait_empty (ind:: ShardedReadIndicator ; options... )
123
+ for sub in ind. indicators
124
+ wait_empty (sub; options... )
125
+ end
126
+ end
127
+
128
+ # ##
129
+ # ## LeftRight.Guard
130
+ # ##
131
+
105
132
@enum LeftOrRight LEFT_READABLE RIGHT_READABLE
106
133
107
134
flip (x:: LeftOrRight ) = x == LEFT_READABLE ? RIGHT_READABLE : LEFT_READABLE
108
135
109
136
abstract type AbstractReadWriteGuard end # TODO : Move it to ConcurrentUtils
110
137
111
- mutable struct Guard {Data} <: AbstractReadWriteGuard
138
+ mutable struct GenericGuard {Data,Indicator } <: AbstractReadWriteGuard
112
139
@const left:: Data
113
140
@const right:: Data
114
141
@atomic versionindex:: Int
115
142
@atomic leftright:: LeftOrRight
116
- @const indicators:: NTuple{2,ReadIndicator }
143
+ @const indicators:: NTuple{2,Indicator }
117
144
@const lock:: ReentrantLock
118
145
119
- global function _Guard (left, right)
146
+ global function _Guard (left, right, Indicator )
120
147
right = right:: typeof (left)
121
- indicators = (ReadIndicator (), ReadIndicator ())
148
+ indicators = (Indicator (), Indicator ())
122
149
lock = ReentrantLock ()
123
- return new {typeof(left)} (left, right, 1 , LEFT_READABLE, indicators, lock)
150
+ D = typeof (left)
151
+ I = typeof (indicators[1 ])
152
+ return new {D,I} (left, right, 1 , LEFT_READABLE, indicators, lock)
124
153
end
125
154
end
126
155
127
- function Guard {Data} (f = Data) where {Data}
156
+ const Guard{Data} = GenericGuard{Data,ShardedReadIndicator}
157
+ const SimpleGuard{Data} = GenericGuard{Data,ReadIndicator}
158
+
159
+ function GenericGuard {Data,Indicator} (f = Data) where {Data,Indicator}
128
160
left = f ():: Data
129
161
right = f ():: Data
130
- return _Guard (left, right):: Guard {Data}
162
+ return _Guard (left, right, Indicator ):: GenericGuard {Data}
131
163
end
132
164
133
- Guard (f) = _Guard (f (), f ())
165
+ GenericGuard {<:Any,Indicator} (f) where {Indicator} = _Guard (f (), f (), Indicator )
134
166
135
- function acquire_read (g:: Guard )
167
+ function acquire_read (g:: GenericGuard )
136
168
versionindex = @atomic :monotonic g. versionindex # [^monotonic_versionindex]
137
169
token = arrive! (g. indicators[versionindex])
138
170
@@ -143,12 +175,12 @@ end
143
175
# [^monotonic_versionindex]: Since `g.versionindex` is just a "performance hint," it can be
144
176
# loaded using `:monotonic`.
145
177
146
- function release_read (:: Guard , token)
178
+ function release_read (:: GenericGuard , token)
147
179
depart! (token)
148
180
return
149
181
end
150
182
151
- function LeftRight. guarding_read (f, g:: Guard )
183
+ function LeftRight. guarding_read (f, g:: GenericGuard )
152
184
token, data = acquire_read (g)
153
185
try
154
186
return f (data)
@@ -157,7 +189,7 @@ function LeftRight.guarding_read(f, g::Guard)
157
189
end
158
190
end
159
191
160
- function LeftRight. guarding (f!, g:: Guard )
192
+ function LeftRight. guarding (f!, g:: GenericGuard )
161
193
lock (g. lock)
162
194
try
163
195
# No need to use `:acquire` since the lock already has ordered the access:
179
211
# [^seq_cst_state_leftright]: Some accesses to `ind.state` and `g.leftright` must have
180
212
# sequential consistent ordering. See discussion below for more details.
181
213
182
- function toggle_and_wait (g:: Guard )
214
+ function toggle_and_wait (g:: GenericGuard )
183
215
prev = @atomic :monotonic g. versionindex # [^monotonic_versionindex]
184
216
next = mod1 (prev + 1 , 2 )
185
217
wait_empty (g. indicators[next]) # [^w1]
0 commit comments