exceptiontry-catchthrowerlang-otp

Erlang using action id, wait function is not working


I just tried to run a simple function in the terminal:

A1 = async:new(fun(Args) -> lists:seq(1, Args) end, 10).

It is showing the output like this: "#Ref<0.2297416181.3164864513.214889>" but when I try to run the wait function it is not working and not showing anything. I was expecting to get a result like [1,2,3,4,5,6,7,8,9,10].

Here is my full code:

-module(async).
-export([new/2, wait/1, poll/1]).

new(Fun, Arg) ->
    Aid = make_ref(),  % Generate a unique action ID
    ComputationPid = spawn_link(fun() -> execute_computation(self(), Fun, Arg) end),
    State = {ComputationPid, none},
    erlang:put(Aid, State),
    Aid.

execute_computation(ParentPid, Fun, Arg) ->
    try
        Res = Fun(Arg),
        ParentPid ! {self(), {ok, Res}}
    catch
        throw:Ex ->
            ParentPid ! {self(), {exception, Ex}}
    end.

wait(Aid) ->
    receive
        {Aid, {ok, Res}} -> Res;
        {Aid, {exception, Ex}} -> throw(Ex)
    end.

poll(Aid) ->
    State = erlang:get(Aid),
    case State of
        undefined -> nothing;
        {_, none} -> nothing;
        {_, Result} -> Result
    end.

This line added in the terminal and it is not showing anything:

Result = async:wait(A1).

Solution

  • The main problem is you're mixing up references and pids. In your execute_computation/3 function, you send messages with the pid as the first element of the tuple:

        ParentPid ! {self(), {ok, Res}}
    

    But in your wait/1 function, you're attempting to receive tuples with a reference as the first element:

    wait(Aid) ->
        receive
            {Aid, {ok, Res}} -> Res;
            {Aid, {exception, Ex}} -> throw(Ex)
        end.
    

    Another problem is that when you spawn the execute_computation/3 call, you're trying to pass the parent pid but the self() call within the fun is the pid of the spawned process, not the parent.

    To fix it, first, change how you spawn the call to execute_computation/3 by passing the parent pid and also passing the reference:

        Self = self(),
        ComputationPid = spawn_link(fun() -> execute_computation(Self, Aid, Fun, Arg) end),
    

    Next, fix the execute_computation function to take 4 arguments and to include the reference in the sent tuple:

    execute_computation(ParentPid, Aid, Fun, Arg) ->
        try
            Res = Fun(Arg),
            ParentPid ! {self(), Aid, {ok, Res}}
        catch
            throw:Ex ->
                ParentPid ! {self(), Aid, {exception, Ex}}
        end.
    

    And finally, fix wait/1 to receive the correct tuple:

    wait(Aid) ->
        receive
            {_Pid, Aid, {ok, Res}} -> Res;
            {_Pid, Aid, {exception, Ex}} -> throw(Ex)
        end.
    

    Note that you don't appear to be using the pid of the execute_computation/3 process that's passed in the tuple, so you might consider dropping it.

    With these changes, things work as expected:

    2> A1 = async:new(fun(Args) -> lists:seq(1, Args) end, 10).
    #Ref<0.942710366.4246732815.239328>
    3> async:wait(A1).
    [1,2,3,4,5,6,7,8,9,10]