1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 2012-2013. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: %% 19: 20: -module(code_parallel_load_SUITE). 21: -export([ 22: all/0, 23: suite/0, 24: init_per_suite/1, 25: end_per_suite/1, 26: init_per_testcase/2, 27: end_per_testcase/2 28: ]). 29: 30: -export([ 31: multiple_load_check_purge_repeat/1, 32: many_load_distributed_only_once/1 33: ]). 34: 35: -define(model, code_parallel_load_SUITE_model). 36: -define(interval, 50). 37: -define(number_of_processes, 160). 38: -define(passes, 4). 39: 40: 41: -include_lib("test_server/include/test_server.hrl"). 42: 43: suite() -> [{ct_hooks,[ts_install_cth]}]. 44: 45: all() -> 46: [ 47: multiple_load_check_purge_repeat, 48: many_load_distributed_only_once 49: ]. 50: 51: 52: init_per_suite(Config) -> 53: Config. 54: 55: end_per_suite(_Config) -> 56: ok. 57: 58: init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> 59: Dog=?t:timetrap(?t:minutes(3)), 60: [{watchdog, Dog}|Config]. 61: 62: end_per_testcase(_Func, Config) -> 63: SConf = ?config(save_config, Config), 64: Pids = proplists:get_value(purge_pids, SConf), 65: 66: case check_old_code(?model) of 67: true -> check_and_purge_processes_code(Pids, ?model); 68: _ -> ok 69: end, 70: case erlang:delete_module(?model) of 71: true -> check_and_purge_processes_code(Pids, ?model); 72: _ -> ok 73: end, 74: Dog=?config(watchdog, Config), 75: ?t:timetrap_cancel(Dog). 76: 77: 78: multiple_load_check_purge_repeat(_Conf) -> 79: Ts = [v1,v2,v3,v4,v5,v6], 80: 81: %% generate code that receives a token, code switches to new code 82: %% then matches this token against a literal code token 83: %% should be identical 84: %% (smoke test for parallel code loading 85: Codes = [{T, generate(?model, [], [ 86: format("check(T) -> receive {_Pid, change, T1} -> " 87: " ~w:check(T1)\n" 88: " after 0 -> T = f(), check(T) end.\n", [?model]), 89: format("f() -> ~w.~n", [T]) 90: ])} || T <- Ts], 91: 92: Pids = setup_code_changer(Codes), 93: {save_config, [{purge_pids,Pids}]}. 94: 95: setup_code_changer([{Token,Code}|Cs] = Codes) -> 96: {module, ?model} = erlang:load_module(?model,Code), 97: Pids = setup_checkers(Token,?number_of_processes), 98: code_changer(Cs, Codes, ?interval,Pids,?passes), 99: Pids. 100: 101: code_changer(_, _, _, Pids, 0) -> 102: [unlink(Pid) || Pid <- Pids], 103: [exit(Pid, die) || Pid <- Pids], 104: io:format("done~n"), 105: ok; 106: code_changer([], Codes, T, Pids, Ps) -> 107: code_changer(Codes, Codes, T, Pids, Ps - 1); 108: code_changer([{Token,Code}|Cs], Codes, T, Pids, Ps) -> 109: receive after T -> 110: io:format("load code with token ~4w : pass ~4w~n", [Token, Ps]), 111: {module, ?model} = erlang:load_module(?model, Code), 112: % this is second time we call load_module for this module 113: % so it should have old code 114: [Pid ! {self(), change, Token} || Pid <- Pids], 115: % should we wait a moment or just blantantly try to check and purge repeatadly? 116: receive after 1 -> ok end, 117: ok = check_and_purge_processes_code(Pids, ?model), 118: code_changer(Cs, Codes, T, Pids, Ps) 119: end. 120: 121: 122: 123: many_load_distributed_only_once(_Conf) -> 124: Ts = [<<"first version">>, <<"second version">>], 125: 126: [{Token1,Code1},{Token2, Code2}] = [{T, generate(?model, [], [ 127: "check({<<\"second version\">> = V, Pid}) -> V = f(), Pid ! {self(), completed, V}, ok;\n" ++ 128: format("check(T) -> receive {Pid, change, T1, B} -> " 129: " Res = erlang:load_module(~w, B), Pid ! {self(), change, Res},\n" 130: " ~w:check({T1, Pid})\n" 131: " after 0 -> T = f(), check(T) end.\n", [?model, ?model]), 132: format("f() -> ~w.~n", [T]) 133: ])} || T <- Ts], 134: 135: 136: {module, ?model} = erlang:load_module(?model, Code1), 137: Pids = setup_checkers(Token1,?number_of_processes), 138: 139: receive after 1000 -> ok end, % give 'em some time to spin up 140: [Pid ! {self(), change, Token2, Code2} || Pid <- Pids], 141: Loads = [receive {Pid, change, Res} -> Res end || Pid <- Pids], 142: [receive {Pid, completed, Token2} -> ok end || Pid <- Pids], 143: 144: ok = ensure_only_one_load(Loads, 0), 145: {save_config, [{purge_pids,Pids}]}. 146: 147: ensure_only_one_load([], 1) -> ok; 148: ensure_only_one_load([], _) -> too_many_loads; 149: ensure_only_one_load([{module, ?model}|Loads], N) -> 150: ensure_only_one_load(Loads, N + 1); 151: ensure_only_one_load([{error, not_purged}|Loads], N) -> 152: ensure_only_one_load(Loads, N). 153: % no other return values are allowed from load_module 154: 155: 156: %% aux 157: 158: setup_checkers(_,0) -> []; 159: setup_checkers(T,N) -> [spawn_link(fun() -> ?model:check(T) end) | setup_checkers(T, N-1)]. 160: 161: check_and_purge_processes_code(Pids, M) -> 162: check_and_purge_processes_code(Pids, M, []). 163: check_and_purge_processes_code([], M, []) -> 164: erlang:purge_module(M), 165: ok; 166: check_and_purge_processes_code([], M, Pending) -> 167: io:format("Processes ~w are still executing old code - retrying.~n", [Pending]), 168: check_and_purge_processes_code(Pending, M, []); 169: check_and_purge_processes_code([Pid|Pids], M, Pending) -> 170: case erlang:check_process_code(Pid, M) of 171: false -> 172: check_and_purge_processes_code(Pids, M, Pending); 173: true -> 174: check_and_purge_processes_code(Pids, M, [Pid|Pending]) 175: end. 176: 177: 178: generate(Module, Attributes, FunStrings) -> 179: FunForms = function_forms(FunStrings), 180: Forms = [ 181: {attribute,1,module,Module}, 182: {attribute,2,export,[FA || {FA,_} <- FunForms]} 183: ] ++ [{attribute, 3, A, V}|| {A, V} <- Attributes] ++ 184: [ Function || {_, Function} <- FunForms], 185: {ok, Module, Bin} = compile:forms(Forms), 186: Bin. 187: 188: 189: function_forms([]) -> []; 190: function_forms([S|Ss]) -> 191: {ok, Ts,_} = erl_scan:string(S), 192: {ok, Form} = erl_parse:parse_form(Ts), 193: Fun = element(3, Form), 194: Arity = element(4, Form), 195: [{{Fun,Arity}, Form}|function_forms(Ss)]. 196: 197: format(F,Ts) -> lists:flatten(io_lib:format(F, Ts)).