safe_exec_env v0.2.5 SafeExecEnv

It is often desirable to run natively compiled code (C, C++, Rust, ..) from a BEAM application via a binding. The motivation may be better performance or simply to access an external library that already exists. The problem is that if that native code crashes then, unlike with native BEAM processest, he entire VM will crash, taking your application along with it,

SafeExecEnv provides a safe(r) way to run natively compiled code from a BEAM application.

If the native code in question can be reasonably expected to never crash, then this precaution is unecessary, but if native code that might crash is being run then this library provides a layer of insulation between the code being executed and the virtual machine the main application is being run on.

Using SafeExecEnv is easy; simply add it to a Supervisor:

defmodule MyApplication do
  def start(_type, _args) do
    children = [SafeExecEnv]
    opts = [strategy: :one_for_one, name: MyApplication.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Then you may run functions safely by calling SafeExecEnv::exec with the function. Captured, anonymous, and Module / Function / Arguments (MFA) style function passing is supported.

You may also start a SafeExecEnv with a name, allowing multiple SafeExecEnvs to be created and later used for execution from the same node:

SafeExecEnv.start_link(%{name: “second_see”}) SafeExecEnv.start_link(%{name: :second_see})

Note: be wary of starting endless named SafeExecEnvs as each one launched creates a new atom in the atom table for its name.

SafeExecEnv works by spawning a second BEAM VM to run the functions in. If that VM crashes then the SafeExecEnv server will also crash. When supervised, this will cause the SafeExenv to be restarted, and the external VM will be started again. Calls to SafeExecEnv may fail during that time, and will need to be tried again once available.

Link to this section Summary

Functions

Executes a function in the safe executable environment and returns the result. The SafeExecEnv server is presumed to be started

Executes a function with the provided argument (in usual “MFA” form) in the safe executable environment and returns the result. The SafeExecEnv server is presumed to be started

Returns the name of the node being used as the safe exec environment

Returns true if the node is running and reachable, otherwise false

Starts a Safe Exec Environment server. This server will launch the external node used to run functions in and monitor it. When the Safe Exec Environment crashes this server will as well. This provides a simple way to ensure a Safe Exec Environment is available by supervising the SafeExecEnv server: if the external node crashes, this local server will crash, the supervisor will restart it, and the external node will be started again for use

Link to this section Functions

Link to this function exec(fun, env_name \\ :global)
exec(fun :: function(), env_name :: :global | String.t()) ::
  any() | {:error, reason :: String.t()}

Executes a function in the safe executable environment and returns the result. The SafeExecEnv server is presumed to be started.

By default the global SafeExecEnv is used, but an execution can be scheduled for a specific SafeExecEnv instance by providing the name used to start the module. @see start_link/1

Link to this function exec(module, fun, args, env_name \\ :global)
exec(
  module :: atom(),
  fun :: atom(),
  args :: list(),
  env_name :: :global | String.t()
) :: any() | {:error, reason :: String.t()}

Executes a function with the provided argument (in usual “MFA” form) in the safe executable environment and returns the result. The SafeExecEnv server is presumed to be started.

By default the global SafeExecEnv is used, but an execution can be scheduled for a specific SafeExecEnv instance by providing the name used to start the module. @see start_link/1

Link to this function get(name \\ :global)

Returns the name of the node being used as the safe exec environment

Link to this function is_alive?(name \\ :global)
is_alive?(name :: String.t() | atom()) :: boolean()

Returns true if the node is running and reachable, otherwise false

Link to this function start_link()
start_link() :: {:ok, pid()}

Starts a Safe Exec Environment server. This server will launch the external node used to run functions in and monitor it. When the Safe Exec Environment crashes this server will as well. This provides a simple way to ensure a Safe Exec Environment is available by supervising the SafeExecEnv server: if the external node crashes, this local server will crash, the supervisor will restart it, and the external node will be started again for use.

A SafeExecEnv server may be started with no arguments, creating a global node that can be used without a name, or with a name allowing multiple Safe Exec Enviroments to be created if desired.

Link to this function start_link(arg1)
start_link(args :: %{name: String.t() | atom()} | any()) :: {:ok, pid()}