How to use WeakRef inside construtor?

Hi there,
I am trying to create a self-updating object which kills its own updating process as soon as it is finalized

mutable struct SelfUpdating
    value
    function SelfUpdating(value)
        finalize(x) = Threads.@spawn println("finalizing $x")
        spawn_task(wx) = Threads.@spawn while true
            sleep(2)
            isnothing(wx.value) && return
            println(wx.value.value)
            wx.value.value += 1
        end
        let x = new(value)
            finalizer(finalize, x)
            spawn_task(WeakRef(x))
            x
        end
    end
end

this unfortunately does not seem to work… I am very surprised.
As you see I already tried to get rid of any variable reference I can imagine.

Still when assigning sf = SelfUpdating(1) and later sf = nothing; GC.gc(), it is not finalized.
Every help is highly appreciated

This appears to work:

mutable struct SelfUpdating
    value
    function SelfUpdating(value)
        finalize(x) = Threads.@spawn println("finalizing $x")
        update!(x) = (x.value += 1)
        spawn_task(wx) = Threads.@spawn while true
            sleep(2)
            isnothing(wx.value) && return
            println(wx.value.value)
            update!(wx.value)
        end
        let x = new(value)
            finalizer(finalize, x)
            spawn_task(WeakRef(x))
            x
        end
    end
end

Don’t ask me why, I know next to nothing about these things, I was just intrigued by the puzzle and stumbled upon a solution.

1 Like

This is really impressive. If I see it correctly, you put the += update into the update! function. That looks totally unreasonable that this works :smiley:
EDIT: Thank you so much!!! I am super happy to have a workaround now :slight_smile:

Hence I guess this is a bug in reference tracking. Does someone know?

2 Likes

What surprises me, given that the above works, is that the following does not work:

mutable struct SelfUpdating
    value
    function SelfUpdating(value)
        finalize(x) = Threads.@spawn println("finalizing $x")
        spawn_task(wx) = Threads.@spawn while true
            sleep(2)
            isnothing(wx.value) && return
            println(wx.value.value)
            let x = wx.value
                x.value += 1
            end
        end
        let x = new(value)
            finalizer(finalize, x)
            spawn_task(WeakRef(x))
            x
        end
    end
end

Specifically, wx.value.value += 1 lowers to wx.value.value = wx.value.value + 1, which is slightly different from x = wx.value; x.value += 1 in that the latter only calls getproperty(wx, :value) once while the former does so twice (once on each side of the equals sign). (Not that this should matter for reference counting, but at least that’s a clear semantic difference.) But from all my experimenting I’m not able to make it work with any such combination of reorganizing statements and adding let, it really seems that a function barrier is necessary.

1 Like