# Eager finalization and smart pointers

**URL:** https://discourse.julialang.org/t/eager-finalization-and-smart-pointers/92462
**Category:** Internals & Design
**Created:** [January 3, 2023, 5:28pm UTC](https://discourse.julialang.org/t/eager-finalization-and-smart-pointers/92462 "2023-01-03T17:28:07Z")
**Posts on this page:** 1
**Showing post:** 2

<div class="post-metadata">

### Author: ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)
#### Post date: [January 3, 2023, 9:07pm UTC](https://discourse.julialang.org/t/eager-finalization-and-smart-pointers/92462/2 "2023-01-03T21:07:53Z")

</div>

Digging deeper, it seems it may be too early to pursue this since there are significant restrictions in terms of what kind of finalizers can be called eagerly. Following @aviatesk 's demonstration, it is still quite impressive how well this works.

> <https://github.com/JuliaLang/julia/pull/46651#issuecomment-1253471390>
>
> Looks great, thanks sooo much for implementing the post-domination analysis, tha…t is very exciting itself.
> I think there seems to be a problem in finalizer insertion point though, MRE would be:
> \`\`\`julia
> const FINALIZATION\_COUNT = Ref(0)
> init\_finalization\_count!() = FINALIZATION\_COUNT\[\] = 0
> get\_finalization\_count() = FINALIZATION\_COUNT\[\]
> @noinline add\_finalization\_count!(x) = FINALIZATION\_COUNT\[\] += x
> @noinline Base.@assume\_effects :nothrow safeprint(io::IO, x...) = (@nospecialize; print(io, x...))
> @test Core.Compiler.is\_finalizer\_inlineable(Base.infer\_effects(add\_finalization\_count!, (Int,)))
> 
> mutable struct DoAllocWithFieldInter
> x::Int
> end
> function register\_finalizer!(obj::DoAllocWithFieldInter)
> finalizer(obj) do this
> add\_finalization\_count!(this.x)
> end
> end
> 
> function cfg\_finalization6(io)
> for i = -999:1000
> o = DoAllocWithFieldInter(0)
> register\_finalizer!(o)
> if i == 1000
> o.x = i # with \`setfield!\`
> elseif i \> 0
> safeprint(io, o.x, '\\n')
> end
> # \<= shouldn't the finalizer be inlined here?
> end
> end
> let src = code\_typed1(cfg\_finalization6, (IO,))
> @test count(isinvoke(:add\_finalization\_count!), src.code) == 1
> end
> let
> init\_finalization\_count!()
> cfg\_finalization6(IOBuffer())
> @test get\_finalization\_count() == 1000 # this fails
> end
> \`\`\`
> 
> Currently the finalizer is inlined after the allocation site, so can't observe the field value changed by \`setfield!\`:
> \`\`\`julia
> julia\> src = code\_typed1(cfg\_finalization6, (IO,))
> CodeInfo(
> 1 ── goto #11 if not true
> 2 ┄─ %2 = φ (#1 =\> -999, #10 =\> %20)::Int64
> │ %3 = φ (#1 =\> -999, #10 =\> %21)::Int64
> │ %4 = %new(Main.DoAllocWithFieldInter, 0)::DoAllocWithFieldInter
> │ %5 = Base.getfield(%4, :x)::Int64
> │ invoke Main.add\_finalization\_count!(%5::Int64)::Int64
> │ %7 = (%2 === 1000)::Bool
> └─── goto #4 if not %7
> 3 ── Base.setfield!(%4, :x, %2)::Int64
> └─── goto #6
> 4 ── %11 = Base.slt\_int(0, %2)::Bool
> └─── goto #6 if not %11
> 5 ── %13 = Base.getfield(%4, :x)::Int64
> └─── invoke Main.safeprint(io::IO, %13::Any, '\\n'::Vararg{Any})::Any
> 6 ┄─ %15 = (%3 === 1000)::Bool
> └─── goto #8 if not %15
> 7 ── goto #9
> 8 ── %18 = Base.add\_int(%3, 1)::Int64
> └─── goto #9
> 9 ┄─ %20 = φ (#8 =\> %18)::Int64
> │ %21 = φ (#8 =\> %18)::Int64
> │ %22 = φ (#7 =\> true, #8 =\> false)::Bool
> │ %23 = Base.not\_int(%22)::Bool
> └─── goto #11 if not %23
> 10 ─ goto #2
> 11 ┄ return nothing
> )
> \`\`\`

```julia
using Test
include(normpath(Sys.BINDIR, "..", "share", "julia", "test", "compiler", "EscapeAnalysis", "setup.jl"))
const FINALIZATION_COUNT = Ref(0)
init_finalization_count!() = FINALIZATION_COUNT[] = 0
get_finalization_count() = FINALIZATION_COUNT[]
@noinline add_finalization_count!(x) = FINALIZATION_COUNT[] += x
@noinline Base.@assume_effects :nothrow safeprint(io::IO, x...) = (@nospecialize; print(io, x...))
@test Core.Compiler.is_finalizer_inlineable(Base.infer_effects(add_finalization_count!, (Int,)))

mutable struct DoAllocWithFieldInter
    x::Int
end
function register_finalizer!(obj::DoAllocWithFieldInter)
    finalizer(obj) do this
        add_finalization_count!(this.x)
    end
end

function cfg_finalization6(io)
    for i = -999:1000
        o = DoAllocWithFieldInter(0)
        register_finalizer!(o)
        if i == 1000
            o.x = i # with `setfield!`
        elseif i > 0
            safeprint(io, o.x, '\n')
        end
        # <= shouldn't the finalizer be inlined here?
    end
end
let src = code_typed1(cfg_finalization6, (IO,))
    @test count(isinvoke(:add_finalization_count!), src.code) == 1
end
let
    init_finalization_count!()
    cfg_finalization6(IOBuffer())
    @test get_finalization_count() == 1000 # this now succeeds!
end

```

---

_[View the full topic](https://discourse.julialang.org/t/eager-finalization-and-smart-pointers/92462)._
