aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRickard Green <[email protected]>2015-05-11 17:56:03 +0200
committerRickard Green <[email protected]>2015-05-12 17:26:37 +0200
commit849a2ed2db2bd54422ec9284468f80cc86978974 (patch)
tree10f63d4ef099645145158301c145372ecfbbfbc2
parent9c78f149517dc02457d4c59e90bc9b03d411e28c (diff)
downloadotp-849a2ed2db2bd54422ec9284468f80cc86978974.tar.gz
otp-849a2ed2db2bd54422ec9284468f80cc86978974.tar.bz2
otp-849a2ed2db2bd54422ec9284468f80cc86978974.zip
Timer fixes, documentation, and test cases
-rw-r--r--erts/doc/src/erlang.xml249
-rw-r--r--erts/emulator/beam/erl_alloc.c16
-rw-r--r--erts/emulator/beam/erl_hl_timer.c66
-rw-r--r--erts/emulator/beam/time.c60
-rw-r--r--erts/emulator/test/after_SUITE.erl29
-rw-r--r--erts/emulator/test/system_info_SUITE.erl31
-rw-r--r--erts/emulator/test/timer_bif_SUITE.erl346
-rw-r--r--erts/preloaded/ebin/erlang.beambin101588 -> 101812 bytes
-rw-r--r--erts/preloaded/src/erlang.erl41
9 files changed, 657 insertions, 181 deletions
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index ba5f80a9c1..6ca57566aa 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -536,29 +536,69 @@
</desc>
</func>
<func>
- <name name="cancel_timer" arity="1"/>
+ <name name="cancel_timer" arity="2"/>
<fsummary>Cancel a timer</fsummary>
<desc>
- <p>Cancels a timer, where <c><anno>TimerRef</anno></c> was returned by
- either
- <seealso marker="#send_after/3">erlang:send_after/3</seealso>
- or
- <seealso marker="#start_timer/3">erlang:start_timer/3</seealso>.
- If the timer is there to be removed, the function returns
- the time in milliseconds left until the timer would have expired,
- otherwise <c>false</c> (which means that <c><anno>TimerRef</anno></c> was
- never a timer, that it has already been cancelled, or that it
- has already delivered its message).</p>
+ <p>Cancels a timer. <c><anno>TimerRef</anno></c> needs to refer to
+ a timer that was created by either
+ <seealso marker="#send_after/4"><c>erlang:send_after()</c></seealso>,
+ or <seealso marker="#start_timer/4"><c>erlang:start_timer()</c></seealso>.</p>
+ <p>Currently available <c><anno>Option</anno>s</c>:</p>
+ <taglist>
+ <tag><c>{async, Async}</c></tag>
+ <item>
+ <p>Asynchronous request for cancellation. <c>Async</c>
+ defaults to <c>false</c>. That is the operation will be
+ performed synchronously. When <c>Async</c> is set to
+ <c>true</c> the cancel operation will be performed
+ asynchronously. That is, <c>cancel_timer()</c> will send
+ a request for cancellation to the timer service that
+ manages the timer, and then return <c>ok</c>.</p></item>
+ <tag><c>{info, Info}</c></tag>
+ <item>
+ <p>Request information about the <c>Result</c> of the
+ cancellation. <c>Info</c> defaults to <c>true</c>. That
+ is information will be given. When <c>Info</c> is set to
+ <c>false</c> no information about the result of the cancel
+ operation will be given. When the operation is performed
+ synchronously the <c>Result</c> will returned from
+ <c>cancel_timer()</c>. When the operation is performed
+ asynchronously, a message on the form
+ <c>{cancel_timer, <anno>TimerRef</anno>, <anno>Result</anno>}</c>
+ will be sent to the caller of <c>cancel_timer()</c> when
+ the operation has been performed.</p></item>
+ </taglist>
+ <p>When the <c><anno>Result</anno></c> equals <c>false</c> a timer
+ corresponding to <c><anno>TimerRef</anno></c> could not be found. This
+ can be either because the timer had expired, been canceled, or because
+ <c><anno>TimerRef</anno></c> do not correspond to a timer. When the
+ <c><anno>Result</anno></c> is an integer, it represents
+ the time in milli seconds left before the timer will expire.</p>
+ <note><p>The timer service that manages the timer may be co-located
+ with another scheduler than the scheduler that the calling process
+ is executing on. In this case communication with the timer
+ service will be performed using asynchronous signals. If the calling
+ process is in critical path and can do other things while waiting
+ for the result of this operation, you want to use the <c>{async, true}</c>
+ option.</p></note>
<p>See also
- <seealso marker="#send_after/3">erlang:send_after/3</seealso>,
- <seealso marker="#start_timer/3">erlang:start_timer/3</seealso>,
+ <seealso marker="#send_after/4"><c>erlang:send_after/4</c></seealso>,
+ <seealso marker="#start_timer/4"><c>erlang:start_timer/4</c></seealso>,
and
- <seealso marker="#read_timer/1">erlang:read_timer/1</seealso>.</p>
+ <seealso marker="#read_timer/2"><c>erlang:read_timer/2</c></seealso>.</p>
<p>Note: Cancelling a timer does not guarantee that the message
has not already been delivered to the message queue.</p>
</desc>
</func>
-
+ <func>
+ <name name="cancel_timer" arity="1"/>
+ <fsummary>Cancel a timer</fsummary>
+ <desc>
+ <p>Cancels a timer. The same as calling
+ <seealso marker="#cancel_timer/2"><c>erlang:cancel_timer(TimerRef,
+ [{async, false}, {info, true}])</c></seealso>.</p>
+ </desc>
+ </func>
<func>
<name name="check_old_code" arity="1"/>
<fsummary>Check if a module has old code</fsummary>
@@ -4505,23 +4545,54 @@ os_prompt% </pre>
</desc>
</func>
<func>
- <name name="read_timer" arity="1"/>
- <fsummary>Number of milliseconds remaining for a timer</fsummary>
- <desc>
- <p><c><anno>TimerRef</anno></c> is a timer reference returned by
- <seealso marker="#send_after/3">erlang:send_after/3</seealso>
- or
- <seealso marker="#start_timer/3">erlang:start_timer/3</seealso>.
- If the timer is active, the function returns the time in
- milliseconds left until the timer will expire, otherwise
- <c>false</c> (which means that <c><anno>TimerRef</anno></c> was never a
- timer, that it has been cancelled, or that it has already
- delivered its message).</p>
+ <name name="read_timer" arity="2"/>
+ <fsummary>Read the state of a timer</fsummary>
+ <desc>
+ <p>Read the state of a timer. <c><anno>TimerRef</anno></c>
+ needs to refer to a timer that was created by either
+ <seealso marker="#send_after/4"><c>erlang:send_after()</c></seealso>,
+ or <seealso marker="#start_timer/4"><c>erlang:start_timer()</c></seealso>.</p>
+ <p>Currently available <c><anno>Option</anno>s</c>:</p>
+ <taglist>
+ <tag><c>{async, Async}</c></tag>
+ <item>
+ <p>Asynchronous request. <c>Async</c> defaults to <c>false</c>. That
+ is the operation will be performed synchronously, and the <c>Result</c>
+ will returned from <c>read_timer()</c>. When <c>Async</c> is set to
+ <c>true</c>, <c>read_timer()</c> will send a request for the
+ <c>Result</c> to a timer service that manages the timer and then
+ return <c>ok</c>. A message on the format
+ <c>{read_timer, <anno>TimerRef</anno>, <anno>Result</anno>}</c>
+ will be sent to the caller of <c>read_timer()</c> when
+ the operation has been processed.</p></item>
+ </taglist>
+ <p>When the <c><anno>Result</anno></c> equals <c>false</c> a timer
+ corresponding to <c><anno>TimerRef</anno></c> could not be found. This
+ can be either because the timer had expired, been canceled, or because
+ <c><anno>TimerRef</anno></c> do not correspond to a timer. When the
+ <c><anno>Result</anno></c> is an integer, it represents
+ the time in milli seconds left before the timer will expire.</p>
+ <note><p>The timer service that manages the timer may be co-located
+ with another scheduler than the scheduler that the calling process
+ is executing on. In this case communication with the timer
+ service will be performed using asynchronous signals. If the calling
+ process is in critical path and can do other things while waiting
+ for the result of this operation, you want to use the <c>{async, true}</c>
+ option.</p></note>
<p>See also
- <seealso marker="#send_after/3">erlang:send_after/3</seealso>,
- <seealso marker="#start_timer/3">erlang:start_timer/3</seealso>,
+ <seealso marker="#send_after/4"><c>erlang:send_after/4</c></seealso>,
+ <seealso marker="#start_timer/4"><c>erlang:start_timer/4</c></seealso>,
and
- <seealso marker="#cancel_timer/1">erlang:cancel_timer/1</seealso>.</p>
+ <seealso marker="#cancel_timer/2"><c>erlang:cancel_timer/2</c></seealso>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="read_timer" arity="1"/>
+ <fsummary>Read the state of a timer</fsummary>
+ <desc>
+ <p>Read the state of a timer. The same as calling
+ <seealso marker="#read_timer/2"><c>erlang:read_timer(TimerRef,
+ [{async, false}])</c></seealso>.</p>
</desc>
</func>
<func>
@@ -4670,6 +4741,63 @@ true</pre>
</desc>
</func>
<func>
+ <name name="send_after" arity="4"/>
+ <fsummary>Start a timer</fsummary>
+ <desc>
+ <p>Starts a timer. When the timer expires, the message
+ <c><anno>Msg</anno></c> will be sent to
+ <c><anno>Dest</anno></c>.</p>
+ <p>If <c><anno>Dest</anno></c> is a <c>pid()</c> it has to
+ be a <c>pid()</c> of a local process, dead or alive.</p>
+ <p>Currently available <c><anno>Option</anno>s</c>:</p>
+ <taglist>
+ <tag><c>{abs, Abs}</c></tag>
+ <item>
+ <p>Absolute timeout. When <c>Abs</c> is <c>false</c>
+ the <c><anno>Time</anno></c> value will be interpreted
+ as a time in milli-seconds relative current
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso>. When <c>Abs</c> is <c>true</c> the
+ <c><anno>Time</anno></c> value will be interpreted as an absolute
+ Erlang monotonic time of milli second time unit. <c>Abs</c>
+ defaults to <c>false</c>.</p>
+ </item>
+ </taglist>
+ <p>The absolute time when the timer is set to expire needs
+ to be in the range between
+ <seealso marker="#system_info_start_time"><c>erlang:system_info(start_time)</c></seealso>
+ and
+ <seealso marker="#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso>.
+ If a negative relative time is specified the time is not
+ allowed to be negative.</p>
+ <p>If <c><anno>Dest</anno></c> is an <c>atom()</c>, it is supposed to be the name of
+ a registered process. The process referred to by the name is
+ looked up at the time of delivery. No error is given if
+ the name does not refer to a process.</p>
+ <p>If <c><anno>Dest</anno></c> is a <c>pid()</c>, the timer will be automatically
+ canceled if the process referred to by the <c>pid()</c> is not alive,
+ or when the process exits. This feature was introduced in
+ erts version 5.4.11. Note that timers will not be
+ automatically canceled when <c><anno>Dest</anno></c> is an <c>atom()</c>.</p>
+ <p>See also
+ <seealso marker="#start_timer/4"><c>erlang:send_timer/4</c></seealso>,
+ <seealso marker="#cancel_timer/2"><c>erlang:cancel_timer/2</c></seealso>,
+ and
+ <seealso marker="#read_timer/2"><c>erlang:read_timer/2</c></seealso>.</p>
+ <p>Failure: <c>badarg</c> if the arguments does not satisfy
+ the requirements specified above.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="send_after" arity="3"/>
+ <fsummary>Start a timer</fsummary>
+ <desc>
+ <p>Starts a timer. The same as calling
+ <seealso marker="#send_timer/4"><c>erlang:send_after(<anno>Time</anno>,
+ <anno>Dest</anno>, <anno>Msg</anno>, [{abs, false}])</c></seealso>.</p>
+ </desc>
+ </func>
+ <func>
<name name="send_after" arity="3"/>
<type_desc variable="Time">0 &lt;= Time &lt;= 4294967295</type_desc>
<fsummary>Start a timer</fsummary>
@@ -4690,9 +4818,9 @@ true</pre>
automatically canceled when <c><anno>Dest</anno></c> is an <c>atom</c>.</p>
<p>See also
<seealso marker="#start_timer/3">erlang:start_timer/3</seealso>,
- <seealso marker="#cancel_timer/1">erlang:cancel_timer/1</seealso>,
+ <seealso marker="#cancel_timer/2">erlang:cancel_timer/2</seealso>,
and
- <seealso marker="#read_timer/1">erlang:read_timer/1</seealso>.</p>
+ <seealso marker="#read_timer/2">erlang:read_timer/2</seealso>.</p>
<p>Failure: <c>badarg</c> if the arguments does not satisfy
the requirements specified above.</p>
</desc>
@@ -5100,15 +5228,35 @@ true</pre>
</desc>
</func>
<func>
- <name name="start_timer" arity="3"/>
- <type_desc variable="Time">0 &lt;= Time &lt;= 4294967295</type_desc>
+ <name name="start_timer" arity="4"/>
<fsummary>Start a timer</fsummary>
<desc>
- <p>Starts a timer which will send the message
- <c>{timeout, <anno>TimerRef</anno>, <anno>Msg</anno>}</c> to <c><anno>Dest</anno></c>
- after <c><anno>Time</anno></c> milliseconds.</p>
- <p>If <c><anno>Dest</anno></c> is a <c>pid()</c> it has to be a <c>pid()</c> of a local process, dead or alive.</p>
- <p>The <c><anno>Time</anno></c> value can, in the current implementation, not be greater than 4294967295.</p>
+ <p>Starts a timer. When the timer expires, the message
+ <c>{timeout, <anno>TimerRef</anno>, <anno>Msg</anno>}</c>
+ will be sent to <c><anno>Dest</anno></c>.</p>
+ <p>If <c><anno>Dest</anno></c> is a <c>pid()</c> it has to
+ be a <c>pid()</c> of a local process, dead or alive.</p>
+ <p>Currently available <c><anno>Option</anno>s</c>:</p>
+ <taglist>
+ <tag><c>{abs, Abs}</c></tag>
+ <item>
+ <p>Absolute timeout. When <c>Abs</c> is <c>false</c>
+ the <c><anno>Time</anno></c> value will be interpreted
+ as a time in milli-seconds relative current
+ <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang
+ monotonic time</seealso>. When <c>Abs</c> is <c>true</c> the
+ <c><anno>Time</anno></c> value will be interpreted as an absolute
+ Erlang monotonic time of milli second time unit. <c>Abs</c>
+ defaults to <c>false</c>.</p>
+ </item>
+ </taglist>
+ <p>The absolute time when the timer is set to expire needs
+ to be in the range between
+ <seealso marker="#system_info_start_time"><c>erlang:system_info(start_time)</c></seealso>
+ and
+ <seealso marker="#system_info_end_time"><c>erlang:system_info(end_time)</c></seealso>.
+ If a negative relative time is specified the time is not
+ allowed to be negative.</p>
<p>If <c><anno>Dest</anno></c> is an <c>atom()</c>, it is supposed to be the name of
a registered process. The process referred to by the name is
looked up at the time of delivery. No error is given if
@@ -5119,15 +5267,24 @@ true</pre>
erts version 5.4.11. Note that timers will not be
automatically canceled when <c><anno>Dest</anno></c> is an <c>atom()</c>.</p>
<p>See also
- <seealso marker="#send_after/3">erlang:send_after/3</seealso>,
- <seealso marker="#cancel_timer/1">erlang:cancel_timer/1</seealso>,
+ <seealso marker="#send_after/4"><c>erlang:send_after/4</c></seealso>,
+ <seealso marker="#cancel_timer/2"><c>erlang:cancel_timer/2</c></seealso>,
and
- <seealso marker="#read_timer/1">erlang:read_timer/1</seealso>.</p>
+ <seealso marker="#read_timer/2"><c>erlang:read_timer/2</c></seealso>.</p>
<p>Failure: <c>badarg</c> if the arguments does not satisfy
the requirements specified above.</p>
</desc>
</func>
<func>
+ <name name="start_timer" arity="3"/>
+ <fsummary>Start a timer</fsummary>
+ <desc>
+ <p>Starts a timer. The same as calling
+ <seealso marker="#start_timer/4"><c>erlang:start_timer(<anno>Time</anno>,
+ <anno>Dest</anno>, <anno>Msg</anno>, [{abs, false}])</c></seealso>.</p>
+ </desc>
+ </func>
+ <func>
<name name="statistics" arity="1" clause_i="1"/>
<fsummary>Information about context switches</fsummary>
<desc>
@@ -6236,6 +6393,14 @@ ok
(i.e. <c>system_info(dynamic_trace)</c> returns
<c>dtrace</c> or <c>systemtap</c>).</p>
</item>
+ <tag><marker id="system_info_end_time"/><c>end_time</c></tag>
+ <item><p>The last <seealso marker="#monotonic_time/0">Erlang monotonic
+ time</seealso> in <c>native</c>
+ <seealso marker="#type_time_unit">time unit</seealso> that
+ can be represented internally in the current Erlang runtime system
+ instance. The time between the
+ <seealso marker="#system_info_start_time">start time</seealso> and
+ the end time is at least a quarter of a millennium.</p></item>
<tag><c>elib_malloc</c></tag>
<item>
<p>This option will be removed in a future release.
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index 71902c2f9f..7d099997d8 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -2331,6 +2331,22 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg)
&size.processes_used,
fi,
ERTS_ALC_T_MSG_REF);
+ add_fix_values(&size.processes,
+ &size.processes_used,
+ fi,
+ ERTS_ALC_T_LL_PTIMER);
+ add_fix_values(&size.processes,
+ &size.processes_used,
+ fi,
+ ERTS_ALC_T_HL_PTIMER);
+ add_fix_values(&size.processes,
+ &size.processes_used,
+ fi,
+ ERTS_ALC_T_BIF_TIMER);
+ add_fix_values(&size.processes,
+ &size.processes_used,
+ fi,
+ ERTS_ALC_T_ABIF_TIMER);
}
if (want.atom || want.atom_used) {
diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c
index 2245799819..f1806d25be 100644
--- a/erts/emulator/beam/erl_hl_timer.c
+++ b/erts/emulator/beam/erl_hl_timer.c
@@ -850,8 +850,6 @@ hl_timer_destroy(ErtsHLTimer *tmr)
if (!(roflgs & ERTS_TMR_ROFLG_BIF_TMR))
erts_free(ERTS_ALC_T_HL_PTIMER, tmr);
else {
- if (tmr->btm.bp)
- free_message_buffer(tmr->btm.bp);
if (roflgs & ERTS_TMR_ROFLG_PRE_ALC)
bif_timer_pre_free(tmr);
else if (roflgs & ERTS_TMR_ROFLG_ABIF_TMR)
@@ -898,10 +896,6 @@ schedule_hl_timer_destroy(ErtsHLTimer *tmr, Uint32 roflgs)
* Message buffer can be dropped at
* once...
*/
- if (tmr->btm.bp) {
- free_message_buffer(tmr->btm.bp);
- tmr->btm.bp = NULL;
- }
size = sizeof(ErtsHLTimer);
}
@@ -1121,6 +1115,7 @@ hlt_bif_timer_timeout(ErtsHLTimer *tmr, Uint32 roflgs)
{
ErtsProcLocks proc_locks = ERTS_PROC_LOCKS_MSG_SEND;
Process *proc;
+ int queued_message = 0;
int dec_refc = 0;
Uint32 is_reg_name = (roflgs & ERTS_TMR_ROFLG_REG_NAME);
ERTS_HLT_ASSERT(roflgs & ERTS_TMR_ROFLG_BIF_TMR);
@@ -1159,6 +1154,7 @@ hlt_bif_timer_timeout(ErtsHLTimer *tmr, Uint32 roflgs)
#endif
);
erts_smp_proc_unlock(proc, ERTS_PROC_LOCKS_MSG_SEND);
+ queued_message = 1;
proc_locks &= ~ERTS_PROC_LOCKS_MSG_SEND;
tmr->btm.bp = NULL;
if (tmr->btm.proc_tree.parent != ERTS_HLT_PFIELD_NOT_IN_TABLE) {
@@ -1172,6 +1168,8 @@ hlt_bif_timer_timeout(ErtsHLTimer *tmr, Uint32 roflgs)
if (dec_refc)
hl_timer_pre_dec_refc(tmr);
}
+ if (!queued_message && tmr->btm.bp)
+ free_message_buffer(tmr->btm.bp);
}
static ERTS_INLINE void
@@ -1689,6 +1687,8 @@ setup_bif_timer(Process *c_p, ErtsMonotonicTime timeout_pos,
rcvr, ERTS_PROC_LOCK_BTM,
ERTS_P2P_FLG_INC_REFC);
if (!proc) {
+ if (tmr->btm.bp)
+ free_message_buffer(tmr->btm.bp);
hlt_delete_timer(esdp, tmr);
hl_timer_destroy(tmr);
}
@@ -1721,6 +1721,9 @@ cancel_bif_timer(ErtsHLTimer *tmr)
if (state != ERTS_TMR_STATE_ACTIVE)
return 0;
+ if (tmr->btm.bp)
+ free_message_buffer(tmr->btm.bp);
+
res = -1;
roflgs = tmr->head.roflgs;
@@ -1834,18 +1837,25 @@ access_sched_local_btm(Process *c_p, Eterm pid,
if (!async)
hsz += REF_THING_SIZE;
else {
- if (is_non_value(tref))
+ if (is_non_value(tref) || proc != c_p)
hsz += REF_THING_SIZE;
hsz += 1; /* upgrade to 3-tuple */
}
if (time_left > (Sint64) MAX_SMALL)
hsz += ERTS_SINT64_HEAP_SIZE(time_left);
- hp = erts_alloc_message_heap(hsz,
- &bp,
- &ohp,
- proc,
- &proc_locks);
+ if (proc == c_p) {
+ bp = NULL;
+ ohp = NULL;
+ hp = HAlloc(c_p, hsz);
+ }
+ else {
+ hp = erts_alloc_message_heap(hsz,
+ &bp,
+ &ohp,
+ proc,
+ &proc_locks);
+ }
#ifdef ERTS_HLT_DEBUG
hp_end = hp + hsz;
@@ -1871,7 +1881,7 @@ access_sched_local_btm(Process *c_p, Eterm pid,
}
else {
Eterm tag = cancel ? am_cancel_timer : am_read_timer;
- if (is_value(tref))
+ if (is_value(tref) && proc == c_p)
ref = tref;
else {
write_ref_thing(hp,
@@ -1987,8 +1997,6 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp,
}
else {
Eterm tag, res, msg;
- ErlOffHeap *ohp;
- ErlHeapFragment* bp;
Uint hsz;
Eterm *hp;
ErtsProcLocks proc_locks = ERTS_PROC_LOCK_MAIN;
@@ -1997,11 +2005,7 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp,
if (time_left > (Sint64) MAX_SMALL)
hsz += ERTS_SINT64_HEAP_SIZE(time_left);
- hp = erts_alloc_message_heap(hsz,
- &bp,
- &ohp,
- c_p,
- &proc_locks);
+ hp = HAlloc(c_p, hsz);
if (cancel)
tag = am_cancel_timer;
else
@@ -2016,7 +2020,7 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp,
msg = TUPLE3(hp, tag, tref, res);
- erts_queue_message(c_p, &proc_locks, bp,
+ erts_queue_message(c_p, &proc_locks, NULL,
msg, NIL
#ifdef USE_VM_PROBES
, NIL
@@ -2166,7 +2170,7 @@ parse_bif_timer_options(Eterm option_list, int *async, int *info,
if (async)
*async = 0;
if (info)
- *info = 0;
+ *info = 1;
if (abs)
*abs = 0;
if (accessor)
@@ -2235,13 +2239,19 @@ exit_cancel_bif_timer(ErtsHLTimer *tmr, void *vesdp)
tmr->btm.proc_tree.parent = ERTS_HLT_PFIELD_NOT_IN_TABLE;
if (sid == (Uint32) esdp->no) {
- if (state == ERTS_TMR_STATE_ACTIVE)
+ if (state == ERTS_TMR_STATE_ACTIVE) {
+ if (tmr->btm.bp)
+ free_message_buffer(tmr->btm.bp);
hlt_delete_timer(esdp, tmr);
+ }
hl_timer_dec_refc(tmr, roflgs);
}
else {
- if (state == ERTS_TMR_STATE_ACTIVE)
+ if (state == ERTS_TMR_STATE_ACTIVE) {
+ if (tmr->btm.bp)
+ free_message_buffer(tmr->btm.bp);
queue_canceled_timer(esdp, sid, (ErtsTimer *) tmr);
+ }
else
hl_timer_dec_refc(tmr, roflgs);
}
@@ -2415,7 +2425,7 @@ BIF_RETTYPE send_after_3(BIF_ALIST_3)
tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL,
0, &timeout_pos, &short_time);
if (tres != 0)
- BIF_ERROR(BIF_P, tres < 0 ? BADARG : SYSTEM_LIMIT);
+ BIF_ERROR(BIF_P, BADARG);
return setup_bif_timer(BIF_P, timeout_pos, short_time,
BIF_ARG_2, BIF_ARG_2, BIF_ARG_3, 0);
@@ -2433,7 +2443,7 @@ BIF_RETTYPE send_after_4(BIF_ALIST_4)
tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL,
abs, &timeout_pos, &short_time);
if (tres != 0)
- BIF_ERROR(BIF_P, tres < 0 ? BADARG : SYSTEM_LIMIT);
+ BIF_ERROR(BIF_P, BADARG);
return setup_bif_timer(BIF_P, timeout_pos, short_time,
BIF_ARG_2, accessor, BIF_ARG_3, 0);
@@ -2447,7 +2457,7 @@ BIF_RETTYPE start_timer_3(BIF_ALIST_3)
tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL,
0, &timeout_pos, &short_time);
if (tres != 0)
- BIF_ERROR(BIF_P, tres < 0 ? BADARG : SYSTEM_LIMIT);
+ BIF_ERROR(BIF_P, BADARG);
return setup_bif_timer(BIF_P, timeout_pos, short_time,
BIF_ARG_2, BIF_ARG_2, BIF_ARG_3, !0);
@@ -2465,7 +2475,7 @@ BIF_RETTYPE start_timer_4(BIF_ALIST_4)
tres = parse_timeout_pos(ERTS_PROC_GET_SCHDATA(BIF_P), BIF_ARG_1, NULL,
abs, &timeout_pos, &short_time);
if (tres != 0)
- BIF_ERROR(BIF_P, tres < 0 ? BADARG : SYSTEM_LIMIT);
+ BIF_ERROR(BIF_P, BADARG);
return setup_bif_timer(BIF_P, timeout_pos, short_time,
BIF_ARG_2, accessor, BIF_ARG_3, !0);
diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c
index ea19d8b362..8bffdedb2b 100644
--- a/erts/emulator/beam/time.c
+++ b/erts/emulator/beam/time.c
@@ -135,39 +135,40 @@ struct ErtsTimerWheel_ {
ErtsMonotonicTime next_timeout_time;
};
-/* get the time (in units of TIW_ITIME) to the next timeout,
- or -1 if there are no timeouts */
-
static ERTS_INLINE ErtsMonotonicTime
-find_next_timeout(ErtsTimerWheel *tiw,
- ErtsMonotonicTime curr_time,
- ErtsMonotonicTime max_search_time)
+find_next_timeout(ErtsSchedulerData *esdp,
+ ErtsTimerWheel *tiw,
+ int search_all,
+ ErtsMonotonicTime curr_time, /* When !search_all */
+ ErtsMonotonicTime max_search_time) /* When !search_all */
{
int start_ix, tiw_pos_ix;
ErtsTWheelTimer *p;
- int true_min_timeout;
+ int true_min_timeout = 0;
ErtsMonotonicTime min_timeout, min_timeout_pos, slot_timeout_pos;
- if (tiw->true_next_timeout_time)
- return tiw->next_timeout_time;
-
if (tiw->nto == 0) { /* no timeouts in wheel */
- true_min_timeout = 0;
- min_timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time + ERTS_MONOTONIC_DAY);
+ if (!search_all)
+ min_timeout_pos = tiw->pos;
+ else {
+ curr_time = erts_get_monotonic_time(esdp);
+ tiw->pos = min_timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time);
+ }
+ min_timeout_pos += ERTS_MONOTONIC_TO_CLKTCKS(ERTS_MONOTONIC_DAY);
goto found_next;
}
- slot_timeout_pos = tiw->pos;
- min_timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time + max_search_time);
+ slot_timeout_pos = min_timeout_pos = tiw->pos;
+ if (search_all)
+ min_timeout_pos += ERTS_MONOTONIC_TO_CLKTCKS(ERTS_MONOTONIC_DAY);
+ else
+ min_timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time + max_search_time);
start_ix = tiw_pos_ix = (int) (tiw->pos & (ERTS_TIW_SIZE-1));
do {
- slot_timeout_pos++;
- if (slot_timeout_pos >= min_timeout_pos) {
- true_min_timeout = 0;
+ if (++slot_timeout_pos >= min_timeout_pos)
break;
- }
p = tiw->w[tiw_pos_ix];
@@ -269,24 +270,16 @@ remove_timer(ErtsTimerWheel *tiw, ErtsTWheelTimer *p)
p->slot = ERTS_TWHEEL_SLOT_INACTIVE;
-#if 0
- p->next = NULL;
- p->prev = NULL;
-#endif
-
tiw->nto--;
}
ErtsMonotonicTime
erts_check_next_timeout_time(ErtsSchedulerData *esdp)
{
- /*
- * Called before a scheduler is about to wait. We wont
- * check more than 10 minutes into the future.
- */
- return find_next_timeout(esdp->timer_wheel,
- erts_get_monotonic_time(esdp),
- ERTS_SEC_TO_MONOTONIC(10*60));
+ ErtsTimerWheel *tiw = esdp->timer_wheel;
+ if (tiw->true_next_timeout_time)
+ return tiw->next_timeout_time;
+ return find_next_timeout(esdp, tiw, 1, 0, 0);
}
#ifndef ERTS_TW_DEBUG
@@ -330,14 +323,11 @@ timeout_timer(ErtsTWheelTimer *p)
{
ErlTimeoutProc timeout;
void *arg;
-#if 0
- p->next = NULL;
- p->prev = NULL;
-#endif
p->slot = ERTS_TWHEEL_SLOT_INACTIVE;
timeout = p->u.func.timeout;
arg = p->u.func.arg;
(*timeout)(arg);
+ ASSERT_NO_LOCKED_LOCKS;
}
void
@@ -508,7 +498,7 @@ erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time)
tiw->next_timeout_time = curr_time + ERTS_MONOTONIC_DAY;
/* Search at most two seconds ahead... */
- (void) find_next_timeout(tiw, curr_time, ERTS_SEC_TO_MONOTONIC(2));
+ (void) find_next_timeout(NULL, tiw, 0, curr_time, ERTS_SEC_TO_MONOTONIC(2));
}
Uint
diff --git a/erts/emulator/test/after_SUITE.erl b/erts/emulator/test/after_SUITE.erl
index 699a1436ce..c855481489 100644
--- a/erts/emulator/test/after_SUITE.erl
+++ b/erts/emulator/test/after_SUITE.erl
@@ -27,7 +27,8 @@
init_per_group/2,end_per_group/2,
t_after/1, receive_after/1, receive_after_big/1,
receive_after_errors/1, receive_var_zero/1, receive_zero/1,
- multi_timeout/1, receive_after_32bit/1]).
+ multi_timeout/1, receive_after_32bit/1,
+ receive_after_blast/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -40,7 +41,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[t_after, receive_after, receive_after_big,
receive_after_errors, receive_var_zero, receive_zero,
- multi_timeout, receive_after_32bit].
+ multi_timeout, receive_after_32bit, receive_after_blast].
groups() ->
[].
@@ -244,4 +245,26 @@ recv_after_32bit(I, T) when I rem 2 =:= 0 ->
receive after T -> exit(timeout) end;
recv_after_32bit(_, _) ->
receive after 16#ffffFFFF -> exit(timeout) end.
-
+
+blaster() ->
+ receive
+ {go, TimeoutTime} ->
+ Tmo = TimeoutTime - erlang:monotonic_time(milli_seconds),
+ receive after Tmo -> ok end
+ end.
+
+spawn_blasters(0) ->
+ [];
+spawn_blasters(N) ->
+ [spawn_monitor(fun () -> blaster() end)|spawn_blasters(N-1)].
+
+receive_after_blast(Config) when is_list(Config) ->
+ PMs = spawn_blasters(10000),
+ TimeoutTime = erlang:monotonic_time(milli_seconds) + 5000,
+ lists:foreach(fun ({P, _}) -> P ! {go, TimeoutTime} end, PMs),
+ lists:foreach(fun ({P, M}) ->
+ receive
+ {'DOWN', M, process, P, normal} ->
+ ok
+ end
+ end, PMs).
diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl
index 0fd6696536..e3ac2d5d83 100644
--- a/erts/emulator/test/system_info_SUITE.erl
+++ b/erts/emulator/test/system_info_SUITE.erl
@@ -264,6 +264,37 @@ memory_test(_Config) ->
[]),
cmp_memory(MWs, "unlink procs"),
+ mem_workers_call(MWs,
+ fun () ->
+ lists:foreach(
+ fun (P) ->
+ Tmr = erlang:start_timer(1 bsl 34,
+ P,
+ hello),
+ Tmrs = case get('BIF_TMRS') of
+ undefined -> [];
+ Rs -> Rs
+ end,
+ true = is_reference(Tmr),
+ put('BIF_TMRS', [Tmr|Tmrs])
+ end, Ps)
+ end,
+ []),
+ cmp_memory(MWs, "start BIF timer procs"),
+
+ mem_workers_call(MWs,
+ fun () ->
+ lists:foreach(fun (Tmr) ->
+ true = is_reference(Tmr),
+ true = is_integer(erlang:cancel_timer(Tmr))
+ end, get('BIF_TMRS')),
+ put('BIF_TMRS', undefined),
+ garbage_collect()
+ end,
+ []),
+ erts_debug:set_internal_state(wait, deallocations),
+ cmp_memory(MWs, "cancel BIF timer procs"),
+
DMs = mem_workers_call(MWs,
fun () ->
lists:map(fun (P) ->
diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl
index 3701dd8437..d406456f98 100644
--- a/erts/emulator/test/timer_bif_SUITE.erl
+++ b/erts/emulator/test/timer_bif_SUITE.erl
@@ -26,11 +26,17 @@
cancel_timer_1/1,
start_timer_big/1, send_after_big/1,
start_timer_e/1, send_after_e/1, cancel_timer_e/1,
- read_timer_trivial/1, read_timer/1,
- cleanup/1, evil_timers/1, registered_process/1]).
+ read_timer_trivial/1, read_timer/1, read_timer_async/1,
+ cleanup/1, evil_timers/1, registered_process/1, same_time_yielding/1,
+ same_time_yielding_with_cancel/1, same_time_yielding_with_cancel_other/1,
+ same_time_yielding_with_cancel_other_accessor/1, auto_cancel_yielding/1]).
-include_lib("test_server/include/test_server.hrl").
+-define(SHORT_TIMEOUT, 5000). %% Bif timers as short as this may be pre-allocated
+-define(TIMEOUT_YIELD_LIMIT, 100).
+-define(AUTO_CANCEL_YIELD_LIMIT, 100).
+
init_per_testcase(_Case, Config) ->
?line Dog=test_server:timetrap(test_server:seconds(30)),
case catch erts_debug:get_internal_state(available_internal_state) of
@@ -45,6 +51,7 @@ end_per_testcase(_Case, Config) ->
ok.
init_per_suite(Config) ->
+ erts_debug:set_internal_state(available_internal_state, true),
Config.
end_per_suite(_Config) ->
@@ -56,8 +63,12 @@ all() ->
[start_timer_1, send_after_1, send_after_2,
cancel_timer_1, start_timer_e, send_after_e,
cancel_timer_e, start_timer_big, send_after_big,
- read_timer_trivial, read_timer, cleanup, evil_timers,
- registered_process].
+ read_timer_trivial, read_timer, read_timer_async,
+ cleanup, evil_timers, registered_process,
+ same_time_yielding, same_time_yielding_with_cancel,
+ same_time_yielding_with_cancel_other,
+ same_time_yielding_with_cancel_other_accessor,
+ auto_cancel_yielding].
groups() ->
[].
@@ -162,7 +173,7 @@ cancel_timer_1(Config) when is_list(Config) ->
start_timer_e(doc) -> ["Error cases for start_timer/3"];
start_timer_e(Config) when is_list(Config) ->
?line {'EXIT', _} = (catch erlang:start_timer(-4, self(), hej)),
- ?line {'EXIT', _} = (catch erlang:start_timer(4728472847827482,
+ ?line {'EXIT', _} = (catch erlang:start_timer(1 bsl 64,
self(), hej)),
?line {'EXIT', _} = (catch erlang:start_timer(4.5, self(), hej)),
@@ -180,7 +191,7 @@ send_after_e(doc) -> ["Error cases for send_after/3"];
send_after_e(suite) -> [];
send_after_e(Config) when is_list(Config) ->
?line {'EXIT', _} = (catch erlang:send_after(-4, self(), hej)),
- ?line {'EXIT', _} = (catch erlang:send_after(4728472847827482,
+ ?line {'EXIT', _} = (catch erlang:send_after(1 bsl 64,
self(), hej)),
?line {'EXIT', _} = (catch erlang:send_after(4.5, self(), hej)),
@@ -213,44 +224,79 @@ read_timer_trivial(Config) when is_list(Config) ->
read_timer(doc) -> ["Test that read_timer/1 seems to return the correct values."];
read_timer(suite) -> [];
read_timer(Config) when is_list(Config) ->
- ?line Big = 1 bsl 31,
- ?line R = erlang:send_after(Big, self(), hej_hopp),
+ process_flag(scheduler, 1),
+ Big = 1 bsl 31,
+ R = erlang:send_after(Big, self(), hej_hopp),
+
+ receive after 200 -> ok end, % Delay and clear reductions.
+ Left = erlang:read_timer(R),
+ Left2 = erlang:cancel_timer(R),
+ case Left == Left2 of
+ true -> ok;
+ false -> Left = Left2 + 1
+ end,
+ false = erlang:read_timer(R),
- ?line receive after 200 -> ok end, % Delay and clear reductions.
- ?line Left = erlang:read_timer(R),
- ?line Left = erlang:cancel_timer(R),
- ?line false = erlang:read_timer(R),
+ case Big - Left of
+ Diff when Diff >= 200, Diff < 10000 ->
+ ok;
+ _Diff ->
+ test_server:fail({big, Big, Left})
+ end,
+ process_flag(scheduler, 0),
+ ok.
- ?line case Big - Left of
- Diff when Diff >= 200, Diff < 10000 ->
- ok;
- _Diff ->
- test_server:fail({big, Big, Left})
- end,
+read_timer_async(doc) -> ["Test that read_timer/1 seems to return the correct values."];
+read_timer_async(suite) -> [];
+read_timer_async(Config) when is_list(Config) ->
+ process_flag(scheduler, 1),
+ Big = 1 bsl 33,
+ R = erlang:send_after(Big, self(), hej_hopp),
+
+ %% Access from another scheduler
+ process_flag(scheduler, erlang:system_info(schedulers_online)),
+
+ receive after 200 -> ok end, % Delay and clear reductions.
+ ok = erlang:read_timer(R, [{async, true}]),
+ ok = erlang:cancel_timer(R, [{async, true}, {info, true}]),
+ ok = erlang:read_timer(R, [{async, true}]),
+
+ {read_timer, R, Left} = receive_one(),
+ {cancel_timer, R, Left2} = receive_one(),
+ case Left == Left2 of
+ true -> ok;
+ false -> Left = Left2 + 1
+ end,
+ {read_timer, R, false} = receive_one(),
+
+ case Big - Left of
+ Diff when Diff >= 200, Diff < 10000 ->
+ ok;
+ _Diff ->
+ test_server:fail({big, Big, Left})
+ end,
+ process_flag(scheduler, 0),
ok.
cleanup(doc) -> [];
cleanup(suite) -> [];
cleanup(Config) when is_list(Config) ->
- {skipped, "Test needs to be UPDATED for new timer implementation"}.
-
-cleanup_test(Config) when is_list(Config) ->
?line Mem = mem(),
%% Timer on dead process
?line P1 = spawn(fun () -> ok end),
?line wait_until(fun () -> process_is_cleaned_up(P1) end),
- ?line T1 = erlang:start_timer(10000, P1, "hej"),
- ?line T2 = erlang:send_after(10000, P1, "hej"),
+ ?line T1 = erlang:start_timer(?SHORT_TIMEOUT*2, P1, "hej"),
+ ?line T2 = erlang:send_after(?SHORT_TIMEOUT*2, P1, "hej"),
receive after 1000 -> ok end,
?line Mem = mem(),
?line false = erlang:read_timer(T1),
?line false = erlang:read_timer(T2),
?line Mem = mem(),
%% Process dies before timeout
- ?line P2 = spawn(fun () -> receive after 500 -> ok end end),
- ?line T3 = erlang:start_timer(10000, P2, "hej"),
- ?line T4 = erlang:send_after(10000, P2, "hej"),
- ?line true = Mem < mem(),
+ ?line P2 = spawn(fun () -> receive after (?SHORT_TIMEOUT div 10) -> ok end end),
+ ?line T3 = erlang:start_timer(?SHORT_TIMEOUT*2, P2, "hej"),
+ ?line T4 = erlang:send_after(?SHORT_TIMEOUT*2, P2, "hej"),
+ ?line true = mem_larger_than(Mem),
?line true = is_integer(erlang:read_timer(T3)),
?line true = is_integer(erlang:read_timer(T4)),
?line wait_until(fun () -> process_is_cleaned_up(P2) end),
@@ -259,21 +305,22 @@ cleanup_test(Config) when is_list(Config) ->
?line false = erlang:read_timer(T4),
?line Mem = mem(),
%% Cancel timer
- ?line P3 = spawn(fun () -> receive after 20000 -> ok end end),
- ?line T5 = erlang:start_timer(10000, P3, "hej"),
- ?line T6 = erlang:send_after(10000, P3, "hej"),
- ?line true = Mem < mem(),
+ ?line P3 = spawn(fun () -> receive after ?SHORT_TIMEOUT*4 -> ok end end),
+ ?line T5 = erlang:start_timer(?SHORT_TIMEOUT*2, P3, "hej"),
+ ?line T6 = erlang:send_after(?SHORT_TIMEOUT*2, P3, "hej"),
+ ?line true = mem_larger_than(Mem),
?line true = is_integer(erlang:cancel_timer(T5)),
?line true = is_integer(erlang:cancel_timer(T6)),
?line false = erlang:read_timer(T5),
?line false = erlang:read_timer(T6),
?line exit(P3, kill),
+ ?line wait_until(fun () -> process_is_cleaned_up(P3) end),
?line Mem = mem(),
%% Timeout
?line Ref = make_ref(),
- ?line T7 = erlang:start_timer(500, self(), Ref),
- ?line T8 = erlang:send_after(500, self(), Ref),
- ?line true = Mem < mem(),
+ ?line T7 = erlang:start_timer(?SHORT_TIMEOUT+1, self(), Ref),
+ ?line T8 = erlang:send_after(?SHORT_TIMEOUT+1, self(), Ref),
+ ?line true = mem_larger_than(Mem),
?line true = is_integer(erlang:read_timer(T7)),
?line true = is_integer(erlang:read_timer(T8)),
?line receive {timeout, T7, Ref} -> ok end,
@@ -423,15 +470,12 @@ evil_recv_timeouts(TOs, N, M) ->
registered_process(doc) -> [];
registered_process(suite) -> [];
registered_process(Config) when is_list(Config) ->
- {skipped, "Test needs to be UPDATED for new timer implementation"}.
-
-registered_process_test(Config) when is_list(Config) ->
?line Mem = mem(),
%% Cancel
- ?line T1 = erlang:start_timer(500, ?MODULE, "hej"),
- ?line T2 = erlang:send_after(500, ?MODULE, "hej"),
+ ?line T1 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, "hej"),
+ ?line T2 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, "hej"),
?line undefined = whereis(?MODULE),
- ?line true = Mem < mem(),
+ ?line true = mem_larger_than(Mem),
?line true = is_integer(erlang:cancel_timer(T1)),
?line true = is_integer(erlang:cancel_timer(T2)),
?line false = erlang:read_timer(T1),
@@ -439,10 +483,10 @@ registered_process_test(Config) when is_list(Config) ->
?line Mem = mem(),
%% Timeout register after start
?line Ref1 = make_ref(),
- ?line T3 = erlang:start_timer(500, ?MODULE, Ref1),
- ?line T4 = erlang:send_after(500, ?MODULE, Ref1),
+ ?line T3 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, Ref1),
+ ?line T4 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, Ref1),
?line undefined = whereis(?MODULE),
- ?line true = Mem < mem(),
+ ?line true = mem_larger_than(Mem),
?line true = is_integer(erlang:read_timer(T3)),
?line true = is_integer(erlang:read_timer(T4)),
?line true = register(?MODULE, self()),
@@ -451,9 +495,9 @@ registered_process_test(Config) when is_list(Config) ->
?line Mem = mem(),
%% Timeout register before start
?line Ref2 = make_ref(),
- ?line T5 = erlang:start_timer(500, ?MODULE, Ref2),
- ?line T6 = erlang:send_after(500, ?MODULE, Ref2),
- ?line true = Mem < mem(),
+ ?line T5 = erlang:start_timer(?SHORT_TIMEOUT+1, ?MODULE, Ref2),
+ ?line T6 = erlang:send_after(?SHORT_TIMEOUT+1, ?MODULE, Ref2),
+ ?line true = mem_larger_than(Mem),
?line true = is_integer(erlang:read_timer(T5)),
?line true = is_integer(erlang:read_timer(T6)),
?line receive {timeout, T5, Ref2} -> ok end,
@@ -462,19 +506,135 @@ registered_process_test(Config) when is_list(Config) ->
?line true = unregister(?MODULE),
?line ok.
-mem() ->
- TSrvs = erts_internal:get_bif_timer_servers(),
- lists:foldl(fun (Tab, Sz) ->
- case lists:member(ets:info(Tab, owner), TSrvs) of
- true ->
- ets:info(Tab, memory) + Sz;
- false ->
- Sz
- end
- end,
- 0,
- ets:all())*erlang:system_info({wordsize,external}).
-
+same_time_yielding(Config) when is_list(Config) ->
+ Mem = mem(),
+ SchdlrsOnln = erlang:system_info(schedulers_online),
+ Tmo = erlang:monotonic_time(milli_seconds) + 3000,
+ Tmrs = lists:map(fun (I) ->
+ process_flag(scheduler, (I rem SchdlrsOnln) + 1),
+ erlang:start_timer(Tmo, self(), hej, [{abs, true}])
+ end,
+ lists:seq(1, (?TIMEOUT_YIELD_LIMIT*3+1)*SchdlrsOnln)),
+ true = mem_larger_than(Mem),
+ lists:foreach(fun (Tmr) -> receive {timeout, Tmr, hej} -> ok end end, Tmrs),
+ Done = erlang:monotonic_time(milli_seconds),
+ true = Done >= Tmo,
+ case erlang:system_info(build_type) of
+ opt -> true = Done < Tmo + 200;
+ _ -> true = Done < Tmo + 1000
+ end,
+ Mem = mem(),
+ ok.
+
+same_time_yielding_with_cancel(Config) when is_list(Config) ->
+ same_time_yielding_with_cancel_test(false, false).
+
+same_time_yielding_with_cancel_other(Config) when is_list(Config) ->
+ same_time_yielding_with_cancel_test(true, false).
+
+same_time_yielding_with_cancel_other_accessor(Config) when is_list(Config) ->
+ same_time_yielding_with_cancel_test(true, true).
+
+do_cancel_tmrs(Tmo, Tmrs, Tester) ->
+ BeginCancel = erlang:convert_time_unit(Tmo,
+ milli_seconds,
+ micro_seconds) - 100,
+ busy_wait_until(fun () ->
+ erlang:monotonic_time(micro_seconds) >= BeginCancel
+ end),
+ lists:foreach(fun (Tmr) ->
+ erlang:cancel_timer(Tmr,
+ [{async, true},
+ {info, true}])
+ end, Tmrs),
+ case Tester == self() of
+ true -> ok;
+ false -> forward_msgs(Tester)
+ end.
+
+same_time_yielding_with_cancel_test(Other, Accessor) ->
+ Mem = mem(),
+ SchdlrsOnln = erlang:system_info(schedulers_online),
+ Tmo = erlang:monotonic_time(milli_seconds) + 3000,
+ Tester = self(),
+ Cancelor = case Other of
+ false ->
+ Tester;
+ true ->
+ spawn(fun () ->
+ receive
+ {timers, Tmrs} ->
+ do_cancel_tmrs(Tmo, Tmrs, Tester)
+ end
+ end)
+ end,
+ Opts = case Accessor of
+ false -> [{abs, true}];
+ true -> [{accessor, Cancelor}, {abs, true}]
+ end,
+ Tmrs = lists:map(fun (I) ->
+ process_flag(scheduler, (I rem SchdlrsOnln) + 1),
+ erlang:start_timer(Tmo, self(), hej, Opts)
+ end,
+ lists:seq(1, (?TIMEOUT_YIELD_LIMIT*3+1)*SchdlrsOnln)),
+ true = mem_larger_than(Mem),
+ case Other of
+ false ->
+ do_cancel_tmrs(Tmo, Tmrs, Tester);
+ true ->
+ Cancelor ! {timers, Tmrs}
+ end,
+ {Tmos, Cncls} = lists:foldl(fun (Tmr, {T, C}) ->
+ receive
+ {timeout, Tmr, hej} ->
+ receive
+ {cancel_timer, Tmr, Info} ->
+ false = Info,
+ {T+1, C}
+ end;
+ {cancel_timer, Tmr, false} ->
+ receive
+ {timeout, Tmr, hej} ->
+ {T+1, C}
+ end;
+ {cancel_timer, Tmr, TimeLeft} ->
+ true = is_integer(TimeLeft),
+ {T, C+1}
+ end
+ end,
+ {0, 0},
+ Tmrs),
+ io:format("Timeouts: ~p Cancels: ~p~n", [Tmos, Cncls]),
+ Mem = mem(),
+ case Other of
+ true -> exit(Cancelor, bang);
+ false -> ok
+ end,
+ {comment,
+ "Timeouts: " ++ integer_to_list(Tmos) ++ " Cancels: "
+ ++ integer_to_list(Cncls)}.
+
+auto_cancel_yielding(Config) when is_list(Config) ->
+ Mem = mem(),
+ SchdlrsOnln = erlang:system_info(schedulers_online),
+ P = spawn(fun () ->
+ lists:foreach(
+ fun (I) ->
+ process_flag(scheduler, (I rem SchdlrsOnln)+1),
+ erlang:start_timer((1 bsl 28)+I*10, self(), hej)
+ end,
+ lists:seq(1,
+ ((?AUTO_CANCEL_YIELD_LIMIT*3+1)
+ *SchdlrsOnln))),
+ receive after infinity -> ok end
+ end),
+ true = mem_larger_than(Mem),
+ exit(P, bang),
+ wait_until(fun () -> process_is_cleaned_up(P) end),
+ receive after 1000 -> ok end,
+ Mem = mem(),
+ ok.
+
process_is_cleaned_up(P) when is_pid(P) ->
undefined == erts_debug:get_internal_state({process_status, P}).
@@ -484,6 +644,19 @@ wait_until(Pred) when is_function(Pred) ->
_ -> receive after 50 -> ok end, wait_until(Pred)
end.
+busy_wait_until(Pred) when is_function(Pred) ->
+ case catch Pred() of
+ true -> ok;
+ _ -> busy_wait_until(Pred)
+ end.
+
+forward_msgs(To) ->
+ receive
+ Msg ->
+ To ! Msg
+ end,
+ forward_msgs(To).
+
get(Time, Msg) ->
receive
Msg ->
@@ -566,5 +739,58 @@ type(X) when is_port(X) -> {port, node(X)};
type(X) when is_binary(X) -> binary;
type(X) when is_atom(X) -> atom;
type(_) -> unknown.
-
+
+mem_larger_than(no_fix_alloc) ->
+ true;
+mem_larger_than(Mem) ->
+ mem() > Mem.
+
+mem() ->
+ erts_debug:set_internal_state(wait, deallocations),
+ erts_debug:set_internal_state(wait, deallocations),
+ case mem_get() of
+ {-1, -1} -> no_fix_alloc;
+ {A, U} -> io:format("mem = ~p ~p~n", [A, U]), U
+ end.
+
+mem_get() ->
+ % Bif timer memory
+ Ref = make_ref(),
+ erlang:system_info({memory_internal, Ref, [fix_alloc]}),
+ mem_recv(erlang:system_info(schedulers), Ref, {0, 0}).
+
+mem_recv(0, _Ref, AU) ->
+ AU;
+mem_recv(N, Ref, AU) ->
+ receive
+ {Ref, _, IL} ->
+ mem_recv(N-1, Ref, mem_parse_ilists(IL, AU))
+ end.
+
+
+mem_parse_ilists([], AU) ->
+ AU;
+mem_parse_ilists([I|Is], AU) ->
+ mem_parse_ilists(Is, mem_parse_ilist(I, AU)).
+
+mem_parse_ilist({fix_alloc, false}, _) ->
+ {-1, -1};
+mem_parse_ilist({fix_alloc, _, IDL}, {A, U}) ->
+ case lists:keyfind(fix_types, 1, IDL) of
+ {fix_types, TL} ->
+ {ThisA, ThisU} = mem_get_btm_aus(TL, 0, 0),
+ {ThisA + A, ThisU + U};
+ {fix_types, Mask, TL} ->
+ {ThisA, ThisU} = mem_get_btm_aus(TL, 0, 0),
+ {(ThisA + A) band Mask , (ThisU + U) band Mask}
+ end.
+
+mem_get_btm_aus([], A, U) ->
+ {A, U};
+mem_get_btm_aus([{BtmType, BtmA, BtmU} | Types],
+ A, U) when BtmType == bif_timer;
+ BtmType == accessor_bif_timer ->
+ mem_get_btm_aus(Types, BtmA+A, BtmU+U);
+mem_get_btm_aus([_|Types], A, U) ->
+ mem_get_btm_aus(Types, A, U).
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index ce61199567..aa9d8ae4fe 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 6aea2c08e4..ba7878bd2c 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -425,19 +425,23 @@ call_on_load_function(_P1) ->
erlang:nif_error(undefined).
%% cancel_timer/1
--spec erlang:cancel_timer(TimerRef) -> Time | false when
+-spec erlang:cancel_timer(TimerRef) -> Result when
TimerRef :: reference(),
- Time :: non_neg_integer().
+ Time :: non_neg_integer(),
+ Result :: Time | false.
cancel_timer(_TimerRef) ->
erlang:nif_error(undefined).
%% cancel_timer/2
--spec erlang:cancel_timer(TimerRef, Options) -> Time | false | ok when
+-spec erlang:cancel_timer(TimerRef, Options) -> Result | ok when
TimerRef :: reference(),
- Option :: {async, boolean()} | {info, boolean()},
+ Async :: boolean(),
+ Info :: boolean(),
+ Option :: {async, Async} | {info, Info},
Options :: [Option],
- Time :: non_neg_integer().
+ Time :: non_neg_integer(),
+ Result :: Time | false.
cancel_timer(_TimerRef, _Options) ->
erlang:nif_error(undefined).
@@ -1478,17 +1482,22 @@ raise(_Class, _Reason, _Stacktrace) ->
erlang:nif_error(undefined).
%% read_timer/1
--spec erlang:read_timer(TimerRef) -> non_neg_integer() | false when
- TimerRef :: reference().
+-spec erlang:read_timer(TimerRef) -> Result when
+ TimerRef :: reference(),
+ Time :: non_neg_integer(),
+ Result :: Time | false.
read_timer(_TimerRef) ->
erlang:nif_error(undefined).
%% read_timer/2
--spec erlang:read_timer(TimerRef, Options) -> non_neg_integer() | false | ok when
+-spec erlang:read_timer(TimerRef, Options) -> Result | ok when
TimerRef :: reference(),
- Option :: {async, boolean()},
- Options :: [Option].
+ Async :: boolean(),
+ Option :: {async, Async},
+ Options :: [Option],
+ Time :: non_neg_integer(),
+ Result :: Time | false.
read_timer(_TimerRef, _Options) ->
erlang:nif_error(undefined).
@@ -1547,7 +1556,8 @@ send_after(_Time, _Dest, _Msg) ->
Dest :: pid() | atom(),
Msg :: term(),
Options :: [Option],
- Option :: {abs, boolean()} | {accessor, pid()},
+ Abs :: boolean(),
+ Option :: {abs, Abs}, %% | {accessor, Accessor} undocumented feature for now,
TimerRef :: reference().
send_after(_Time, _Dest, _Msg, _Options) ->
@@ -1634,7 +1644,8 @@ start_timer(_Time, _Dest, _Msg) ->
Dest :: pid() | atom(),
Msg :: term(),
Options :: [Option],
- Option :: {abs, boolean()} | {accessor, pid()},
+ Abs :: boolean(),
+ Option :: {abs, Abs}, %% | {accessor, Accessor} undocumented feature for now,
TimerRef :: reference().
start_timer(_Time, _Dest, _Msg, _Options) ->
@@ -3589,7 +3600,11 @@ blocks_size([], Acc) ->
get_fix_proc([{ProcType, A1, U1}| Rest], {A0, U0}) when ProcType == proc;
ProcType == monitor_sh;
ProcType == nlink_sh;
- ProcType == msg_ref ->
+ ProcType == msg_ref;
+ ProcType == ll_ptimer;
+ ProcType == hl_ptimer;
+ ProcType == bif_timer;
+ ProcType == accessor_bif_timer ->
get_fix_proc(Rest, {A0+A1, U0+U1});
get_fix_proc([_|Rest], Acc) ->
get_fix_proc(Rest, Acc);