2626```
2727"""
2828mutable struct ReentrantLock <: AbstractLock
29- locked_by:: Union{Task, Nothing}
30- cond_wait:: ThreadSynchronizer
31- reentrancy_cnt:: Int
32-
33- ReentrantLock () = new (nothing , ThreadSynchronizer (), 0 )
29+ # offset = 16
30+ @atomic locked_by:: Union{Task, Nothing}
31+ # offset32 = 20, offset64 = 24
32+ reentrancy_cnt:: UInt32
33+ # offset32 = 24, offset64 = 28
34+ @atomic havelock:: UInt8 # 0x0 = none, 0x1 = lock, 0x2 = conflict
35+ # offset32 = 28, offset64 = 32
36+ cond_wait:: ThreadSynchronizer # 2 words
37+ # offset32 = 36, offset64 = 48
38+ # sizeof32 = 20, sizeof64 = 32
39+ # now add padding to make this a full cache line to minimize false sharing between objects
40+ _:: NTuple{Int === Int32 ? 2 : 3, Int}
41+ # offset32 = 44, offset64 = 72 == sizeof+offset
42+ # sizeof32 = 28, sizeof64 = 56
43+
44+ ReentrantLock () = new (nothing , 0x0000_0000 , 0x00 , ThreadSynchronizer ())
3445end
3546
3647assert_havelock (l:: ReentrantLock ) = assert_havelock (l, l. locked_by)
@@ -42,7 +53,7 @@ Check whether the `lock` is held by any task/thread.
4253This should not be used for synchronization (see instead [`trylock`](@ref)).
4354"""
4455function islocked (rl:: ReentrantLock )
45- return rl. reentrancy_cnt != 0
56+ return rl. havelock != 0
4657end
4758
4859"""
@@ -55,23 +66,26 @@ return `false`.
5566
5667Each successful `trylock` must be matched by an [`unlock`](@ref).
5768"""
58- function trylock (rl:: ReentrantLock )
59- t = current_task ()
60- if t === rl. locked_by
61- rl. reentrancy_cnt += 1
69+ @inline function trylock (rl:: ReentrantLock )
70+ ct = current_task ()
71+ if rl. locked_by === ct
72+ # @assert rl.havelock !== 0x00
73+ rl. reentrancy_cnt += 0x0000_0001
6274 return true
6375 end
64- lock (rl. cond_wait)
65- if rl. reentrancy_cnt == 0
66- rl. locked_by = t
67- rl. reentrancy_cnt = 1
68- GC. disable_finalizers ()
69- got = true
70- else
71- got = false
76+ return _trylock (rl, ct)
77+ end
78+ @noinline function _trylock (rl:: ReentrantLock , ct:: Task )
79+ GC. disable_finalizers ()
80+ if (@atomicreplace :acquire rl. havelock 0x00 => 0x01 ). success
81+ # @assert rl.locked_by === nothing
82+ # @assert rl.reentrancy_cnt === 0
83+ rl. reentrancy_cnt = 0x0000_0001
84+ @atomic :release rl. locked_by = ct
85+ return true
7286 end
73- unlock (rl . cond_wait )
74- return got
87+ GC . enable_finalizers ( )
88+ return false
7589end
7690
7791"""
@@ -83,28 +97,23 @@ wait for it to become available.
8397
8498Each `lock` must be matched by an [`unlock`](@ref).
8599"""
86- function lock (rl:: ReentrantLock )
87- t = current_task ()
88- if t === rl. locked_by
89- rl. reentrancy_cnt += 1
90- else
91- lock (rl. cond_wait)
92- while true
93- if rl. reentrancy_cnt == 0
94- rl. locked_by = t
95- rl. reentrancy_cnt = 1
96- GC. disable_finalizers ()
97- break
98- end
99- try
100- wait (rl. cond_wait)
101- catch
102- unlock (rl. cond_wait)
103- rethrow ()
100+ @inline function lock (rl:: ReentrantLock )
101+ trylock (rl) || (@noinline function slowlock (rl:: ReentrantLock )
102+ c = rl. cond_wait
103+ lock (c. lock)
104+ try
105+ while true
106+ if (@atomicreplace rl. havelock 0x01 => 0x02 ). old == 0x00 # :sequentially_consistent ? # now either 0x00 or 0x02
107+ # it was unlocked, so try to lock it ourself
108+ _trylock (rl, current_task ()) && break
109+ else # it was locked, so now wait for the release to notify us
110+ wait (c)
111+ end
104112 end
113+ finally
114+ unlock (c. lock)
105115 end
106- unlock (rl. cond_wait)
107- end
116+ end )(rl)
108117 return
109118end
110119
@@ -116,58 +125,42 @@ Releases ownership of the `lock`.
116125If this is a recursive lock which has been acquired before, decrement an
117126internal counter and return immediately.
118127"""
119- function unlock (rl:: ReentrantLock )
120- t = current_task ()
121- n = rl. reentrancy_cnt
122- n == 0 && error (" unlock count must match lock count" )
123- rl. locked_by === t || error (" unlock from wrong thread" )
124- if n > 1
125- rl. reentrancy_cnt = n - 1
126- else
127- lock (rl. cond_wait)
128- rl. reentrancy_cnt = 0
129- rl. locked_by = nothing
130- if ! isempty (rl. cond_wait. waitq)
131- try
132- notify (rl. cond_wait)
133- catch
134- unlock (rl. cond_wait)
135- rethrow ()
128+ @inline function unlock (rl:: ReentrantLock )
129+ rl. locked_by === current_task () ||
130+ error (rl. reentrancy_cnt == 0x0000_0000 ? " unlock count must match lock count" : " unlock from wrong thread" )
131+ (@noinline function _unlock (rl:: ReentrantLock )
132+ n = rl. reentrancy_cnt - 0x0000_0001
133+ rl. reentrancy_cnt = n
134+ if n == 0x0000_00000
135+ @atomic :monotonic rl. locked_by = nothing
136+ if (@atomicswap :release rl. havelock = 0x00 ) == 0x02
137+ (@noinline function notifywaiters (rl)
138+ cond_wait = rl. cond_wait
139+ lock (cond_wait)
140+ try
141+ notify (cond_wait)
142+ finally
143+ unlock (cond_wait)
144+ end
145+ end )(rl)
136146 end
147+ return true
137148 end
138- GC. enable_finalizers ()
139- unlock (rl. cond_wait)
140- end
141- return
149+ return false
150+ end )(rl) && GC. enable_finalizers ()
151+ nothing
142152end
143153
144154function unlockall (rl:: ReentrantLock )
145- t = current_task ()
146- n = rl. reentrancy_cnt
147- rl. locked_by === t || error (" unlock from wrong thread" )
148- n == 0 && error (" unlock count must match lock count" )
149- lock (rl. cond_wait)
150- rl. reentrancy_cnt = 0
151- rl. locked_by = nothing
152- if ! isempty (rl. cond_wait. waitq)
153- try
154- notify (rl. cond_wait)
155- catch
156- unlock (rl. cond_wait)
157- rethrow ()
158- end
159- end
160- GC. enable_finalizers ()
161- unlock (rl. cond_wait)
155+ n = @atomicswap :not_atomic rl. reentrancy_cnt = 0x0000_0001
156+ unlock (rl)
162157 return n
163158end
164159
165- function relockall (rl:: ReentrantLock , n:: Int )
166- t = current_task ()
160+ function relockall (rl:: ReentrantLock , n:: UInt32 )
167161 lock (rl)
168- n1 = rl. reentrancy_cnt
169- rl. reentrancy_cnt = n
170- n1 == 1 || concurrency_violation ()
162+ old = @atomicswap :not_atomic rl. reentrancy_cnt = n
163+ old == 0x0000_0001 || concurrency_violation ()
171164 return
172165end
173166
0 commit comments