Skip to content

Failure when run inside module #105

@grantbruer

Description

@grantbruer

I am trying to run some parallel code defined inside a module via include_string, and I can't quite figure out how it works or how it's supposed to work.

It seems like a bug for such a simple case to not work, so I'm making an issue here. But it may be something that can't be fixed with Julia's current module system.

Main question: Is there a way to use a sandbox module without breaking Distributed.jl?

Short failing code

A simple remote call works when run in module Main:

using Distributed
worker_ids = addprocs(2)
remotecall_wait(() -> println("I want to run this remotely"), 2)

But doesn't work when in a different module:

module sandbox
using Distributed
worker_ids = addprocs(2)
remotecall_wait(() -> println("I want to run this remotely"), 2)
end
ERROR: On worker 2:
UndefVarError: sandbox not defined
Stacktrace:
  [1] deserialize_module
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:996
  [2] handle_deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:895
  [3] deserialize

Test scripts

I made a test script to figure out how to work with this. I run the script in a sandbox module and in the Main module.

Driver script mfe_driver.jl.

filename = ARGS[1]
include_string(Module(:sandbox_mod), read(filename, String), filename)

Escape-to-main method: mfe.jl.

The second is a script that escapes the sandbox module to get the function to run remotely: mfe.jl.

mfe.jl
using Distributed

worker_ids = addprocs(2)
try
    println("================== Main process - try to run remotely")
    try
        remotecall_wait(() -> println("I want to run this remotely"), 2)
    catch e
        showerror(stdout, e)
    end
    println()
    println()
    println("================== Main process - define and run function locally")
    function I_want_to_run_this_remotely()
        println("I want to run this remotely")
    end
    I_want_to_run_this_remotely()
    println()
    println()
    println("================== Main process - run remotely")
    try
        remotecall_wait(I_want_to_run_this_remotely, 2)
    catch e
        showerror(stdout, e)
    end
    println()
    println()
    println("================== Main process - define function in local Main and run locally")
    @everywhere [1] begin
        using Distributed
        println(" process $(myid()) has module $(@__MODULE__)")
        function I_want_to_run_this_remotely()
            println("I want to run this remotely")
        end
    end
    Main.I_want_to_run_this_remotely()
    println()
    println()
    println("================== Main process - run remotely")
    try
        remotecall_wait(Main.I_want_to_run_this_remotely, 2)
    catch e
        showerror(stdout, e)
    end
    println()
    println()
    println("================== Main process - define function in everyone's Main")
    @everywhere begin
        using Distributed
        println(" process $(myid()) has module $(@__MODULE__)")
        function I_want_to_run_this_remotely()
            println("I want to run this remotely")
        end
    end
    println()
    println()
    println("================== Main process - run remotely")
    try
        remotecall_wait(Main.I_want_to_run_this_remotely, 2)
    catch e
        showerror(stdout, e)
    end
finally
    rmprocs(worker_ids)
end
  • In the non-sandboxed case, remoteprocess_call works as expected.
  • The only case that fails is when the function is defined on the driver process using @everywhere. And I don't understand how that is different from defining the function on the driver process without @everywhere.
Output of "julia mfe.jl"
================== Main process - try to run remotely
      From worker 2:	I want to run this remotely


================== Main process - define and run function locally
I want to run this remotely


================== Main process - run remotely
      From worker 2:	I want to run this remotely


================== Main process - define function in local Main and run locally
 process 1 has module Main
I want to run this remotely


================== Main process - run remotely
On worker 2:
UndefVarError: #I_want_to_run_this_remotely not defined
Stacktrace:
  [1] deserialize_datatype
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:1364
  [2] handle_deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:866
  [3] deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:813
  [4] handle_deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:873
  [5] deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:813 [inlined]
  [6] deserialize_msg
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/messages.jl:87
  [7] #invokelatest#2
    @ ./essentials.jl:729 [inlined]
  [8] invokelatest
    @ ./essentials.jl:726 [inlined]
  [9] message_handler_loop
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/process_messages.jl:176
 [10] process_tcp_streams
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/process_messages.jl:133
 [11] #103
    @ ./task.jl:484

================== Main process - define function in everyone's Main
 process 1 has module Main
      From worker 2:	 process 2 has module Main
      From worker 3:	 process 3 has module Main


================== Main process - run remotely
      From worker 2:	I want to run this remotely
  • In the sandboxed case, the remotecall_wait works only if the function is defined in Main for each process.
Output of "julia mfe_driver.jl mfe.jl"
================== Main process - try to run remotely
On worker 2:
UndefVarError: sandbox_mod not defined
Stacktrace:
  [1] deserialize_module
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:996
  [2] handle_deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:895
  [3] deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:813
  [4] deserialize_datatype
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:1363
  [5] handle_deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:866
  [6] deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:813
  [7] handle_deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:873
  [8] deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:813 [inlined]
  [9] deserialize_msg
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/messages.jl:87
 [10] #invokelatest#2
    @ ./essentials.jl:729 [inlined]
 [11] invokelatest
    @ ./essentials.jl:726 [inlined]
 [12] message_handler_loop
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/process_messages.jl:176
 [13] process_tcp_streams
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/process_messages.jl:133
 [14] #103
    @ ./task.jl:484

================== Main process - define and run function locally
I want to run this remotely


================== Main process - run remotely
On worker 2:
UndefVarError: sandbox_mod not defined
Stacktrace:
  [1] deserialize_module
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:996
  [2] handle_deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:895
  [3] deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:813
  [4] deserialize_datatype
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:1363
  [5] handle_deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:866
  [6] deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:813
  [7] handle_deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:873
  [8] deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:813 [inlined]
  [9] deserialize_msg
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/messages.jl:87
 [10] #invokelatest#2
    @ ./essentials.jl:729 [inlined]
 [11] invokelatest
    @ ./essentials.jl:726 [inlined]
 [12] message_handler_loop
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/process_messages.jl:176
 [13] process_tcp_streams
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/process_messages.jl:133
 [14] #103
    @ ./task.jl:484

================== Main process - define function in local Main and run locally
 process 1 has module Main
I want to run this remotely


================== Main process - run remotely
On worker 2:
UndefVarError: #I_want_to_run_this_remotely not defined
Stacktrace:
  [1] deserialize_datatype
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:1364
  [2] handle_deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:866
  [3] deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:813
  [4] handle_deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:873
  [5] deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:813 [inlined]
  [6] deserialize_msg
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/messages.jl:87
  [7] #invokelatest#2
    @ ./essentials.jl:729 [inlined]
  [8] invokelatest
    @ ./essentials.jl:726 [inlined]
  [9] message_handler_loop
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/process_messages.jl:176
 [10] process_tcp_streams
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/process_messages.jl:133
 [11] #103
    @ ./task.jl:484

================== Main process - define function in everyone's Main
 process 1 has module Main
      From worker 3:	 process 3 has module Main
      From worker 2:	 process 2 has module Main


================== Main process - run remotely
      From worker 2:	I want to run this remotely

Define-sandbox-everywhere method: mfe_define_module.jl

I don't want to escape the sandbox to define things in the Main scope, so I tried to work within the sandbox.

This file tries to define the sandbox module for the worker processes.

mfe_define_module.jl
using Distributed

worker_ids = addprocs(2)
try
    println("================== Main process - determine current module information")
    current_module_local = split(string(@__MODULE__), ".")[end]
    println("  Driver module: $(@__MODULE__)")
    println("  Driver module string: $current_module_local")
    println()
    println()
    println("================== Main process - define module in workers")
    @everywhere worker_ids begin
        using Distributed
        driver_module = try
            Module(Symbol(Meta.parse($current_module_local)))
        catch e
            showerror(stdout, e)
        end
        println("Process $(myid()): ", driver_module, " ", typeof(driver_module))
    end
    println()
    println()
    driver_module = @__MODULE__
    println("================== Main process - import module in workers")
    try
        @everywhere worker_ids begin
            println("Process $(myid()): ", driver_module, " ", typeof(driver_module))
        end
    catch e
        showerror(stdout, e)
    end
    println()
    println()
    println("================== Main process - try to run remotely")
    try
        remotecall_wait(() -> println("I want to run this remotely"), 2)
    catch e
        showerror(stdout, e)
    end
    println()
    println()
    println("================== Main process - define and run function locally")
    function I_want_to_run_this_remotely()
        println("I want to run this remotely")
    end
    I_want_to_run_this_remotely()
    println()
    println()
    println("================== Main process - run remotely")
    try
        remotecall_wait(I_want_to_run_this_remotely, 2)
    catch e
        showerror(stdout, e)
    end
finally
    rmprocs(worker_ids)
end
  • It successfully defines a module of the same name in the worker processes, but it is still not possible to call functions with remotecall_wait.
Output of "julia mfe_driver.jl mfe_define_module.jl"
================== Main process - determine current module information
  Driver module: Main.sandbox_mod
  Driver module string: sandbox_mod


================== Main process - define module in workers
      From worker 2:	Process 2: Main.sandbox_mod Module
      From worker 3:	Process 3: Main.sandbox_mod Module


================== Main process - import module in workers
      From worker 2:	Process 2: Main.sandbox_mod Module
      From worker 3:	Process 3: Main.sandbox_mod Module


================== Main process - try to run remotely
On worker 2:
UndefVarError: sandbox_mod not defined
Stacktrace:
  [1] deserialize_module
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:996
  [2] handle_deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:895
  [3] deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:813
  [4] deserialize_datatype
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:1363
  [5] handle_deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:866
  [6] deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:813
  [7] handle_deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:873
  [8] deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:813 [inlined]
  [9] deserialize_msg
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/messages.jl:87
 [10] #invokelatest#2
    @ ./essentials.jl:729 [inlined]
 [11] invokelatest
    @ ./essentials.jl:726 [inlined]
 [12] message_handler_loop
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/process_messages.jl:176
 [13] process_tcp_streams
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/process_messages.jl:133
 [14] #103
    @ ./task.jl:484

================== Main process - define and run function locally
I want to run this remotely


================== Main process - run remotely
On worker 2:
UndefVarError: sandbox_mod not defined
Stacktrace:
  [1] deserialize_module
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:996
  [2] handle_deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:895
  [3] deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:813
  [4] deserialize_datatype
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:1363
  [5] handle_deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:866
  [6] deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:813
  [7] handle_deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:873
  [8] deserialize
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Serialization/src/Serialization.jl:813 [inlined]
  [9] deserialize_msg
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/messages.jl:87
 [10] #invokelatest#2
    @ ./essentials.jl:729 [inlined]
 [11] invokelatest
    @ ./essentials.jl:726 [inlined]
 [12] message_handler_loop
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/process_messages.jl:176
 [13] process_tcp_streams
    @ ~/.julia/juliaup/julia-1.8.5+0.x64.linux.gnu/share/julia/stdlib/v1.8/Distributed/src/process_messages.jl:133
 [14] #103
    @ ./task.jl:484

Version info

I tested this with Julia installed by juliaup version 1.13.0, with Julia versions below.

Julia Version 1.8.5
Commit 17cfb8e65ea (2023-01-08 06:45 UTC)
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 8 × Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-13.0.1 (ORCJIT, skylake)
  Threads: 1 on 8 virtual cores

and

Julia Version 1.11.0-rc1
Commit 3a35aec36d1 (2024-06-25 10:23 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 8 × Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz
  WORD_SIZE: 64
  LLVM: libLLVM-16.0.6 (ORCJIT, skylake)
Threads: 1 default, 0 interactive, 1 GC (on 8 virtual cores)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions