Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add apply_* functions to the timermodule that accept anonymous functions #7649

Merged
merged 1 commit into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 141 additions & 6 deletions lib/stdlib/doc/src/timer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,31 @@
</datatypes>

<funcs>
<func>
<name name="apply_after" arity="2" since="OTP @OTP-18808@"/>
<fsummary>Spawn a process evaluating <c>erlang:apply(Function, [])</c>
after a specified <c>Time</c>.</fsummary>
<desc>
<p>Evaluates <c>spawn(erlang, apply, [<anno>Function</anno>,
[]])</c> after <c><anno>Time</anno></c> milliseconds.</p>
Comment on lines +77 to +78
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<p>Evaluates <c>spawn(erlang, apply, [<anno>Function</anno>,
[]])</c> after <c><anno>Time</anno></c> milliseconds.</p>
<p>Evaluates <c>spawn(<anno>Function</anno>)</c>
after <c><anno>Time</anno></c> milliseconds.</p>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, while that has the same outcome, spawn(Function) is not what it does. It does call spawn(erlang, apply, [Function, []]), as it currently says.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK.

<p>Returns <c>{ok, <anno>TRef</anno>}</c> or
<c>{error, <anno>Reason</anno>}</c>.</p>
</desc>
</func>

<func>
<name name="apply_after" arity="3" since="OTP @OTP-18808@"/>
<fsummary>Spawn a process evaluating <c>erlang:apply(Function, Arguments)</c>
after a specified <c>Time</c>.</fsummary>
<desc>
<p>Evaluates <c>spawn(erlang, apply, [<anno>Function</anno>,
<anno>Arguments</anno>])</c> after <c><anno>Time</anno></c>
milliseconds.</p>
<p>Returns <c>{ok, <anno>TRef</anno>}</c> or
<c>{error, <anno>Reason</anno>}</c>.</p>
</desc>
</func>

<func>
<name name="apply_after" arity="4" since=""/>
<fsummary>Spawn a process evaluating <c>Module:Function(Arguments)</c>
Expand All @@ -82,6 +107,34 @@
</desc>
</func>

<func>
<name name="apply_interval" arity="2" since="OTP @OTP-18808@"/>
<fsummary>Spawn a process evaluating <c>erlang:apply(Function, [])</c>
repeatedly at intervals of <c>Time</c>.</fsummary>
<desc>
<p>Evaluates <c>spawn(erlang, apply, [<anno>Function</anno>,
[]])</c> repeatedly at intervals of
<c><anno>Time</anno></c>, irrespective of whether a previously
spawned process has finished or not.</p>
<p>Returns <c>{ok, <anno>TRef</anno>}</c> or
<c>{error, <anno>Reason</anno>}</c>.</p>
</desc>
</func>

<func>
<name name="apply_interval" arity="3" since="OTP @OTP-18808@"/>
<fsummary>Spawn a process evaluating <c>erlang:apply(Function, Arguments)</c>
repeatedly at intervals of <c>Time</c>.</fsummary>
<desc>
<p>Evaluates <c>spawn(erlang, apply, [<anno>Function</anno>,
<anno>Arguments</anno>])</c> repeatedly at intervals of
<c><anno>Time</anno></c>, irrespective of whether a previously
spawned process has finished or not.</p>
<p>Returns <c>{ok, <anno>TRef</anno>}</c> or
<c>{error, <anno>Reason</anno>}</c>.</p>
</desc>
</func>

<func>
<name name="apply_interval" arity="4" since=""/>
<fsummary>Spawn a process evaluating <c>Module:Function(Arguments)</c>
Expand Down Expand Up @@ -111,6 +164,34 @@
</desc>
</func>

<func>
<name name="apply_repeatedly" arity="2" since="OTP @OTP-18808@"/>
<fsummary>Spawn a process evaluating <c>erlang:apply(Function, [])</c>
repeatedly at intervals of <c>Time</c>.</fsummary>
<desc>
<p>Evaluates <c>spawn(erlang, apply, [<anno>Function</anno>,
[]])</c> repeatedly at intervals of
<c><anno>Time</anno></c>, waiting for the spawned process to
finish before starting the next.</p>
<p>Returns <c>{ok, <anno>TRef</anno>}</c> or
<c>{error, <anno>Reason</anno>}</c>.</p>
</desc>
</func>

<func>
<name name="apply_repeatedly" arity="3" since="OTP @OTP-18808@"/>
<fsummary>Spawn a process evaluating <c>erlang:apply(Function, Arguments)</c>
repeatedly at intervals of <c>Time</c>.</fsummary>
<desc>
<p>Evaluates <c>spawn(erlang, apply, [<anno>Function</anno>,
<anno>Arguments</anno>])</c> repeatedly at intervals of
<c><anno>Time</anno></c>, waiting for the spawned process to
finish before starting the next.</p>
<p>Returns <c>{ok, <anno>TRef</anno>}</c> or
<c>{error, <anno>Reason</anno>}</c>.</p>
</desc>
</func>

<func>
<name name="apply_repeatedly" arity="4" since="OTP 26.0"/>
<fsummary>Spawn a process evaluating <c>Module:Function(Arguments)</c>
Expand Down Expand Up @@ -405,22 +486,76 @@ timer:cancel(R),

<p>An interval timer, that is, a timer created by evaluating any of the
functions
<seemfa marker="#apply_interval/2"><c>apply_interval/2</c></seemfa>,
<seemfa marker="#apply_interval/3"><c>apply_interval/3</c></seemfa>,
<seemfa marker="#apply_interval/4"><c>apply_interval/4</c></seemfa>,
<seemfa marker="#send_interval/3"><c>send_interval/3</c></seemfa>, and
<seemfa marker="#send_interval/2"><c>send_interval/2</c></seemfa>
<seemfa marker="#apply_repeatedly/2"><c>apply_repeatedly/2</c></seemfa>,
<seemfa marker="#apply_repeatedly/3"><c>apply_repeatedly/3</c></seemfa>,
<seemfa marker="#apply_repeatedly/4"><c>apply_repeatedly/4</c></seemfa>,
<seemfa marker="#send_interval/2"><c>send_interval/2</c></seemfa>, and
<seemfa marker="#send_interval/3"><c>send_interval/3</c></seemfa>
is linked to the process to which the timer performs its task.</p>

<p>A one-shot timer, that is, a timer created by evaluating any of the
functions
<seemfa marker="#apply_after/2"><c>apply_after/2</c></seemfa>,
<seemfa marker="#apply_after/3"><c>apply_after/3</c></seemfa>,
<seemfa marker="#apply_after/4"><c>apply_after/4</c></seemfa>,
<seemfa marker="#send_after/3"><c>send_after/3</c></seemfa>,
<seemfa marker="#send_after/2"><c>send_after/2</c></seemfa>,
<seemfa marker="#exit_after/3"><c>exit_after/3</c></seemfa>,
<seemfa marker="#send_after/3"><c>send_after/3</c></seemfa>,
<seemfa marker="#exit_after/2"><c>exit_after/2</c></seemfa>,
<seemfa marker="#kill_after/2"><c>kill_after/2</c></seemfa>, and
<seemfa marker="#kill_after/1"><c>kill_after/1</c></seemfa>
<seemfa marker="#exit_after/3"><c>exit_after/3</c></seemfa>,
<seemfa marker="#kill_after/1"><c>kill_after/1</c></seemfa>, and
<seemfa marker="#kill_after/2"><c>kill_after/2</c></seemfa>
is not linked to any process. Hence, such a timer is removed only
when it reaches its time-out, or if it is explicitly removed by a call to
<seemfa marker="#cancel/1"><c>cancel/1</c></seemfa>.</p>

<p>The functions given to
<seemfa marker="#apply_after/2"><c>apply_after/2</c></seemfa>,
<seemfa marker="#apply_after/3"><c>apply_after/3</c></seemfa>,
<seemfa marker="#apply_interval/2"><c>apply_interval/2</c></seemfa>,
<seemfa marker="#apply_interval/3"><c>apply_interval/3</c></seemfa>,
<seemfa marker="#apply_repeatedly/2"><c>apply_repeatedly/2</c></seemfa>, and
<seemfa marker="#apply_repeatedly/3"><c>apply_repeatedly/3</c></seemfa>,
or denoted by <c>Module</c>, <c>Function</c> and <c>Arguments</c> given to
<seemfa marker="#apply_after/4"><c>apply_after/4</c></seemfa>,
<seemfa marker="#apply_interval/4"><c>apply_interval/4</c></seemfa>, and
<seemfa marker="#apply_repeatedly/4"><c>apply_repeatedly/4</c></seemfa>
are executed in a freshly-spawned process, and therefore calls to <c>self()</c>
in those functions will return the Pid of this process, which is different
from the process that called <c>timer:apply_*</c>.</p>
<p><em>Example</em></p>
<p>In the following example, the intention is to set a timer to execute a
function after 1 second, which performs a fictional task, and then wants
to inform the process which set the timer about its completion, by sending
it a <c>done</c> message.</p>
<p>Using <c>self()</c> <em>inside</em> the timed function, the code below does
not work as intended. The task gets done, but the <c>done</c> message gets
sent to the wrong process and is lost.</p>
<pre>
1> <input>timer:apply_after(1000, fun() -> do_something(), self() ! done end).</input>
{ok,TRef}
2> <input>receive done -> done after 5000 -> timeout end.</input>
%% ... 5s pass...
timeout</pre>
<p>The code below calls <c>self()</c> in the process which sets the timer and
assigns it to a variable, which is then used in the function to send the
<c>done</c> message to, and so works as intended.</p>
<pre>
1> <input>Target = self()</input>
&lt;0.82.0&gt;
2> <input>timer:apply_after(1000, fun() -> do_something(), Target ! done end).</input>
{ok,TRef}
3> <input>receive done -> done after 5000 -> timeout end.</input>
%% ... 1s passes...
done</pre>
<p>Another option is to pass the message target as a parameter to the function.</p>
<pre>
1> <input>timer:apply_after(1000, fun(Target) -> do_something(), Target ! done end, [self()]).</input>
{ok,TRef}
2> <input>receive done -> done after 5000 -> timeout end.</input>
%% ... 1s passes...
done</pre>
</section>
</erlref>
92 changes: 85 additions & 7 deletions lib/stdlib/src/timer.erl
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@
%%
-module(timer).

-export([apply_after/4,
-export([apply_after/2, apply_after/3, apply_after/4,
send_after/3, send_after/2,
exit_after/3, exit_after/2, kill_after/2, kill_after/1,
apply_interval/4, apply_repeatedly/4,
apply_interval/2, apply_interval/3, apply_interval/4,
apply_repeatedly/2, apply_repeatedly/3, apply_repeatedly/4,
send_interval/3, send_interval/2,
cancel/1, sleep/1, tc/1, tc/2, tc/3, tc/4, now_diff/2,
seconds/1, minutes/1, hours/1, hms/3]).
Expand All @@ -40,7 +41,9 @@

%% Validations
-define(valid_time(T), is_integer(T), T >= 0).
-define(valid_mfa(M, F, A), is_atom(M), is_atom(F), is_list(A)).
-define(valid_apply(F), is_function(F, 0)).
-define(valid_apply(F, A), is_list(A), is_function(F, length(A))).
-define(valid_apply(M, F, A), is_atom(M), is_atom(F), is_list(A)).

%%
%% Time is in milliseconds.
Expand All @@ -52,6 +55,31 @@
%%
%% Interface functions
%%
-spec apply_after(Time, Function) ->
{'ok', TRef} | {'error', Reason}
when Time :: time(),
Function :: fun(() -> _),
TRef :: tref(),
Reason :: term().
apply_after(Time, F)
when ?valid_apply(F) ->
apply_after(Time, erlang, apply, [F, []]);
apply_after(_Time, _F) ->
{error, badarg}.

-spec apply_after(Time, Function, Arguments) ->
{'ok', TRef} | {'error', Reason}
when Time :: time(),
Function :: fun((...) -> _),
Arguments :: [term()],
TRef :: tref(),
Reason :: term().
apply_after(Time, F, A)
when ?valid_apply(F, A) ->
apply_after(Time, erlang, apply, [F, A]);
apply_after(_Time, _F, _A) ->
{error, badarg}.

-spec apply_after(Time, Module, Function, Arguments) ->
{'ok', TRef} | {'error', Reason}
when Time :: time(),
Expand All @@ -61,12 +89,12 @@
TRef :: tref(),
Reason :: term().
apply_after(0, M, F, A)
when ?valid_mfa(M, F, A) ->
when ?valid_apply(M, F, A) ->
_ = do_apply({M, F, A}, false),
{ok, {instant, make_ref()}};
apply_after(Time, M, F, A)
when ?valid_time(Time),
?valid_mfa(M, F, A) ->
?valid_apply(M, F, A) ->
req(apply_once, {system_time(), Time, {M, F, A}});
apply_after(_Time, _M, _F, _A) ->
{error, badarg}.
Expand Down Expand Up @@ -146,6 +174,31 @@ kill_after(Time, Pid) ->
kill_after(Time) ->
exit_after(Time, self(), kill).

-spec apply_interval(Time, Function) ->
{'ok', TRef} | {'error', Reason}
when Time :: time(),
Function :: fun(() -> _),
TRef :: tref(),
Reason :: term().
apply_interval(Time, F)
when ?valid_apply(F) ->
apply_interval(Time, erlang, apply, [F, []]);
apply_interval(_Time, _F) ->
{error, badarg}.

-spec apply_interval(Time, Function, Arguments) ->
{'ok', TRef} | {'error', Reason}
when Time :: time(),
Function :: fun((...) -> _),
Arguments :: [term()],
TRef :: tref(),
Reason :: term().
apply_interval(Time, F, A)
when ?valid_apply(F, A) ->
apply_interval(Time, erlang, apply, [F, A]);
apply_interval(_Time, _F, _A) ->
{error, badarg}.

-spec apply_interval(Time, Module, Function, Arguments) ->
{'ok', TRef} | {'error', Reason}
when Time :: time(),
Expand All @@ -156,11 +209,36 @@ kill_after(Time) ->
Reason :: term().
apply_interval(Time, M, F, A)
when ?valid_time(Time),
?valid_mfa(M, F, A) ->
?valid_apply(M, F, A) ->
req(apply_interval, {system_time(), Time, self(), {M, F, A}});
apply_interval(_Time, _M, _F, _A) ->
{error, badarg}.

-spec apply_repeatedly(Time, Function) ->
{'ok', TRef} | {'error', Reason}
when Time :: time(),
Function :: fun(() -> _),
TRef :: tref(),
Reason :: term().
apply_repeatedly(Time, F)
when ?valid_apply(F) ->
apply_repeatedly(Time, erlang, apply, [F, []]);
apply_repeatedly(_Time, _F) ->
{error, badarg}.

-spec apply_repeatedly(Time, Function, Arguments) ->
{'ok', TRef} | {'error', Reason}
when Time :: time(),
Function :: fun((...) -> _),
Arguments :: [term()],
TRef :: tref(),
Reason :: term().
apply_repeatedly(Time, F, A)
when ?valid_apply(F, A) ->
apply_repeatedly(Time, erlang, apply, [F, A]);
apply_repeatedly(_Time, _F, _A) ->
{error, badarg}.

-spec apply_repeatedly(Time, Module, Function, Arguments) ->
{'ok', TRef} | {'error', Reason}
when Time :: time(),
Expand All @@ -171,7 +249,7 @@ apply_interval(_Time, _M, _F, _A) ->
Reason :: term().
apply_repeatedly(Time, M, F, A)
when ?valid_time(Time),
?valid_mfa(M, F, A) ->
?valid_apply(M, F, A) ->
req(apply_repeatedly, {system_time(), Time, self(), {M, F, A}});
apply_repeatedly(_Time, _M, _F, _A) ->
{error, badarg}.
Expand Down
Loading
Loading