diff options
author | Björn Gustavsson <[email protected]> | 2018-12-06 13:07:42 +0100 |
---|---|---|
committer | Björn Gustavsson <[email protected]> | 2018-12-18 11:40:07 +0100 |
commit | 1ea57e5498d92b730644cb37b659d16e51f56b4d (patch) | |
tree | 3464aa1912825e297d0609abe0af8b3eeb1af315 /erts/emulator/test | |
parent | 472b0669788e155f28851999b4e60bf8302ca2d5 (diff) | |
download | otp-1ea57e5498d92b730644cb37b659d16e51f56b4d.tar.gz otp-1ea57e5498d92b730644cb37b659d16e51f56b4d.tar.bz2 otp-1ea57e5498d92b730644cb37b659d16e51f56b4d.zip |
Make length/1 yielding
The guard BIF `length/1` would calculate the length of the list in one
go without yielding, even if the list was were long. To make it even
worse, the call to `length/1` would only cost a single reduction.
This commit reimplements `length/1` so that it eats a number of
reductions proportional to the length of the list, and yields if the
available reductions run out.
Diffstat (limited to 'erts/emulator/test')
-rw-r--r-- | erts/emulator/test/bif_SUITE.erl | 54 |
1 files changed, 51 insertions, 3 deletions
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index 9e7bcd5255..3eedf2f6a6 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -37,7 +37,8 @@ group_leader_prio/1, group_leader_prio_dirty/1, is_process_alive/1, process_info_blast/1, - os_env_case_sensitivity/1]). + os_env_case_sensitivity/1, + test_length/1]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -52,7 +53,8 @@ all() -> erl_crash_dump_bytes, min_max, erlang_halt, is_builtin, error_stacktrace, error_stacktrace_during_call_trace, group_leader_prio, group_leader_prio_dirty, - is_process_alive, process_info_blast, os_env_case_sensitivity]. + is_process_alive, process_info_blast, os_env_case_sensitivity, + test_length]. %% Uses erlang:display to test that erts_printf does not do deep recursion display(Config) when is_list(Config) -> @@ -1181,7 +1183,53 @@ consume_msgs() -> after 0 -> ok end. - + +%% Test that length/1 returns the correct result after trapping, and +%% also that the argument is correct in the stacktrace for a badarg +%% exception. + +test_length(_Config) -> + {Start,Inc} = case test_server:timetrap_scale_factor() of + 1 -> {16*4000,3977}; + _ -> {100,1} + end, + Good = lists:reverse(lists:seq(1, Start)), + Bad = Good ++ [bad|cons], + test_length(Start, 10*Start, Inc, Good, Bad), + + %% Test that calling length/1 from a match spec works. + MsList = lists:seq(1, 2*Start), + MsInput = [{tag,Good},{tag,MsList}], + Ms0 = [{{tag,'$1'},[{'>',{length,'$1'},Start}],['$1']}], + Ms = ets:match_spec_compile(Ms0), + [MsList] = ets:match_spec_run(MsInput, Ms), + ok. + +test_length(I, N, Inc, Good, Bad) when I < N -> + Length = id(length), + I = length(Good), + I = erlang:Length(Good), + + %% Test length/1 in guards. + if + length(Good) =:= I -> + ok + end, + if + length(Bad) =:= I -> + error(should_fail); + true -> + ok + end, + + {'EXIT',{badarg,[{erlang,length,[[I|_]],_}|_]}} = (catch length(Bad)), + {'EXIT',{badarg,[{erlang,length,[[I|_]],_}|_]}} = (catch erlang:Length(Bad)), + IncSeq = lists:seq(I + 1, I + Inc), + test_length(I+Inc, N, Inc, + lists:reverse(IncSeq, Good), + lists:reverse(IncSeq, Bad)); +test_length(_, _, _, _, _) -> ok. + %% helpers id(I) -> I. |