Reputation: 2589
In Erlang, can I call some function f
(BIF or not), whose job is to spawn a process, run the function argf
I provided, and doesn't "return" until argf
has "returned", and do this without using receive
clause (the reason for this is that f
will be invoked in a gen_server, I don't want pollute the gen_server's mailbox).
A snippet would look like this:
%% some code omitted ...
F = fun() -> blah, blah, timer:sleep(10000) end,
f(F), %% like `spawn(F), but doesn't return until 10 seconds has passed`
%% ...
Upvotes: 0
Views: 3298
Reputation: 525
Main way to communicate with process in Erlang VM space is message passing with erlang:send/2
or erlang:send/3
functions (alias !
). But you can "hack" Erlang and use multiple way for communicating over process.
You can use erlang:link/1
to communicate stat of the process, its mainly used in case of your process is dying or is ended or something is wrong (exception or throw).
You can use erlang:monitor/2
, this is similar to erlang:link/1
except the message go directly into process mailbox.
You can also hack Erlang, and use some internal way (shared ETS
/DETS
/Mnesia
tables) or use external methods (database or other things like that). This is clearly not recommended and "destroy" Erlang philosophy... But you can do it.
Its seems your problem can be solved with supervisor
behavior. supervisor
support many strategies to control supervised process:
one_for_one
: If one child process terminates and is to be restarted, only that child process is affected. This is the default restart strategy.one_for_all
: If one child process terminates and is to be restarted, all other child processes are terminated and then all child processes are restarted.rest_for_one
: If one child process terminates and is to be restarted, the 'rest' of the child processes (that is, the child processes after the terminated child process in the start order) are terminated. Then the terminated child process and all child processes after it are restarted.simple_one_for_one
: A simplified one_for_one supervisor, where all child processes are dynamically added instances of the same process type, that is, running the same code.
You can also modify or create your own supervisor strategy from scratch or base on supervisor_bridge
.
So, to summarize, you need a process who wait for one or more terminating process. This behavior is supported natively with OTP, but you can also create your own model. For doing that, you need to share status of every started process, using cache or database, or when your process is spawned. Something like that:
Fun = fun
MyFun (ParentProcess, {result, Data})
when is_pid(ParentProcess) ->
ParentProcess ! {self(), Data};
MyFun (ParentProcess, MyData)
when is_pid(ParentProcess) ->
% do something
MyFun(ParentProcess, MyData2) end.
spawn(fun() -> Fun(self(), InitData) end).
EDIT: forgot to add an example without send
/receive
. I use an ETS
table to store every result from lambda function. This ETS
table is set when we spawn this process. To get result, we can select data from this table. Note, the key of the row is the process id of the process.
spawner(Ets, Fun, Args)
when is_integer(Ets),
is_function(Fun) ->
spawn(fun() -> Fun(Ets, Args) end).
Fun = fun
F(Ets, {result, Data}) ->
ets:insert(Ets, {self(), Data});
F(Ets, Data) ->
% do something here
Data2 = Data,
F(Ets, Data2) end.
Upvotes: 1
Reputation: 20916
You will not "pollute" the gen_servers mailbox if you spawn+wait for message before you return from the call or cast. A more serious problem with this maybe that you will block the gen_server while you are waiting for the other process to terminate. A way around this is to not explicitly wait but return from the call/cast and then when the completion message arrives handle it in handle_info/2
and then do what is necessary.
If the spawning is done in a handle_call
and you want to return the "result" of that process then you can delay returning the value to the original call from the handle_info
handling the process termination message.
Note that however you do it a gen_server:call
has a timeout value, either implicit or explicit, and if no reply is returned it generates an error in the calling process.
Upvotes: 1
Reputation: 14042
The only way to communicate between processes is message passing (of course you can consider to poll for a specific key in an ets or a file but I dont like this).
If you use a spawn_monitor function in f/1 to start the F process and then have a receive block only matching the possible system messages from this monitor:
f(F) ->
{_Pid, MonitorRef} = spawn_monitor(F),
receive
{_Tag, MonitorRef, _Type, _Object, _Info} -> ok
end.
you will not mess your gen_server mailbox. The example is the minimum code, you can add a timeout (fixed or parameter), execute some code on normal or error completion...
Upvotes: 2