Mutable structs in parallel, is this a bug?


#1

Hello,

I ran into this today, if you have a module with a mutable struct:

module container2
export cont2
mutable struct cont2
 a
end
end

When the fields of the mutable struct are changed on the master node, this change is not detected on the other procs:

addprocs(3)

3-element Array{Int64,1}:
 2
 3
 4

using container2

bla=cont2((0.,0.))
container2.cont2((0.0, 0.0))


@sync for ip in procs()
    @spawnat ip @show bla.a
end

bla.a = (0.0, 0.0)
	From worker 2:	bla.a = (0.0, 0.0)
	From worker 4:	bla.a = (0.0, 0.0)
	From worker 3:	bla.a = (0.0, 0.0)

bla.a=((1.,1.))

@sync for ip in procs()
    @spawnat ip @show bla.a
end
bla.a = (1.0, 1.0)
	From worker 2:	bla.a = (0.0, 0.0)
	From worker 4:	bla.a = (0.0, 0.0)
	From worker 3:	bla.a = (0.0, 0.0)

That was surprising behavior to me, and it can lead to bugs. Is this expected behavior?

Thank you


#2

How should the remote processes know that the value has been updated? Use @everywhere:

julia> bla = container2.cont2((0., 0.))
container2.cont2((0.0, 0.0))

julia> @sync for ip in procs()
           @spawnat ip @show bla.a
       end
bla.a = (0.0, 0.0)
	From worker 2:	bla.a = (0.0, 0.0)
	From worker 3:	bla.a = (0.0, 0.0)
	From worker 4:	bla.a = (0.0, 0.0)

julia> bla.a = ((1., 1.))
(1.0, 1.0)

julia> @sync for ip in procs()
           @spawnat ip @show bla.a
       end
bla.a = (1.0, 1.0)
	From worker 2:	bla.a = (0.0, 0.0)
	From worker 4:	bla.a = (0.0, 0.0)
	From worker 3:	bla.a = (0.0, 0.0)

julia> @everywhere bla.a = ((2., 2.))

julia> @sync for ip in procs()
           @spawnat ip @show bla.a
       end
bla.a = (2.0, 2.0)
	From worker 2:	bla.a = (2.0, 2.0)
	From worker 4:	bla.a = (2.0, 2.0)
	From worker 3:	bla.a = (2.0, 2.0)

#3

The point is that @spawnat interpolates the variables from Main to the other processes (@everywhere does not for example). So the first call defines bla everywhere, by “sending” bla from the master.

In subsequent calls, @spawnat checks, if bla has changed (if the variable was rebound, for example), it re-interpolates the new variable. But if only the contents have mutated, the change is not detected and the variable is not re-interpolated (for efficiency reasons I am guessing). I found it surprising, I thought it was always synchronizing the variable. Is that documented?