Skip to content
Open
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
123 changes: 70 additions & 53 deletions lib/stdlib/src/timer.erl
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@
-moduledoc """
Timer functions.

This module provides useful functions related to time. Unless otherwise stated,
This module provides functions related to time. Unless otherwise stated,
time is always measured in _milliseconds_. All timer functions return
immediately, regardless of work done by another process.
immediately; the requested operations are performed asynchronously.

Successful evaluations of the timer functions give return values containing a
timer reference, denoted `TRef`. By using `cancel/1`, the returned reference can
be used to cancel any requested action. A `TRef` is an Erlang term, which
contents must not be changed.
On success, the timer functions return `{ok, TRef}`, where `TRef` is a timer
reference. A timer reference is an opaque term whose contents must not be
changed, and which can be passed to `cancel/1` to cancel the requested
action.

The time-outs are not exact, but are _at least_ as long as requested.
The timeouts are not exact, but are _at least_ as long as requested.

Creating timers using `erlang:send_after/3` and `erlang:start_timer/3` is more
efficient than using the timers provided by this module. However, the timer
Expand All @@ -49,7 +49,7 @@ ERTS User's guide.

_Example 1_

The following example shows how to print "Hello World\!" in 5 seconds:
The following example shows how to print "Hello World!" after 5 seconds:

```erlang
1> timer:apply_after(5000, io, format, ["~nHello World!~n", []]).
Expand All @@ -59,53 +59,53 @@ Hello World!

_Example 2_

The following example shows a process performing a certain action, and if this
action is not completed within a certain limit, the process is killed:
The following example shows a process that is killed if it does not finish
its task within a certain time limit:

```erlang
Pid = spawn(mod, fun, [foo, bar]),
%% If pid is not finished in 10 seconds, kill him
{ok, R} = timer:kill_after(timer:seconds(10), Pid),
Pid = spawn(Mod, Fun, Args),
%% If the process is not done in 10 seconds, kill it
{ok, TRef} = timer:kill_after(timer:seconds(10), Pid),
...
%% We change our mind...
timer:cancel(R),
%% We changed our mind...
timer:cancel(TRef),
...
```

## Notes on timer behavior

A timer can always be removed by calling `cancel/1`.

An interval timer, that is, a timer created by evaluating any of the functions
An interval timer created by evaluating any of the functions
`apply_interval/2`, `apply_interval/3`, `apply_interval/4`,
`apply_repeatedly/2`, `apply_repeatedly/3`, `apply_repeatedly/4`,
`send_interval/2`, and `send_interval/3` is linked to the process to which the
timer performs its task.
`apply_repeatedly/2`, `apply_repeatedly/3`, and `apply_repeatedly/4` is
automatically cancelled when the process that created it terminates.

An interval timer created by evaluating `send_interval/2` or `send_interval/3`
is automatically cancelled when the destination process terminates.

A one-shot timer, that is, a timer created by evaluating any of the functions
`apply_after/2`, `apply_after/3`, `apply_after/4`, `send_after/2`,
`send_after/3`, `exit_after/2`, `exit_after/3`, `kill_after/1`, and
`kill_after/2` 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
`cancel/1`.
`kill_after/2` is removed only when it reaches its timeout, or if it is
explicitly removed by a call to `cancel/1`.

The functions given to `apply_after/2`, `apply_after/3`, `apply_interval/2`,
`apply_interval/3`, `apply_repeatedly/2`, and `apply_repeatedly/3`, or denoted
by `Module`, `Function` and `Arguments` given to `apply_after/4`,
`apply_interval/4`, and `apply_repeatedly/4` are executed in a freshly-spawned
process, and therefore calls to `self/0` in those functions will return the Pid
of this process, which is different from the process that called
`timer:apply_*`.
`apply_interval/4`, and `apply_repeatedly/4` are executed in a freshly spawned
process, and therefore calls to `self/0` in those functions will return the
process identifier of this process, which is different from the process that
called `timer:apply_*`.

_Example_

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 `done`
message.
In the following example, a timer is set to execute a function after 1 second.
The function performs a fictional task and then informs the process that set
the timer about its completion, by sending it a `done` message.

Using `self/0` _inside_ the timed function, the code below does not work as
intended. The task gets done, but the `done` message gets sent to the wrong
The code below, which uses `self/0` _inside_ the timed function, does not work
as intended. The task gets done, but the `done` message gets sent to the wrong
process and is lost.

```erlang
Expand All @@ -116,12 +116,12 @@ process and is lost.
timeout
```

The code below calls `self/0` in the process which sets the timer and assigns it
to a variable, which is then used in the function to send the `done` message to,
and so works as intended.
The code below works as intended. It calls `self/0` in the process that sets
the timer, assigns the result to a variable, and uses that variable in the
function as the destination of the `done` message.

```erlang
1> Target = self()
1> Target = self().
<0.82.0>
2> timer:apply_after(1000, fun() -> do_something(), Target ! done end).
{ok,TRef}
Expand Down Expand Up @@ -242,8 +242,8 @@ apply_after(_Time, _M, _F, _A) ->
-doc """
Evaluates `Destination ! Message` after `Time` milliseconds.

`Destination` can be a remote or local process identifier, an atom of a
registered name or a tuple `{RegName, Node}` for a registered name at another node.
`Destination` can be a local or remote process identifier, a registered name,
or a tuple `{RegName, Node}` for a registered name on the node `Node`.

See also [the Timer Module section in the Efficiency Guide](`e:system:commoncaveats.md#timer-module`).
""".
Expand All @@ -256,7 +256,7 @@ See also [the Timer Module section in the Efficiency Guide](`e:system:commoncave
send_after(0, PidOrRegName, Message)
when is_pid(PidOrRegName);
is_atom(PidOrRegName) ->
PidOrRegName ! Message,
_ = do_apply({?MODULE, send, [PidOrRegName, Message]}, false),
{ok, {instant, make_ref()}};
send_after(0, {RegName, Node} = Dest, Message)
when is_atom(RegName),
Expand All @@ -267,21 +267,37 @@ send_after(Time, Pid, Message)
when ?valid_time(Time),
is_pid(Pid),
node(Pid) =:= node() ->
TRef = erlang:send_after(Time, Pid, Message),
{ok, {send_local, TRef}};
do_send_local(Time, Pid, Message);
send_after(Time, Pid, Message)
when is_pid(Pid) ->
apply_after(Time, ?MODULE, send, [Pid, Message]);
send_after(Time, RegName, Message)
when is_atom(RegName) ->
apply_after(Time, ?MODULE, send, [RegName, Message]);
when ?valid_time(Time),
is_atom(RegName) ->
do_send_local(Time, RegName, Message);
send_after(Time, {RegName, Node}, Message)
when ?valid_time(Time),
is_atom(RegName),
Node =:= node() ->
do_send_local(Time, RegName, Message);
send_after(Time, {RegName, Node} = Dest, Message)
when is_atom(RegName),
is_atom(Node) ->
apply_after(Time, ?MODULE, send, [Dest, Message]);
send_after(_Time, _PidOrRegName, _Message) ->
{error, badarg}.

do_send_local(Time, Dest, Message) ->
try
erlang:send_after(Time, Dest, Message)
of
TRef ->
{ok, {send_local, TRef}}
catch
error:badarg ->
{error, badarg}
end.

-doc(#{equiv => send_after(Time, self(), Message)}).
-spec send_after(Time, Message) -> {'ok', TRef} | {'error', Reason}
when Time :: time(),
Expand All @@ -293,7 +309,7 @@ send_after(Time, Message) ->

-doc """
Sends an exit signal with reason `Reason1` to `Target`, which can be a local
process identifier or an atom of a registered name.
process identifier or a registered name.
""".
-spec exit_after(Time, Target, Reason1) -> {'ok', TRef} | {'error', Reason2}
when Time :: time(),
Expand Down Expand Up @@ -471,8 +487,8 @@ apply_repeatedly(_Time, _M, _F, _A) ->
-doc """
Evaluates `Destination ! Message` repeatedly after `Time` milliseconds.

`Destination` can be a remote or local process identifier, an atom of a registered
name or a tuple `{RegName, Node}` for a registered name at another node.
`Destination` can be a local or remote process identifier, a registered name,
or a tuple `{RegName, Node}` for a registered name on the node `Node`.
""".
-spec send_interval(Time, Destination, Message) -> {'ok', TRef} | {'error', Reason}
when Time :: time(),
Expand Down Expand Up @@ -506,8 +522,8 @@ send_interval(Time, Message) ->
send_interval(Time, self(), Message).

-doc """
Cancels a previously requested time-out. `TRef` is a unique timer reference
returned by the related timer function.
Cancels a previously requested timer. `TRef` is a unique timer reference
returned by the timer function that requested the timer.

Returns `{ok, cancel}`, or `{error, Reason}` when `TRef` is not a timer
reference.
Expand Down Expand Up @@ -666,7 +682,7 @@ tc(M, F, A, TimeUnit) ->
%%
-doc """
Calculates the time difference `Tdiff = T2 - T1` in _microseconds_, where `T1`
and `T2` are time-stamp tuples on the same format as returned from
and `T2` are timestamp tuples in the same format as returned by
`erlang:timestamp/0` or `os:timestamp/0`.
""".
-spec now_diff(T2, T1) -> Tdiff
Expand Down Expand Up @@ -716,10 +732,11 @@ hms(H, M, S) ->
-doc """
Starts the timer server.

Normally, the server does not need to be started explicitly. It is started dynamically
if it is needed. This is useful during development, but in a target system the server
is to be started explicitly. Use configuration parameters for [Kernel](`e:kernel:index.html`)
for this.
Normally, the server does not need to be started explicitly. It is started
dynamically when it is needed. This is useful during development, but in a
target system the server should be started explicitly. To this end, set the
[Kernel configuration parameter `start_timer`](`e:kernel:kernel_app.md#start_timer`)
to `true`.
""".
-spec start() -> 'ok'.
start() ->
Expand Down
6 changes: 3 additions & 3 deletions lib/stdlib/test/timer_simple_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ send_after4(Config) when is_list(Config) ->
send_after5(Config) when is_list(Config) ->
Msg = make_ref(),
Name = register_name(self()),
{ok, {once, _}} = timer:send_after(500, Name, Msg),
{ok, {send_local, _}} = timer:send_after(500, Name, Msg),
ok = get_mess(2000, Msg),
unregister(Name).

Expand All @@ -335,7 +335,7 @@ send_after5(Config) when is_list(Config) ->
send_after6(Config) when is_list(Config) ->
Msg = make_ref(),
Name = register_name(self()),
{ok, {once, _}} = timer:send_after(500, {Name, node()}, Msg),
{ok, {send_local, _}} = timer:send_after(500, {Name, node()}, Msg),
ok = get_mess(2000, Msg),
unregister(Name).

Expand All @@ -344,7 +344,7 @@ send_after6(Config) when is_list(Config) ->
send_after7(Config) when is_list(Config) ->
Msg = make_ref(),
Name = make_name(),
{ok, {once, _}} = timer:send_after(500, Name, Msg),
{ok, {send_local, _}} = timer:send_after(500, Name, Msg),
register(Name, self()),
ok = get_mess(2000, Msg),
unregister(Name).
Expand Down
Loading