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
EDIT: Thank you so much!!! I am super happy to have a workaround now
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