diff --git a/src/Rasters.jl b/src/Rasters.jl index 46795200b..04caf7a6d 100644 --- a/src/Rasters.jl +++ b/src/Rasters.jl @@ -110,7 +110,6 @@ include("read.jl") include("write.jl") include("show.jl") include("plotrecipes.jl") -include("sectorlock.jl") include("methods/burning/edges.jl") include("methods/burning/allocs.jl") diff --git a/src/methods/burning/geometry.jl b/src/methods/burning/geometry.jl index 5afae6204..4263ead93 100644 --- a/src/methods/burning/geometry.jl +++ b/src/methods/burning/geometry.jl @@ -23,7 +23,7 @@ function _burn_geometry!(B::AbstractRaster, ::GI.AbstractFeatureCollectionTrait, end # Where geoms is an iterator function _burn_geometry!(B::AbstractRaster, trait::Nothing, geoms; - collapse::Union{Bool,Nothing}=nothing, lock=SectorLocks(), verbose=true, progress=true, threaded=true, + collapse::Union{Bool,Nothing}=nothing, lock=Threads.SpinLock(), verbose=true, progress=true, threaded=true, allocs=_burning_allocs(B; threaded), kw... )::Bool range = _geomindices(geoms) diff --git a/src/methods/rasterize.jl b/src/methods/rasterize.jl index b26167e2f..70a36bb18 100644 --- a/src/methods/rasterize.jl +++ b/src/methods/rasterize.jl @@ -75,7 +75,7 @@ struct Rasterizer{T,G,F,R,O,I,M} op::O init::I missingval::M - lock::Union{SectorLocks,Nothing} + lock::Union{Threads.SpinLock,Nothing} shape::Symbol boundary::Symbol verbose::Bool @@ -138,7 +138,7 @@ function Rasterizer(geom, fill, fillitr; @warn "currently `:points` rasterization of multiple non-`PointTrait` geometries may be innaccurate for `reducer` methods besides $stable_reductions. Make a Rasters.jl github issue if you need this to work" end eltype, missingval = get_eltype_missingval(eltype, missingval, fillitr, init, filename, op, reducer) - lock = threaded ? SectorLocks() : nothing + lock = threaded ? Threads.SpinLock() : nothing return Rasterizer(eltype, geom, fillitr, reducer, op, init, missingval, lock, shape, boundary, verbose, progress, threaded) end @@ -573,7 +573,7 @@ function _rasterize!(A, ::GI.AbstractGeometryTrait, geom, fill, r::Rasterizer; a boolmask!(bools, geom; allocs, lock, shape, boundary, verbose, progress) hasburned = any(bools) if hasburned - # Avoid race conditions with a SectorLock + # Avoid race conditions isnothing(lock) || Base.lock(lock, V) _fill!(V, bools, fill, op, init, missingval) isnothing(lock) || Base.unlock(lock) diff --git a/src/sectorlock.jl b/src/sectorlock.jl deleted file mode 100644 index 28c53e616..000000000 --- a/src/sectorlock.jl +++ /dev/null @@ -1,83 +0,0 @@ -############################## -# SectorLocks - -# These lets us lock part of the array so we can work in parallel -# without always blocking other threads. - -# Usually most polygons cover a small subset of a raster. -mutable struct SectorLock - lock::Threads.SpinLock - sector::CartesianIndices{2,Tuple{UnitRange{Int},UnitRange{Int}}} -end -SectorLock() = SectorLock(Threads.SpinLock(), CartesianIndices((1:0, 1:0))) - -Base.lock(sl::SectorLock) = lock(sl.lock) -Base.unlock(sl::SectorLock) = unlock(sl.lock) -Base.islocked(sl::SectorLock) = islocked(sl.lock) - -# One lock for each thread - this is only for threads -struct SectorLocks - seclocks::Vector{SectorLock} - spinlock::Threads.SpinLock -end -SectorLocks(n::Int) = SectorLocks([SectorLock() for _ in 1:n], Threads.SpinLock()) -SectorLocks() = SectorLocks(_nthreads()) - -Base.length(sl::SectorLocks) = length(sl.seclocks) -Base.getindex(sl::SectorLocks, i::Int) = sl.seclocks[i] -Base.iterate(sl::SectorLocks, args...) = iterate(sl.seclocks, args...) -Base.eachindex(sl::SectorLocks) = 1:length(sl) - -Base.lock(sl::SectorLocks, sector::RasterStack) = Base.lock(sl, first(sector)) -function Base.lock(sl::SectorLocks, A::Raster) - o = otherdims(A, DEFAULT_POINT_ORDER) - if length(o) > 0 - slice = first(eachslice(A; dims=o)) - return Base.lock(sl, parent(slice)) - else - return Base.lock(sl, parent(A)) - end -end -# Base.lock(sl::SectorLocks, sector::DiskArrays.SubDiskArray) = Base.lock(sl, sector.v) -Base.lock(sl::SectorLocks, sector::SubArray) = Base.lock(sl, CartesianIndices(_unitranges(sector.indices...))) -Base.lock(sl::SectorLocks, sector::Tuple) = Base.lock(sl, CartesianIndices(sector)) -Base.lock(sl::SectorLocks, sector::AbstractArray) = Base.lock(sl, CartesianIndices(sector)) -function Base.lock(seclocks::SectorLocks, sector::CartesianIndices; grouping=1) - idx = Threads.threadid() - thread_lock = seclocks.seclocks[idx] - thread_lock.sector = sector - # Lock the main lock so no other sectors can be locked - lock(seclocks.spinlock) - # Check for any other lock that intersects our sector - for i in eachindex(seclocks) - # Ignore a lock from this thread - i == idx && continue - seclock = seclocks[i] - # Most of the time the lock will not intersect - if islocked(seclock) && _intersects(seclock, sector) - # If it does, lock the sector and wait - # All other threads are also waiting, - # but if this doens't happen often its fine. - lock(seclock) - unlock(seclock) - end - end - # Lock this sector - lock(thread_lock) - # Unlock the main spinlock so other sectors can be locked - unlock(seclocks.spinlock) - # Work is now done in the locked sector, knowing no other thread - # can write to it. `unlock` is run when it is finished. -end -Base.unlock(sl::SectorLocks) = begin - # println("unlocking thread", idx) - unlock(sl[Threads.threadid()]) -end - -_intersects(seclock::SectorLock, sector) = _intersects(seclock.sector, sector) -_intersects(sector1, sector2) = length(intersect(sector1, sector2)) > 0 - -_unitranges(x1, xs...) = (_unitranges(x1)..., _unitranges(xs...)...) -_unitranges(x1::UnitRange) = (x1,) -_unitranges(x1::AbstractRange{Int}) = (UnitRange(min(first(x1), last(x1)), max(first(x1), last(x1))),) -_unitranges(x1) = ()