Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
maleadt committed Nov 24, 2020
1 parent 3c9e3dd commit 3c7998d
Show file tree
Hide file tree
Showing 4 changed files with 14 additions and 36 deletions.
34 changes: 6 additions & 28 deletions src/pool.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,39 +24,17 @@ end

Base.unlock(nrl::NonReentrantLock) = unlock(nrl.rl)

# the above lock is taken around code that might gc, which might reenter through finalizers.
# avoid that by temporarily disabling finalizers running concurrently on this thread.
enable_finalizers(on::Bool) = ccall(:jl_gc_enable_finalizers, Cvoid,
(Ptr{Cvoid}, Int32,), Core.getptls(), on)
macro safe_lock(l, ex)
quote
temp = $(esc(l))
lock(temp)
enable_finalizers(false)
try
$(esc(ex))
finally
unlock(temp)
enable_finalizers(true)
end
end
end

# if we actually want to acquire these locks from a finalizer, we can't just wait on them
# (which might cause a task switch). as the lock can only be taken by another thread that
# should be running, and not a concurrent task we'd need to switch to, we can safely spin.
macro safe_lock_spin(l, ex)
# a safe way to acquire locks from finalizers, where we can't wait (which switches tasks)
macro spinlock(l, ex)
quote
temp = $(esc(l))
while !trylock(temp)
# we can't yield here
end
enable_finalizers(false) # retains compatibility with non-finalizer callers
try
$(esc(ex))
finally
unlock(temp)
enable_finalizers(true)
end
end
end
Expand Down Expand Up @@ -299,7 +277,7 @@ a [`OutOfGPUMemoryError`](@ref) if the allocation request cannot be satisfied.

# record the memory block
ptr = pointer(block)
@safe_lock allocated_lock begin
@lock allocated_lock begin
@assert !haskey(allocated[dev], ptr)
allocated[dev][ptr] = block, 1
end
Expand Down Expand Up @@ -337,7 +315,7 @@ multiple calls to `free` before this buffer is put back into the memory pool.
dev = device()

# look up the memory block
@safe_lock_spin allocated_lock begin
@spinlock allocated_lock begin
block, refcount = allocated[dev][ptr]
allocated[dev][ptr] = block, refcount+1
end
Expand Down Expand Up @@ -365,7 +343,7 @@ Releases a buffer pointed to by `ptr` to the memory pool.
# so perform our own error handling.
try
# look up the memory block, and bail out if its refcount isn't 1
block = @safe_lock_spin allocated_lock begin
block = @spinlock allocated_lock begin
block, refcount = allocated[dev][ptr]
if refcount == 1
delete!(allocated[dev], ptr)
Expand Down Expand Up @@ -473,7 +451,7 @@ end

## utilities

used_memory(dev=device()) = @safe_lock allocated_lock begin
used_memory(dev=device()) = @lock allocated_lock begin
mapreduce(sizeof, +, values(allocated[dev]); init=0)
end

Expand Down
6 changes: 3 additions & 3 deletions src/pool/binned.jl
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ end

# repopulate the "available" pools from the list of freed blocks
function pool_repopulate(dev)
blocks = @safe_lock freed_lock begin
blocks = @lock freed_lock begin
isempty(freed[dev]) && return
blocks = Set(freed[dev])
empty!(freed[dev])
Expand Down Expand Up @@ -232,7 +232,7 @@ function pool_free(dev, block)

# we don't do any work here to reduce pressure on the GC (spending time in finalizers)
# and to simplify locking (preventing concurrent access during GC interventions)
@safe_lock_spin freed_lock begin
@spinlock freed_lock begin
push!(freed[dev], block)
end
end
Expand All @@ -245,7 +245,7 @@ function pool_init()
end

function cached_memory(dev=device())
sz = @safe_lock freed_lock mapreduce(sizeof, +, freed[dev]; init=0)
sz = @lock freed_lock mapreduce(sizeof, +, freed[dev]; init=0)
@lock pool_lock for (pid, pl) in enumerate(pools_avail[dev])
bytes = poolsize(pid)
sz += bytes * length(pl)
Expand Down
4 changes: 2 additions & 2 deletions src/pool/simple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ end
function pool_free(dev, block)
# we don't do any work here to reduce pressure on the GC (spending time in finalizers)
# and to simplify locking (preventing concurrent access during GC interventions)
@safe_lock_spin freed_lock begin
@spinlock freed_lock begin
push!(freed[dev], block)
end
end
Expand All @@ -116,7 +116,7 @@ function pool_init()
end

function cached_memory(dev=device())
sz = @safe_lock freed_lock mapreduce(sizeof, +, freed[dev]; init=0)
sz = @lock freed_lock mapreduce(sizeof, +, freed[dev]; init=0)
sz += @lock pool_lock mapreduce(sizeof, +, pool[dev]; init=0)
return sz
end
6 changes: 3 additions & 3 deletions src/pool/split.jl
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ end

# repopulate the pools from the list of freed blocks
function pool_repopulate(dev)
blocks = @safe_lock freed_lock begin
blocks = @lock freed_lock begin
isempty(freed[dev]) && return
blocks = Set(freed[dev])
empty!(freed[dev])
Expand Down Expand Up @@ -304,7 +304,7 @@ function pool_free(dev, block)
# and to simplify locking (preventing concurrent access during GC interventions)
block.state == ALLOCATED || error("Cannot free a $(block.state) block")
block.state = FREED
@safe_lock_spin freed_lock begin
@spinlock freed_lock begin
push!(freed[dev], block)
end
end
Expand All @@ -329,7 +329,7 @@ function pool_reclaim(dev, sz::Int=typemax(Int))
end

function cached_memory(dev=device())
sz = @safe_lock freed_lock mapreduce(sizeof, +, freed[dev]; init=0)
sz = @lock freed_lock mapreduce(sizeof, +, freed[dev]; init=0)
@lock pool_lock for pool in (pool_small[dev], pool_large[dev], pool_huge[dev])
sz += mapreduce(sizeof, +, pool; init=0)
end
Expand Down

0 comments on commit 3c7998d

Please sign in to comment.