1+ # ##
2+ # ## ReadIndicator (single counter)
3+ # ##
4+ 
15#  TODO : maybe use "NO_WAITERS_MASK" so that depart can use `iszero`
26const  HAS_WAITER_MASK =  ~ (typemax (UInt) >>  1 )
37
@@ -102,37 +106,65 @@ end
102106#  used for establishing the happens-before edge required for non-atomic store/load of
103107#  `ind.waiter` field.
104108
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+ 
105132@enum  LeftOrRight LEFT_READABLE RIGHT_READABLE
106133
107134flip (x:: LeftOrRight ) =  x ==  LEFT_READABLE ?  RIGHT_READABLE :  LEFT_READABLE
108135
109136abstract type  AbstractReadWriteGuard end   #  TODO : Move it to ConcurrentUtils
110137
111- mutable struct  Guard {Data} <:  AbstractReadWriteGuard 
138+ mutable struct  GenericGuard {Data,Indicator } <:  AbstractReadWriteGuard 
112139    @const  left:: Data 
113140    @const  right:: Data 
114141    @atomic  versionindex:: Int 
115142    @atomic  leftright:: LeftOrRight 
116-     @const  indicators:: NTuple{2,ReadIndicator } 
143+     @const  indicators:: NTuple{2,Indicator } 
117144    @const  lock:: ReentrantLock 
118145
119-     global  function  _Guard (left, right)
146+     global  function  _Guard (left, right, Indicator )
120147        right =  right:: typeof (left)
121-         indicators =  (ReadIndicator (), ReadIndicator ())
148+         indicators =  (Indicator (), Indicator ())
122149        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)
124153    end 
125154end 
126155
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}
128160    left =  f ():: Data 
129161    right =  f ():: Data 
130-     return  _Guard (left, right):: Guard {Data}
162+     return  _Guard (left, right, Indicator ):: GenericGuard {Data}
131163end 
132164
133- Guard (f) =  _Guard (f (), f ())
165+ GenericGuard {<:Any,Indicator} (f) where  {Indicator}  =  _Guard (f (), f (), Indicator )
134166
135- function  acquire_read (g:: Guard )
167+ function  acquire_read (g:: GenericGuard )
136168    versionindex =  @atomic  :monotonic  g. versionindex  #  [^monotonic_versionindex]
137169    token =  arrive! (g. indicators[versionindex])
138170
@@ -143,12 +175,12 @@ end
143175#  [^monotonic_versionindex]: Since `g.versionindex` is just a "performance hint," it can be
144176#  loaded using `:monotonic`.
145177
146- function  release_read (:: Guard , token)
178+ function  release_read (:: GenericGuard , token)
147179    depart! (token)
148180    return 
149181end 
150182
151- function  LeftRight. guarding_read (f, g:: Guard )
183+ function  LeftRight. guarding_read (f, g:: GenericGuard )
152184    token, data =  acquire_read (g)
153185    try 
154186        return  f (data)
@@ -157,7 +189,7 @@ function LeftRight.guarding_read(f, g::Guard)
157189    end 
158190end 
159191
160- function  LeftRight. guarding (f!, g:: Guard )
192+ function  LeftRight. guarding (f!, g:: GenericGuard )
161193    lock (g. lock)
162194    try 
163195        #  No need to use `:acquire` since the lock already has ordered the access:
179211#  [^seq_cst_state_leftright]: Some accesses to `ind.state` and `g.leftright` must have
180212#  sequential consistent ordering.  See discussion below for more details.
181213
182- function  toggle_and_wait (g:: Guard )
214+ function  toggle_and_wait (g:: GenericGuard )
183215    prev =  @atomic  :monotonic  g. versionindex  #  [^monotonic_versionindex]
184216    next =  mod1 (prev +  1 , 2 )
185217    wait_empty (g. indicators[next])  #  [^w1]
0 commit comments