Skip to content

Commit

Permalink
Merge pull request JuliaLang#17590 from JuliaLang/jn/islocked
Browse files Browse the repository at this point in the history
add islocked predicate and align API for task and thread locks
  • Loading branch information
vtjnash authored Aug 4, 2016
2 parents 08ae28a + c3b55f2 commit 40d298f
Show file tree
Hide file tree
Showing 7 changed files with 339 additions and 102 deletions.
25 changes: 0 additions & 25 deletions base/docs/helpdb/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2974,14 +2974,6 @@ lower-case. Returns a `String`.
"""
bytes2hex

"""
unlock(l::ReentrantLock)
Releases ownership of the lock by the current task. If the lock had been acquired before, it
just decrements an internal counter and returns immediately.
"""
unlock

"""
BigFloat(x)
Expand Down Expand Up @@ -3704,14 +3696,6 @@ Return an iterator over all keys in a collection. `collect(keys(d))` returns an
"""
keys

"""
ReentrantLock()
Creates a reentrant lock. The same task can acquire the lock as many times as required. Each
lock must be matched with an unlock.
"""
ReentrantLock

"""
real(z)
Expand Down Expand Up @@ -4361,15 +4345,6 @@ category Letter, i.e. a character whose category code begins with 'L'.
"""
isalpha

"""
lock(l::ReentrantLock)
Associates `l` with the current task. If `l` is already locked by a different task, waits
for it to become available. The same task can acquire the lock multiple times. Each "lock"
must be matched by an "unlock"
"""
lock

"""
transpose(A)
Expand Down
2 changes: 2 additions & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,7 @@ export
Condition,
consume,
current_task,
islocked,
istaskdone,
istaskstarted,
lock,
Expand All @@ -994,6 +995,7 @@ export
ReentrantLock,
schedule,
task_local_storage,
trylock,
unlock,
yield,
yieldto,
Expand Down
92 changes: 87 additions & 5 deletions base/lock.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
# This file is a part of Julia. License is MIT: http://julialang.org/license

# Advisory reentrant lock
"""
ReentrantLock()
Creates a reentrant lock for synchronizing Tasks.
The same task can acquire the lock as many times as required.
Each `lock` must be matched with an `unlock`.
This lock is NOT threadsafe. See `Threads.Mutex` for a threadsafe lock.
"""
type ReentrantLock
locked_by::Nullable{Task}
cond_wait::Condition
Expand All @@ -9,6 +18,48 @@ type ReentrantLock
ReentrantLock() = new(nothing, Condition(), 0)
end

"""
islocked(the_lock) -> Status (Boolean)
Check whether the lock is held by any task/thread.
This should not be used for synchronization (see instead `trylock`).
"""
function islocked(rl::ReentrantLock)
return rl.reentrancy_cnt != 0
end

"""
trylock(the_lock) -> Success (Boolean)
Acquires the lock if it is available,
returning `true` if successful.
If the lock is already locked by a different task/thread,
returns `false`.
Each successful `trylock` must be matched by an `unlock`.
"""
function trylock(rl::ReentrantLock)
t = current_task()
if rl.reentrancy_cnt == 0
rl.locked_by = t
rl.reentrancy_cnt = 1
return true
elseif t == get(rl.locked_by)
rl.reentrancy_cnt += 1
return true
end
return false
end

"""
lock(the_lock)
Acquires the lock when it becomes available.
If the lock is already locked by a different task/thread,
it waits for it to become available.
Each `lock` must be matched by an `unlock`.
"""
function lock(rl::ReentrantLock)
t = current_task()
while true
Expand All @@ -24,25 +75,48 @@ function lock(rl::ReentrantLock)
end
end

"""
unlock(the_lock)
Releases ownership of the lock.
If this is a recursive lock which has been acquired before, it
just decrements an internal counter and returns immediately.
"""
function unlock(rl::ReentrantLock)
rl.reentrancy_cnt = rl.reentrancy_cnt - 1
if rl.reentrancy_cnt < 0
if rl.reentrancy_cnt == 0
error("unlock count must match lock count")
end
rl.reentrancy_cnt -= 1
if rl.reentrancy_cnt == 0
rl.locked_by = nothing
notify(rl.cond_wait)
end
return rl
return
end

"""
Semaphore(sem_size)
Creates a counting semaphore that allows at most `sem_size`
acquires to be in use at any time.
Each acquire must be mached with a release.
This construct is NOT threadsafe.
"""
type Semaphore
sem_size::Int
curr_cnt::Int
cond_wait::Condition
Semaphore(sem_size) = new(sem_size, 0, Condition())
Semaphore(sem_size) = sem_size > 0 ? new(sem_size, 0, Condition()) : throw(ArgumentError("Semaphore size must be > 0"))
end

"""
acquire(s::Semaphore)
Wait for one of the `sem_size` permits to be available,
blocking until one can be acquired.
"""
function acquire(s::Semaphore)
while true
if s.curr_cnt < s.sem_size
Expand All @@ -54,7 +128,15 @@ function acquire(s::Semaphore)
end
end

"""
release(s::Semaphore)
Return one permit to the pool,
possibly allowing another task to acquire it
and resume execution.
"""
function release(s::Semaphore)
s.curr_cnt = s.curr_cnt - 1
@assert s.curr_cnt > 0 "release count must match acquire count"
s.curr_cnt -= 1
notify(s.cond_wait; all=false)
end
Loading

0 comments on commit 40d298f

Please sign in to comment.