1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2017-2020. 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(persistent_term_SUITE).
22-include_lib("common_test/include/ct.hrl").
23
24-export([all/0,suite/0,init_per_suite/1,end_per_suite/1,
25	 init_per_testcase/2, end_per_testcase/2,
26	 basic/1,purging/1,sharing/1,get_trapping/1,
27         destruction/1,
28         get_all_race/1,
29         info/1,info_trapping/1,killed_while_trapping/1,
30         off_heap_values/1,keys/1,collisions/1,
31         init_restart/1, put_erase_trapping/1,
32         killed_while_trapping_put/1,
33         killed_while_trapping_erase/1,
34         error_info/1,
35	 whole_message/1,
36         shared_magic_ref/1,
37	 non_message_signal/1]).
38
39%%
40-export([test_init_restart_cmd/1]).
41
42%% Test writing helper
43-export([find_colliding_keys/0]).
44
45
46suite() ->
47    [{ct_hooks,[ts_install_cth]},
48     {timetrap,{minutes,10}}].
49
50all() ->
51    [basic,purging,sharing,get_trapping,info,info_trapping,
52     destruction,
53     get_all_race,
54     killed_while_trapping,off_heap_values,keys,collisions,
55     init_restart, put_erase_trapping, killed_while_trapping_put,
56     killed_while_trapping_erase,
57     error_info,
58     whole_message,
59     shared_magic_ref,
60     non_message_signal].
61
62init_per_suite(Config) ->
63    erts_debug:set_internal_state(available_internal_state, true),
64    %% Put a term in the dict so that we know that the testcases handle
65    %% stray terms left by stdlib or other test suites.
66    persistent_term:put(init_per_suite, {?MODULE}),
67    Config.
68
69end_per_suite(Config) ->
70    persistent_term:erase(init_per_suite),
71    erts_debug:set_internal_state(available_internal_state, false),
72    Config.
73
74init_per_testcase(_, Config) ->
75    Config.
76
77end_per_testcase(_, _Config) ->
78    ok;
79end_per_testcase(get_all_race, _Config) ->
80    get_all_race_cleanup(),
81    ok.
82
83basic(_Config) ->
84    Chk = chk(),
85    N = 777,
86    Seq = lists:seq(1, N),
87    par(2, N, Seq, Chk),
88    seq(3, Seq, Chk),
89    seq(3, Seq, Chk),                                %Same values.
90    _ = [begin
91             Key = {?MODULE,{key,I}},
92             true = persistent_term:erase(Key),
93             false = persistent_term:erase(Key),
94             {'EXIT',{badarg,_}} = (catch persistent_term:get(Key)),
95             {not_present,Key} = persistent_term:get(Key, {not_present,Key})
96         end || I <- Seq],
97    [] = [P || {{?MODULE,_},_}=P <- pget(Chk)],
98    chk(Chk).
99
100par(C, N, Seq, Chk) ->
101    _ = [spawn_link(fun() ->
102                            ok = persistent_term:put({?MODULE,{key,I}},
103                                                     {value,C*I})
104                    end) || I <- Seq],
105    Result = wait(N, Chk),
106    _ = [begin
107             Double = C*I,
108             {{?MODULE,{key,I}},{value,Double}} = Res
109         end || {I,Res} <- lists:zip(Seq, Result)],
110    ok.
111
112seq(C, Seq, Chk) ->
113    _ = [ok = persistent_term:put({?MODULE,{key,I}}, {value,C*I}) ||
114            I <- Seq],
115    All = pget(Chk),
116    All = [P || {{?MODULE,_},_}=P <- All],
117    All = [{Key,persistent_term:get(Key)} || {Key,_} <- All],
118    Result = lists:sort(All),
119    _ = [begin
120             Double = C*I,
121             {{?MODULE,{key,I}},{value,Double}} = Res
122         end || {I,Res} <- lists:zip(Seq, Result)],
123    ok.
124
125wait(N, Chk) ->
126    All = [P || {{?MODULE,_},_}=P <- pget(Chk)],
127    case length(All) of
128        N ->
129            All = [{Key,persistent_term:get(Key)} || {Key,_} <- All],
130            lists:sort(All);
131        _ ->
132            receive after 10 -> ok end,
133            wait(N, Chk)
134    end.
135
136%% Make sure that terms that have been erased are copied into all
137%% processes that still hold a pointer to them.
138
139purging(_Config) ->
140    Chk = chk(),
141    do_purging(fun(K) -> persistent_term:put(K, {?MODULE,new}) end,
142               replaced),
143    do_purging(fun persistent_term:erase/1, erased),
144    chk(Chk).
145
146do_purging(Eraser, Type) ->
147    Parent = self(),
148    Key = {?MODULE,?FUNCTION_NAME},
149    ok = persistent_term:put(Key, {term,[<<"abc",0:777/unit:8>>]}),
150    Ps0 = [spawn_monitor(fun() -> purging_tester(Parent, Key) end) ||
151              _ <- lists:seq(1, 50)],
152    Ps = maps:from_list(Ps0),
153    purging_recv(gotten, Ps),
154    Eraser(Key),
155    _ = [P ! {Parent,Type} || P <- maps:keys(Ps)],
156    purging_wait(Ps).
157
158purging_recv(Tag, Ps) when map_size(Ps) > 0 ->
159    receive
160        {Pid,Tag} ->
161            true = is_map_key(Pid, Ps),
162            purging_recv(Tag, maps:remove(Pid, Ps))
163    end;
164purging_recv(_, _) -> ok.
165
166purging_wait(Ps) when map_size(Ps) > 0 ->
167    receive
168        {'DOWN',Ref,process,Pid,Reason} ->
169            normal = Reason,
170            Ref = map_get(Pid, Ps),
171            purging_wait(maps:remove(Pid, Ps))
172    end;
173purging_wait(_) -> ok.
174
175purging_tester(Parent, Key) ->
176    Term = persistent_term:get(Key),
177    purging_check_term(Term),
178    0 = erts_debug:size_shared(Term),
179    Parent ! {self(),gotten},
180    receive
181        {Parent,erased} ->
182            {'EXIT',{badarg,_}} = (catch persistent_term:get(Key)),
183            purging_tester_1(Term, 1);
184        {Parent,replaced} ->
185            {?MODULE,new} = persistent_term:get(Key),
186            purging_tester_1(Term, 1)
187    end.
188
189%% Wait for the term to be copied into this process.
190purging_tester_1(Term, Timeout) ->
191    purging_check_term(Term),
192    receive after Timeout -> ok end,
193    case erts_debug:size_shared(Term) of
194        0 ->
195            case Timeout of
196                1000 ->
197                    flush_later_ops(),
198                    purging_tester_1(Term, 1);
199                _ ->
200                    purging_tester_1(Term, Timeout*10)
201            end;
202        Size ->
203            %% The term has been copied into this process.
204            purging_check_term(Term),
205            Size = erts_debug:size(Term)
206    end.
207
208purging_check_term({term,[<<"abc",0:777/unit:8>>]}) ->
209    ok.
210
211%% Make sure terms are really deallocated when overwritten or erased.
212destruction(Config) ->
213    ok = erts_test_destructor:init(Config),
214
215    NKeys = 100,
216    Keys = lists:seq(0,NKeys-1),
217    [begin
218         V = erts_test_destructor:send(self(), K),
219         persistent_term:put({?MODULE,K}, V)
220     end
221     || K <- Keys],
222
223    %% Erase or overwrite all keys in "random" order.
224    lists:foldl(fun(_, K) ->
225                        case erlang:phash2(K) band 1 of
226                            0 ->
227                                %%io:format("erase key ~p\n", [K]),
228                                persistent_term:erase({?MODULE,K});
229                            1 ->
230                                %%io:format("replace key ~p\n", [K]),
231                                persistent_term:put({?MODULE,K}, value)
232                        end,
233                        (K + 13) rem NKeys
234                end,
235                17, Keys),
236
237    destruction_1(Keys).
238
239destruction_1(Keys) ->
240    erlang:garbage_collect(),
241
242    %% Receive all destruction messages
243    MsgLst = destruction_recv(length(Keys), [], 2),
244    ok = case lists:sort(MsgLst) of
245             Keys ->
246                 ok;
247             _ ->
248                 io:format("GOT ~p\n", [MsgLst]),
249                 io:format("MISSING ~p\n", [Keys -- MsgLst]),
250                 error
251         end,
252
253    %% Cleanup all remaining
254    [persistent_term:erase({?MODULE,K}) || K <- Keys],
255    ok.
256
257destruction_recv(0, Acc, _) ->
258    Acc;
259destruction_recv(N, Acc, Flush) ->
260    receive M ->
261            destruction_recv(N-1, [M | Acc], Flush)
262    after 1000 ->
263            io:format("TIMEOUT. Missing ~p destruction messages.\n", [N]),
264            case Flush of
265                0 ->
266                    Acc;
267                _ ->
268                    io:format("Try flush last literal area cleanup...\n"),
269                    flush_later_ops(),
270                    destruction_recv(N, Acc, Flush-1)
271            end
272    end.
273
274%% Both persistent_term itself and erts_literal_are_collector use
275%% erts_schedule_thr_prgr_later_cleanup_op() to schedule purge and deallocation
276%% of literals. To avoid waiting forever on sleeping schedulers we flush
277%% all later ops to make these cleanup jobs go through.
278flush_later_ops() ->
279    try
280        erts_debug:set_internal_state(wait, thread_progress)
281    catch
282        error:system_limit ->
283            ok % already ongoing; called by other process
284    end,
285    ok.
286
287
288%% Test that sharing is preserved when storing terms.
289
290sharing(_Config) ->
291    Chk = chk(),
292    Depth = 10,
293    Size = 2*Depth,
294    Shared = lists:foldl(fun(_, A) -> [A|A] end,
295                         [], lists:seq(1, Depth)),
296    Size = erts_debug:size(Shared),
297    Key = {?MODULE,?FUNCTION_NAME},
298    ok = persistent_term:put(Key, Shared),
299    SharedStored = persistent_term:get(Key),
300    Size = erts_debug:size(SharedStored),
301    0 = erts_debug:size_shared(SharedStored),
302
303    {Pid,Ref} = spawn_monitor(fun() ->
304                                      Term = persistent_term:get(Key),
305                                      Size = erts_debug:size(Term),
306                                      0 = erts_debug:size_shared(Term),
307                                      true = Term =:= SharedStored
308                              end),
309    receive
310        {'DOWN',Ref,process,Pid,normal} ->
311            true = persistent_term:erase(Key),
312            Size = erts_debug:size(SharedStored),
313            chk(Chk)
314    end.
315
316%% Test trapping of persistent_term:get/0.
317
318get_trapping(_Config) ->
319    Chk = chk(),
320
321    %% Assume that the get/0 traps after 4000 iterations
322    %% in a non-debug emulator.
323    N = case test_server:timetrap_scale_factor() of
324            1 -> 10000;
325            _ -> 1000
326        end,
327    spawn_link(fun() -> get_trapping_create(N) end),
328    All = do_get_trapping(N, [], Chk),
329    N = get_trapping_check_result(lists:sort(All), 1),
330    erlang:garbage_collect(),
331    get_trapping_erase(N),
332    chk(Chk).
333
334do_get_trapping(N, Prev, Chk) ->
335    case pget(Chk) of
336        Prev when length(Prev) >= N ->
337            All = [P || {{?MODULE,{get_trapping,_}},_}=P <- Prev],
338            case length(All) of
339                N -> All;
340                _ -> do_get_trapping(N, Prev, Chk)
341            end;
342        New ->
343            receive after 1 -> ok end,
344            do_get_trapping(N, New, Chk)
345    end.
346
347get_trapping_create(0) ->
348    ok;
349get_trapping_create(N) ->
350    ok = persistent_term:put({?MODULE,{get_trapping,N}}, N),
351    get_trapping_create(N-1).
352
353get_trapping_check_result([{{?MODULE,{get_trapping,N}},N}|T], N) ->
354    get_trapping_check_result(T, N+1);
355get_trapping_check_result([], N) -> N-1.
356
357get_trapping_erase(0) ->
358    ok;
359get_trapping_erase(N) ->
360    true = persistent_term:erase({?MODULE,{get_trapping,N}}),
361    get_trapping_erase(N-1).
362
363%% Test retrieving information about persistent terms.
364
365info(_Config) ->
366    Chk = chk(),
367
368    %% White box test of info/0.
369    N = 100,
370    try
371        Overhead = info_literal_area_overhead(),
372        io:format("Overhead = ~p\n", [Overhead]),
373        info_wb(N, Overhead, info_info())
374    after
375        _ = [_ = persistent_term:erase({?MODULE,I}) ||
376                I <- lists:seq(1, N)]
377    end,
378
379    chk(Chk).
380
381%% White box test of persistent_term:info/0. We take into account
382%% that there might already exist persistent terms (created by the
383%% OTP standard libraries), but we assume that they are not
384%% changed during the execution of this test case.
385
386info_wb(0, _, _) ->
387    ok;
388info_wb(N, Overhead, {BaseCount,BaseMemory}) ->
389    Key = {?MODULE,N},
390    Value = lists:seq(1, N),
391    ok = persistent_term:put(Key, Value),
392
393    %% Calculate the extra memory needed for this term.
394    WordSize = erlang:system_info(wordsize),
395    ExtraMemory = Overhead + 2 * N * WordSize,
396
397    %% Call persistent_term:info/0.
398    {Count,Memory} = info_info(),
399
400    %% There should be one more persistent term.
401    Count = BaseCount + 1,
402
403    %% Verify that the amount of memory is correct.
404    case BaseMemory + ExtraMemory of
405        Memory ->
406            %% Exactly right. The size of the hash table was not changed.
407            ok;
408        Expected ->
409            %% The size of the hash table has been doubled to avoid filling
410            %% the table to more than 50 percent. The previous number
411            %% of entries must have been exactly half the size of the
412            %% hash table. The expected number of extra words added by
413            %% the resizing will be twice that number.
414            ExtraWords = BaseCount * 2,
415            true = ExtraWords * WordSize =:= (Memory - Expected)
416    end,
417    info_wb(N-1, Overhead, {Count,Memory}).
418
419info_info() ->
420    #{count:=Count,memory:=Memory} = persistent_term:info(),
421    true = is_integer(Count) andalso Count >= 0,
422    true = is_integer(Memory) andalso Memory >= 0,
423    {Count,Memory}.
424
425%% Calculate the number of extra bytes needed for storing each term in
426%% the literal, assuming that the key is a tuple of size 2 with
427%% immediate elements. The calculated number is the size of the
428%% ErtsLiteralArea struct excluding the storage for the literal term
429%% itself.
430
431info_literal_area_overhead() ->
432    Key1 = {?MODULE,1},
433    Key2 = {?MODULE,2},
434    #{memory:=Mem0} = persistent_term:info(),
435    ok = persistent_term:put(Key1, literal),
436    #{memory:=Mem1} = persistent_term:info(),
437    ok = persistent_term:put(Key2, literal),
438    #{memory:=Mem2} = persistent_term:info(),
439    true = persistent_term:erase(Key1),
440    true = persistent_term:erase(Key2),
441
442    %% The size of the hash table may have doubled when inserting
443    %% one of the keys. To avoiding counting the change in the hash
444    %% table size, take the smaller size increase.
445    min(Mem2-Mem1, Mem1-Mem0).
446
447%% Test trapping of persistent_term:info/0.
448
449info_trapping(_Config) ->
450    Chk = chk(),
451
452    %% Assume that the info/0 traps after 4000 iterations
453    %% in a non-debug emulator.
454    N = case test_server:timetrap_scale_factor() of
455            1 -> 10000;
456            _ -> 1000
457        end,
458    spawn_link(fun() -> info_trapping_create(N) end),
459    All = do_info_trapping(N, 0, Chk),
460    N = info_trapping_check_result(lists:sort(All), 1),
461    erlang:garbage_collect(),
462    info_trapping_erase(N),
463    chk(Chk).
464
465do_info_trapping(N, PrevMem, Chk) ->
466    case info_info() of
467        {M,Mem} when M >= N ->
468            true = Mem >= PrevMem,
469            All = [P || {{?MODULE,{info_trapping,_}},_}=P <- pget(Chk)],
470            case length(All) of
471                N -> All;
472                _ -> do_info_trapping(N, PrevMem, Chk)
473            end;
474        {_,Mem} ->
475            true = Mem >= PrevMem,
476            receive after 1 -> ok end,
477            do_info_trapping(N, Mem, Chk)
478    end.
479
480info_trapping_create(0) ->
481    ok;
482info_trapping_create(N) ->
483    ok = persistent_term:put({?MODULE,{info_trapping,N}}, N),
484    info_trapping_create(N-1).
485
486info_trapping_check_result([{{?MODULE,{info_trapping,N}},N}|T], N) ->
487    info_trapping_check_result(T, N+1);
488info_trapping_check_result([], N) -> N-1.
489
490info_trapping_erase(0) ->
491    ok;
492info_trapping_erase(N) ->
493    true = persistent_term:erase({?MODULE,{info_trapping,N}}),
494    info_trapping_erase(N-1).
495
496%% Test that hash tables are deallocated if a process running
497%% persistent_term:get/0 is killed.
498
499killed_while_trapping(_Config) ->
500    Chk = chk(),
501    N = case test_server:timetrap_scale_factor() of
502            1 -> 20000;
503            _ -> 2000
504        end,
505    kwt_put(N),
506    kwt_spawn(10),
507    kwt_erase(N),
508    chk(Chk).
509
510kwt_put(0) ->
511    ok;
512kwt_put(N) ->
513    ok = persistent_term:put({?MODULE,{kwt,N}}, N),
514    kwt_put(N-1).
515
516kwt_spawn(0) ->
517    ok;
518kwt_spawn(N) ->
519    Pids = [spawn(fun kwt_getter/0) || _ <- lists:seq(1, 20)],
520    erlang:yield(),
521    _ = [exit(Pid, kill) || Pid <- Pids],
522    kwt_spawn(N-1).
523
524kwt_getter() ->
525    _ = persistent_term:get(),
526    kwt_getter().
527
528kwt_erase(0) ->
529    ok;
530kwt_erase(N) ->
531    true = persistent_term:erase({?MODULE,{kwt,N}}),
532    kwt_erase(N-1).
533
534%% Test storing off heap values (such as ref-counted binaries).
535
536off_heap_values(_Config) ->
537    Chk = chk(),
538    Key = {?MODULE,?FUNCTION_NAME},
539    Val = {a,list_to_binary(lists:seq(0, 255)),make_ref(),fun() -> ok end},
540    ok = persistent_term:put(Key, Val),
541    FetchedVal = persistent_term:get(Key),
542    Val = FetchedVal,
543    true = persistent_term:erase(Key),
544    off_heap_values_wait(FetchedVal, Val),
545    chk(Chk).
546
547off_heap_values_wait(FetchedVal, Val) ->
548    case erts_debug:size_shared(FetchedVal) of
549        0 ->
550            Val = FetchedVal,
551            ok;
552        _ ->
553            erlang:yield(),
554            off_heap_values_wait(FetchedVal, Val)
555    end.
556
557%% Test some more data types as keys. Use the module name as a key
558%% to minimize the risk of collision with any key used
559%% by the OTP libraries.
560
561keys(_Config) ->
562    Chk = chk(),
563    do_key(?MODULE),
564    do_key([?MODULE]),
565    do_key(?MODULE_STRING),
566    do_key(list_to_binary(?MODULE_STRING)),
567    chk(Chk).
568
569do_key(Key) ->
570    Val = term_to_binary(Key),
571    ok = persistent_term:put(Key, Val),
572    StoredVal = persistent_term:get(Key),
573    Val = StoredVal,
574    true = persistent_term:erase(Key).
575
576%% Create persistent terms with keys that are known to collide.
577%% Delete them in random order, making sure that all others
578%% terms can still be found.
579
580collisions(_Config) ->
581    Chk = chk(),
582
583    %% Create persistent terms with random keys.
584    Keys = lists:flatten(colliding_keys()),
585    Kvs = [{K,rand:uniform(1000)} || K <- Keys],
586    _ = [ok = persistent_term:put(K, V) || {K,V} <- Kvs],
587    _ = [V = persistent_term:get(K) || {K,V} <- Kvs],
588
589    %% Now delete the persistent terms in random order.
590    collisions_delete(lists:keysort(2, Kvs), Chk),
591
592    chk(Chk).
593
594collisions_delete([{Key,Val}|Kvs], Chk) ->
595    Val = persistent_term:get(Key),
596    true = persistent_term:erase(Key),
597    true = lists:sort(pget(Chk)) =:= lists:sort(Kvs),
598    _ = [V = persistent_term:get(K) || {K,V} <- Kvs],
599    collisions_delete(Kvs, Chk);
600collisions_delete([], _) ->
601    ok.
602
603colliding_keys() ->
604    %% Collisions found by find_colliding_keys() below
605    L = [[77674392,148027],
606	 [103370644,950908],
607	 [106444046,870178],
608	 [22217246,735880],
609	 [18088843,694607],
610	 [63426007,612179],
611	 [117354942,906431],
612	 [121434305,94282311,816072],
613	 [118441466,93873772,783366],
614	 [124338174,1414801,123089],
615	 [20240282,17113486,923647],
616	 [126495528,61463488,164994],
617	 [125341723,5729072,445539],
618	 [127450932,80442669,348245],
619	 [123354692,85724182,14241288,180793],
620	 [99159367,65959274,61680971,289939],
621	 [107637580,104512101,62639807,181644],
622	 [139547511,51654420,2062545,151944],
623	 [88078274,73031465,53388204,428872],
624	 [141314238,75761379,55699508,861797],
625	 [88045216,59272943,21030492,180903]],
626
627    %% Verify that the keys still collide (this will fail if the
628    %% internal hash function has been changed).
629    case erlang:system_info(wordsize) of
630        8 ->
631            verify_colliding_keys(L);
632        4 ->
633            %% Not guaranteed to collide on a 32-bit system.
634            ok
635    end,
636
637    L.
638
639verify_colliding_keys([[K|Ks]|Gs]) ->
640    Hash = internal_hash(K),
641    [Hash] = lists:usort([internal_hash(Key) || Key <- Ks]),
642    verify_colliding_keys(Gs);
643verify_colliding_keys([]) ->
644    ok.
645
646internal_hash(Term) ->
647    erts_debug:get_internal_state({internal_hash,Term}).
648
649%% Use this function to (re)generate the list in colliding_keys/0
650find_colliding_keys() ->
651    MaxCollSz = 4,
652    OfEachSz = 7,
653    erts_debug:set_internal_state(available_internal_state, true),
654    MaxInserts = 1 bsl 20,
655    T = ets:new(x, [set, private]),
656    ok = fck_loop_1(T, 1, MaxInserts, MaxCollSz, OfEachSz),
657    fck_collect(T, MaxCollSz, OfEachSz, []).
658
659fck_collect(_T, 1, _OfEachSz, Acc) ->
660    Acc;
661fck_collect(T, CollSz, OfEachSz, Acc) ->
662    {List, _} = ets:select(T,
663			   [{{'$1','$2'}, [{'==',{length,'$2'},CollSz}], ['$2']}],
664			   OfEachSz),
665    fck_collect(T, CollSz-1, OfEachSz, List ++ Acc).
666
667
668fck_loop_1(T, Key, 0, MaxCollSz, MaxSzLeft) ->
669    fck_loop_2(T, Key, MaxCollSz, MaxSzLeft);
670fck_loop_1(T, Key, Inserts, MaxCollSz, MaxSzLeft) ->
671    Hash = internal_hash(Key),
672    case ets:insert_new(T, {Hash, [Key]}) of
673	true ->
674	    fck_loop_1(T, Key+1, Inserts-1, MaxCollSz, MaxSzLeft);
675	false ->
676	    [{Hash, KeyList}] = ets:lookup(T, Hash),
677	    true = ets:insert(T, {Hash, [Key | KeyList]}),
678	    fck_loop_1(T, Key+1, Inserts, MaxCollSz, MaxSzLeft)
679    end.
680
681fck_loop_2(_T, _Key, _MaxCollSz, 0) ->
682    ok;
683fck_loop_2(T, Key, MaxCollSz, MaxSzLeft0) ->
684    Hash = internal_hash(Key),
685    case ets:lookup(T, Hash) of
686	[] ->
687	    fck_loop_2(T, Key+1, MaxCollSz, MaxSzLeft0);
688	[{Hash, KeyList}] ->
689	    true = ets:insert(T, {Hash, [Key | KeyList]}),
690	    MaxSzLeft1 = case length(KeyList)+1 of
691			     MaxCollSz ->
692				 MaxSzLeft0 - 1;
693			     _ ->
694				 MaxSzLeft0
695			 end,
696	    fck_loop_2(T, Key+1, MaxCollSz, MaxSzLeft1)
697    end.
698
699
700
701%% OTP-17700 Bug skipped refc++ of shared magic reference
702shared_magic_ref(_Config) ->
703    Ref = atomics:new(10, []),
704    persistent_term:put(shared_magic_ref, {Ref, Ref}),
705    shared_magic_ref_cont().
706
707shared_magic_ref_cont() ->
708    erlang:garbage_collect(),
709    {Ref, Ref} = persistent_term:get(shared_magic_ref),
710    0 = atomics:get(Ref, 1),  %% would definitely fail on debug vm
711    ok.
712
713
714%% Test that all persistent terms are erased by init:restart/0.
715
716init_restart(_Config) ->
717    File = "command_file",
718    ok = file:write_file(File, term_to_binary(restart)),
719    {ok,[[Erl]]} = init:get_argument(progname),
720    ModPath = filename:dirname(code:which(?MODULE)),
721    Cmd = Erl ++ " -pa " ++ ModPath ++ " -noshell "
722        "-run " ++ ?MODULE_STRING ++ " test_init_restart_cmd " ++
723        File,
724    io:format("~s\n", [Cmd]),
725    Expected = "12ok",
726    case os:cmd(Cmd) of
727        Expected ->
728            ok;
729        Actual ->
730            io:format("Expected: ~s", [Expected]),
731            io:format("Actual:   ~s\n", [Actual]),
732            ct:fail(unexpected_output)
733    end.
734
735test_init_restart_cmd([File]) ->
736    try
737        do_test_init_restart_cmd(File)
738    catch
739        C:R ->
740            io:format("\n~p ~p\n", [C,R]),
741            halt()
742    end,
743    receive
744        _ -> ok
745    end.
746
747do_test_init_restart_cmd(File) ->
748    {ok,Bin} = file:read_file(File),
749    Seq = lists:seq(1, 50),
750    case binary_to_term(Bin) of
751        restart ->
752            _ = [persistent_term:put({?MODULE,I}, {value,I}) ||
753                    I <- Seq],
754            ok = file:write_file(File, term_to_binary(was_restarted)),
755            io:put_chars("1"),
756            init:restart(),
757            receive
758                _ -> ok
759            end;
760        was_restarted ->
761            io:put_chars("2"),
762            ok = file:delete(File),
763            _ = [begin
764                     Key = {?MODULE,I},
765                     {'EXIT',{badarg,_}} = (catch persistent_term:get(Key))
766                 end || I <- Seq],
767            io:put_chars("ok"),
768            init:stop()
769    end.
770
771%% Test that the literal is copied when removed also when
772%% the whole message is a literal...
773
774whole_message(Config) when is_list(Config) ->
775    whole_message_test(on_heap),
776    whole_message_test(off_heap),
777    ok.
778
779whole_message_test(MQD) ->
780    io:format("Testing on ~p~n", [MQD]),
781    Go = make_ref(),
782    Done = make_ref(),
783    TestRef = make_ref(),
784    Tester = self(),
785    persistent_term:put(test_ref, TestRef),
786    Pid = spawn_opt(fun () ->
787                             receive Go -> ok end,
788                             receive TestRef -> ok end,
789                             receive TestRef -> ok end,
790                             receive TestRef -> ok end,
791                             receive [TestRef] -> ok end,
792                             receive [TestRef] -> ok end,
793                             receive [TestRef] -> ok end,
794                             Tester ! Done
795                     end, [link, {message_queue_data, MQD}]),
796    Pid ! persistent_term:get(test_ref),
797    Pid ! persistent_term:get(test_ref),
798    Pid ! persistent_term:get(test_ref),
799    %% Throw in some messages with a reference from the heap
800    %% while we're at it...
801    Pid ! [persistent_term:get(test_ref)],
802    Pid ! [persistent_term:get(test_ref)],
803    Pid ! [persistent_term:get(test_ref)],
804    persistent_term:erase(test_ref),
805    receive after 1000 -> ok end,
806    Pid ! Go,
807    receive Done -> ok end,
808    unlink(Pid),
809    exit(Pid, kill),
810    false = is_process_alive(Pid),
811    ok.
812
813%% Check that there is the same number of persistents terms before
814%% and after each test case.
815
816chk() ->
817    {xtra_info(), persistent_term:get()}.
818
819chk({Info1, _Initial} = Chk) ->
820    #{count := Count, memory := Memory1, table := Table1} = Info1,
821    case xtra_info() of
822        Info1 ->
823            ok;
824        #{count := Count, memory := Memory2, table := Table2}=Info2
825          when Memory2 > Memory1,
826               Table2 > Table1 ->
827            %% Check increased memory is only table growth hysteresis
828            MemDiff = Memory2 - Memory1,
829            TabDiff = (Table2 - Table1) * erlang:system_info(wordsize),
830            {MemDiff,MemDiff} = {MemDiff, TabDiff},
831
832            case (Count / Table2) of
833                Load when Load >= 0.25 ->
834                    ok;
835                _ ->
836                    chk_fail("Hash table too large", Info1, Info2)
837            end;
838        Info2 ->
839            chk_fail("Memory diff", Info1, Info2)
840    end,
841    Key = {?MODULE,?FUNCTION_NAME},
842    ok = persistent_term:put(Key, {term,Info1}),
843    Term = persistent_term:get(Key),
844    true = persistent_term:erase(Key),
845    chk_not_stuck(Term, 1),
846    [persistent_term:erase(K) || {K, _} <- pget(Chk)],
847    ok.
848
849xtra_info() ->
850    maps:merge(persistent_term:info(),
851               erts_debug:get_internal_state(persistent_term)).
852
853chk_fail(Error, Info1, Info2) ->
854    io:format("Info1 = ~p\n", [Info1]),
855    io:format("Info2 = ~p\n", [Info2]),
856    ct:fail(Error).
857
858chk_not_stuck(Term, Timeout) ->
859    %% Hash tables to be deleted are put onto a queue.
860    %% Make sure that the queue isn't stuck by a table with
861    %% a non-zero ref count.
862
863    case erts_debug:size_shared(Term) of
864        0 ->
865            receive after Timeout -> ok end,
866            case Timeout of
867                1000 ->
868                    flush_later_ops(),
869                    chk_not_stuck(Term, 1);
870                _ ->
871                    chk_not_stuck(Term, Timeout*10)
872            end;
873        _ ->
874            ok
875    end.
876
877pget({_, Initial}) ->
878    persistent_term:get() -- Initial.
879
880
881killed_while_trapping_put(_Config) ->
882    repeat(
883      fun() ->
884              NrOfPutsInChild = 10000,
885              do_puts(2500, my_value),
886              Pid =
887                  spawn(fun() ->
888                                do_puts(NrOfPutsInChild, my_value2)
889                        end),
890              timer:sleep(1),
891              erlang:exit(Pid, kill),
892              do_erases(NrOfPutsInChild)
893      end,
894      10),
895    ok.
896
897killed_while_trapping_erase(_Config) ->
898    repeat(
899      fun() ->
900              NrOfErases = 2500,
901              do_puts(NrOfErases, my_value),
902              Pid =
903                  spawn(fun() ->
904                                do_erases(NrOfErases)
905                        end),
906              timer:sleep(1),
907              erlang:exit(Pid, kill),
908              do_erases(NrOfErases)
909      end,
910      10),
911    ok.
912
913put_erase_trapping(_Config) ->
914    NrOfItems = 5000,
915    do_puts(NrOfItems, first),
916    do_puts(NrOfItems, second),
917    do_erases(NrOfItems),
918    ok.
919
920do_puts(0, _) -> ok;
921do_puts(NrOfPuts, ValuePrefix) ->
922    Key = {?MODULE, NrOfPuts},
923    Value = {ValuePrefix, NrOfPuts},
924    erts_debug:set_internal_state(reds_left, rand:uniform(250)),
925    persistent_term:put(Key, Value),
926    Value = persistent_term:get(Key),
927    do_puts(NrOfPuts - 1, ValuePrefix).
928
929do_erases(0) -> ok;
930do_erases(NrOfErases) ->
931    Key = {?MODULE,NrOfErases},
932    erts_debug:set_internal_state(reds_left, rand:uniform(500)),
933    persistent_term:erase(Key),
934    not_found = persistent_term:get(Key, not_found),
935    do_erases(NrOfErases - 1).
936
937repeat(_Fun, 0) ->
938    ok;
939repeat(Fun, N) ->
940    Fun(),
941    repeat(Fun, N-1).
942
943error_info(_Config) ->
944    L = [{erase, [{?MODULE,my_key}], [no_fail]},
945         {get, [{?MODULE,certainly_not_existing}]},
946         {get, [{?MODULE,certainly_not_existing}, default], [no_fail]},
947         {put, 2}                               %Can't fail.
948        ],
949    do_error_info(L).
950
951do_error_info(L0) ->
952    L1 = lists:foldl(fun({_,A}, Acc) when is_integer(A) -> Acc;
953                        ({F,A}, Acc) -> [{F,A,[]}|Acc];
954                        ({F,A,Opts}, Acc) -> [{F,A,Opts}|Acc]
955                     end, [], L0),
956    Tests = ordsets:from_list([{F,length(A)} || {F,A,_} <- L1] ++
957                                  [{F,A} || {F,A} <- L0, is_integer(A)]),
958    Bifs0 = [{F,A} || {F,A} <- persistent_term:module_info(exports),
959                      A =/= 0,
960                      F =/= module_info],
961    Bifs = ordsets:from_list(Bifs0),
962    NYI = [{F,lists:duplicate(A, '*'),nyi} || {F,A} <- Bifs -- Tests],
963    L = lists:sort(NYI ++ L1),
964    do_error_info(L, []).
965
966do_error_info([{_,Args,nyi}=H|T], Errors) ->
967    case lists:all(fun(A) -> A =:= '*' end, Args) of
968        true ->
969            do_error_info(T, [{nyi,H}|Errors]);
970        false ->
971            do_error_info(T, [{bad_nyi,H}|Errors])
972    end;
973do_error_info([{F,Args,Opts}|T], Errors) ->
974    eval_bif_error(F, Args, Opts, T, Errors);
975do_error_info([], Errors0) ->
976    case lists:sort(Errors0) of
977        [] ->
978            ok;
979        [_|_]=Errors ->
980            io:format("\n~p\n", [Errors]),
981            ct:fail({length(Errors),errors})
982    end.
983
984eval_bif_error(F, Args, Opts, T, Errors0) ->
985    try apply(persistent_term, F, Args) of
986        Result ->
987            case lists:member(no_fail, Opts) of
988                true ->
989                    do_error_info(T, Errors0);
990                false ->
991                    do_error_info(T, [{should_fail,{F,Args},Result}|Errors0])
992            end
993    catch
994        error:Reason:Stk ->
995            SF = fun(Mod, _, _) -> Mod =:= test_server end,
996            Str = erl_error:format_exception(error, Reason, Stk, #{stack_trim_fun => SF}),
997            BinStr = iolist_to_binary(Str),
998            ArgStr = lists:join(", ", [io_lib:format("~p", [A]) || A <- Args]),
999            io:format("\nerlang:~p(~s)\n~ts", [F,ArgStr,BinStr]),
1000
1001            case Stk of
1002                [{persistent_term,ActualF,ActualArgs,Info}|_] ->
1003                    RE = <<"[*][*][*] argument \\d+:">>,
1004                    Errors1 = case re:run(BinStr, RE, [{capture, none}]) of
1005                                  match ->
1006                                      Errors0;
1007                                  nomatch when Reason =:= system_limit ->
1008                                      Errors0;
1009                                  nomatch ->
1010                                      [{no_explanation,{F,Args},Info}|Errors0]
1011                              end,
1012
1013                    Errors = case {ActualF,ActualArgs} of
1014                                 {F,Args} ->
1015                                     Errors1;
1016                                 _ ->
1017                                     [{renamed,{F,length(Args)},{ActualF,ActualArgs}}|Errors1]
1018                             end,
1019
1020                    do_error_info(T, Errors);
1021                _ ->
1022                    Errors = [{renamed,{F,length(Args)},hd(Stk)}|Errors0],
1023                    do_error_info(T, Errors)
1024            end
1025    end.
1026
1027
1028%% OTP-17298
1029get_all_race(_Config) ->
1030    N = 20 * erlang:system_info(schedulers_online),
1031    persistent_term:put(get_all_race, N),
1032    SPs = [spawn_link(fun() -> gar_setter(Seq) end) || Seq <- lists:seq(1, N)],
1033    GPs = [spawn_link(fun gar_getter/0) || _ <- lists:seq(1, N)],
1034    receive after 2000 -> ok end,
1035    [begin unlink(Pid), exit(Pid,kill) end || Pid <- (SPs ++ GPs)],
1036    ok.
1037
1038get_all_race_cleanup() ->
1039    N = persistent_term:get(get_all_race, 0),
1040    _ = persistent_term:erase(get_all_race),
1041    [_ = persistent_term:erase(Seq) || Seq <- lists:seq(1, N)],
1042    ok.
1043
1044gar_getter() ->
1045    erts_debug:set_internal_state(reds_left, 1),
1046    _ = persistent_term:get(),
1047    gar_getter().
1048
1049gar_setter(Key) ->
1050    erts_debug:set_internal_state(reds_left, 1),
1051    persistent_term:erase(Key),
1052    persistent_term:put(Key, {complex, term}),
1053    gar_setter(Key).
1054
1055%% Test that literals in non-message signals are copied
1056%% when removed...
1057%%
1058%% Currently the only non-message signal that may carry
1059%% literals are alias-message signals. Exit signals have
1060%% been added to the test since they should be added
1061%% next...
1062
1063non_message_signal(Config) when is_list(Config) ->
1064    non_message_signal_test(on_heap),
1065    non_message_signal_test(off_heap),
1066    ok.
1067
1068non_message_signal_test(MQD) ->
1069    io:format("Testing on ~p~n", [MQD]),
1070    process_flag(scheduler, 1),
1071    process_flag(priority, max),
1072    MultiSched = erlang:system_info(schedulers) > 1,
1073    {TokOpts, RecvOpts}
1074        = case MultiSched of
1075              true ->
1076                  {[link, {scheduler, 2}, {priority, high}],
1077                   [link, {scheduler, 2}, {priority, low},
1078		    {message_queue_data, MQD}]};
1079              false ->
1080                  {[link], [link, {message_queue_data, MQD}]}
1081          end,
1082    RecvPrepared = make_ref(),
1083    TokPrepared = make_ref(),
1084    Go = make_ref(),
1085    Done = make_ref(),
1086    TestRef = make_ref(),
1087    Tester = self(),
1088    persistent_term:put(test_ref, TestRef),
1089    Pid = spawn_opt(fun () ->
1090			    process_flag(trap_exit, true),
1091                            Tester ! {RecvPrepared, alias()},
1092                            receive Go -> ok end,
1093			    recv_msg(TestRef, 50),
1094			    recv_msg([TestRef], 50),
1095                            recv_msg({'EXIT', Tester, TestRef}, 50),
1096                            recv_msg({'EXIT', Tester, [TestRef]}, 50),
1097                            Tester ! Done
1098                    end, RecvOpts),
1099    Alias = receive {RecvPrepared, A} -> A end,
1100    Tok = spawn_opt(fun () ->
1101                            Tester ! TokPrepared,
1102                            tok_loop()
1103                    end, TokOpts),
1104    receive TokPrepared -> ok end,
1105    lists:foreach(fun (_) -> Alias ! persistent_term:get(test_ref) end,
1106		  lists:seq(1, 50)),
1107    lists:foreach(fun (_) -> Alias ! [persistent_term:get(test_ref)] end,
1108		  lists:seq(1, 50)),
1109    lists:foreach(fun (_) -> exit(Pid, persistent_term:get(test_ref)) end,
1110		  lists:seq(1, 50)),
1111    lists:foreach(fun (_) -> exit(Pid, [persistent_term:get(test_ref)]) end,
1112		  lists:seq(1, 50)),
1113    persistent_term:erase(test_ref),
1114    receive after 1000 -> ok end,
1115    unlink(Tok),
1116    exit(Tok, kill),
1117    receive after 1000 -> ok end,
1118    Pid ! Go,
1119    receive Done -> ok end,
1120    unlink(Pid),
1121    exit(Pid, kill),
1122    false = is_process_alive(Pid),
1123    false = is_process_alive(Tok),
1124    ok.
1125
1126recv_msg(_Msg, 0) ->
1127    ok;
1128recv_msg(Msg, N) ->
1129    receive
1130        Msg ->
1131            recv_msg(Msg, N-1)
1132    end.
1133
1134tok_loop() ->
1135    tok_loop().
1136