@@ -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,41 @@ function trylock(f, l::AbstractLock)
157
154
return false
158
155
end
159
156
157
+ @eval Threads begin
158
+ """
159
+ Threads.Condition([lock])
160
+
161
+ A thread-safe version of [`Base.Condition`](@ref).
162
+
163
+ !!! compat "Julia 1.1"
164
+ This functionality requires at least Julia 1.1.
165
+ """
166
+ const Condition = Base. GenericCondition{Base. ReentrantLock}
167
+
168
+ """
169
+ Special note for [`Threads.Condition`](@ref):
170
+
171
+ The caller must be holding the [`lock`](@ref) that owns `c` before calling this method.
172
+ The calling task will be blocked until some other task wakes it,
173
+ usually by calling [`notify`](@ref)` on the same Condition object.
174
+ The lock will be atomically released when blocking (even if it was locked recursively),
175
+ and will be reacquired before returning.
176
+ """
177
+ wait (c:: Condition )
178
+ end
179
+
160
180
"""
161
181
Semaphore(sem_size)
162
182
163
183
Create a counting semaphore that allows at most `sem_size`
164
184
acquires to be in use at any time.
165
185
Each acquire must be matched with a release.
166
-
167
- This construct is NOT threadsafe.
168
186
"""
169
187
mutable struct Semaphore
170
188
sem_size:: Int
171
189
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" ))
190
+ cond_wait:: Threads.Condition
191
+ Semaphore (sem_size) = sem_size > 0 ? new (sem_size, 0 , Threads . Condition ()) : throw (ArgumentError (" Semaphore size must be > 0" ))
174
192
end
175
193
176
194
"""
@@ -180,14 +198,16 @@ Wait for one of the `sem_size` permits to be available,
180
198
blocking until one can be acquired.
181
199
"""
182
200
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
201
+ lock (s. cond_wait)
202
+ try
203
+ while s. curr_cnt >= s. sem_size
188
204
wait (s. cond_wait)
189
205
end
206
+ s. curr_cnt = s. curr_cnt + 1
207
+ finally
208
+ unlock (s. cond_wait)
190
209
end
210
+ return
191
211
end
192
212
193
213
"""
@@ -198,8 +218,57 @@ possibly allowing another task to acquire it
198
218
and resume execution.
199
219
"""
200
220
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 )
221
+ lock (s. cond_wait)
222
+ try
223
+ s. curr_cnt > 0 || error (" release count must match acquire count" )
224
+ s. curr_cnt -= 1
225
+ notify (s. cond_wait; all= false )
226
+ finally
227
+ unlock (s. cond_wait)
228
+ end
204
229
return
205
230
end
231
+
232
+
233
+ """
234
+ Event()
235
+
236
+ Create a level-triggered event source. Tasks that call [`wait`](@ref) on an
237
+ `Event` are suspended and queued until `notify` is called on the `Event`.
238
+ After `notify` is called, the `Event` remains in a signaled state and
239
+ tasks will no longer block when waiting for it.
240
+
241
+ !!! compat "Julia 1.1"
242
+ This functionality requires at least Julia 1.1.
243
+ """
244
+ mutable struct Event
245
+ notify:: Threads.Condition
246
+ set:: Bool
247
+ Event () = new (Threads. Condition (), false )
248
+ end
249
+
250
+ function wait (e:: Event )
251
+ e. set && return
252
+ lock (e. notify)
253
+ try
254
+ while ! e. set
255
+ wait (e. notify)
256
+ end
257
+ finally
258
+ unlock (e. notify)
259
+ end
260
+ nothing
261
+ end
262
+
263
+ function notify (e:: Event )
264
+ lock (e. notify)
265
+ try
266
+ if ! e. set
267
+ e. set = true
268
+ notify (e. notify)
269
+ end
270
+ finally
271
+ unlock (e. notify)
272
+ end
273
+ nothing
274
+ end
0 commit comments