Copyright © 2021-2023 VMware, Inc. or its affiliates. All rights reserved.
Version: 0.2.3
Authors: Jean-Sébastien Pédron (jean-sebastien@rabbitmq.com), Michael Davis (mcarsondavis@gmail.com), The RabbitMQ team (info@rabbitmq.com).
Horus is a library that extracts an anonymous function's code and creates a standalone version of it in a new module at runtime.
The goal is to have a storable and transferable function which does not depend on the availability of the module that defined it.
To achieve that goal, Horus extracts the assembly code of the anonymous function and creates a standalone Erlang module based on it. This module can be stored, transfered to another Erlang node and executed anywhere without the presence of the initial anonymous function's module.
Horus caches assembly code and generated modules. This avoids the need to call the code server or the compiler again and again.
Note that at this point, the cache is never invalidated and purged. Memory management of the cache will be a future improvement.
In the process, Horus pays attention to modules' checksums. Therefore, if a module is reloaded during a code upgrade, a new standalone function will be generated from the apparently same anonymous function. This applies to all modules called by that anonymous function.
It's possible for an anonymous function to take inputs from arguments of course, but also from the scope it is defined in:
Pid = self(),
Fun = fun() ->
Pid ! stop
end,
It is even possible to take another anonymous function from the scope:
InnerFun = fun(Ret) ->
{ok, Ret}
end,
OuterFun = fun(Ret) ->
InnerFun(Ret)
end,
These variables are "stored" in the anonymous function's environment by Erlang. They are also taken into account by Horus during the extraction. In particular, if the environment references another anonymous function, it will be extracted too.
The returned standalone function contains a standalone copy of the environment. Thus you don't have to worry about.
However, that environment is not part of the generated module. Therefore, a
single module will generated for each of InnerFun
and OuterFun
from the
example above.
This avoids the multiplication of generated modules which are loaded at execution time. With this distinction, a single module is generated and loaded. The environment stored in the returned standalone function is then passed to that generated module during execution.
Let's take the simplest anonymous function:
fun() ->
ok
end
Horus generates the following assembly form:
{
%% This is the generated module name. It is created from the name and
%% origin of the extraction function and a hash. The hash permits
%% several copies of the same function after code reloading.
'horus__erl_eval__-expr/6-fun-2-__97040876',
%% This is the list of exported functions. `run/0' is the entry point
%% corresponding to the extracted function and has the same arity (or
%% perhaps a greater arity if the anonymous function took variables
%% from the scope).
%%
%% There could be other non-exported functions in the module corresponding
%% to the functions called by the top-level anonymous function.
[{run,0},
{module_info,0},
{module_info,1}],
%% These are module attributes. Things like `-dialyzer(...).'. There are
%% none in the generated module.
[],
%% The actual functions present in the module at last!
[
{function,
run, %% The name of the function.
0, %% Its arity.
2, %% The label where the code starts.
[
%% Some metadata for this function:
{label,1},
{func_info,{atom,'horus__erl_eval__-expr/6-fun-2-__97040876'},
{atom,run},
0},
%% The function body for all its clauses.
{label,2},
{move,{atom,ok},{x,0}},
return]},
%% The `module/{0,1}' functions are added so the generated module is
%% compatible with debuggers.
{function,module_info,0,4,
[{label,3},
{line,[{location,"horus.erl",0}]},
{func_info,{atom,'horus__erl_eval__-expr/6-fun-2-__97040876'},
{atom,module_info},
0},
{label,4},
{move,{atom,'horus__erl_eval__-expr/6-fun-2-__97040876'},
{x,0}},
{call_ext_only,1,{extfunc,erlang,get_module_info,1}}]},
{function,module_info,1,6,
[{label,5},
{line,[{location,"horus.erl",0}]},
{func_info,{atom,'horus__erl_eval__-expr/6-fun-2-__97040876'},
{atom,module_info},
1},
{label,6},
{move,{x,0},{x,1}},
{move,{atom,'horus__erl_eval__-expr/6-fun-2-__97040876'},
{x,0}},
{call_ext_only,2,{extfunc,erlang,get_module_info,2}}]}],
7}}}
Generated by EDoc