aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator
diff options
context:
space:
mode:
authorRickard Green <[email protected]>2015-05-12 17:33:03 +0200
committerRickard Green <[email protected]>2015-05-12 17:33:03 +0200
commit4030cfbb8de3445e80b3dcb3a1a45d0c6bac6483 (patch)
tree0d1208936003936433ba3793ee5b677dbc9d1697 /erts/emulator
parentff2400d7b5b1b090e128bc537d7bdd94c6aad97e (diff)
parent849a2ed2db2bd54422ec9284468f80cc86978974 (diff)
downloadotp-4030cfbb8de3445e80b3dcb3a1a45d0c6bac6483.tar.gz
otp-4030cfbb8de3445e80b3dcb3a1a45d0c6bac6483.tar.bz2
otp-4030cfbb8de3445e80b3dcb3a1a45d0c6bac6483.zip
Merge branch 'rickard/timer-optimization/OTP-12650'
* rickard/timer-optimization/OTP-12650: Timer fixes, documentation, and test cases Conflicts: erts/emulator/beam/erl_hl_timer.c
Diffstat (limited to 'erts/emulator')
-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
6 files changed, 422 insertions, 126 deletions
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index b832626e7b..dcae5509ec 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 cf92ded415..51cd843935 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);
@@ -1152,6 +1147,7 @@ hlt_bif_timer_timeout(ErtsHLTimer *tmr, Uint32 roflgs)
erts_queue_message(proc, &proc_locks, tmr->btm.bp,
tmr->btm.message, NIL);
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) {
@@ -1165,6 +1161,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
@@ -1682,6 +1680,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);
}
@@ -1714,6 +1714,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;
@@ -1827,18 +1830,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;
@@ -1864,7 +1874,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,
@@ -1975,8 +1985,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;
@@ -1985,11 +1993,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
@@ -2004,7 +2008,7 @@ try_access_sched_remote_btm(ErtsSchedulerData *esdp,
msg = TUPLE3(hp, tag, tref, res);
- erts_queue_message(c_p, &proc_locks, bp, msg, NIL);
+ erts_queue_message(c_p, &proc_locks, NULL, msg, NIL);
proc_locks &= ~ERTS_PROC_LOCK_MAIN;
if (proc_locks)
@@ -2149,7 +2153,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)
@@ -2218,13 +2222,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);
}
@@ -2398,7 +2408,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);
@@ -2416,7 +2426,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);
@@ -2430,7 +2440,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);
@@ -2448,7 +2458,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).