Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
d6ab292
fix: add POOL_DEBUG validation to function definition macro forms
mgyoo86 Mar 7, 2026
417b106
refactor: extract debug logic from utils.jl into dedicated debug.jl
mgyoo86 Mar 7, 2026
5b45f49
feat: add POOL_SAFETY tier system with guard-level invalidation on re…
mgyoo86 Mar 7, 2026
fabdc97
refactor: rename POOL_SAFETY to POOL_SAFETY_LV for clarity and consis…
mgyoo86 Mar 8, 2026
c2e4b7b
feat(legacy): backport POOL_SAFETY guard invalidation to LTS path
mgyoo86 Mar 8, 2026
61efb03
feat: add Level 2 poisoning (NaN/sentinel fill) to POOL_SAFETY
mgyoo86 Mar 8, 2026
cfa8657
feat: add compile-time escape detection for @with_pool macros
mgyoo86 Mar 8, 2026
69d1583
test: expand compile-time escape detection with integration and edge …
mgyoo86 Mar 8, 2026
ce58bc1
feat: upgrade container escapes to compile-time errors, add destructu…
mgyoo86 Mar 8, 2026
7ef7b23
refactor: introduce PoolEscapeError with rich formatted showerror
mgyoo86 Mar 8, 2026
cf5b304
feat: scan all return points for escape detection (branches, loops, t…
mgyoo86 Mar 8, 2026
7165972
feat: show exact escape points with line numbers and highlighted expr…
mgyoo86 Mar 8, 2026
237e662
fix: enhance error message formatting in PoolEscapeError showerror fu…
mgyoo86 Mar 8, 2026
86fe2dd
feat: enhance escape detection with alias tracking, @skip_check_vars,…
mgyoo86 Mar 9, 2026
6988338
feat: add recursive _validate_pool_return for Tuple/NamedTuple/Pair c…
mgyoo86 Mar 9, 2026
35e685f
feat: extend escape detection to Dict, Set, and Vector element recursion
mgyoo86 Mar 9, 2026
e862b42
feat: enhance escape detection error messages with type/size info
mgyoo86 Mar 9, 2026
bbcc41c
refactor: replace ErrorException with PoolRuntimeEscapeError for form…
mgyoo86 Mar 9, 2026
46d96bc
feat: add borrow registry for acquire call-site tracking (POOL_SAFETY…
mgyoo86 Mar 9, 2026
be92fdc
feat: validate explicit return statements in @with_pool for escape de…
mgyoo86 Mar 9, 2026
385353b
feat: enhance escape error with full-line callsite and return-site tr…
mgyoo86 Mar 9, 2026
bedffd4
feat: classify escape error variables as view/array/BitArray/containe…
mgyoo86 Mar 9, 2026
2f78a0b
fix: suppress stacktrace in compile-time PoolEscapeError display
mgyoo86 Mar 9, 2026
9c53d25
refactor: replace @skip_check_vars with opaque _test_leak() in tests
mgyoo86 Mar 9, 2026
0a474ef
feat: add declaration site tracking to compile-time escape errors
mgyoo86 Mar 9, 2026
b2aaac2
refactor: replace @static if STATIC_POOL_CHECKS with tag dispatch
mgyoo86 Mar 10, 2026
9f578eb
Runic formatting
mgyoo86 Mar 10, 2026
f3f6647
fix: restore compile-time escape throw + add tag dispatch to legacy path
mgyoo86 Mar 10, 2026
97ea189
fix: clear stale _pending_callsite after borrow recording + reset cle…
mgyoo86 Mar 10, 2026
1984c3a
Runic formatting
mgyoo86 Mar 10, 2026
78e23b7
increase test coverage
mgyoo86 Mar 10, 2026
4002151
Runic formatting
mgyoo86 Mar 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/AdaptiveArrayPools.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export zeros!, ones!, trues!, falses!, similar!, reshape!, default_eltype # Con
export unsafe_zeros!, unsafe_ones!, unsafe_similar! # Unsafe convenience functions
export Bit # Sentinel type for BitArray (use with acquire!, trues!, falses!)
export @with_pool, @maybe_with_pool
export STATIC_POOLING, MAYBE_POOLING, POOL_DEBUG
export STATIC_POOLING, MAYBE_POOLING, POOL_DEBUG, POOL_SAFETY_LV, STATIC_POOL_CHECKS
export PoolEscapeError, EscapePoint
export USE_POOLING, MAYBE_POOLING_ENABLED # Deprecated aliases (backward compat)
export checkpoint!, rewind!, reset!
export get_task_local_cuda_pool, get_task_local_cuda_pools # CUDA (stubs, overridden by extension)
Expand All @@ -28,6 +29,7 @@ export DisabledPool, DISABLED_CPU, pooling_enabled # Disabled pool support
include("convenience.jl")
include("state.jl")
include("task_local_pool.jl")
include("debug.jl")
include("macros.jl")
else
export CACHE_WAYS, set_cache_ways! # N-way cache configuration (legacy only)
Expand All @@ -38,6 +40,7 @@ else
include("convenience.jl")
include("legacy/state.jl")
include("task_local_pool.jl")
include("debug.jl")
include("macros.jl")
end

Expand Down
28 changes: 23 additions & 5 deletions src/acquire.jl
Original file line number Diff line number Diff line change
Expand Up @@ -327,12 +327,16 @@ Internal implementation of acquire!. Called directly by macro-transformed code
"""
@inline function _acquire_impl!(pool::AbstractArrayPool, ::Type{T}, n::Int) where {T}
tp = get_typed_pool!(pool, T)
return get_view!(tp, n)
result = get_view!(tp, n)
_maybe_record_borrow!(pool, tp)
return result
end

@inline function _acquire_impl!(pool::AbstractArrayPool, ::Type{T}, dims::Vararg{Int, N}) where {T, N}
tp = get_typed_pool!(pool, T)
return get_view!(tp, dims)
result = get_view!(tp, dims)
_maybe_record_borrow!(pool, tp)
return result
end

@inline function _acquire_impl!(pool::AbstractArrayPool, ::Type{T}, dims::NTuple{N, Int}) where {T, N}
Expand All @@ -349,17 +353,23 @@ Internal implementation of unsafe_acquire!. Called directly by macro-transformed
"""
@inline function _unsafe_acquire_impl!(pool::AbstractArrayPool, ::Type{T}, n::Int) where {T}
tp = get_typed_pool!(pool, T)
return get_array!(tp, (n,))
result = get_array!(tp, (n,))
_maybe_record_borrow!(pool, tp)
return result
end

@inline function _unsafe_acquire_impl!(pool::AbstractArrayPool, ::Type{T}, dims::Vararg{Int, N}) where {T, N}
tp = get_typed_pool!(pool, T)
return get_array!(tp, dims)
result = get_array!(tp, dims)
_maybe_record_borrow!(pool, tp)
return result
end

@inline function _unsafe_acquire_impl!(pool::AbstractArrayPool, ::Type{T}, dims::NTuple{N, Int}) where {T, N}
tp = get_typed_pool!(pool, T)
return get_array!(tp, dims)
result = get_array!(tp, dims)
_maybe_record_borrow!(pool, tp)
return result
end

# Similar-style
Expand Down Expand Up @@ -403,18 +413,21 @@ See also: [`unsafe_acquire!`](@ref) for native array access.
"""
@inline function acquire!(pool::AbstractArrayPool, ::Type{T}, n::Int) where {T}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct acquire! call>")
return _acquire_impl!(pool, T, n)
end

# Multi-dimensional support (zero-allocation with N-D cache)
@inline function acquire!(pool::AbstractArrayPool, ::Type{T}, dims::Vararg{Int, N}) where {T, N}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct acquire! call>")
return _acquire_impl!(pool, T, dims...)
end

# Tuple support: allows acquire!(pool, T, size(A)) where size(A) returns NTuple{N,Int}
@inline function acquire!(pool::AbstractArrayPool, ::Type{T}, dims::NTuple{N, Int}) where {T, N}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct acquire! call>")
return _acquire_impl!(pool, T, dims...)
end

Expand All @@ -435,6 +448,7 @@ end
"""
@inline function acquire!(pool::AbstractArrayPool, x::AbstractArray)
_record_type_touch!(pool, eltype(x))
_set_pending_callsite!(pool, "<direct acquire! call>")
return _acquire_impl!(pool, eltype(x), size(x))
end

Expand Down Expand Up @@ -490,17 +504,20 @@ See also: [`acquire!`](@ref) for view-based access.
"""
@inline function unsafe_acquire!(pool::AbstractArrayPool, ::Type{T}, n::Int) where {T}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct unsafe_acquire! call>")
return _unsafe_acquire_impl!(pool, T, n)
end

@inline function unsafe_acquire!(pool::AbstractArrayPool, ::Type{T}, dims::Vararg{Int, N}) where {T, N}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct unsafe_acquire! call>")
return _unsafe_acquire_impl!(pool, T, dims...)
end

# Tuple support
@inline function unsafe_acquire!(pool::AbstractArrayPool, ::Type{T}, dims::NTuple{N, Int}) where {T, N}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct unsafe_acquire! call>")
return _unsafe_acquire_impl!(pool, T, dims)
end

Expand All @@ -521,6 +538,7 @@ end
"""
@inline function unsafe_acquire!(pool::AbstractArrayPool, x::AbstractArray)
_record_type_touch!(pool, eltype(x))
_set_pending_callsite!(pool, "<direct unsafe_acquire! call>")
return _unsafe_acquire_impl!(pool, eltype(x), size(x))
end

Expand Down
21 changes: 16 additions & 5 deletions src/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -139,17 +139,23 @@ end
# Bit type: returns BitArray{N} with shared chunks (SIMD optimized, N-D cached)
@inline function _unsafe_acquire_impl!(pool::AbstractArrayPool, ::Type{Bit}, n::Int)
tp = get_typed_pool!(pool, Bit)::BitTypedPool
return get_bitarray!(tp, n)
result = get_bitarray!(tp, n)
_maybe_record_borrow!(pool, tp)
return result
end

@inline function _unsafe_acquire_impl!(pool::AbstractArrayPool, ::Type{Bit}, dims::Vararg{Int, N}) where {N}
tp = get_typed_pool!(pool, Bit)::BitTypedPool
return get_bitarray!(tp, dims)
result = get_bitarray!(tp, dims)
_maybe_record_borrow!(pool, tp)
return result
end

@inline function _unsafe_acquire_impl!(pool::AbstractArrayPool, ::Type{Bit}, dims::NTuple{N, Int}) where {N}
tp = get_typed_pool!(pool, Bit)::BitTypedPool
return get_bitarray!(tp, dims)
result = get_bitarray!(tp, dims)
_maybe_record_borrow!(pool, tp)
return result
end

# ==============================================================================
Expand Down Expand Up @@ -193,19 +199,24 @@ end
# ==============================================================================

# Check if BitArray chunks overlap with the pool's BitTypedPool storage
function _check_bitchunks_overlap(arr::BitArray, pool::AdaptiveArrayPool)
function _check_bitchunks_overlap(arr::BitArray, pool::AdaptiveArrayPool, original_val = arr)
arr_chunks = arr.chunks
arr_ptr = UInt(pointer(arr_chunks))
arr_len = length(arr_chunks) * sizeof(UInt64)
arr_end = arr_ptr + arr_len

return_site = let rs = pool._pending_return_site
isempty(rs) ? nothing : rs
end

for v in pool.bits.vectors
v_chunks = v.chunks
v_ptr = UInt(pointer(v_chunks))
v_len = length(v_chunks) * sizeof(UInt64)
v_end = v_ptr + v_len
if !(arr_end <= v_ptr || v_end <= arr_ptr)
error("Safety Violation: The function returned a BitArray backed by pool memory. This is unsafe as the memory will be reclaimed. Please return a copy (copy) or a scalar.")
callsite = _lookup_borrow_callsite(pool, v)
_throw_pool_escape_error(original_val, Bit, callsite, return_site)
end
end
return nothing
Expand Down
28 changes: 28 additions & 0 deletions src/convenience.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,25 @@ See also: [`ones!`](@ref), [`similar!`](@ref), [`acquire!`](@ref)
"""
@inline function zeros!(pool::AbstractArrayPool, ::Type{T}, dims::Vararg{Int, N}) where {T, N}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct zeros! call>")
return _zeros_impl!(pool, T, dims...)
end

@inline function zeros!(pool::AbstractArrayPool, dims::Vararg{Int, N}) where {N}
_record_type_touch!(pool, default_eltype(pool))
_set_pending_callsite!(pool, "<direct zeros! call>")
return _zeros_impl!(pool, default_eltype(pool), dims...)
end

@inline function zeros!(pool::AbstractArrayPool, ::Type{T}, dims::NTuple{N, Int}) where {T, N}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct zeros! call>")
return _zeros_impl!(pool, T, dims...)
end

@inline function zeros!(pool::AbstractArrayPool, dims::NTuple{N, Int}) where {N}
_record_type_touch!(pool, default_eltype(pool))
_set_pending_callsite!(pool, "<direct zeros! call>")
return _zeros_impl!(pool, default_eltype(pool), dims...)
end

Expand Down Expand Up @@ -117,21 +121,25 @@ See also: [`zeros!`](@ref), [`similar!`](@ref), [`acquire!`](@ref)
"""
@inline function ones!(pool::AbstractArrayPool, ::Type{T}, dims::Vararg{Int, N}) where {T, N}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct ones! call>")
return _ones_impl!(pool, T, dims...)
end

@inline function ones!(pool::AbstractArrayPool, dims::Vararg{Int, N}) where {N}
_record_type_touch!(pool, default_eltype(pool))
_set_pending_callsite!(pool, "<direct ones! call>")
return _ones_impl!(pool, default_eltype(pool), dims...)
end

@inline function ones!(pool::AbstractArrayPool, ::Type{T}, dims::NTuple{N, Int}) where {T, N}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct ones! call>")
return _ones_impl!(pool, T, dims...)
end

@inline function ones!(pool::AbstractArrayPool, dims::NTuple{N, Int}) where {N}
_record_type_touch!(pool, default_eltype(pool))
_set_pending_callsite!(pool, "<direct ones! call>")
return _ones_impl!(pool, default_eltype(pool), dims...)
end

Expand Down Expand Up @@ -187,10 +195,12 @@ See also: [`falses!`](@ref), [`ones!`](@ref), [`acquire!`](@ref)
"""
@inline function trues!(pool::AbstractArrayPool, dims::Vararg{Int, N}) where {N}
_record_type_touch!(pool, Bit)
_set_pending_callsite!(pool, "<direct trues! call>")
return _trues_impl!(pool, dims...)
end
@inline function trues!(pool::AbstractArrayPool, dims::NTuple{N, Int}) where {N}
_record_type_touch!(pool, Bit)
_set_pending_callsite!(pool, "<direct trues! call>")
return _trues_impl!(pool, dims...)
end

Expand Down Expand Up @@ -227,10 +237,12 @@ See also: [`trues!`](@ref), [`zeros!`](@ref), [`acquire!`](@ref)
"""
@inline function falses!(pool::AbstractArrayPool, dims::Vararg{Int, N}) where {N}
_record_type_touch!(pool, Bit)
_set_pending_callsite!(pool, "<direct falses! call>")
return _falses_impl!(pool, dims...)
end
@inline function falses!(pool::AbstractArrayPool, dims::NTuple{N, Int}) where {N}
_record_type_touch!(pool, Bit)
_set_pending_callsite!(pool, "<direct falses! call>")
return _falses_impl!(pool, dims...)
end

Expand Down Expand Up @@ -274,21 +286,25 @@ See also: [`zeros!`](@ref), [`ones!`](@ref), [`acquire!`](@ref)
"""
@inline function similar!(pool::AbstractArrayPool, x::AbstractArray)
_record_type_touch!(pool, eltype(x))
_set_pending_callsite!(pool, "<direct similar! call>")
return _similar_impl!(pool, x)
end

@inline function similar!(pool::AbstractArrayPool, x::AbstractArray, ::Type{T}) where {T}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct similar! call>")
return _similar_impl!(pool, x, T)
end

@inline function similar!(pool::AbstractArrayPool, x::AbstractArray, dims::Vararg{Int, N}) where {N}
_record_type_touch!(pool, eltype(x))
_set_pending_callsite!(pool, "<direct similar! call>")
return _similar_impl!(pool, x, dims...)
end

@inline function similar!(pool::AbstractArrayPool, x::AbstractArray, ::Type{T}, dims::Vararg{Int, N}) where {T, N}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct similar! call>")
return _similar_impl!(pool, x, T, dims...)
end

Expand Down Expand Up @@ -398,21 +414,25 @@ See also: [`unsafe_ones!`](@ref), [`zeros!`](@ref), [`unsafe_acquire!`](@ref)
"""
@inline function unsafe_zeros!(pool::AbstractArrayPool, ::Type{T}, dims::Vararg{Int, N}) where {T, N}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct unsafe_zeros! call>")
return _unsafe_zeros_impl!(pool, T, dims...)
end

@inline function unsafe_zeros!(pool::AbstractArrayPool, dims::Vararg{Int, N}) where {N}
_record_type_touch!(pool, default_eltype(pool))
_set_pending_callsite!(pool, "<direct unsafe_zeros! call>")
return _unsafe_zeros_impl!(pool, default_eltype(pool), dims...)
end

@inline function unsafe_zeros!(pool::AbstractArrayPool, ::Type{T}, dims::NTuple{N, Int}) where {T, N}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct unsafe_zeros! call>")
return _unsafe_zeros_impl!(pool, T, dims...)
end

@inline function unsafe_zeros!(pool::AbstractArrayPool, dims::NTuple{N, Int}) where {N}
_record_type_touch!(pool, default_eltype(pool))
_set_pending_callsite!(pool, "<direct unsafe_zeros! call>")
return _unsafe_zeros_impl!(pool, default_eltype(pool), dims...)
end

Expand Down Expand Up @@ -465,21 +485,25 @@ See also: [`unsafe_zeros!`](@ref), [`ones!`](@ref), [`unsafe_acquire!`](@ref)
"""
@inline function unsafe_ones!(pool::AbstractArrayPool, ::Type{T}, dims::Vararg{Int, N}) where {T, N}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct unsafe_ones! call>")
return _unsafe_ones_impl!(pool, T, dims...)
end

@inline function unsafe_ones!(pool::AbstractArrayPool, dims::Vararg{Int, N}) where {N}
_record_type_touch!(pool, default_eltype(pool))
_set_pending_callsite!(pool, "<direct unsafe_ones! call>")
return _unsafe_ones_impl!(pool, default_eltype(pool), dims...)
end

@inline function unsafe_ones!(pool::AbstractArrayPool, ::Type{T}, dims::NTuple{N, Int}) where {T, N}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct unsafe_ones! call>")
return _unsafe_ones_impl!(pool, T, dims...)
end

@inline function unsafe_ones!(pool::AbstractArrayPool, dims::NTuple{N, Int}) where {N}
_record_type_touch!(pool, default_eltype(pool))
_set_pending_callsite!(pool, "<direct unsafe_ones! call>")
return _unsafe_ones_impl!(pool, default_eltype(pool), dims...)
end

Expand Down Expand Up @@ -535,21 +559,25 @@ See also: [`similar!`](@ref), [`unsafe_acquire!`](@ref)
"""
@inline function unsafe_similar!(pool::AbstractArrayPool, x::AbstractArray)
_record_type_touch!(pool, eltype(x))
_set_pending_callsite!(pool, "<direct unsafe_similar! call>")
return _unsafe_similar_impl!(pool, x)
end

@inline function unsafe_similar!(pool::AbstractArrayPool, x::AbstractArray, ::Type{T}) where {T}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct unsafe_similar! call>")
return _unsafe_similar_impl!(pool, x, T)
end

@inline function unsafe_similar!(pool::AbstractArrayPool, x::AbstractArray, dims::Vararg{Int, N}) where {N}
_record_type_touch!(pool, eltype(x))
_set_pending_callsite!(pool, "<direct unsafe_similar! call>")
return _unsafe_similar_impl!(pool, x, dims...)
end

@inline function unsafe_similar!(pool::AbstractArrayPool, x::AbstractArray, ::Type{T}, dims::Vararg{Int, N}) where {T, N}
_record_type_touch!(pool, T)
_set_pending_callsite!(pool, "<direct unsafe_similar! call>")
return _unsafe_similar_impl!(pool, x, T, dims...)
end

Expand Down
Loading