1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(ref_SUITE).
22
23-export([all/0, suite/0]).
24-export([wrap_1/1]).
25-export([compare_list/1, compare_ets/1]).
26-export([internal_size/1, external_size/1]).
27
28-export([init_per_testcase/2, end_per_testcase/2]).
29
30-export([loop_ref/1]).
31
32-include_lib("common_test/include/ct.hrl").
33
34suite() ->
35    [{ct_hooks,[ts_install_cth]},
36     {timetrap, {minutes, 2}}].
37
38init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
39    [{testcase, Func}|Config].
40
41end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
42    ok.
43
44all() ->
45    [wrap_1, compare_list, compare_ets, internal_size, external_size].
46
47%% Check that refs don't wrap around easily.
48wrap_1(Config) when is_list(Config) ->
49    spawn_link(?MODULE, loop_ref, [self()]),
50    receive
51        done ->
52            ct:fail(wrapfast)
53    after 30000 ->
54              ok
55    end,
56    ok.
57
58loop_ref(Parent) ->
59    Ref0 = make_ref(),
60    loop_ref(Ref0, first, 0),
61    Parent ! done.
62
63loop_ref(R, R, _) -> ok;
64loop_ref(R0, _, N) ->
65    loop_ref(R0, make_ref(), N+1).
66
67%% Check that ref ordering works
68compare_list(Config) when is_list(Config) ->
69    %% Although this test uses external refs, it would apply the same to plain refs
70    ExtRef1 = <<131,114,0,3,100,0,3,110,64,98,3, 0,0,173,156, 0,216,0,4, 0,0,0,0>>,
71    ExtRef2 = <<131,114,0,3,100,0,3,110,64,98,3, 0,1,31,27,   129,4,0,1, 0,0,0,0>>,
72
73    Ref1 = binary_to_term(ExtRef1), %% #Ref<n@b.0.14155780.44444>
74    Ref2 = binary_to_term(ExtRef2), %% #Ref<n@b.0.2164523009.73499>
75    OrderedList = [Ref1, Ref2],
76    OrderedList = lists:sort(OrderedList),
77    ok.
78
79%% This is the scarier case since it makes terms "invisible" in ets or Mnesia
80%% (the underlying fault cause is the same as compare_list/1)
81compare_ets(Config) when is_list(Config) ->
82    W2s = [610350147,899574699,2994196869,686384822,2397690439, 923302211],
83    ExtRefBase = <<131,114,0,3,100,0,3,110,64,98,3>>,
84    ExtRefs = [<<ExtRefBase/binary, 1:32, W2:32, 0:32>> || W2 <- W2s],
85    Refs = [binary_to_term(Bin) || Bin <- ExtRefs],
86
87    Ets = ets:new(refbug, [ordered_set]),
88    ets:insert(Ets, [{Ref,Ref} || Ref <- Refs]),
89    0 = length([R || R <- ets:tab2list(Ets), ets:lookup(Ets, element(1,R)) == []]),
90    ok.
91
92internal_size(Config) when is_list(Config) ->
93    %% Verifies that the range of heap size used for internal references
94    %% matches what the documentation say in the advanced chapter of the
95    %% efficiency guide. Note that the values in the efficiency guide
96    %% also add the word referencing the heap structure.
97
98    %% Ordinary internal reference
99    ORef = check_internal_size(make_ref()),
100    io:format("ORef = ~p~n", [ORef]),
101
102    %% Internal pid reference (reference containing a pid)
103    PRef = check_internal_size(alias()),
104    io:format("PRef = ~p~n", [PRef]),
105
106    %% Internal magic reference
107    MRef = check_internal_size(ets:new(blipp, [])),
108    io:format("MRef = ~p~n", [MRef]),
109
110    ok.
111
112check_internal_size(Ref) when is_reference(Ref), node(Ref) == node() ->
113    case erlang:system_info(wordsize) of
114        4 ->
115            case erts_debug:size(Ref) of
116                Sz when 3 =< Sz, Sz =< 6 ->
117                    Sz;
118                Sz ->
119                    error({internal_ref_size_out_of_range, Sz})
120            end;
121        8 ->
122            case erts_debug:size(Ref) of
123                Sz when 3 =< Sz, Sz =< 5 ->
124                    Sz;
125                Sz ->
126                    error({internal_ref_size_out_of_range, Sz})
127            end
128    end.
129
130external_size(Config) when is_list(Config) ->
131    %% Verifies that the range of heap size used for external references
132    %% matches what the documentation say in the advanced chapter of the
133    %% efficiency guide. Note that the values in the efficiency guide
134    %% also add the word referencing the heap structure.
135    {ok, Node} = start_node(Config),
136
137    %% Ordinary external reference
138    ORef = check_external_size(erpc:call(Node, fun () -> make_ref() end)),
139    io:format("ORef = ~p~n", [ORef]),
140
141    %% External pid reference (reference containing a pid) (nothing produce
142    %% this yet, but we need to handle it)
143    PRef = check_external_size(erts_test_utils:mk_ext_ref({Node, 4711},
144                                                          [1, 2, 3, 4, 5])),
145    io:format("PRef = ~p~n", [PRef]),
146
147
148    stop_node(Node),
149    ok.
150
151check_external_size(Ref) when is_reference(Ref) ->
152    case erlang:system_info(wordsize) of
153        4 ->
154            case erts_debug:size(Ref) of
155                Sz when 6 =< Sz, Sz =< 8 ->
156                    Sz;
157                Sz ->
158                    error({internal_ref_size_out_of_range, Sz})
159            end;
160        8 ->
161            case erts_debug:size(Ref) of
162                Sz when 5 =< Sz, Sz =< 6 ->
163                    Sz;
164                Sz ->
165                    error({internal_ref_size_out_of_range, Sz})
166            end
167    end.
168
169%% Internal stuff...
170
171make_nodename(Config) when is_list(Config) ->
172    list_to_atom(atom_to_list(?MODULE)
173                 ++ "-"
174                 ++ atom_to_list(proplists:get_value(testcase, Config))
175                 ++ "-"
176                 ++ integer_to_list(erlang:system_time(second))
177                 ++ "-"
178                 ++ integer_to_list(erlang:unique_integer([positive]))).
179
180start_node(Config) ->
181    start_node(Config, "").
182
183start_node(Config, Args) when is_list(Config) ->
184    Pa = filename:dirname(code:which(?MODULE)),
185    Name = make_nodename(Config),
186    test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
187
188stop_node(Node) ->
189    test_server:stop_node(Node).
190