@spawn at fails when called inside a function in a module

I have @spawnat expression run inside a function in a module and get the following error:

module Test
using Distributed

function test()
    t = @spawnat :2 1==1
    @show fetch(t)
end

end

julia> Test.test()
ERROR: On worker 2:
UndefVarError: Test not defined

It looks like somehow the remote process needs to know about Test but I don’t understand why. t is just a local variable and it must have no binding to the name Test.

It’s because the remote process has to know where to put the result. There’s a module wide sync variable for distributed operations, according to the source of @spawnat and what the macro expands to:

[sukera@tempman ~]$ julia -q -p 2
julia> using Distributed

julia> module Test
       using Distributed

       function test()
           t = @spawnat :2 1==1
           @show fetch(t)
       end

       end
Main.Test

julia> using .Test

julia> @code_lowered Test.test()
CodeInfo(
1 ─       Core.NewvarNode(:(value))
│         Core.NewvarNode(:(t))
│         #1 = %new(Main.Test.:(var"#1#2"))
│   %4  = #1
│         ref = Distributed.spawnat($(QuoteNode(2)), %4)
└──       goto #3 if not false
2 ─       Distributed.put!(Main.Test.:(var"##sync#48"), ref)
3 ┄       t = ref
│   %9  = Main.Test.fetch(t)
│         value = %9
│   %11 = Base.repr(%9)
│         Base.println("fetch(t) = ", %11)
└──       return value
)

Distributed creates a Future, passes that to the external process to place the result into and then assigns that Future to t. I’m not sure if that requires knowing the Test module though, may be a bug.

EDIT: thinking about this some more, it does require knowing about the containing module, because there may be more than one module referencing that variable. I guess it could be gensymd, but then how would that be communicated to the remote process?

1 Like

How could there be more than one module referencing that variable t? t is only visible inside Test.test() and the module Test lives only in the calling process.

It’s not about t. @spawnat doesn’t even know about it - it’s a seperate, special variable that’s the problem here whose exclusive purpose is facilitating communication between processes. It’s the

that’s the problem, not t. This variable is required to make sure that the remote process knows where to send the result to.

Thank you for the useful comment! Come to think of it, it seems inevitable for the remote process to know the namespace of where the remote call is made, in order to push a result back to the calling process. And the expanded macro looks in line with the speculation. So, the correct way to call test() would be (indeed it succeeds):

@everywhere module Test ... end

Test.test()
1 Like