@@ -8,25 +8,22 @@ Creates a re-entrant lock for synchronizing [`Task`](@ref)s.
8
8
The same task can acquire the lock as many times as required.
9
9
Each [`lock`](@ref) must be matched with an [`unlock`](@ref).
10
10
"""
11
- mutable struct GenericReentrantLock{ThreadLock <: AbstractLock } <: AbstractLock
11
+ mutable struct ReentrantLock <: AbstractLock
12
12
locked_by:: Union{Task, Nothing}
13
- cond_wait:: GenericCondition{ThreadLock }
13
+ cond_wait:: GenericCondition{Threads.SpinLock }
14
14
reentrancy_cnt:: Int
15
15
16
- GenericReentrantLock {ThreadLock} () where {ThreadLock <: AbstractLock } = new (nothing , GenericCondition {ThreadLock } (), 0 )
16
+ ReentrantLock () = new (nothing , GenericCondition {Threads.SpinLock } (), 0 )
17
17
end
18
18
19
- # A basic single-threaded, Julia-aware lock:
20
- const ReentrantLockST = GenericReentrantLock{CooperativeLock}
21
-
22
19
23
20
"""
24
21
islocked(lock) -> Status (Boolean)
25
22
26
23
Check whether the `lock` is held by any task/thread.
27
24
This should not be used for synchronization (see instead [`trylock`](@ref)).
28
25
"""
29
- function islocked (rl:: GenericReentrantLock )
26
+ function islocked (rl:: ReentrantLock )
30
27
return rl. reentrancy_cnt != 0
31
28
end
32
29
@@ -40,7 +37,7 @@ return `false`.
40
37
41
38
Each successful `trylock` must be matched by an [`unlock`](@ref).
42
39
"""
43
- function trylock (rl:: GenericReentrantLock )
40
+ function trylock (rl:: ReentrantLock )
44
41
t = current_task ()
45
42
lock (rl. cond_wait)
46
43
try
@@ -67,7 +64,7 @@ wait for it to become available.
67
64
68
65
Each `lock` must be matched by an [`unlock`](@ref).
69
66
"""
70
- function lock (rl:: GenericReentrantLock )
67
+ function lock (rl:: ReentrantLock )
71
68
t = current_task ()
72
69
lock (rl. cond_wait)
73
70
try
@@ -95,7 +92,7 @@ Releases ownership of the `lock`.
95
92
If this is a recursive lock which has been acquired before, decrement an
96
93
internal counter and return immediately.
97
94
"""
98
- function unlock (rl:: GenericReentrantLock )
95
+ function unlock (rl:: ReentrantLock )
99
96
t = current_task ()
100
97
rl. reentrancy_cnt == 0 && error (" unlock count must match lock count" )
101
98
rl. locked_by == t || error (" unlock from wrong thread" )
@@ -112,7 +109,7 @@ function unlock(rl::GenericReentrantLock)
112
109
return
113
110
end
114
111
115
- function unlockall (rl:: GenericReentrantLock )
112
+ function unlockall (rl:: ReentrantLock )
116
113
t = current_task ()
117
114
n = rl. reentrancy_cnt
118
115
rl. locked_by == t || error (" unlock from wrong thread" )
@@ -128,7 +125,7 @@ function unlockall(rl::GenericReentrantLock)
128
125
return n
129
126
end
130
127
131
- function relockall (rl:: GenericReentrantLock , n:: Int )
128
+ function relockall (rl:: ReentrantLock , n:: Int )
132
129
t = current_task ()
133
130
lock (rl)
134
131
n1 = rl. reentrancy_cnt
@@ -157,20 +154,33 @@ function trylock(f, l::AbstractLock)
157
154
return false
158
155
end
159
156
157
+ @eval Threads begin
158
+ const Condition = Base. GenericCondition{ReentrantLock}
159
+
160
+ """
161
+ Special note for [`Threads.Condition`](@ref):
162
+
163
+ The caller must be holding the [`lock`](@ref) that owns `c` before calling this method.
164
+ The calling task will be blocked until some other task wakes it,
165
+ usually by calling [`notify`](@ref)` on the same Condition object.
166
+ The lock will be atomically released when blocking (even if it was locked recursively),
167
+ and will be reacquired before returning.
168
+ """
169
+ wait (c:: Threads.Condition )
170
+ end
171
+
160
172
"""
161
173
Semaphore(sem_size)
162
174
163
175
Create a counting semaphore that allows at most `sem_size`
164
176
acquires to be in use at any time.
165
177
Each acquire must be matched with a release.
166
-
167
- This construct is NOT threadsafe.
168
178
"""
169
179
mutable struct Semaphore
170
180
sem_size:: Int
171
181
curr_cnt:: Int
172
- cond_wait:: ConditionST
173
- Semaphore (sem_size) = sem_size > 0 ? new (sem_size, 0 , ConditionST ()) : throw (ArgumentError (" Semaphore size must be > 0" ))
182
+ cond_wait:: Threads.Condition
183
+ Semaphore (sem_size) = sem_size > 0 ? new (sem_size, 0 , Threads . Condition ()) : throw (ArgumentError (" Semaphore size must be > 0" ))
174
184
end
175
185
176
186
"""
@@ -180,14 +190,16 @@ Wait for one of the `sem_size` permits to be available,
180
190
blocking until one can be acquired.
181
191
"""
182
192
function acquire (s:: Semaphore )
183
- while true
184
- if s. curr_cnt < s. sem_size
185
- s. curr_cnt = s. curr_cnt + 1
186
- return
187
- else
193
+ lock (s. cond_wait)
194
+ try
195
+ while s. curr_cnt >= s. sem_size
188
196
wait (s. cond_wait)
189
197
end
198
+ s. curr_cnt = s. curr_cnt + 1
199
+ finally
200
+ unlock (s. cond_wait)
190
201
end
202
+ return
191
203
end
192
204
193
205
"""
@@ -198,8 +210,57 @@ possibly allowing another task to acquire it
198
210
and resume execution.
199
211
"""
200
212
function release (s:: Semaphore )
201
- @assert s. curr_cnt > 0 " release count must match acquire count"
202
- s. curr_cnt -= 1
203
- notify (s. cond_wait; all= false )
213
+ lock (s. cond_wait)
214
+ try
215
+ s. curr_cnt > 0 || error (" release count must match acquire count" )
216
+ s. curr_cnt -= 1
217
+ notify (s. cond_wait; all= false )
218
+ finally
219
+ unlock (s. cond_wait)
220
+ end
204
221
return
205
222
end
223
+
224
+
225
+ """
226
+ Event()
227
+
228
+ Create a level-triggered event source. Tasks that call [`wait`](@ref) on an
229
+ `Event` are suspended and queued until `notify` is called on the `Event`.
230
+ After `notify` is called, the `Event` remains in a signaled state and
231
+ tasks will no longer block when waiting for it.
232
+
233
+ !!! compat "Julia 1.1"
234
+ This functionality requires at least Julia 1.1.
235
+ """
236
+ mutable struct Event
237
+ notify:: Threads.Condition
238
+ set:: Bool
239
+ Event () = new (Threads. Condition (), false )
240
+ end
241
+
242
+ function wait (e:: Event )
243
+ e. set && return
244
+ lock (e. notify)
245
+ try
246
+ while ! e. set
247
+ wait (e. notify)
248
+ end
249
+ finally
250
+ unlock (e. notify)
251
+ end
252
+ nothing
253
+ end
254
+
255
+ function notify (e:: Event )
256
+ lock (e. notify)
257
+ try
258
+ if ! e. set
259
+ e. set = true
260
+ notify (e. notify)
261
+ end
262
+ finally
263
+ unlock (e. notify)
264
+ end
265
+ nothing
266
+ end
0 commit comments