aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/test/node_container_SUITE.erl
blob: 300b4ed036d51843c265f750c826cbbe6a624b31 (plain) (tree)
1
2
3
4
5


                   
                                                        
   










                                                                           













                                                                         
                                           
 
                                                           

                                                 

                                     









                                



                      
                          

                           
 

                                 
                                
 
 
         
                                                                          

                                                      
                                             
                                            
 



                         

                                                                                         
                                    
 

                                                                  










                                                                             


                                                        
                                   
           
 
                                                       

       








                                                                                

                                                                        
                                                         
                                                      
                               






                                          
                     






                                                    
                                






                                                     






                                              
                      






                                                    
       




                           
                                                                                   
                                             
























                                                           
           
















                                  
        
 




                 
                                                                           









                                                                                
 




                               
 
                                                                     


























                                      

                                                                    


























                                       

                                                                     


























                                      


                                                                                
 






                                                                     
                                    
 




                                             










                                                                     
                                   
 



                                            










                                                                     
                                               
 





                                                        





                    
                                
                                      
























                                                                          


                           
                                                
                                             

                                                                  


                                             
                                                                





                                                    
                                                                                         
       

                                            


















                                                                   


                                 



                                                                         
                                                                              

                      








                                                 








                                                                    



                            

                                                                       
                                              


























                                                                       




                               

                                                                       
                                                 












































                                                                       




                                  

                                                                       
                                                    

                                                                  


















                                                                        
                                       

                                                      
                                                                        



                                                 
                                                                                         
       



                      

                                                                       
                                        























                                                              



                             

                                                                       
                                               










                                                      

                                               














                                                                        



                      

                                                                       
                                          




















                                                                   
 
                                                      
 
                                         
                     




                                      











                                                














                                                   

                         

                                              
                            







                                           

                                                       
         

                                               












                                             

                                                

                                       
 

                                                              























                                                                           
                                  






                                                




                          

                                                           







                                                                    
        
 



                                                                 
                                              




                                                     



                                                            
 


                                                                        








                                                                   


                      













                                                     


                                            

                      
              

                                                       

        
































                                                                                                         






















                                                                                             



                                                                              


        









                                                                          
                                                                     
                    











                                                                            
           





                                                                              



                                                                        
















                                                                               





                                                                                






                                                              

                                                                     





























                                                                        





                                                                        
                                                                             
                                 
                                             
                                                         





                                                               


                                                       









                                                               


                                           


                                                        










                                                                           
                                                              











                                                                              









                                                                        


                         








                                                                  
                   
                         

                  

                                       









                                   

                                                          

        

                             
          
                                                  

                                                          

                      
                                             

                 
                                           


                                








                                  


                                 





















                                                                    






                                                             




                                                                      












                                                                           

        



                                                   



                                                               











                                                                          

        



                                                           
                                                             
                                                              

                                                              

                                                                 
                                                             











                                                                          

                                                             
                                                              
                                                             















                                                                            



              



                                                                          














                                                         

                                        
        
%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2002-2018. All Rights Reserved.
%% 
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%% 
%% %CopyrightEnd%
%%

%%%----------------------------------------------------------------------
%%% File    : node_container_SUITE.erl
%%% Author  : Rickard <[email protected]>
%%% Purpose : 
%%% Created : 24 Jul 2002 by Rickard <[email protected]>
%%%----------------------------------------------------------------------

-module(node_container_SUITE).
-author('[email protected]').

-include_lib("common_test/include/ct.hrl").

-export([all/0, suite/0, init_per_suite/1, end_per_suite/1,
         init_per_testcase/2, end_per_testcase/2,
         node_container_refc_check/1]).

-export([term_to_binary_to_term_eq/1,
         round_trip_eq/1,
         cmp/1,
         ref_eq/1,
         node_table_gc/1,
         dist_link_refc/1,
         dist_monitor_refc/1,
         node_controller_refc/1,
         ets_refc/1,
         match_spec_refc/1,
         timer_refc/1,
         pid_wrap/1,
         port_wrap/1,
         bad_nc/1,
         unique_pid/1,
         iter_max_procs/1,
         magic_ref/1,
         dist_entry_gc/1]).

suite() ->
    [{ct_hooks,[ts_install_cth]},
     {timetrap, {minutes, 12}}].


all() -> 
    [dist_entry_gc, term_to_binary_to_term_eq, round_trip_eq, cmp, ref_eq,
     node_table_gc, dist_link_refc, dist_monitor_refc,
     node_controller_refc, ets_refc, match_spec_refc,
     timer_refc, pid_wrap, port_wrap, bad_nc,
     unique_pid, iter_max_procs, magic_ref].

init_per_suite(Config) ->
    Config.

end_per_suite(_Config) ->
    erts_debug:set_internal_state(available_internal_state, true),
    erts_debug:set_internal_state(node_tab_delayed_delete, -1), %% restore original value
    available_internal_state(false).

available_internal_state(Bool) when Bool == true; Bool == false ->
    case {Bool,
          (catch erts_debug:get_internal_state(available_internal_state))} of
        {true, true} ->
            true;
        {false, true} ->
            erts_debug:set_internal_state(available_internal_state, false),
            true;
        {true, _} ->
            erts_debug:set_internal_state(available_internal_state, true),
            false;
        {false, _} ->
            false
    end.

init_per_testcase(_Case, Config) when is_list(Config) ->
    available_internal_state(true),
    Config.

end_per_testcase(_Case, Config) when is_list(Config) ->
    ok.

%%%
%%% The test cases -------------------------------------------------------------
%%%

-define(MAX_PIDS_PORTS, ((1 bsl 28) - 1)).

%%
%% Test case: term_to_binary_to_term_eq
%%
%% Tests that node container terms that are converted to external format
%% and back stay equal to themselves.
term_to_binary_to_term_eq(Config) when is_list(Config) ->
    ThisNode = {node(), erlang:system_info(creation)},
    % Get local node containers
    LPid = self(),
    LXPid = mk_pid(ThisNode, 32767, 8191),
    LPort = hd(erlang:ports()),
    LXPort = mk_port(ThisNode, 268435455),
    LLRef = make_ref(),
    LHLRef = mk_ref(ThisNode, [47, 11]),
    LSRef = mk_ref(ThisNode, [4711]),
    % Test local nc:s
    LPid = binary_to_term(term_to_binary(LPid)),
    LXPid = binary_to_term(term_to_binary(LXPid)),
    LPort = binary_to_term(term_to_binary(LPort)),
    LXPort = binary_to_term(term_to_binary(LXPort)),
    LLRef = binary_to_term(term_to_binary(LLRef)),
    LHLRef = binary_to_term(term_to_binary(LHLRef)),
    LSRef = binary_to_term(term_to_binary(LSRef)),
    % Get remote node containers
    ttbtteq_do_remote({get_nodename(), 3}),
    ttbtteq_do_remote({get_nodename(), 4}),
    ttbtteq_do_remote({get_nodename(), 16#adec0ded}),
    nc_refc_check(node()),
    ok.

ttbtteq_do_remote(RNode) ->
    RPid = mk_pid(RNode, 4711, 1),
    RXPid = mk_pid(RNode, 32767, 8191),
    RPort = mk_port(RNode, 4711),
    RXPort = mk_port(RNode, 268435455),
    RLRef = mk_ref(RNode, [4711, 4711, 4711]),
    RHLRef = mk_ref(RNode, [4711, 4711]),
    RSRef = mk_ref(RNode, [4711]),
    % Test remote nc:s
    RPid = binary_to_term(term_to_binary(RPid)),
    RXPid = binary_to_term(term_to_binary(RXPid)),
    RPort = binary_to_term(term_to_binary(RPort)),
    RXPort = binary_to_term(term_to_binary(RXPort)),
    RLRef = binary_to_term(term_to_binary(RLRef)),
    RHLRef = binary_to_term(term_to_binary(RHLRef)),
    RSRef = binary_to_term(term_to_binary(RSRef)),
    ok.


%%
%% Test case: round_trip_eq
%%
%% Tests that node containers that are sent between nodes stay equal to themselves.
round_trip_eq(Config) when is_list(Config) ->
    ThisNode = {node(), erlang:system_info(creation)},
    NodeFirstName = get_nodefirstname(),
    {ok, Node} = start_node(NodeFirstName),
    Self = self(),
    RPid = spawn_link(Node,
                      fun () ->
                              receive
                                  {Self, Data} ->
                                      Self ! {self(), Data}
                              end
                      end),
    SentPid = self(),
    SentXPid = mk_pid(ThisNode, 17471, 8190),
    SentPort = hd(erlang:ports()),
    SentXPort = mk_port(ThisNode, 268435451),
    SentLRef = make_ref(),
    SentHLRef = mk_ref(ThisNode, [4711, 17]),
    SentSRef = mk_ref(ThisNode, [4711]),
    RPid ! {Self, {SentPid,
                   SentXPid,
                   SentPort,
                   SentXPort,
                   SentLRef,
                   SentHLRef,
                   SentSRef}},
    receive
        {RPid, {RecPid,
                RecXPid,
                RecPort,
                RecXPort,
                RecLRef,
                RecHLRef,
                RecSRef}} ->
            stop_node(Node),
            SentPid = RecPid,
            SentXPid = RecXPid,
            SentPort = RecPort,
            SentXPort = RecXPort,
            SentLRef = RecLRef,
            SentHLRef = RecHLRef,
            SentSRef = RecSRef,
            nc_refc_check(node()),
            ok
    end.



%%
%% Test case: cmp
%%
%% Tests that Erlang term comparison works as it should on node containers.
cmp(Config) when is_list(Config) ->

    %% Inter type comparison ---------------------------------------------------

    %% The Erlang term order:
    %% number < atom < ref < fun < port < pid < tuple < nil < cons < binary
    RNode = {get_nodename(), 2},

    IRef = make_ref(),
    ERef = mk_ref({get_nodename(), 2}, [1,2,3]),

    IPid = self(),
    EPid = mk_pid(RNode, 1, 2),

    IPort = hd(erlang:ports()),
    EPort = mk_port(RNode, 1),

    %% Test pids ----------------------------------------------------
    true = 1 < IPid,
    true = 1.3 < IPid,
    true = (1 bsl 64) < IPid,
    true = an_atom < IPid,
    true = IRef < IPid,
    true = ERef < IPid,
    true = fun () -> a_fun end < IPid,
    true = IPort < IPid,
    true = EPort < IPid,
    true = IPid < {a, tuple},
    true = IPid < [],
    true = IPid < [a|cons],
    true = IPid < <<"a binary">>,

    true = 1 < EPid,
    true = 1.3 < EPid,
    true = (1 bsl 64) < EPid,
    true = an_atom < EPid,
    true = IRef < EPid,
    true = ERef < EPid,
    true = fun () -> a_fun end < EPid,
    true = IPort < EPid,
    true = EPort < EPid,
    true = EPid < {a, tuple},
    true = EPid < [],
    true = EPid < [a|cons],
    true = EPid < <<"a binary">>,

    %% Test ports --------------------------------------------------
    true = 1 < IPort,
    true = 1.3 < IPort,
    true = (1 bsl 64) < IPort,
    true = an_atom < IPort,
    true = IRef < IPort,
    true = ERef < IPort,
    true = fun () -> a_fun end < IPort,
    true = IPort < IPid,
    true = IPort < EPid,
    true = IPort < {a, tuple},
    true = IPort < [],
    true = IPort < [a|cons],
    true = IPort < <<"a binary">>,

    true = 1 < EPort,
    true = 1.3 < EPort,
    true = (1 bsl 64) < EPort,
    true = an_atom < EPort,
    true = IRef < EPort,
    true = ERef < EPort,
    true = fun () -> a_fun end < EPort,
    true = EPort < IPid,
    true = EPort < EPid,
    true = EPort < {a, tuple},
    true = EPort < [],
    true = EPort < [a|cons],
    true = EPort < <<"a binary">>,

    %% Test refs ----------------------------------------------------
    true = 1 < IRef,
    true = 1.3 < IRef,
    true = (1 bsl 64) < IRef,
    true = an_atom < IRef,
    true = IRef < fun () -> a_fun end,
    true = IRef < IPort,
    true = IRef < EPort,
    true = IRef < IPid,
    true = IRef < EPid,
    true = IRef < {a, tuple},
    true = IRef < [],
    true = IRef < [a|cons],
    true = IRef < <<"a binary">>,

    true = 1 < ERef,
    true = 1.3 < ERef,
    true = (1 bsl 64) < ERef,
    true = an_atom < ERef,
    true = ERef < fun () -> a_fun end,
    true = ERef < IPort,
    true = ERef < EPort,
    true = ERef < IPid,
    true = ERef < EPid,
    true = ERef < {a, tuple},
    true = ERef < [],
    true = ERef < [a|cons],
    true = ERef < <<"a binary">>,


    %% Intra type comparison ---------------------------------------------------


    %% Test pids ----------------------------------------------------
    %%
    %% Significance (most -> least):
    %%   serial, number, nodename, creation
    %%

    Pid = mk_pid({b@b, 2}, 4711, 1),

    true = mk_pid({a@b, 1}, 4710, 2) > Pid,
    true = mk_pid({a@b, 1}, 4712, 1) > Pid,
    true = mk_pid({c@b, 1}, 4711, 1) > Pid,
    true = mk_pid({b@b, 3}, 4711, 1) > Pid,
    true = mk_pid({b@b, 2}, 4711, 1) =:= Pid,

    %% Test ports ---------------------------------------------------
    %%
    %% Significance (most -> least):
    %%   nodename, creation, number 
    %%
    %% OBS: Comparison between ports has changed in R9. This
    %%      since it wasn't stable in R8 (and eariler releases).
    %%      Significance used to be: dist_slot, number,
    %%      creation.

    Port = mk_port({b@b, 2}, 4711),

    true = mk_port({c@b, 1}, 4710) > Port,
    true = mk_port({b@b, 3}, 4710) > Port,
    true = mk_port({b@b, 2}, 4712) > Port,
    true = mk_port({b@b, 2}, 4711) =:= Port,

    %% Test refs ----------------------------------------------------
    %% Significance (most -> least):
    %% nodename, creation, (number high, number mid), number low, 
    %%
    %% OBS: Comparison between refs has changed in R9. This
    %%      since it wasn't stable in R8 (and eariler releases).
    %%      Significance used to be: dist_slot, number,
    %%      creation.
    %%

    Ref = mk_ref({b@b, 2}, [4711, 4711, 4711]),

    true = mk_ref({c@b, 1}, [4710, 4710, 4710]) > Ref,
    true = mk_ref({b@b, 3}, [4710, 4710, 4710]) > Ref,
    true = mk_ref({b@b, 2}, [4710, 4710, 4712]) > Ref,
    true = mk_ref({b@b, 2}, [4710, 4712, 4711]) > Ref,
    true = mk_ref({b@b, 2}, [4712, 4711, 4711]) > Ref,
    true = mk_ref({b@b, 2}, [4711, 4711, 4711]) =:= Ref,

    ok.

%%
%% Test case: ref_eq
%%
%% Test that one word refs works
ref_eq(Config) when is_list(Config) ->
    ThisNode = {node(), erlang:system_info(creation)},
    AnotherNode = {get_nodename(),2},
    LLongRef = mk_ref(ThisNode, [4711, 0, 0]),
    LHalfLongRef = mk_ref(ThisNode, [4711, 0]),
    LShortRef = mk_ref(ThisNode, [4711]),
    true = LLongRef =:= LShortRef,
    true = LLongRef =:= LHalfLongRef,
    true = LLongRef =:= LLongRef,
    true = LHalfLongRef =:= LShortRef,
    true = LHalfLongRef =:= LHalfLongRef,
    true = LShortRef =:= LShortRef,
    false = LShortRef == mk_ref(ThisNode, [4711, 0, 1]), % Not any more
    RLongRef = mk_ref(AnotherNode, [4711, 0, 0]),
    RHalfLongRef = mk_ref(AnotherNode, [4711, 0]),
    RShortRef = mk_ref(AnotherNode, [4711]),
    true = RLongRef =:= RShortRef,
    true = RLongRef =:= RHalfLongRef,
    true = RLongRef =:= RLongRef,
    true = RHalfLongRef =:= RShortRef,
    true = RHalfLongRef =:= RHalfLongRef,
    true = RShortRef =:= RShortRef,
    false = RShortRef == mk_ref(AnotherNode, [4711, 0, 1]), % Not any more
    nc_refc_check(node()),
    ok.

%%
%% Test case: node_table_gc
%%
%% Tests that node tables are garbage collected.
node_table_gc(Config) when is_list(Config) ->
    erts_debug:set_internal_state(available_internal_state, true),
    erts_debug:set_internal_state(node_tab_delayed_delete, 0),
    PreKnown = nodes(known),
    io:format("PreKnown = ~p~n", [PreKnown]),
    make_node_garbage(0, 200000, 1000, []),
    receive after 1000 -> ok end, %% Wait for thread progress...
    PostKnown = nodes(known),
    PostAreas = erlang:system_info(allocated_areas),
    io:format("PostKnown = ~p~n", [PostKnown]),
    io:format("PostAreas = ~p~n", [PostAreas]),
    true = length(PostKnown) =< length(PreKnown),
    nc_refc_check(node()),
    erts_debug:set_internal_state(node_tab_delayed_delete, -1), %% restore original value
    ok.

make_node_garbage(N, L, I, Ps) when N < L ->
    Self = self(),
    P = spawn_link(fun () ->
                           % Generate two node entries and one dist
                           % entry per node name
                           PL1 = make_faked_pid_list(N,
                                                     I div 2,
                                                     1),
                           put(a, PL1),
                           PL2 = make_faked_pid_list(N,
                                                     I div 2,
                                                     2),
                           put(b, PL2),
                           Self ! {self(), length(nodes(known))}
                   end),
    receive
        {P, KnownLength} ->
            true = KnownLength >= I div 2
    end,
    make_node_garbage(N+(I div 2)*2, L, I, [P|Ps]);
make_node_garbage(_, _, _, Ps) ->
    %% Cleanup garbage...
    ProcIsCleanedUp
    = fun (Proc) ->
              undefined == erts_debug:get_internal_state({process_status,
                                                          Proc})
      end,
    lists:foreach(fun (P) -> wait_until(fun () -> ProcIsCleanedUp(P) end) end,
                  Ps),
    ok.


make_faked_pid_list(Start, No, Creation) ->
    make_faked_pid_list(Start, No, Creation, []).

make_faked_pid_list(_Start, 0, _Creation, Acc) ->
    Acc;
make_faked_pid_list(Start, No, Creation, Acc) ->
    make_faked_pid_list(Start+1,
                        No-1,
                        Creation,
                        [mk_pid({"faked_node-"
                                 ++ integer_to_list(Start rem 50000)
                                 ++ "@"
                                 ++ atom_to_list(?MODULE),
                                 Creation},
                                4711,
                                3) | Acc]).

%%
%% Test case: dist_link_refc
%%
%% Tests that external reference counts are incremented and decremented
%% as they should for distributed links
dist_link_refc(Config) when is_list(Config) ->
    NodeFirstName = get_nodefirstname(),
    {ok, Node} = start_node(NodeFirstName),
    RP = spawn_execer(Node),
    LP = spawn_link_execer(node()),
    true = sync_exec(RP, fun () -> link(LP) end),
    wait_until(fun () ->
                       {links, Links} = process_info(LP, links),
                       lists:member(RP, Links)
               end),
    NodeCre = sync_exec(RP, fun() -> erlang:system_info(creation) end),
    1 = reference_type_count(
          link,
          refering_entity_id({process, LP},
                             get_node_references({Node, NodeCre}))),
    exec(RP, fun() -> exit(normal) end),
    wait_until(fun () ->
                       {links, Links} = process_info(LP, links),
                       not lists:member(RP, Links)
               end),
    0 = reference_type_count(
          link,
          refering_entity_id({process, LP},
                             get_node_references({Node, NodeCre}))),
    exit(LP, normal),
    stop_node(Node),
    nc_refc_check(node()),
    ok.


%%
%% Test case: dist_monitor_refc
%%
%% Tests that external reference counts are incremented and decremented
%% as they should for distributed monitors
dist_monitor_refc(Config) when is_list(Config) ->
    NodeFirstName = get_nodefirstname(),
    {ok, Node} = start_node(NodeFirstName),
    RP = spawn_execer(Node),
    LP = spawn_link_execer(node()),
    RMon = sync_exec(RP, fun () -> erlang:monitor(process, LP) end),
    true = is_reference(RMon),
    LMon = sync_exec(LP, fun () -> erlang:monitor(process, RP) end),
    true = is_reference(LMon),
    NodeCre = sync_exec(RP, fun() -> erlang:system_info(creation) end),
    wait_until(fun () ->
                       {monitored_by, MonBy}
                       = process_info(LP, monitored_by),
                       {monitors, Mon}
                       = process_info(LP, monitors),
                       (lists:member(RP, MonBy)
                        and lists:member({process,RP}, Mon))
               end),
    3 = reference_type_count(
          monitor,
          refering_entity_id({process, LP},
                             get_node_references({Node, NodeCre}))),
    exec(RP, fun () -> exit(normal) end),
    wait_until(fun () ->
                       {monitored_by, MonBy}
                       = process_info(LP, monitored_by),
                       {monitors, Mon}
                       = process_info(LP, monitors),
                       ((not lists:member(RP, MonBy))
                        and (not lists:member({process,RP}, Mon)))
               end),
    ok = sync_exec(LP,
                   fun () ->
                           receive
                               {'DOWN', LMon, process, _, _} ->
                                   ok
                           end
                   end),
    0 = reference_type_count(
          link,
          refering_entity_id({process, LP},
                             get_node_references({Node, NodeCre}))),
    exit(LP, normal),
    stop_node(Node),
    nc_refc_check(node()),
    ok.


%%
%% Test case: node_controller_refc
%%
%% Tests that external reference counts are incremented and decremented
%% as they should for entities controlling a connections.
node_controller_refc(Config) when is_list(Config) ->
    erts_debug:set_internal_state(available_internal_state, true),
    erts_debug:set_internal_state(node_tab_delayed_delete, 0),
    NodeFirstName = get_nodefirstname(),
    {ok, Node} = start_node(NodeFirstName),
    true = lists:member(Node, nodes()),
    1 = reference_type_count(control, get_dist_references(Node)),
    P = spawn_link_execer(node()),
    Node
    = sync_exec(P,
                fun () ->
                        put(remote_net_kernel,
                            rpc:call(Node,erlang,whereis,[net_kernel])),
                        node(get(remote_net_kernel))
                end),
    Creation = rpc:call(Node, erlang, system_info, [creation]),
    monitor_node(Node,true),
    stop_node(Node),
    receive {nodedown, Node} -> ok end,
    DistRefs = get_dist_references(Node),
    true = reference_type_count(node, DistRefs) > 0,
    0 = reference_type_count(control, DistRefs),
    % Get rid of all references to Node
    exec(P, fun () -> exit(normal) end),
    wait_until(fun () -> not is_process_alive(P) end),
    lists:foreach(fun (Proc) -> garbage_collect(Proc) end, processes()),
    false = get_node_references({Node,Creation}),
    false = get_dist_references(Node),
    false = lists:member(Node, nodes(known)),
    nc_refc_check(node()),
    erts_debug:set_internal_state(node_tab_delayed_delete, -1), %% restore original value
    ok.

%%
%% Test case: ets_refc
%%
%% Tests that external reference counts are incremented and decremented
%% as they should for data stored in ets tables.
ets_refc(Config) when is_list(Config) ->
    RNode = {get_nodename(), 1},
    RPid = mk_pid(RNode, 4711, 2),
    RPort = mk_port(RNode, 4711),
    RRef = mk_ref(RNode, [4711, 47, 11]),
    Tab = ets:new(ets_refc, []),
    0 = reference_type_count(ets, get_node_references(RNode)),
    true = ets:insert(Tab, [{a, self()},
                            {b, RPid},
                            {c, hd(erlang:ports())},
                            {d, RPort},
                            {e, make_ref()}]),
    2 = reference_type_count(ets, get_node_references(RNode)),
    true = ets:insert(Tab, {f, RRef}),
    3 = reference_type_count(ets, get_node_references(RNode)),
    true = ets:delete(Tab, d),
    2 = reference_type_count(ets, get_node_references(RNode)),
    true = ets:delete_all_objects(Tab),
    0 = reference_type_count(ets, get_node_references(RNode)),
    true = ets:insert(Tab, [{b, RPid}, {e, make_ref()}]),
    1 = reference_type_count(ets, get_node_references(RNode)),
    true = ets:delete(Tab),
    0 = reference_type_count(ets, get_node_references(RNode)),
    nc_refc_check(node()),
    ok.

%%
%% Test case: match_spec_refc
%%
%% Tests that external reference counts are incremented and decremented
%% as they should for data stored in match specifications.
match_spec_refc(Config) when is_list(Config) ->
    RNode = {get_nodename(), 1},
    RPid = mk_pid(RNode, 4711, 2),
    RPort = mk_port(RNode, 4711),
    RRef = mk_ref(RNode, [4711, 47, 11]),
    ok = do_match_spec_test(RNode, RPid, RPort, RRef),
    garbage_collect(),
    NodeRefs = get_node_references(RNode),
    0 = reference_type_count(binary, NodeRefs),
    0 = reference_type_count(ets, NodeRefs),
    nc_refc_check(node()),
    ok.

do_match_spec_test(RNode, RPid, RPort, RRef) ->
    Tab = ets:new(match_spec_refc, []),
    true = ets:insert(Tab, [{a, RPid, RPort, RRef},
                            {b, self(), RPort, RRef},
                            {c, RPid, RPort, make_ref()},
                            {d, RPid, RPort, RRef}]),
    {M1, C1} = ets:select(Tab, [{{'$1',RPid,RPort,RRef},[],['$1']}], 1),
    NodeRefs = get_node_references(RNode),
    3 = reference_type_count(binary, NodeRefs),
    10 = reference_type_count(ets, NodeRefs),
    {M2, C2} = ets:select(C1),
    '$end_of_table' = ets:select(C2),
    ets:delete(Tab),
    [a,d] = lists:sort(M1++M2),
    ok.


%%
%% Test case: ets_refc
%%
%% Tests that external reference counts are incremented and decremented
%% as they should for data stored in bif timers.
timer_refc(Config) when is_list(Config) ->
    RNode = {get_nodename(), 1},
    RPid = mk_pid(RNode, 4711, 2),
    RPort = mk_port(RNode, 4711),
    RRef = mk_ref(RNode, [4711, 47, 11]),
    0 = reference_type_count(timer, get_node_references(RNode)),
    Pid = spawn(fun () -> receive after infinity -> ok end end),
    erlang:start_timer(10000, Pid, {RPid, RPort, RRef}),
    3 = reference_type_count(timer, get_node_references(RNode)),
    exit(Pid, kill),
    Mon = erlang:monitor(process, Pid),
    receive {'DOWN', Mon, process, Pid, _} -> ok end,
    0 = reference_type_count(timer, get_node_references(RNode)),
    erlang:send_after(500, Pid, {timer, RPid, RPort, RRef}),
    0 = reference_type_count(timer, get_node_references(RNode)),
    erlang:send_after(500, self(), {timer, RPid, RPort, RRef}),
    erlang:send_after(400, bananfluga, {timer, RPid, RPort, RRef}),
    6 = reference_type_count(timer, get_node_references(RNode)),
    receive {timer, RPid, RPort, RRef} -> ok end,
    0 = reference_type_count(timer, get_node_references(RNode)),
    nc_refc_check(node()),
    ok.

pid_wrap(Config) when is_list(Config) -> pp_wrap(pid).

port_wrap(Config) when is_list(Config) ->
    case os:type() of
        {unix, _} ->
            pp_wrap(port);
        _ ->
            {skip, "Only run on unix"}
    end.

get_next_id(pid) ->
    erts_debug:get_internal_state(next_pid);
get_next_id(port) ->
    erts_debug:get_internal_state(next_port).

set_next_id(pid, N) ->
    erts_debug:set_internal_state(next_pid, N);
set_next_id(port, N) ->
    erts_debug:set_internal_state(next_port, N).

pp_wrap(What) ->
    N = set_high_pp_next(What),
    Cre = N + 100,
    io:format("no creations = ~p~n", [Cre]),
    PreCre = get_next_id(What),
    io:format("pre creations = ~p~n", [PreCre]),
    true = is_integer(PreCre),
    do_pp_creations(What, Cre),
    PostCre = get_next_id(What),
    io:format("post creations = ~p~n", [PostCre]),
    true = is_integer(PostCre),
    true = PreCre > PostCre,
    Now = set_next_id(What, ?MAX_PIDS_PORTS div 2),
    io:format("reset to = ~p~n", [Now]),
    true = is_integer(Now),
    ok.

set_high_pp_next(What) ->
    set_high_pp_next(What, ?MAX_PIDS_PORTS-1).

set_high_pp_next(What, N) ->
    M = set_next_id(What, N),
    true = is_integer(M),
    case {M >= N, M =< ?MAX_PIDS_PORTS} of
        {true, true} ->
            ?MAX_PIDS_PORTS - M + 1;
        _ ->
            set_high_pp_next(What, N - 100)
    end.

do_pp_creations(_What, N) when is_integer(N), N =< 0 ->
    done;
do_pp_creations(pid, N) when is_integer(N) ->
    %% Create new pid and make sure it works...
    Me = self(),
    Ref = make_ref(),
    Pid = spawn_link(fun () ->
                             receive
                                 Ref ->
                                     Me ! Ref
                             end
                     end),
    Pid ! Ref,
    receive
        Ref ->
            do_pp_creations(pid, N - 1)
    end;
do_pp_creations(port, N) when is_integer(N) ->
    %% Create new port and make sure it works...
    "hej" = os:cmd("echo hej") -- "\n",
    do_pp_creations(port, N - 1).

bad_nc(Config) when is_list(Config) ->
    % Make sure emulator don't crash on bad node containers...
    MaxPidNum = (1 bsl 15) - 1,
    MaxPidSer = ?MAX_PIDS_PORTS bsr 15,
    ThisNode = {node(), erlang:system_info(creation)},
    {'EXIT', {badarg, mk_pid, _}}
    = (catch mk_pid(ThisNode, MaxPidNum + 1, 17)),
    {'EXIT', {badarg, mk_pid, _}}
    = (catch mk_pid(ThisNode, 4711, MaxPidSer + 1)),
    {'EXIT', {badarg, mk_port, _}}
    = (catch mk_port(ThisNode, ?MAX_PIDS_PORTS + 1)),
    {'EXIT', {badarg, mk_ref, _}}
    = (catch mk_ref(ThisNode,[(1 bsl 18), 4711, 4711])),
    {'EXIT', {badarg, mk_ref, _}}
    = (catch mk_ref(ThisNode, [4711, 4711, 4711, 4711, 4711, 4711, 4711])),
    RemNode = {x@y, 2},
    {'EXIT', {badarg, mk_pid, _}}
    = (catch mk_pid(RemNode, MaxPidNum + 1, MaxPidSer)),
    {'EXIT', {badarg, mk_pid, _}}
    = (catch mk_pid(RemNode, MaxPidNum, MaxPidSer + 1)),
    {'EXIT', {badarg, mk_port, _}}
    = (catch mk_port(RemNode, ?MAX_PIDS_PORTS + 1)),
    {'EXIT', {badarg, mk_ref, _}}
    = (catch mk_ref(RemNode, [(1 bsl 18), 4711, 4711])),
    {'EXIT', {badarg, mk_ref, _}}
    = (catch mk_ref(RemNode, [4711, 4711, 4711, 4711, 4711, 4711, 4711])),
    BadNode = {x@y, bad_creation},
    {'EXIT', {badarg, mk_pid, _}}
    = (catch mk_pid(BadNode, 4711, 17)),
    {'EXIT', {badarg, mk_port, _}}
    = (catch mk_port(BadNode, 4711)),
    {'EXIT', {badarg, mk_ref, _}}
    = (catch mk_ref(BadNode, [4711, 4711, 17])),
    ok.



-define(NO_PIDS, 1000000).

unique_pid(Config) when is_list(Config) ->
    case catch erlang:system_info(modified_timing_level) of
        Level when is_integer(Level) ->
            {skip,
             "Modified timing (level " ++ integer_to_list(Level)
             ++ ") is enabled. spawn() is too slow for this "
             " test when modified timing is enabled."};
        _ ->
            ?NO_PIDS = length(lists:usort(mkpidlist(?NO_PIDS, []))),
            ok
    end.

mkpidlist(0, Ps) -> Ps;
mkpidlist(N, Ps) -> mkpidlist(N-1, [spawn(fun () -> ok end)|Ps]).


iter_max_procs(Config) when is_list(Config) ->
    NoMoreTests = make_ref(),
    erlang:send_after(10000, self(), NoMoreTests),
    Res = chk_max_proc_line(),
    Res = chk_max_proc_line(),
    done = chk_max_proc_line_until(NoMoreTests, Res),
    Cmt = io_lib:format("max processes = ~p; "
                        "process line length = ~p",
                        [element(2, Res), element(1, Res)]),
    {comment, lists:flatten(Cmt)}.

max_proc_line(Root, Parent, N) ->
    Me = self(),
    case catch spawn_link(fun () -> max_proc_line(Root, Me, N+1) end) of
        {'EXIT', {system_limit, _}} when Root /= self() ->
            Root ! {proc_line_length, N, self()},
            receive remove_proc_line -> Parent ! {exiting, Me} end;
        P when is_pid(P), Root =/= self() ->
            receive {exiting, P} -> Parent ! {exiting, Me} end;
        P when is_pid(P) ->
            P;
        Unexpected ->
            exit({unexpected_spawn_result, Unexpected})
    end.

chk_max_proc_line() ->
    Child = max_proc_line(self(), self(), 0),
    receive
        {proc_line_length, PLL, End} ->
            PC = erlang:system_info(process_count),
            LP = length(processes()),
            io:format("proc line length = ~p; "
                      "process count = ~p; "
                      "length processes = ~p~n",
                      [PLL, PC, LP]),
                  End ! remove_proc_line,
                  PC = LP,
                  receive {exiting, Child} -> ok end,
                  {PLL, PC}
    end.

chk_max_proc_line_until(NoMoreTests, Res) ->
    receive
        NoMoreTests ->
            done
    after 0 ->
              Res = chk_max_proc_line(),
              chk_max_proc_line_until(NoMoreTests, Res)
    end.

magic_ref(Config) when is_list(Config) ->
    {MRef0, Addr0} = erts_debug:set_internal_state(make, magic_ref),
    true = is_reference(MRef0),
    {Addr0, 1, true} = erts_debug:get_internal_state({magic_ref,MRef0}),
    MRef1 = binary_to_term(term_to_binary(MRef0)),
    {Addr0, 2, true} = erts_debug:get_internal_state({magic_ref,MRef1}),
    MRef0 = MRef1,
    Me = self(),
    {Pid, Mon} = spawn_opt(fun () ->
				   receive
				       {Me, MRef} ->
					   Me ! {self(), erts_debug:get_internal_state({magic_ref,MRef})}
				   end
			   end,
			   [link, monitor]),
    Pid ! {self(), MRef0},
    receive
	{Pid, Info} ->
	    {Addr0, 3, true} = Info
    end,
    receive
	{'DOWN', Mon, process, Pid, _} ->
	    ok
    end,
    {Addr0, 2, true} = erts_debug:get_internal_state({magic_ref,MRef0}),
    id(MRef0),
    id(MRef1),
    MRefExt = term_to_binary(erts_debug:set_internal_state(make, magic_ref)),
    garbage_collect(),
    {MRef2, _Addr2} = binary_to_term(MRefExt),
    true = is_reference(MRef2),
    true = erts_debug:get_internal_state({magic_ref,MRef2}),
    ok.


lost_pending_connection(Node) ->
    _ = (catch erts_internal:new_connection(Node)),
    ok.

dist_entry_gc(Config) when is_list(Config) ->
    Me = self(),
    {ok, Node} = start_node(get_nodefirstname(), "+zdntgc 0"),
    P = spawn_link(Node,
                   fun () ->
                           LostNode = list_to_atom("lost_pending_connection@" ++ hostname()),
                           lost_pending_connection(LostNode),
                           garbage_collect(), %% Could crash...
                           Me ! {self(), ok}
                   end),
    receive
        {P, ok} -> ok
    end,
    unlink(P),
    stop_node(Node),
    ok.

%%
%% -- Internal utils ---------------------------------------------------------
%%

id(X) ->
    X.

-define(ND_REFS, erts_debug:get_internal_state(node_and_dist_references)).

node_container_refc_check(Node) when is_atom(Node) ->
    AIS = available_internal_state(true),
    nc_refc_check(Node),
    available_internal_state(AIS).

nc_refc_check(Node) when is_atom(Node) ->
    Ref = make_ref(),
    Self = self(),
    io:format("Starting reference count check of node ~w~n", [Node]),
    spawn_link(Node,
               fun () ->
                       {{node_references, NodeRefs},
                        {dist_references, DistRefs}} = ?ND_REFS,
                       check_nd_refc({node(), erlang:system_info(creation)},
                                     NodeRefs,
                                     DistRefs,
                                     fun (ErrMsg) ->
                                             Self ! {Ref, ErrMsg, failed},
                                             exit(normal)
                                     end),
                       Self ! {Ref, succeded}
               end),
    receive
        {Ref, ErrorMsg, failed} ->
            io:format("~s~n", [ErrorMsg]),
            ct:fail(reference_count_check_failed);
        {Ref, succeded} ->
            io:format("Reference count check of node ~w succeded!~n", [Node]),
            ok
    end.

check_nd_refc({ThisNodeName, ThisCreation}, NodeRefs, DistRefs, Fail) ->
    case catch begin
                   check_refc(ThisNodeName,ThisCreation,"node table",NodeRefs),
                   check_refc(ThisNodeName,ThisCreation,"dist table",DistRefs),
                   ok
               end of
        ok ->
            ok;
        {'EXIT', Reason} ->
            {Y,Mo,D} = date(),
            {H,Mi,S} = time(),
            ErrMsg = io_lib:format("~n"
                                   "*** Reference count check of node ~w "
                                   "failed (~p) at ~w~w~w ~w:~w:~w~n"
                                   "*** Node table references:~n ~p~n"
                                   "*** Dist table references:~n ~p~n",
                                   [node(), Reason, Y, Mo, D, H, Mi, S,
                                    NodeRefs, DistRefs]),
            Fail(lists:flatten(ErrMsg))
    end.


check_refc(ThisNodeName,ThisCreation,Table,EntryList) when is_list(EntryList) ->
    lists:foreach(
      fun ({Entry, Refc, ReferrerList}) ->
              {DelayedDeleteTimer,
               FoundRefs} =
              lists:foldl(
                fun ({Referrer, ReferencesList}, {DDT, A1}) ->
                        {case Referrer of
                             {system,delayed_delete_timer} ->
                                 true;
                             {system,thread_progress_delete_timer} ->
                                 true;
                             _ ->
                                 DDT
                         end,
                         A1 + lists:foldl(fun ({_T,Rs},A2) ->
                                                  A2+Rs
                                          end,
                                          0,
                                          ReferencesList)}
                end,
                {false, 0},
                ReferrerList),

              %% Reference count equals found references?
              case {Refc, FoundRefs, DelayedDeleteTimer} of
                  {X, X, _} ->
                      ok;
                  {0, 1, true} ->
                      ok;
                  _ ->
                      exit({invalid_reference_count, Table, Entry})
              end,

              %% All entries in table referred to?
              case {Entry, Refc} of
                  {ThisNodeName, 0} -> ok;
                  {{ThisNodeName, ThisCreation}, 0} -> ok;
                  {_, 0} when DelayedDeleteTimer == false ->
                      exit({not_referred_entry_in_table, Table, Entry});
                  {_, _} -> ok 
              end

      end,
      EntryList),
    ok.

get_node_references({NodeName, Creation} = Node) when is_atom(NodeName),
                                                      is_integer(Creation) ->
    {{node_references, NodeRefs},
     {dist_references, DistRefs}} = ?ND_REFS,
    check_nd_refc({node(), erlang:system_info(creation)},
                  NodeRefs,
                  DistRefs,
                  fun (ErrMsg) ->
                          io:format("~s", [ErrMsg]),
                          ct:fail(reference_count_check_failed)
                  end),
    find_references(Node, NodeRefs).

get_dist_references(NodeName) when is_atom(NodeName) ->
    {{node_references, NodeRefs},
     {dist_references, DistRefs}} = ?ND_REFS,
    check_nd_refc({node(), erlang:system_info(creation)},
                  NodeRefs,
                  DistRefs,
                  fun (ErrMsg) ->
                          io:format("~s", [ErrMsg]),
                          ct:fail(reference_count_check_failed)
                  end),
    find_references(NodeName, DistRefs).

find_references(N, NRefList) ->
    case lists:keysearch(N, 1, NRefList) of
        {value, {N, _, ReferrersList}} -> ReferrersList;
        _ -> false
    end.

%% Currently unused
% refering_entity_type(RefererType, ReferingEntities) ->
%     lists:filter(fun ({{RT, _}, _}) when RT == RefererType ->
% 			 true;
% 		     (_) ->
% 			 false
% 		 end,
% 		 ReferingEntities).

refering_entity_id(ReferingEntityId, [{ReferingEntityId,_} = ReferingEntity
                                      | _ReferingEntities]) ->
    ReferingEntity;
refering_entity_id(ReferingEntityId, [_ | ReferingEntities]) ->
    refering_entity_id(ReferingEntityId, ReferingEntities);
refering_entity_id(_, []) ->
    false.

reference_type_count(_, false) ->
    0;
reference_type_count(Type, {_, _ReferenceCountList} = ReferingEntity) ->
    reference_type_count(Type, [ReferingEntity]);
reference_type_count(Type, ReferingEntities) when is_list(ReferingEntities) ->
    lists:foldl(fun ({_, ReferenceCountList}, Acc1) ->
                        lists:foldl(fun ({T, N}, Acc2) when T == Type ->
                                            N + Acc2;
                                        (_, Acc2) ->
                                            Acc2
                                    end,
                                    Acc1,
                                    ReferenceCountList)
                end,
                0,
                ReferingEntities).


start_node(Name, Args) ->
    Pa = filename:dirname(code:which(?MODULE)),
    Res = test_server:start_node(Name,
                                 slave,
                                 [{args, "-pa "++Pa++" "++Args}]),
    {ok, Node} = Res,
    rpc:call(Node, erts_debug, set_internal_state,
             [available_internal_state, true]),
    Res.

start_node(Name) ->
    start_node(Name, "").

stop_node(Node) ->
    nc_refc_check(Node),
    true = test_server:stop_node(Node).

hostname() ->
    from($@, atom_to_list(node())).

from(H, [H | T]) -> T;
from(H, [_ | T]) -> from(H, T);
from(_H, []) -> [].

wait_until(Pred) ->
    case Pred() of
        true -> ok;
        false -> receive after 100 -> wait_until(Pred) end
    end.

get_nodefirstname_string() ->
    atom_to_list(?MODULE)
    ++ "-"
    ++ integer_to_list(erlang:system_time(second))
    ++ "-"
    ++ integer_to_list(erlang:unique_integer([positive])).

get_nodefirstname() ->
    list_to_atom(get_nodefirstname_string()).

get_nodename() ->
    list_to_atom(get_nodefirstname_string()
                 ++ "@"
                 ++ hostname()).



-define(VERSION_MAGIC,       131).

-define(ATOM_EXT,            100).
-define(REFERENCE_EXT,       101).
-define(PORT_EXT,            102).
-define(PID_EXT,             103).
-define(NEW_REFERENCE_EXT,   114).
-define(NEW_PID_EXT,         $X).
-define(NEW_PORT_EXT,        $Y).
-define(NEWER_REFERENCE_EXT, $Z).

uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 ->
    [(Uint bsr 24) band 16#ff,
     (Uint bsr 16) band 16#ff,
     (Uint bsr 8) band 16#ff,
     Uint band 16#ff];
uint32_be(Uint) ->
    exit({badarg, uint32_be, [Uint]}).


uint16_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 16 ->
    [(Uint bsr 8) band 16#ff,
     Uint band 16#ff];
uint16_be(Uint) ->
    exit({badarg, uint16_be, [Uint]}).

uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 ->
    Uint band 16#ff;
uint8(Uint) ->
    exit({badarg, uint8, [Uint]}).


pid_tag(bad_creation) -> ?PID_EXT;
pid_tag(Creation) when Creation =< 3 -> ?PID_EXT;
pid_tag(_Creation) -> ?NEW_PID_EXT.

enc_creation(bad_creation) -> uint8(4);
enc_creation(Creation) when Creation =< 3 -> uint8(Creation);
enc_creation(Creation) -> uint32_be(Creation).

mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) ->
    mk_pid({atom_to_list(NodeName), Creation}, Number, Serial);
mk_pid({NodeName, Creation}, Number, Serial) ->
    case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
					      pid_tag(Creation),
					      ?ATOM_EXT,
					      uint16_be(length(NodeName)),
					      NodeName,
					      uint32_be(Number),
					      uint32_be(Serial),
					      enc_creation(Creation)])) of
	Pid when is_pid(Pid) ->
	    Pid;
	{'EXIT', {badarg, _}} ->
	    exit({badarg, mk_pid, [{NodeName, Creation}, Number, Serial]});
	Other ->
	    exit({unexpected_binary_to_term_result, Other})
    end.

port_tag(bad_creation) -> ?PORT_EXT;
port_tag(Creation) when Creation =< 3 -> ?PORT_EXT;
port_tag(_Creation) -> ?NEW_PORT_EXT.

mk_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
    mk_port({atom_to_list(NodeName), Creation}, Number);
mk_port({NodeName, Creation}, Number) ->
    case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
					      port_tag(Creation),
					      ?ATOM_EXT,
					      uint16_be(length(NodeName)),
					      NodeName,
					      uint32_be(Number),
					      enc_creation(Creation)])) of
	Port when is_port(Port) ->
	    Port;
	{'EXIT', {badarg, _}} ->
	    exit({badarg, mk_port, [{NodeName, Creation}, Number]});
	Other ->
	    exit({unexpected_binary_to_term_result, Other})
    end.

ref_tag(bad_creation) -> ?NEW_REFERENCE_EXT;
ref_tag(Creation) when Creation =< 3 -> ?NEW_REFERENCE_EXT;
ref_tag(_Creation) -> ?NEWER_REFERENCE_EXT.

mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName),
					   is_list(Numbers) ->
    mk_ref({atom_to_list(NodeName), Creation}, Numbers);
mk_ref({NodeName, Creation}, [Number]) when is_list(NodeName),
					    Creation =< 3,
					    is_integer(Number) ->
    case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
                                              ?REFERENCE_EXT,
                                              ?ATOM_EXT,
                                              uint16_be(length(NodeName)),
                                              NodeName,
                                              uint32_be(Number),
                                              uint8(Creation)])) of
        Ref when is_reference(Ref) ->
            Ref;
        {'EXIT', {badarg, _}} ->
            exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]});
        Other ->
            exit({unexpected_binary_to_term_result, Other})
    end;
mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
					   is_list(Numbers) ->
    case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
					      ref_tag(Creation),
					      uint16_be(length(Numbers)),
					      ?ATOM_EXT,
					      uint16_be(length(NodeName)),
					      NodeName,
					      enc_creation(Creation),
					      lists:map(fun (N) ->
								uint32_be(N)
							end,
							Numbers)])) of
	Ref when is_reference(Ref) ->
	    Ref;
	{'EXIT', {badarg, _}} ->
	    exit({badarg, mk_ref, [{NodeName, Creation}, Numbers]});
	Other ->
	    exit({unexpected_binary_to_term_result, Other})
    end.

exec_loop() ->
    receive
        {exec_fun, Fun} when is_function(Fun) ->
            Fun();
        {sync_exec_fun, From, Fun} when is_pid(From), is_function(Fun)  ->
            From ! {sync_exec_fun_res, self(), Fun()}
    end,
    exec_loop().

spawn_execer(Node) ->
    spawn(Node, fun () -> exec_loop() end).

spawn_link_execer(Node) ->
    spawn_link(Node, fun () -> exec_loop() end).

exec(Pid, Fun) when is_pid(Pid), is_function(Fun) ->
    Pid ! {exec_fun, Fun}.

sync_exec(Pid, Fun) when is_pid(Pid), is_function(Fun) ->
    Pid ! {sync_exec_fun, self(), Fun},
    receive
        {sync_exec_fun_res, Pid, Res} ->
            Res
    end.