1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 1996-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: %% Description: Tests supervisor.erl 20: 21: -module(supervisor_SUITE). 22: 23: -include_lib("common_test/include/ct.hrl"). 24: -define(TIMEOUT, ?t:minutes(1)). 25: 26: %% Testserver specific export 27: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 28: init_per_group/2,end_per_group/2, init_per_testcase/2, 29: end_per_testcase/2]). 30: 31: %% Internal export 32: -export([init/1, terminate_all_children/1, 33: middle9212/0, gen_server9212/0, handle_info/2]). 34: 35: %% API tests 36: -export([ sup_start_normal/1, sup_start_ignore_init/1, 37: sup_start_ignore_child/1, sup_start_ignore_temporary_child/1, 38: sup_start_ignore_temporary_child_start_child/1, 39: sup_start_ignore_temporary_child_start_child_simple/1, 40: sup_start_error_return/1, sup_start_fail/1, sup_stop_infinity/1, 41: sup_stop_timeout/1, sup_stop_brutal_kill/1, child_adm/1, 42: child_adm_simple/1, child_specs/1, extra_return/1]). 43: 44: %% Tests concept permanent, transient and temporary 45: -export([ permanent_normal/1, transient_normal/1, 46: temporary_normal/1, 47: permanent_shutdown/1, transient_shutdown/1, 48: temporary_shutdown/1, 49: faulty_application_shutdown/1, 50: permanent_abnormal/1, transient_abnormal/1, 51: temporary_abnormal/1, temporary_bystander/1]). 52: 53: %% Restart strategy tests 54: -export([ one_for_one/1, 55: one_for_one_escalation/1, one_for_all/1, 56: one_for_all_escalation/1, one_for_all_other_child_fails_restart/1, 57: simple_one_for_one/1, simple_one_for_one_escalation/1, 58: rest_for_one/1, rest_for_one_escalation/1, 59: rest_for_one_other_child_fails_restart/1, 60: simple_one_for_one_extra/1, simple_one_for_one_shutdown/1]). 61: 62: %% Misc tests 63: -export([child_unlink/1, tree/1, count_children_memory/1, 64: do_not_save_start_parameters_for_temporary_children/1, 65: do_not_save_child_specs_for_temporary_children/1, 66: simple_one_for_one_scale_many_temporary_children/1, 67: simple_global_supervisor/1, hanging_restart_loop/1, 68: hanging_restart_loop_simple/1]). 69: 70: %%------------------------------------------------------------------------- 71: 72: suite() -> 73: [{ct_hooks,[ts_install_cth]}]. 74: 75: all() -> 76: [{group, sup_start}, {group, sup_stop}, child_adm, 77: child_adm_simple, extra_return, child_specs, 78: {group, restart_one_for_one}, 79: {group, restart_one_for_all}, 80: {group, restart_simple_one_for_one}, 81: {group, restart_rest_for_one}, 82: {group, normal_termination}, 83: {group, shutdown_termination}, 84: {group, abnormal_termination}, child_unlink, tree, 85: count_children_memory, do_not_save_start_parameters_for_temporary_children, 86: do_not_save_child_specs_for_temporary_children, 87: simple_one_for_one_scale_many_temporary_children, temporary_bystander, 88: simple_global_supervisor, hanging_restart_loop, hanging_restart_loop_simple]. 89: 90: groups() -> 91: [{sup_start, [], 92: [sup_start_normal, sup_start_ignore_init, 93: sup_start_ignore_child, sup_start_ignore_temporary_child, 94: sup_start_ignore_temporary_child_start_child, 95: sup_start_ignore_temporary_child_start_child_simple, 96: sup_start_error_return, sup_start_fail]}, 97: {sup_stop, [], 98: [sup_stop_infinity, sup_stop_timeout, 99: sup_stop_brutal_kill]}, 100: {normal_termination, [], 101: [permanent_normal, transient_normal, temporary_normal]}, 102: {shutdown_termination, [], 103: [permanent_shutdown, transient_shutdown, temporary_shutdown, 104: faulty_application_shutdown]}, 105: {abnormal_termination, [], 106: [permanent_abnormal, transient_abnormal, 107: temporary_abnormal]}, 108: {restart_one_for_one, [], 109: [one_for_one, one_for_one_escalation]}, 110: {restart_one_for_all, [], 111: [one_for_all, one_for_all_escalation, 112: one_for_all_other_child_fails_restart]}, 113: {restart_simple_one_for_one, [], 114: [simple_one_for_one, simple_one_for_one_shutdown, 115: simple_one_for_one_extra, simple_one_for_one_escalation]}, 116: {restart_rest_for_one, [], 117: [rest_for_one, rest_for_one_escalation, 118: rest_for_one_other_child_fails_restart]}]. 119: 120: init_per_suite(Config) -> 121: Config. 122: 123: end_per_suite(_Config) -> 124: ok. 125: 126: init_per_group(_GroupName, Config) -> 127: Config. 128: 129: end_per_group(_GroupName, Config) -> 130: Config. 131: 132: init_per_testcase(count_children_memory, Config) -> 133: try erlang:memory() of 134: _ -> 135: erts_debug:set_internal_state(available_internal_state, true), 136: Dog = ?t:timetrap(?TIMEOUT), 137: [{watchdog,Dog}|Config] 138: catch error:notsup -> 139: {skip, "+Meamin used during test; erlang:memory/1 not available"} 140: end; 141: init_per_testcase(_Case, Config) -> 142: Dog = ?t:timetrap(?TIMEOUT), 143: [{watchdog,Dog}|Config]. 144: 145: end_per_testcase(count_children_memory, Config) -> 146: catch erts_debug:set_internal_state(available_internal_state, false), 147: ?t:timetrap_cancel(?config(watchdog,Config)), 148: ok; 149: end_per_testcase(_Case, Config) -> 150: ?t:timetrap_cancel(?config(watchdog,Config)), 151: ok. 152: 153: start_link(InitResult) -> 154: supervisor:start_link({local, sup_test}, ?MODULE, InitResult). 155: 156: %% Simulate different supervisors callback. 157: init(fail) -> 158: erlang:error({badmatch,2}); 159: init(InitResult) -> 160: InitResult. 161: 162: %% Respect proplist return of supervisor:count_children 163: get_child_counts(Supervisor) -> 164: Counts = supervisor:count_children(Supervisor), 165: [proplists:get_value(specs, Counts), 166: proplists:get_value(active, Counts), 167: proplists:get_value(supervisors, Counts), 168: proplists:get_value(workers, Counts)]. 169: 170: %%------------------------------------------------------------------------- 171: %% Test cases starts here. 172: %% ------------------------------------------------------------------------- 173: %% Tests that the supervisor process starts correctly and that it can 174: %% be terminated gracefully. 175: sup_start_normal(Config) when is_list(Config) -> 176: process_flag(trap_exit, true), 177: {ok, Pid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 178: terminate(Pid, shutdown). 179: 180: %%------------------------------------------------------------------------- 181: %% Tests what happens if init-callback returns ignore. 182: sup_start_ignore_init(Config) when is_list(Config) -> 183: process_flag(trap_exit, true), 184: ignore = start_link(ignore), 185: check_exit_reason(normal). 186: 187: %%------------------------------------------------------------------------- 188: %% Tests what happens if init-callback returns ignore. 189: sup_start_ignore_child(Config) when is_list(Config) -> 190: process_flag(trap_exit, true), 191: {ok, _Pid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 192: Child1 = {child1, {supervisor_1, start_child, [ignore]}, 193: permanent, 1000, worker, []}, 194: Child2 = {child2, {supervisor_1, start_child, []}, permanent, 195: 1000, worker, []}, 196: 197: {ok, undefined} = supervisor:start_child(sup_test, Child1), 198: {ok, CPid2} = supervisor:start_child(sup_test, Child2), 199: 200: [{child2, CPid2, worker, []},{child1, undefined, worker, []}] 201: = supervisor:which_children(sup_test), 202: [2,1,0,2] = get_child_counts(sup_test). 203: 204: %%------------------------------------------------------------------------- 205: %% Tests what happens if child's init-callback returns ignore for a 206: %% temporary child when ChildSpec is returned directly from supervisor 207: %% init callback. 208: %% Child spec shall NOT be saved!!! 209: sup_start_ignore_temporary_child(Config) when is_list(Config) -> 210: process_flag(trap_exit, true), 211: Child1 = {child1, {supervisor_1, start_child, [ignore]}, 212: temporary, 1000, worker, []}, 213: Child2 = {child2, {supervisor_1, start_child, []}, temporary, 214: 1000, worker, []}, 215: {ok, _Pid} = start_link({ok, {{one_for_one, 2, 3600}, [Child1,Child2]}}), 216: 217: [{child2, CPid2, worker, []}] = supervisor:which_children(sup_test), 218: true = is_pid(CPid2), 219: [1,1,0,1] = get_child_counts(sup_test). 220: 221: %%------------------------------------------------------------------------- 222: %% Tests what happens if child's init-callback returns ignore for a 223: %% temporary child when child is started with start_child/2. 224: %% Child spec shall NOT be saved!!! 225: sup_start_ignore_temporary_child_start_child(Config) when is_list(Config) -> 226: process_flag(trap_exit, true), 227: {ok, _Pid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 228: Child1 = {child1, {supervisor_1, start_child, [ignore]}, 229: temporary, 1000, worker, []}, 230: Child2 = {child2, {supervisor_1, start_child, []}, temporary, 231: 1000, worker, []}, 232: 233: {ok, undefined} = supervisor:start_child(sup_test, Child1), 234: {ok, CPid2} = supervisor:start_child(sup_test, Child2), 235: 236: [{child2, CPid2, worker, []}] = supervisor:which_children(sup_test), 237: [1,1,0,1] = get_child_counts(sup_test). 238: 239: %%------------------------------------------------------------------------- 240: %% Tests what happens if child's init-callback returns ignore for a 241: %% temporary child when child is started with start_child/2, and the 242: %% supervisor is simple_one_for_one. 243: %% Child spec shall NOT be saved!!! 244: sup_start_ignore_temporary_child_start_child_simple(Config) 245: when is_list(Config) -> 246: process_flag(trap_exit, true), 247: Child1 = {child1, {supervisor_1, start_child, [ignore]}, 248: temporary, 1000, worker, []}, 249: {ok, _Pid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child1]}}), 250: 251: {ok, undefined} = supervisor:start_child(sup_test, []), 252: {ok, CPid2} = supervisor:start_child(sup_test, []), 253: 254: [{undefined, CPid2, worker, []}] = supervisor:which_children(sup_test), 255: [1,1,0,1] = get_child_counts(sup_test). 256: 257: %%------------------------------------------------------------------------- 258: %% Tests what happens if init-callback returns a invalid value. 259: sup_start_error_return(Config) when is_list(Config) -> 260: process_flag(trap_exit, true), 261: {error, Term} = start_link(invalid), 262: check_exit_reason(Term). 263: 264: %%------------------------------------------------------------------------- 265: %% Tests what happens if init-callback fails. 266: sup_start_fail(Config) when is_list(Config) -> 267: process_flag(trap_exit, true), 268: {error, Term} = start_link(fail), 269: check_exit_reason(Term). 270: 271: %%------------------------------------------------------------------------- 272: %% See sup_stop/1 when Shutdown = infinity, this walue is allowed for 273: %% children of type supervisor _AND_ worker. 274: sup_stop_infinity(Config) when is_list(Config) -> 275: process_flag(trap_exit, true), 276: {ok, Pid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 277: Child1 = {child1, {supervisor_1, start_child, []}, 278: permanent, infinity, supervisor, []}, 279: Child2 = {child2, {supervisor_1, start_child, []}, permanent, 280: infinity, worker, []}, 281: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 282: {ok, CPid2} = supervisor:start_child(sup_test, Child2), 283: link(CPid1), 284: link(CPid2), 285: 286: terminate(Pid, shutdown), 287: check_exit_reason(CPid1, shutdown), 288: check_exit_reason(CPid2, shutdown). 289: 290: %%------------------------------------------------------------------------- 291: %% See sup_stop/1 when Shutdown = 1000 292: sup_stop_timeout(Config) when is_list(Config) -> 293: process_flag(trap_exit, true), 294: {ok, Pid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 295: Child1 = {child1, {supervisor_1, start_child, []}, 296: permanent, 1000, worker, []}, 297: Child2 = {child2, {supervisor_1, start_child, []}, permanent, 298: 1000, worker, []}, 299: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 300: link(CPid1), 301: {ok, CPid2} = supervisor:start_child(sup_test, Child2), 302: link(CPid2), 303: 304: CPid2 ! {sleep, 200000}, 305: 306: terminate(Pid, shutdown), 307: 308: check_exit_reason(CPid1, shutdown), 309: check_exit_reason(CPid2, killed). 310: 311: 312: %%------------------------------------------------------------------------- 313: %% See sup_stop/1 when Shutdown = brutal_kill 314: sup_stop_brutal_kill(Config) when is_list(Config) -> 315: process_flag(trap_exit, true), 316: {ok, Pid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 317: Child1 = {child1, {supervisor_1, start_child, []}, 318: permanent, 1000, worker, []}, 319: Child2 = {child2, {supervisor_1, start_child, []}, permanent, 320: brutal_kill, worker, []}, 321: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 322: link(CPid1), 323: {ok, CPid2} = supervisor:start_child(sup_test, Child2), 324: link(CPid2), 325: 326: terminate(Pid, shutdown), 327: 328: check_exit_reason(CPid1, shutdown), 329: check_exit_reason(CPid2, killed). 330: 331: %%------------------------------------------------------------------------- 332: %% The start function provided to start a child may return {ok, Pid} 333: %% or {ok, Pid, Info}, if it returns the latter check that the 334: %% supervisor ignores the Info, and includes it unchanged in return 335: %% from start_child/2 and restart_child/2. 336: extra_return(Config) when is_list(Config) -> 337: process_flag(trap_exit, true), 338: Child = {child1, {supervisor_1, start_child, [extra_return]}, 339: permanent, 1000, 340: worker, []}, 341: {ok, _Pid} = start_link({ok, {{one_for_one, 2, 3600}, [Child]}}), 342: [{child1, CPid, worker, []}] = supervisor:which_children(sup_test), 343: link(CPid), 344: {error, not_found} = supervisor:terminate_child(sup_test, hej), 345: {error, not_found} = supervisor:delete_child(sup_test, hej), 346: {error, not_found} = supervisor:restart_child(sup_test, hej), 347: {error, running} = supervisor:delete_child(sup_test, child1), 348: {error, running} = supervisor:restart_child(sup_test, child1), 349: [{child1, CPid, worker, []}] = supervisor:which_children(sup_test), 350: [1,1,0,1] = get_child_counts(sup_test), 351: 352: ok = supervisor:terminate_child(sup_test, child1), 353: check_exit_reason(CPid, shutdown), 354: [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test), 355: [1,0,0,1] = get_child_counts(sup_test), 356: 357: {ok, CPid2,extra_return} = 358: supervisor:restart_child(sup_test, child1), 359: [{child1, CPid2, worker, []}] = supervisor:which_children(sup_test), 360: [1,1,0,1] = get_child_counts(sup_test), 361: 362: ok = supervisor:terminate_child(sup_test, child1), 363: ok = supervisor:terminate_child(sup_test, child1), 364: ok = supervisor:delete_child(sup_test, child1), 365: {error, not_found} = supervisor:restart_child(sup_test, child1), 366: [] = supervisor:which_children(sup_test), 367: [0,0,0,0] = get_child_counts(sup_test), 368: 369: {ok, CPid3, extra_return} = supervisor:start_child(sup_test, Child), 370: [{child1, CPid3, worker, []}] = supervisor:which_children(sup_test), 371: [1,1,0,1] = get_child_counts(sup_test), 372: 373: ok. 374: %%------------------------------------------------------------------------- 375: %% Test API functions start_child/2, terminate_child/2, delete_child/2 376: %% restart_child/2, which_children/1, count_children/1. Only correct 377: %% childspecs are used, handling of incorrect childspecs is tested in 378: %% child_specs/1. 379: child_adm(Config) when is_list(Config) -> 380: process_flag(trap_exit, true), 381: Child = {child1, {supervisor_1, start_child, []}, permanent, 1000, 382: worker, []}, 383: {ok, _Pid} = start_link({ok, {{one_for_one, 2, 3600}, [Child]}}), 384: [{child1, CPid, worker, []}] = supervisor:which_children(sup_test), 385: [1,1,0,1] = get_child_counts(sup_test), 386: link(CPid), 387: 388: %% Start of an already runnig process 389: {error,{already_started, CPid}} = 390: supervisor:start_child(sup_test, Child), 391: 392: %% Termination 393: {error, not_found} = supervisor:terminate_child(sup_test, hej), 394: {'EXIT',{noproc,{gen_server,call, _}}} = 395: (catch supervisor:terminate_child(foo, child1)), 396: ok = supervisor:terminate_child(sup_test, child1), 397: check_exit_reason(CPid, shutdown), 398: [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test), 399: [1,0,0,1] = get_child_counts(sup_test), 400: %% Like deleting something that does not exist, it will succeed! 401: ok = supervisor:terminate_child(sup_test, child1), 402: 403: %% Start of already existing but not running process 404: {error,already_present} = supervisor:start_child(sup_test, Child), 405: 406: %% Restart 407: {ok, CPid2} = supervisor:restart_child(sup_test, child1), 408: [{child1, CPid2, worker, []}] = supervisor:which_children(sup_test), 409: [1,1,0,1] = get_child_counts(sup_test), 410: {error, running} = supervisor:restart_child(sup_test, child1), 411: {error, not_found} = supervisor:restart_child(sup_test, child2), 412: 413: %% Deletion 414: {error, running} = supervisor:delete_child(sup_test, child1), 415: {error, not_found} = supervisor:delete_child(sup_test, hej), 416: {'EXIT',{noproc,{gen_server,call, _}}} = 417: (catch supervisor:delete_child(foo, child1)), 418: ok = supervisor:terminate_child(sup_test, child1), 419: ok = supervisor:delete_child(sup_test, child1), 420: {error, not_found} = supervisor:restart_child(sup_test, child1), 421: [] = supervisor:which_children(sup_test), 422: [0,0,0,0] = get_child_counts(sup_test), 423: 424: %% Start 425: {'EXIT',{noproc,{gen_server,call, _}}} = 426: (catch supervisor:start_child(foo, Child)), 427: {ok, CPid3} = supervisor:start_child(sup_test, Child), 428: [{child1, CPid3, worker, []}] = supervisor:which_children(sup_test), 429: [1,1,0,1] = get_child_counts(sup_test), 430: 431: %% Terminate with Pid not allowed when not simple_one_for_one 432: {error,not_found} = supervisor:terminate_child(sup_test, CPid3), 433: [{child1, CPid3, worker, []}] = supervisor:which_children(sup_test), 434: [1,1,0,1] = get_child_counts(sup_test), 435: 436: {'EXIT',{noproc,{gen_server,call,[foo,which_children,infinity]}}} 437: = (catch supervisor:which_children(foo)), 438: {'EXIT',{noproc,{gen_server,call,[foo,count_children,infinity]}}} 439: = (catch supervisor:count_children(foo)), 440: ok. 441: %%------------------------------------------------------------------------- 442: %% The API functions terminate_child/2, delete_child/2 restart_child/2 443: %% are not valid for a simple_one_for_one supervisor check that the 444: %% correct error message is returned. 445: child_adm_simple(Config) when is_list(Config) -> 446: Child = {child, {supervisor_1, start_child, []}, permanent, 1000, 447: worker, []}, 448: {ok, _Pid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), 449: %% In simple_one_for_one all children are added dynamically 450: [] = supervisor:which_children(sup_test), 451: [1,0,0,0] = get_child_counts(sup_test), 452: 453: %% Start 454: {'EXIT',{noproc,{gen_server,call, _}}} = 455: (catch supervisor:start_child(foo, [])), 456: {ok, CPid1} = supervisor:start_child(sup_test, []), 457: [{undefined, CPid1, worker, []}] = 458: supervisor:which_children(sup_test), 459: [1,1,0,1] = get_child_counts(sup_test), 460: 461: {ok, CPid2} = supervisor:start_child(sup_test, []), 462: Children = supervisor:which_children(sup_test), 463: 2 = length(Children), 464: true = lists:member({undefined, CPid2, worker, []}, Children), 465: true = lists:member({undefined, CPid1, worker, []}, Children), 466: [1,2,0,2] = get_child_counts(sup_test), 467: 468: %% Termination 469: {error, simple_one_for_one} = supervisor:terminate_child(sup_test, child1), 470: [1,2,0,2] = get_child_counts(sup_test), 471: ok = supervisor:terminate_child(sup_test,CPid1), 472: [_] = supervisor:which_children(sup_test), 473: [1,1,0,1] = get_child_counts(sup_test), 474: false = erlang:is_process_alive(CPid1), 475: %% Terminate non-existing proccess is ok 476: ok = supervisor:terminate_child(sup_test,CPid1), 477: [_] = supervisor:which_children(sup_test), 478: [1,1,0,1] = get_child_counts(sup_test), 479: %% Terminate pid which is not a child of this supervisor is not ok 480: NoChildPid = spawn_link(fun() -> receive after infinity -> ok end end), 481: {error, not_found} = supervisor:terminate_child(sup_test, NoChildPid), 482: true = erlang:is_process_alive(NoChildPid), 483: 484: %% Restart 485: {error, simple_one_for_one} = supervisor:restart_child(sup_test, child1), 486: 487: %% Deletion 488: {error, simple_one_for_one} = supervisor:delete_child(sup_test, child1), 489: ok. 490: 491: %%------------------------------------------------------------------------- 492: %% Tests child specs, invalid formats should be rejected. 493: child_specs(Config) when is_list(Config) -> 494: process_flag(trap_exit, true), 495: {ok, _Pid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 496: {error, _} = supervisor:start_child(sup_test, hej), 497: 498: %% Bad child specs 499: B1 = {child, mfa, permanent, 1000, worker, []}, 500: B2 = {child, {m,f,[a]}, prmanent, 1000, worker, []}, 501: B3 = {child, {m,f,[a]}, permanent, -10, worker, []}, 502: B4 = {child, {m,f,[a]}, permanent, 10, wrker, []}, 503: B5 = {child, {m,f,[a]}, permanent, 1000, worker, dy}, 504: B6 = {child, {m,f,[a]}, permanent, 1000, worker, [1,2,3]}, 505: 506: %% Correct child specs! 507: %% <Modules> (last parameter in a child spec) can be [] as we do 508: %% not test code upgrade here. 509: C1 = {child, {m,f,[a]}, permanent, infinity, supervisor, []}, 510: C2 = {child, {m,f,[a]}, permanent, 1000, supervisor, []}, 511: C3 = {child, {m,f,[a]}, temporary, 1000, worker, dynamic}, 512: C4 = {child, {m,f,[a]}, transient, 1000, worker, [m]}, 513: C5 = {child, {m,f,[a]}, permanent, infinity, worker, [m]}, 514: 515: {error, {invalid_mfa,mfa}} = supervisor:start_child(sup_test, B1), 516: {error, {invalid_restart_type, prmanent}} = 517: supervisor:start_child(sup_test, B2), 518: {error, {invalid_shutdown,-10}} 519: = supervisor:start_child(sup_test, B3), 520: {error, {invalid_child_type,wrker}} 521: = supervisor:start_child(sup_test, B4), 522: {error, {invalid_modules,dy}} 523: = supervisor:start_child(sup_test, B5), 524: 525: {error, {invalid_mfa,mfa}} = supervisor:check_childspecs([B1]), 526: {error, {invalid_restart_type,prmanent}} = 527: supervisor:check_childspecs([B2]), 528: {error, {invalid_shutdown,-10}} = supervisor:check_childspecs([B3]), 529: {error, {invalid_child_type,wrker}} 530: = supervisor:check_childspecs([B4]), 531: {error, {invalid_modules,dy}} = supervisor:check_childspecs([B5]), 532: {error, {invalid_module, 1}} = 533: supervisor:check_childspecs([B6]), 534: 535: ok = supervisor:check_childspecs([C1]), 536: ok = supervisor:check_childspecs([C2]), 537: ok = supervisor:check_childspecs([C3]), 538: ok = supervisor:check_childspecs([C4]), 539: ok = supervisor:check_childspecs([C5]), 540: ok. 541: 542: %%------------------------------------------------------------------------- 543: %% A permanent child should always be restarted. 544: permanent_normal(Config) when is_list(Config) -> 545: {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 546: Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000, 547: worker, []}, 548: 549: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 550: 551: terminate(SupPid, CPid1, child1, normal), 552: 553: [{child1, Pid ,worker,[]}] = supervisor:which_children(sup_test), 554: case is_pid(Pid) of 555: true -> 556: ok; 557: false -> 558: test_server:fail({permanent_child_not_restarted, Child1}) 559: end, 560: [1,1,0,1] = get_child_counts(sup_test). 561: 562: %%------------------------------------------------------------------------- 563: %% A transient child should not be restarted if it exits with reason 564: %% normal. 565: transient_normal(Config) when is_list(Config) -> 566: {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 567: Child1 = {child1, {supervisor_1, start_child, []}, transient, 1000, 568: worker, []}, 569: 570: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 571: 572: terminate(SupPid, CPid1, child1, normal), 573: 574: [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test), 575: [1,0,0,1] = get_child_counts(sup_test). 576: 577: %%------------------------------------------------------------------------- 578: %% A temporary process should never be restarted. 579: temporary_normal(Config) when is_list(Config) -> 580: {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 581: Child1 = {child1, {supervisor_1, start_child, []}, temporary, 1000, 582: worker, []}, 583: 584: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 585: 586: terminate(SupPid, CPid1, child1, normal), 587: 588: [] = supervisor:which_children(sup_test), 589: [0,0,0,0] = get_child_counts(sup_test). 590: 591: %%------------------------------------------------------------------------- 592: %% A permanent child should always be restarted. 593: permanent_shutdown(Config) when is_list(Config) -> 594: {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 595: Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000, 596: worker, []}, 597: 598: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 599: 600: terminate(SupPid, CPid1, child1, shutdown), 601: 602: [{child1, CPid2 ,worker,[]}] = supervisor:which_children(sup_test), 603: case is_pid(CPid2) of 604: true -> 605: ok; 606: false -> 607: test_server:fail({permanent_child_not_restarted, Child1}) 608: end, 609: [1,1,0,1] = get_child_counts(sup_test), 610: 611: terminate(SupPid, CPid2, child1, {shutdown, some_info}), 612: 613: [{child1, CPid3 ,worker,[]}] = supervisor:which_children(sup_test), 614: case is_pid(CPid3) of 615: true -> 616: ok; 617: false -> 618: test_server:fail({permanent_child_not_restarted, Child1}) 619: end, 620: 621: [1,1,0,1] = get_child_counts(sup_test). 622: 623: %%------------------------------------------------------------------------- 624: %% A transient child should not be restarted if it exits with reason 625: %% shutdown or {shutdown,Term}. 626: transient_shutdown(Config) when is_list(Config) -> 627: {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 628: Child1 = {child1, {supervisor_1, start_child, []}, transient, 1000, 629: worker, []}, 630: 631: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 632: 633: terminate(SupPid, CPid1, child1, shutdown), 634: 635: [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test), 636: [1,0,0,1] = get_child_counts(sup_test), 637: 638: {ok, CPid2} = supervisor:restart_child(sup_test, child1), 639: 640: terminate(SupPid, CPid2, child1, {shutdown, some_info}), 641: 642: [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test), 643: [1,0,0,1] = get_child_counts(sup_test). 644: 645: %%------------------------------------------------------------------------- 646: %% A temporary process should never be restarted. 647: temporary_shutdown(Config) when is_list(Config) -> 648: {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 649: Child1 = {child1, {supervisor_1, start_child, []}, temporary, 1000, 650: worker, []}, 651: 652: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 653: 654: terminate(SupPid, CPid1, child1, shutdown), 655: 656: [] = supervisor:which_children(sup_test), 657: [0,0,0,0] = get_child_counts(sup_test), 658: 659: {ok, CPid2} = supervisor:start_child(sup_test, Child1), 660: 661: terminate(SupPid, CPid2, child1, {shutdown, some_info}), 662: 663: [] = supervisor:which_children(sup_test), 664: [0,0,0,0] = get_child_counts(sup_test). 665: 666: %%------------------------------------------------------------------------- 667: %% Faulty application should shutdown and pass on errors 668: faulty_application_shutdown(Config) when is_list(Config) -> 669: 670: %% Set some paths 671: AppDir = filename:join(?config(data_dir, Config), "app_faulty"), 672: EbinDir = filename:join(AppDir, "ebin"), 673: 674: %% Start faulty app 675: code:add_patha(EbinDir), 676: 677: %% {error, 678: %% {{shutdown, 679: %% {failed_to_start_child, 680: %% app_faulty, 681: %% {undef, 682: %% [{an_undefined_module_with,an_undefined_function,[argument1,argument2], 683: %% []}, 684: %% {app_faulty_server,init,1, 685: %% [{file,"app_faulty/src/app_faulty_server.erl"},{line,16}]}, 686: %% {gen_server,init_it,6, 687: %% [{file,"gen_server.erl"},{line,304}]}, 688: %% {proc_lib,init_p_do_apply,3, 689: %% [{file,"proc_lib.erl"},{line,227}]}]}}}, 690: %% {app_faulty,start,[normal,[]]}}} 691: 692: {error, Error} = application:start(app_faulty), 693: {{shutdown, {failed_to_start_child,app_faulty,{undef, CallStack}}}, 694: {app_faulty,start,_}} = Error, 695: [{an_undefined_module_with,an_undefined_function,_,_}|_] = CallStack, 696: ok = application:unload(app_faulty), 697: ok. 698: 699: %%------------------------------------------------------------------------- 700: %% A permanent child should always be restarted. 701: permanent_abnormal(Config) when is_list(Config) -> 702: {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 703: Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000, 704: worker, []}, 705: 706: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 707: terminate(SupPid, CPid1, child1, abnormal), 708: 709: [{child1, Pid ,worker,[]}] = supervisor:which_children(sup_test), 710: case is_pid(Pid) of 711: true -> 712: ok; 713: false -> 714: test_server:fail({permanent_child_not_restarted, Child1}) 715: end, 716: [1,1,0,1] = get_child_counts(sup_test). 717: 718: %%------------------------------------------------------------------------- 719: %% A transient child should be restarted if it exits with reason abnormal. 720: transient_abnormal(Config) when is_list(Config) -> 721: {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 722: Child1 = {child1, {supervisor_1, start_child, []}, transient, 1000, 723: worker, []}, 724: 725: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 726: terminate(SupPid, CPid1, child1, abnormal), 727: 728: [{child1, Pid ,worker,[]}] = supervisor:which_children(sup_test), 729: case is_pid(Pid) of 730: true -> 731: ok; 732: false -> 733: test_server:fail({transient_child_not_restarted, Child1}) 734: end, 735: [1,1,0,1] = get_child_counts(sup_test). 736: 737: %%------------------------------------------------------------------------- 738: %% A temporary process should never be restarted. 739: temporary_abnormal(Config) when is_list(Config) -> 740: {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 741: Child1 = {child1, {supervisor_1, start_child, []}, temporary, 1000, 742: worker, []}, 743: 744: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 745: terminate(SupPid, CPid1, child1, abnormal), 746: 747: [] = supervisor:which_children(sup_test), 748: [0,0,0,0] = get_child_counts(sup_test). 749: 750: %%------------------------------------------------------------------------- 751: %% A temporary process killed as part of a rest_for_one or one_for_all 752: %% restart strategy should not be restarted given its args are not 753: %% saved. Otherwise the supervisor hits its limit and crashes. 754: temporary_bystander(_Config) -> 755: Child1 = {child1, {supervisor_1, start_child, []}, permanent, 100, 756: worker, []}, 757: Child2 = {child2, {supervisor_1, start_child, []}, temporary, 100, 758: worker, []}, 759: {ok, SupPid1} = supervisor:start_link(?MODULE, {ok, {{one_for_all, 2, 300}, []}}), 760: {ok, SupPid2} = supervisor:start_link(?MODULE, {ok, {{rest_for_one, 2, 300}, []}}), 761: unlink(SupPid1), % otherwise we crash with it 762: unlink(SupPid2), % otherwise we crash with it 763: {ok, CPid1} = supervisor:start_child(SupPid1, Child1), 764: {ok, _CPid2} = supervisor:start_child(SupPid1, Child2), 765: {ok, CPid3} = supervisor:start_child(SupPid2, Child1), 766: {ok, _CPid4} = supervisor:start_child(SupPid2, Child2), 767: terminate(SupPid1, CPid1, child1, normal), 768: terminate(SupPid2, CPid3, child1, normal), 769: timer:sleep(350), 770: catch link(SupPid1), 771: catch link(SupPid2), 772: %% The supervisor would die attempting to restart child2 773: true = erlang:is_process_alive(SupPid1), 774: true = erlang:is_process_alive(SupPid2), 775: %% Child2 has not been restarted 776: [{child1, _, _, _}] = supervisor:which_children(SupPid1), 777: [{child1, _, _, _}] = supervisor:which_children(SupPid2). 778: 779: %%------------------------------------------------------------------------- 780: %% Test the one_for_one base case. 781: one_for_one(Config) when is_list(Config) -> 782: process_flag(trap_exit, true), 783: Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000, 784: worker, []}, 785: Child2 = {child2, {supervisor_1, start_child, []}, permanent, 1000, 786: worker, []}, 787: {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 788: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 789: {ok, CPid2} = supervisor:start_child(sup_test, Child2), 790: 791: terminate(SupPid, CPid1, child1, abnormal), 792: Children = supervisor:which_children(sup_test), 793: if length(Children) == 2 -> 794: case lists:keysearch(CPid2, 2, Children) of 795: {value, _} -> ok; 796: _ -> test_server:fail(bad_child) 797: end; 798: true -> test_server:fail({bad_child_list, Children}) 799: end, 800: [2,2,0,2] = get_child_counts(sup_test), 801: 802: %% Test restart frequency property 803: terminate(SupPid, CPid2, child2, abnormal), 804: 805: [{Id4, Pid4, _, _}|_] = supervisor:which_children(sup_test), 806: terminate(SupPid, Pid4, Id4, abnormal), 807: check_exit([SupPid]). 808: 809: %%------------------------------------------------------------------------- 810: %% Test restart escalation on a one_for_one supervisor. 811: one_for_one_escalation(Config) when is_list(Config) -> 812: process_flag(trap_exit, true), 813: 814: Child1 = {child1, {supervisor_1, start_child, [error]}, 815: permanent, 1000, 816: worker, []}, 817: Child2 = {child2, {supervisor_1, start_child, []}, permanent, 1000, 818: worker, []}, 819: 820: {ok, SupPid} = start_link({ok, {{one_for_one, 4, 3600}, []}}), 821: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 822: {ok, CPid2} = supervisor:start_child(sup_test, Child2), 823: link(CPid2), 824: 825: terminate(SupPid, CPid1, child1, abnormal), 826: check_exit([SupPid, CPid2]). 827: 828: 829: %%------------------------------------------------------------------------- 830: %% Test the one_for_all base case. 831: one_for_all(Config) when is_list(Config) -> 832: process_flag(trap_exit, true), 833: 834: Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000, 835: worker, []}, 836: Child2 = {child2, {supervisor_1, start_child, []}, permanent, 1000, 837: worker, []}, 838: {ok, SupPid} = start_link({ok, {{one_for_all, 2, 3600}, []}}), 839: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 840: {ok, CPid2} = supervisor:start_child(sup_test, Child2), 841: link(CPid2), 842: 843: terminate(SupPid, CPid1, child1, abnormal), 844: check_exit([CPid2]), 845: 846: Children = supervisor:which_children(sup_test), 847: if length(Children) == 2 -> ok; 848: true -> 849: test_server:fail({bad_child_list, Children}) 850: end, 851: 852: %% Test that no old children is still alive 853: not_in_child_list([CPid1, CPid2], lists:map(fun({_,P,_,_}) -> P end, Children)), 854: 855: [2,2,0,2] = get_child_counts(sup_test), 856: 857: %%% Test restart frequency property 858: [{Id3, Pid3, _, _}|_] = supervisor:which_children(sup_test), 859: terminate(SupPid, Pid3, Id3, abnormal), 860: [{Id4, Pid4, _, _}|_] = supervisor:which_children(sup_test), 861: terminate(SupPid, Pid4, Id4, abnormal), 862: check_exit([SupPid]). 863: 864: 865: %%------------------------------------------------------------------------- 866: %% Test restart escalation on a one_for_all supervisor. 867: one_for_all_escalation(Config) when is_list(Config) -> 868: process_flag(trap_exit, true), 869: 870: Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000, 871: worker, []}, 872: Child2 = {child2, {supervisor_1, start_child, [error]}, 873: permanent, 1000, 874: worker, []}, 875: {ok, SupPid} = start_link({ok, {{one_for_all, 4, 3600}, []}}), 876: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 877: {ok, CPid2} = supervisor:start_child(sup_test, Child2), 878: link(CPid2), 879: 880: terminate(SupPid, CPid1, child1, abnormal), 881: check_exit([CPid2, SupPid]). 882: 883: 884: %%------------------------------------------------------------------------- 885: %% Test that the supervisor terminates a restarted child when a different 886: %% child fails to start. 887: one_for_all_other_child_fails_restart(Config) when is_list(Config) -> 888: process_flag(trap_exit, true), 889: Self = self(), 890: Child1 = {child1, {supervisor_3, start_child, [child1, Self]}, 891: permanent, 1000, worker, []}, 892: Child2 = {child2, {supervisor_3, start_child, [child2, Self]}, 893: permanent, 1000, worker, []}, 894: Children = [Child1, Child2], 895: StarterFun = fun() -> 896: {ok, SupPid} = start_link({ok, {{one_for_all, 3, 3600}, Children}}), 897: Self ! {sup_pid, SupPid}, 898: receive {stop, Self} -> ok end 899: end, 900: StarterPid = spawn_link(StarterFun), 901: Ok = {{ok, undefined}, Self}, 902: %% Let the children start. 903: Child1Pid = receive {child1, Pid1} -> Pid1 end, 904: Child1Pid ! Ok, 905: Child2Pid = receive {child2, Pid2} -> Pid2 end, 906: Child2Pid ! Ok, 907: %% Supervisor started. 908: SupPid = receive {sup_pid, Pid} -> Pid end, 909: link(SupPid), 910: exit(Child1Pid, die), 911: %% Let child1 restart but don't let child2. 912: Child1Pid2 = receive {child1, Pid3} -> Pid3 end, 913: Child1Pid2Ref = erlang:monitor(process, Child1Pid2), 914: Child1Pid2 ! Ok, 915: Child2Pid2 = receive {child2, Pid4} -> Pid4 end, 916: Child2Pid2 ! {{stop, normal}, Self}, 917: %% Check child1 is terminated. 918: receive 919: {'DOWN', Child1Pid2Ref, _, _, shutdown} -> 920: ok; 921: {_childName, _Pid} -> 922: exit(SupPid, kill), 923: check_exit([StarterPid, SupPid]), 924: test_server:fail({restarting_child_not_terminated, Child1Pid2}) 925: end, 926: %% Let the restart complete. 927: Child1Pid3 = receive {child1, Pid5} -> Pid5 end, 928: Child1Pid3 ! Ok, 929: Child2Pid3 = receive {child2, Pid6} -> Pid6 end, 930: Child2Pid3 ! Ok, 931: StarterPid ! {stop, Self}, 932: check_exit([StarterPid, SupPid]). 933: 934: 935: %%------------------------------------------------------------------------- 936: %% Test the simple_one_for_one base case. 937: simple_one_for_one(Config) when is_list(Config) -> 938: process_flag(trap_exit, true), 939: Child = {child, {supervisor_1, start_child, []}, permanent, 1000, 940: worker, []}, 941: {ok, SupPid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), 942: {ok, CPid1} = supervisor:start_child(sup_test, []), 943: {ok, CPid2} = supervisor:start_child(sup_test, []), 944: 945: terminate(SupPid, CPid1, child1, abnormal), 946: 947: Children = supervisor:which_children(sup_test), 948: if length(Children) == 2 -> 949: case lists:keysearch(CPid2, 2, Children) of 950: {value, _} -> ok; 951: _ -> test_server:fail(bad_child) 952: end; 953: true -> test_server:fail({bad_child_list, Children}) 954: end, 955: [1,2,0,2] = get_child_counts(sup_test), 956: 957: %% Test restart frequency property 958: terminate(SupPid, CPid2, child2, abnormal), 959: 960: [{Id4, Pid4, _, _}|_] = supervisor:which_children(sup_test), 961: 962: terminate(SupPid, Pid4, Id4, abnormal), 963: check_exit([SupPid]). 964: 965: 966: %%------------------------------------------------------------------------- 967: %% Test simple_one_for_one children shutdown accordingly to the 968: %% supervisor's shutdown strategy. 969: simple_one_for_one_shutdown(Config) when is_list(Config) -> 970: process_flag(trap_exit, true), 971: ShutdownTime = 1000, 972: Child = {child, {supervisor_2, start_child, []}, 973: permanent, 2*ShutdownTime, worker, []}, 974: {ok, SupPid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), 975: 976: %% Will be gracefully shutdown 977: {ok, _CPid1} = supervisor:start_child(sup_test, [ShutdownTime]), 978: {ok, _CPid2} = supervisor:start_child(sup_test, [ShutdownTime]), 979: 980: %% Will be killed after 2*ShutdownTime milliseconds 981: {ok, _CPid3} = supervisor:start_child(sup_test, [5*ShutdownTime]), 982: 983: {T, ok} = timer:tc(fun terminate/2, [SupPid, shutdown]), 984: if T < 1000*ShutdownTime -> 985: %% Because supervisor's children wait before exiting, it can't 986: %% terminate quickly 987: test_server:fail({shutdown_too_short, T}); 988: T >= 1000*5*ShutdownTime -> 989: test_server:fail({shutdown_too_long, T}); 990: true -> 991: check_exit([SupPid]) 992: end. 993: 994: 995: %%------------------------------------------------------------------------- 996: %% Tests automatic restart of children who's start function return 997: %% extra info. 998: simple_one_for_one_extra(Config) when is_list(Config) -> 999: process_flag(trap_exit, true), 1000: Child = {child, {supervisor_1, start_child, [extra_info]}, 1001: permanent, 1000, worker, []}, 1002: {ok, SupPid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), 1003: {ok, CPid1, extra_info} = supervisor:start_child(sup_test, []), 1004: {ok, CPid2, extra_info} = supervisor:start_child(sup_test, []), 1005: link(CPid2), 1006: terminate(SupPid, CPid1, child1, abnormal), 1007: Children = supervisor:which_children(sup_test), 1008: if length(Children) == 2 -> 1009: case lists:keysearch(CPid2, 2, Children) of 1010: {value, _} -> ok; 1011: _ -> test_server:fail(bad_child) 1012: end; 1013: true -> test_server:fail({bad_child_list, Children}) 1014: end, 1015: [1,2,0,2] = get_child_counts(sup_test), 1016: terminate(SupPid, CPid2, child2, abnormal), 1017: [{Id4, Pid4, _, _}|_] = supervisor:which_children(sup_test), 1018: terminate(SupPid, Pid4, Id4, abnormal), 1019: check_exit([SupPid]). 1020: 1021: %%------------------------------------------------------------------------- 1022: %% Test restart escalation on a simple_one_for_one supervisor. 1023: simple_one_for_one_escalation(Config) when is_list(Config) -> 1024: process_flag(trap_exit, true), 1025: Child = {child, {supervisor_1, start_child, []}, permanent, 1000, 1026: worker, []}, 1027: {ok, SupPid} = start_link({ok, {{simple_one_for_one, 4, 3600}, [Child]}}), 1028: {ok, CPid1} = supervisor:start_child(sup_test, [error]), 1029: link(CPid1), 1030: {ok, CPid2} = supervisor:start_child(sup_test, []), 1031: link(CPid2), 1032: 1033: terminate(SupPid, CPid1, child, abnormal), 1034: check_exit([SupPid, CPid2]). 1035: 1036: %%------------------------------------------------------------------------- 1037: %% Test the rest_for_one base case. 1038: rest_for_one(Config) when is_list(Config) -> 1039: process_flag(trap_exit, true), 1040: Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000, 1041: worker, []}, 1042: Child2 = {child2, {supervisor_1, start_child, []}, permanent, 1000, 1043: worker, []}, 1044: Child3 = {child3, {supervisor_1, start_child, []}, permanent, 1000, 1045: worker, []}, 1046: {ok, SupPid} = start_link({ok, {{rest_for_one, 2, 3600}, []}}), 1047: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 1048: link(CPid1), 1049: {ok, CPid2} = supervisor:start_child(sup_test, Child2), 1050: {ok, CPid3} = supervisor:start_child(sup_test, Child3), 1051: link(CPid3), 1052: [3,3,0,3] = get_child_counts(sup_test), 1053: 1054: terminate(SupPid, CPid2, child2, abnormal), 1055: 1056: %% Check that Cpid3 did die 1057: check_exit([CPid3]), 1058: 1059: Children = supervisor:which_children(sup_test), 1060: is_in_child_list([CPid1], Children), 1061: 1062: if length(Children) == 3 -> 1063: ok; 1064: true -> 1065: test_server:fail({bad_child_list, Children}) 1066: end, 1067: [3,3,0,3] = get_child_counts(sup_test), 1068: 1069: %% Test that no old children is still alive 1070: Pids = lists:map(fun({_,P,_,_}) -> P end, Children), 1071: not_in_child_list([CPid2, CPid3], Pids), 1072: in_child_list([CPid1], Pids), 1073: 1074: %% Test restart frequency property 1075: [{child3, Pid3, _, _}|_] = supervisor:which_children(sup_test), 1076: 1077: terminate(SupPid, Pid3, child3, abnormal), 1078: 1079: [_,{child2, Pid4, _, _}|_] = supervisor:which_children(sup_test), 1080: 1081: terminate(SupPid, Pid4, child2, abnormal), 1082: check_exit([SupPid]). 1083: 1084: %%------------------------------------------------------------------------- 1085: %% Test restart escalation on a rest_for_one supervisor. 1086: rest_for_one_escalation(Config) when is_list(Config) -> 1087: process_flag(trap_exit, true), 1088: Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000, 1089: worker, []}, 1090: Child2 = {child2, {supervisor_1, start_child, [error]}, 1091: permanent, 1000, 1092: worker, []}, 1093: {ok, SupPid} = start_link({ok, {{rest_for_one, 4, 3600}, []}}), 1094: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 1095: {ok, CPid2} = supervisor:start_child(sup_test, Child2), 1096: link(CPid2), 1097: 1098: terminate(SupPid, CPid1, child1, abnormal), 1099: check_exit([CPid2, SupPid]). 1100: 1101: 1102: %%------------------------------------------------------------------------- 1103: %% Test that the supervisor terminates a restarted child when a different 1104: %% child fails to start. 1105: rest_for_one_other_child_fails_restart(Config) when is_list(Config) -> 1106: process_flag(trap_exit, true), 1107: Self = self(), 1108: Child1 = {child1, {supervisor_3, start_child, [child1, Self]}, 1109: permanent, 1000, worker, []}, 1110: Child2 = {child2, {supervisor_3, start_child, [child2, Self]}, 1111: permanent, 1000, worker, []}, 1112: Children = [Child1, Child2], 1113: StarterFun = fun() -> 1114: {ok, SupPid} = start_link({ok, {{rest_for_one, 3, 3600}, Children}}), 1115: Self ! {sup_pid, SupPid}, 1116: receive {stop, Self} -> ok end 1117: end, 1118: StarterPid = spawn_link(StarterFun), 1119: Ok = {{ok, undefined}, Self}, 1120: %% Let the children start. 1121: Child1Pid = receive {child1, Pid1} -> Pid1 end, 1122: Child1Pid ! Ok, 1123: Child2Pid = receive {child2, Pid2} -> Pid2 end, 1124: Child2Pid ! Ok, 1125: %% Supervisor started. 1126: SupPid = receive {sup_pid, Pid} -> Pid end, 1127: link(SupPid), 1128: exit(Child1Pid, die), 1129: %% Let child1 restart but don't let child2. 1130: Child1Pid2 = receive {child1, Pid3} -> Pid3 end, 1131: Child1Pid2 ! Ok, 1132: Child2Pid2 = receive {child2, Pid4} -> Pid4 end, 1133: Child2Pid2 ! {{stop, normal}, Self}, 1134: %% Let child2 restart. 1135: receive 1136: {child2, Child2Pid3} -> 1137: Child2Pid3 ! Ok; 1138: {child1, _Child1Pid3} -> 1139: exit(SupPid, kill), 1140: check_exit([StarterPid, SupPid]), 1141: test_server:fail({restarting_started_child, Child1Pid2}) 1142: end, 1143: StarterPid ! {stop, Self}, 1144: check_exit([StarterPid, SupPid]). 1145: 1146: 1147: %%------------------------------------------------------------------------- 1148: %% Test that the supervisor does not hang forever if the child unliks 1149: %% and then is terminated by the supervisor. 1150: child_unlink(Config) when is_list(Config) -> 1151: 1152: {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), 1153: 1154: Child = {naughty_child, {naughty_child, 1155: start_link, [SupPid]}, permanent, 1156: 1000, worker, [supervisor_SUITE]}, 1157: 1158: {ok, _ChildPid} = supervisor:start_child(sup_test, Child), 1159: 1160: Pid = spawn(supervisor, terminate_child, [SupPid, naughty_child]), 1161: 1162: SupPid ! foo, 1163: timer:sleep(5000), 1164: %% If the supervisor did not hang it will have got rid of the 1165: %% foo message that we sent. 1166: case erlang:process_info(SupPid, message_queue_len) of 1167: {message_queue_len, 0}-> 1168: ok; 1169: _ -> 1170: exit(Pid, kill), 1171: test_server:fail(supervisor_hangs) 1172: end. 1173: %%------------------------------------------------------------------------- 1174: %% Test a basic supervison tree. 1175: tree(Config) when is_list(Config) -> 1176: process_flag(trap_exit, true), 1177: 1178: Child1 = {child1, {supervisor_1, start_child, []}, 1179: permanent, 1000, 1180: worker, []}, 1181: Child2 = {child2, {supervisor_1, start_child, []}, 1182: permanent, 1000, 1183: worker, []}, 1184: Child3 = {child3, {supervisor_1, start_child, [error]}, 1185: permanent, 1000, 1186: worker, []}, 1187: Child4 = {child4, {supervisor_1, start_child, []}, 1188: permanent, 1000, 1189: worker, []}, 1190: 1191: ChildSup1 = {supchild1, 1192: {supervisor, start_link, 1193: [?MODULE, {ok, {{one_for_one, 4, 3600}, [Child1, Child2]}}]}, 1194: permanent, infinity, 1195: supervisor, []}, 1196: ChildSup2 = {supchild2, 1197: {supervisor, start_link, 1198: [?MODULE, {ok, {{one_for_one, 4, 3600}, []}}]}, 1199: permanent, infinity, 1200: supervisor, []}, 1201: 1202: %% Top supervisor 1203: {ok, SupPid} = start_link({ok, {{one_for_all, 4, 3600}, []}}), 1204: 1205: %% Child supervisors 1206: {ok, Sup1} = supervisor:start_child(SupPid, ChildSup1), 1207: {ok, Sup2} = supervisor:start_child(SupPid, ChildSup2), 1208: [2,2,2,0] = get_child_counts(SupPid), 1209: 1210: %% Workers 1211: [{_, CPid2, _, _},{_, CPid1, _, _}] = 1212: supervisor:which_children(Sup1), 1213: [2,2,0,2] = get_child_counts(Sup1), 1214: [0,0,0,0] = get_child_counts(Sup2), 1215: 1216: %% Dynamic children 1217: {ok, CPid3} = supervisor:start_child(Sup2, Child3), 1218: {ok, CPid4} = supervisor:start_child(Sup2, Child4), 1219: [2,2,0,2] = get_child_counts(Sup1), 1220: [2,2,0,2] = get_child_counts(Sup2), 1221: 1222: %% Test that the only the process that dies is restarted 1223: terminate(Sup2, CPid4, child4, abnormal), 1224: 1225: [{_, CPid2, _, _},{_, CPid1, _, _}] = 1226: supervisor:which_children(Sup1), 1227: [2,2,0,2] = get_child_counts(Sup1), 1228: 1229: [{_, NewCPid4, _, _},{_, CPid3, _, _}] = 1230: supervisor:which_children(Sup2), 1231: [2,2,0,2] = get_child_counts(Sup2), 1232: 1233: false = NewCPid4 == CPid4, 1234: 1235: %% Test that supervisor tree is restarted, but not dynamic children. 1236: terminate(Sup2, CPid3, child3, abnormal), 1237: 1238: timer:sleep(1000), 1239: 1240: [{supchild2, NewSup2, _, _},{supchild1, NewSup1, _, _}] = 1241: supervisor:which_children(SupPid), 1242: [2,2,2,0] = get_child_counts(SupPid), 1243: 1244: [{child2, _, _, _},{child1, _, _, _}] = 1245: supervisor:which_children(NewSup1), 1246: [2,2,0,2] = get_child_counts(NewSup1), 1247: 1248: [] = supervisor:which_children(NewSup2), 1249: [0,0,0,0] = get_child_counts(NewSup2). 1250: 1251: %%------------------------------------------------------------------------- 1252: %% Test that count_children does not eat memory. 1253: count_children_memory(Config) when is_list(Config) -> 1254: process_flag(trap_exit, true), 1255: Child = {child, {supervisor_1, start_child, []}, temporary, 1000, 1256: worker, []}, 1257: {ok, SupPid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), 1258: [supervisor:start_child(sup_test, []) || _Ignore <- lists:seq(1,1000)], 1259: 1260: garbage_collect(), 1261: _Size1 = proc_memory(), 1262: Children = supervisor:which_children(sup_test), 1263: _Size2 = proc_memory(), 1264: ChildCount = get_child_counts(sup_test), 1265: _Size3 = proc_memory(), 1266: 1267: [supervisor:start_child(sup_test, []) || _Ignore2 <- lists:seq(1,1000)], 1268: 1269: garbage_collect(), 1270: Children2 = supervisor:which_children(sup_test), 1271: Size4 = proc_memory(), 1272: ChildCount2 = get_child_counts(sup_test), 1273: Size5 = proc_memory(), 1274: 1275: garbage_collect(), 1276: Children3 = supervisor:which_children(sup_test), 1277: Size6 = proc_memory(), 1278: ChildCount3 = get_child_counts(sup_test), 1279: Size7 = proc_memory(), 1280: 1281: 1000 = length(Children), 1282: [1,1000,0,1000] = ChildCount, 1283: 2000 = length(Children2), 1284: [1,2000,0,2000] = ChildCount2, 1285: Children3 = Children2, 1286: ChildCount3 = ChildCount2, 1287: 1288: %% count_children consumes memory using an accumulator function, 1289: %% but the space can be reclaimed incrementally, 1290: %% which_children may generate garbage that will be reclaimed later. 1291: case (Size5 =< Size4) of 1292: true -> ok; 1293: false -> 1294: test_server:fail({count_children, used_more_memory,Size4,Size5}) 1295: end, 1296: case Size7 =< Size6 of 1297: true -> ok; 1298: false -> 1299: test_server:fail({count_children, used_more_memory,Size6,Size7}) 1300: end, 1301: 1302: [terminate(SupPid, Pid, child, kill) || {undefined, Pid, worker, _Modules} <- Children3], 1303: [1,0,0,0] = get_child_counts(sup_test). 1304: 1305: proc_memory() -> 1306: erts_debug:set_internal_state(wait, deallocations), 1307: erlang:memory(processes_used). 1308: 1309: %%------------------------------------------------------------------------- 1310: %% Temporary children shall not be restarted so they should not save 1311: %% start parameters, as it potentially can take up a huge amount of 1312: %% memory for no purpose. 1313: do_not_save_start_parameters_for_temporary_children(Config) when is_list(Config) -> 1314: process_flag(trap_exit, true), 1315: dont_save_start_parameters_for_temporary_children(one_for_all), 1316: dont_save_start_parameters_for_temporary_children(one_for_one), 1317: dont_save_start_parameters_for_temporary_children(rest_for_one), 1318: dont_save_start_parameters_for_temporary_children(simple_one_for_one). 1319: 1320: start_children(_,_, 0) -> 1321: ok; 1322: start_children(Sup, Args, N) -> 1323: Spec = child_spec(Args, N), 1324: {ok, _, _} = supervisor:start_child(Sup, Spec), 1325: start_children(Sup, Args, N-1). 1326: 1327: child_spec([_|_] = SimpleOneForOneArgs, _) -> 1328: SimpleOneForOneArgs; 1329: child_spec({Name, MFA, RestartType, Shutdown, Type, Modules}, N) -> 1330: NewName = list_to_atom((atom_to_list(Name) ++ integer_to_list(N))), 1331: {NewName, MFA, RestartType, Shutdown, Type, Modules}. 1332: 1333: %%------------------------------------------------------------------------- 1334: %% Temporary children shall not be restarted so supervisors should not 1335: %% save their spec when they terminate. 1336: do_not_save_child_specs_for_temporary_children(Config) when is_list(Config) -> 1337: process_flag(trap_exit, true), 1338: dont_save_child_specs_for_temporary_children(one_for_all, kill), 1339: dont_save_child_specs_for_temporary_children(one_for_one, kill), 1340: dont_save_child_specs_for_temporary_children(rest_for_one, kill), 1341: 1342: dont_save_child_specs_for_temporary_children(one_for_all, normal), 1343: dont_save_child_specs_for_temporary_children(one_for_one, normal), 1344: dont_save_child_specs_for_temporary_children(rest_for_one, normal), 1345: 1346: dont_save_child_specs_for_temporary_children(one_for_all, abnormal), 1347: dont_save_child_specs_for_temporary_children(one_for_one, abnormal), 1348: dont_save_child_specs_for_temporary_children(rest_for_one, abnormal), 1349: 1350: dont_save_child_specs_for_temporary_children(one_for_all, supervisor), 1351: dont_save_child_specs_for_temporary_children(one_for_one, supervisor), 1352: dont_save_child_specs_for_temporary_children(rest_for_one, supervisor). 1353: 1354: %%------------------------------------------------------------------------- 1355: dont_save_start_parameters_for_temporary_children(simple_one_for_one = Type) -> 1356: Permanent = {child, {supervisor_1, start_child, []}, 1357: permanent, 1000, worker, []}, 1358: Transient = {child, {supervisor_1, start_child, []}, 1359: transient, 1000, worker, []}, 1360: Temporary = {child, {supervisor_1, start_child, []}, 1361: temporary, 1000, worker, []}, 1362: {ok, Sup1} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, [Permanent]}}), 1363: {ok, Sup2} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, [Transient]}}), 1364: {ok, Sup3} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, [Temporary]}}), 1365: 1366: LargeList = lists:duplicate(10, "Potentially large"), 1367: 1368: start_children(Sup1, [LargeList], 100), 1369: start_children(Sup2, [LargeList], 100), 1370: start_children(Sup3, [LargeList], 100), 1371: 1372: [{memory,Mem1}] = process_info(Sup1, [memory]), 1373: [{memory,Mem2}] = process_info(Sup2, [memory]), 1374: [{memory,Mem3}] = process_info(Sup3, [memory]), 1375: 1376: true = (Mem3 < Mem1) and (Mem3 < Mem2), 1377: 1378: terminate(Sup1, shutdown), 1379: terminate(Sup2, shutdown), 1380: terminate(Sup3, shutdown); 1381: 1382: dont_save_start_parameters_for_temporary_children(Type) -> 1383: {ok, Sup1} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, []}}), 1384: {ok, Sup2} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, []}}), 1385: {ok, Sup3} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, []}}), 1386: 1387: LargeList = lists:duplicate(10, "Potentially large"), 1388: 1389: Permanent = {child1, {supervisor_1, start_child, [LargeList]}, 1390: permanent, 1000, worker, []}, 1391: Transient = {child2, {supervisor_1, start_child, [LargeList]}, 1392: transient, 1000, worker, []}, 1393: Temporary = {child3, {supervisor_1, start_child, [LargeList]}, 1394: temporary, 1000, worker, []}, 1395: 1396: start_children(Sup1, Permanent, 100), 1397: start_children(Sup2, Transient, 100), 1398: start_children(Sup3, Temporary, 100), 1399: 1400: [{memory,Mem1}] = process_info(Sup1, [memory]), 1401: [{memory,Mem2}] = process_info(Sup2, [memory]), 1402: [{memory,Mem3}] = process_info(Sup3, [memory]), 1403: 1404: true = (Mem3 < Mem1) and (Mem3 < Mem2), 1405: 1406: terminate(Sup1, shutdown), 1407: terminate(Sup2, shutdown), 1408: terminate(Sup3, shutdown). 1409: 1410: dont_save_child_specs_for_temporary_children(Type, TerminateHow)-> 1411: {ok, Sup} = 1412: supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, []}}), 1413: 1414: Permanent = {child1, {supervisor_1, start_child, []}, 1415: permanent, 1000, worker, []}, 1416: Transient = {child2, {supervisor_1, start_child, []}, 1417: transient, 1000, worker, []}, 1418: Temporary = {child3, {supervisor_1, start_child, []}, 1419: temporary, 1000, worker, []}, 1420: 1421: permanent_child_spec_saved(Permanent, Sup, TerminateHow), 1422: 1423: transient_child_spec_saved(Transient, Sup, TerminateHow), 1424: 1425: temporary_child_spec_not_saved(Temporary, Sup, TerminateHow), 1426: 1427: terminate(Sup, shutdown). 1428: 1429: permanent_child_spec_saved(ChildSpec, Sup, supervisor = TerminateHow) -> 1430: already_present(Sup, ChildSpec, TerminateHow); 1431: 1432: permanent_child_spec_saved(ChildSpec, Sup, TerminateHow) -> 1433: restarted(Sup, ChildSpec, TerminateHow). 1434: 1435: transient_child_spec_saved(ChildSpec, Sup, supervisor = TerminateHow) -> 1436: already_present(Sup, ChildSpec, TerminateHow); 1437: 1438: transient_child_spec_saved(ChildSpec, Sup, normal = TerminateHow) -> 1439: already_present(Sup, ChildSpec, TerminateHow); 1440: 1441: transient_child_spec_saved(ChildSpec, Sup, TerminateHow) -> 1442: restarted(Sup, ChildSpec, TerminateHow). 1443: 1444: temporary_child_spec_not_saved({Id, _,_,_,_,_} = ChildSpec, Sup, TerminateHow) -> 1445: {ok, Pid} = supervisor:start_child(Sup, ChildSpec), 1446: terminate(Sup, Pid, Id, TerminateHow), 1447: {ok, _} = supervisor:start_child(Sup, ChildSpec). 1448: 1449: already_present(Sup, {Id,_,_,_,_,_} = ChildSpec, TerminateHow) -> 1450: {ok, Pid} = supervisor:start_child(Sup, ChildSpec), 1451: terminate(Sup, Pid, Id, TerminateHow), 1452: {error, already_present} = supervisor:start_child(Sup, ChildSpec), 1453: {ok, _} = supervisor:restart_child(Sup, Id). 1454: 1455: restarted(Sup, {Id,_,_,_,_,_} = ChildSpec, TerminateHow) -> 1456: {ok, Pid} = supervisor:start_child(Sup, ChildSpec), 1457: terminate(Sup, Pid, Id, TerminateHow), 1458: %% Permanent processes will be restarted by the supervisor 1459: %% when not terminated by api 1460: {error, {already_started, _}} = supervisor:start_child(Sup, ChildSpec). 1461: 1462: 1463: %%------------------------------------------------------------------------- 1464: %% OTP-9242: Pids for dynamic temporary children were saved as a list, 1465: %% which caused bad scaling when adding/deleting many processes. 1466: simple_one_for_one_scale_many_temporary_children(_Config) -> 1467: process_flag(trap_exit, true), 1468: Child = {child, {supervisor_1, start_child, []}, temporary, 1000, 1469: worker, []}, 1470: {ok, _SupPid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), 1471: 1472: C1 = [begin 1473: {ok,P} = supervisor:start_child(sup_test,[]), 1474: P 1475: end || _<- lists:seq(1,1000)], 1476: {T1,done} = timer:tc(?MODULE,terminate_all_children,[C1]), 1477: 1478: C2 = [begin 1479: {ok,P} = supervisor:start_child(sup_test,[]), 1480: P 1481: end || _<- lists:seq(1,10000)], 1482: {T2,done} = timer:tc(?MODULE,terminate_all_children,[C2]), 1483: 1484: if T1 > 0 -> 1485: Scaling = T2 div T1, 1486: if Scaling > 20 -> 1487: %% The scaling shoul be linear (i.e.10, really), but we 1488: %% give some extra here to avoid failing the test 1489: %% unecessarily. 1490: ?t:fail({bad_scaling,Scaling}); 1491: true -> 1492: ok 1493: end; 1494: true -> 1495: %% Means T2 div T1 -> infinity 1496: ok 1497: end. 1498: 1499: 1500: terminate_all_children([C|Cs]) -> 1501: ok = supervisor:terminate_child(sup_test,C), 1502: terminate_all_children(Cs); 1503: terminate_all_children([]) -> 1504: done. 1505: 1506: 1507: %%------------------------------------------------------------------------- 1508: %% OTP-9212. Restart of global supervisor. 1509: simple_global_supervisor(_Config) -> 1510: kill_supervisor(), 1511: kill_worker(), 1512: exit_worker(), 1513: restart_worker(), 1514: ok. 1515: 1516: kill_supervisor() -> 1517: {Top, Sup2_1, Server_1} = start9212(), 1518: 1519: %% Killing a supervisor isn't really supported, but try it anyway... 1520: exit(Sup2_1, kill), 1521: timer:sleep(200), 1522: Sup2_2 = global:whereis_name(sup2), 1523: Server_2 = global:whereis_name(server), 1524: true = is_pid(Sup2_2), 1525: true = is_pid(Server_2), 1526: true = Sup2_1 =/= Sup2_2, 1527: true = Server_1 =/= Server_2, 1528: 1529: stop9212(Top). 1530: 1531: handle_info({fail, With, After}, _State) -> 1532: timer:sleep(After), 1533: erlang:error(With). 1534: 1535: kill_worker() -> 1536: {Top, _Sup2, Server_1} = start9212(), 1537: exit(Server_1, kill), 1538: timer:sleep(200), 1539: Server_2 = global:whereis_name(server), 1540: true = is_pid(Server_2), 1541: true = Server_1 =/= Server_2, 1542: stop9212(Top). 1543: 1544: exit_worker() -> 1545: %% Very much the same as kill_worker(). 1546: {Top, _Sup2, Server_1} = start9212(), 1547: Server_1 ! {fail, normal, 0}, 1548: timer:sleep(200), 1549: Server_2 = global:whereis_name(server), 1550: true = is_pid(Server_2), 1551: true = Server_1 =/= Server_2, 1552: stop9212(Top). 1553: 1554: restart_worker() -> 1555: {Top, _Sup2, Server_1} = start9212(), 1556: ok = supervisor:terminate_child({global, sup2}, child), 1557: {ok, _Child} = supervisor:restart_child({global, sup2}, child), 1558: Server_2 = global:whereis_name(server), 1559: true = is_pid(Server_2), 1560: true = Server_1 =/= Server_2, 1561: stop9212(Top). 1562: 1563: start9212() -> 1564: Middle = {middle,{?MODULE,middle9212,[]}, permanent,2000,supervisor,[]}, 1565: InitResult = {ok, {{one_for_all,3,60}, [Middle]}}, 1566: {ok, TopPid} = start_link(InitResult), 1567: 1568: Sup2 = global:whereis_name(sup2), 1569: Server = global:whereis_name(server), 1570: true = is_pid(Sup2), 1571: true = is_pid(Server), 1572: {TopPid, Sup2, Server}. 1573: 1574: stop9212(Top) -> 1575: Old = process_flag(trap_exit, true), 1576: exit(Top, kill), 1577: timer:sleep(200), 1578: undefined = global:whereis_name(sup2), 1579: undefined = global:whereis_name(server), 1580: check_exit([Top]), 1581: _ = process_flag(trap_exit, Old), 1582: ok. 1583: 1584: middle9212() -> 1585: Child = {child, {?MODULE,gen_server9212,[]},permanent, 2000, worker, []}, 1586: InitResult = {ok, {{one_for_all,3,60}, [Child]}}, 1587: supervisor:start_link({global,sup2}, ?MODULE, InitResult). 1588: 1589: gen_server9212() -> 1590: InitResult = {ok, []}, 1591: gen_server:start_link({global,server}, ?MODULE, InitResult, []). 1592: 1593: 1594: %%------------------------------------------------------------------------- 1595: %% Test that child and supervisor can be shutdown while hanging in restart loop. 1596: %% See OTP-9549. 1597: hanging_restart_loop(Config) when is_list(Config) -> 1598: process_flag(trap_exit, true), 1599: {ok, Pid} = start_link({ok, {{one_for_one, 8, 10}, []}}), 1600: Child1 = {child1, {supervisor_deadlock, start_child, []}, 1601: permanent, brutal_kill, worker, []}, 1602: 1603: %% Ets table with state read by supervisor_deadlock.erl 1604: ets:new(supervisor_deadlock,[set,named_table,public]), 1605: ets:insert(supervisor_deadlock,{fail_start,false}), 1606: 1607: {ok, CPid1} = supervisor:start_child(sup_test, Child1), 1608: link(CPid1), 1609: 1610: ets:insert(supervisor_deadlock,{fail_start,true}), 1611: supervisor_deadlock:restart_child(), 1612: timer:sleep(2000), % allow restart to happen before proceeding 1613: 1614: {error, already_present} = supervisor:start_child(sup_test, Child1), 1615: {error, restarting} = supervisor:restart_child(sup_test, child1), 1616: {error, restarting} = supervisor:delete_child(sup_test, child1), 1617: [{child1,restarting,worker,[]}] = supervisor:which_children(sup_test), 1618: [1,0,0,1] = get_child_counts(sup_test), 1619: 1620: ok = supervisor:terminate_child(sup_test, child1), 1621: check_exit_reason(CPid1, error), 1622: [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test), 1623: 1624: ets:insert(supervisor_deadlock,{fail_start,false}), 1625: {ok, CPid2} = supervisor:restart_child(sup_test, child1), 1626: link(CPid2), 1627: 1628: ets:insert(supervisor_deadlock,{fail_start,true}), 1629: supervisor_deadlock:restart_child(), 1630: timer:sleep(2000), % allow restart to happen before proceeding 1631: 1632: %% Terminating supervisor. 1633: %% OTP-9549 fixes so this does not give a timetrap timeout - 1634: %% i.e. that supervisor does not hang in restart loop. 1635: terminate(Pid,shutdown), 1636: 1637: %% Check that child died with reason from 'restart' request above 1638: check_exit_reason(CPid2, error), 1639: undefined = whereis(sup_test), 1640: ok. 1641: 1642: %%------------------------------------------------------------------------- 1643: %% Test that child and supervisor can be shutdown while hanging in 1644: %% restart loop, simple_one_for_one. 1645: %% See OTP-9549. 1646: hanging_restart_loop_simple(Config) when is_list(Config) -> 1647: process_flag(trap_exit, true), 1648: Child1 = {child1, {supervisor_deadlock, start_child, []}, 1649: permanent, brutal_kill, worker, []}, 1650: {ok, Pid} = start_link({ok, {{simple_one_for_one, 8, 10}, [Child1]}}), 1651: 1652: %% Ets table with state read by supervisor_deadlock.erl 1653: ets:new(supervisor_deadlock,[set,named_table,public]), 1654: ets:insert(supervisor_deadlock,{fail_start,false}), 1655: 1656: {ok, CPid1} = supervisor:start_child(sup_test, []), 1657: link(CPid1), 1658: 1659: ets:insert(supervisor_deadlock,{fail_start,true}), 1660: supervisor_deadlock:restart_child(), 1661: timer:sleep(2000), % allow restart to happen before proceeding 1662: 1663: {error, simple_one_for_one} = supervisor:restart_child(sup_test, child1), 1664: {error, simple_one_for_one} = supervisor:delete_child(sup_test, child1), 1665: [{undefined,restarting,worker,[]}] = supervisor:which_children(sup_test), 1666: [1,0,0,1] = get_child_counts(sup_test), 1667: 1668: ok = supervisor:terminate_child(sup_test, CPid1), 1669: check_exit_reason(CPid1, error), 1670: [] = supervisor:which_children(sup_test), 1671: 1672: ets:insert(supervisor_deadlock,{fail_start,false}), 1673: {ok, CPid2} = supervisor:start_child(sup_test, []), 1674: link(CPid2), 1675: 1676: ets:insert(supervisor_deadlock,{fail_start,true}), 1677: supervisor_deadlock:restart_child(), 1678: timer:sleep(2000), % allow restart to happen before proceeding 1679: 1680: %% Terminating supervisor. 1681: %% OTP-9549 fixes so this does not give a timetrap timeout - 1682: %% i.e. that supervisor does not hang in restart loop. 1683: terminate(Pid,shutdown), 1684: 1685: %% Check that child died with reason from 'restart' request above 1686: check_exit_reason(CPid2, error), 1687: undefined = whereis(sup_test), 1688: ok. 1689: 1690: %%------------------------------------------------------------------------- 1691: terminate(Pid, Reason) when Reason =/= supervisor -> 1692: terminate(dummy, Pid, dummy, Reason). 1693: 1694: terminate(Sup, _, ChildId, supervisor) -> 1695: ok = supervisor:terminate_child(Sup, ChildId); 1696: terminate(_, ChildPid, _, kill) -> 1697: Ref = erlang:monitor(process, ChildPid), 1698: exit(ChildPid, kill), 1699: receive 1700: {'DOWN', Ref, process, ChildPid, killed} -> 1701: ok 1702: end; 1703: terminate(_, ChildPid, _, shutdown) -> 1704: Ref = erlang:monitor(process, ChildPid), 1705: exit(ChildPid, shutdown), 1706: receive 1707: {'DOWN', Ref, process, ChildPid, shutdown} -> 1708: ok 1709: end; 1710: terminate(_, ChildPid, _, {shutdown, Term}) -> 1711: Ref = erlang:monitor(process, ChildPid), 1712: exit(ChildPid, {shutdown, Term}), 1713: receive 1714: {'DOWN', Ref, process, ChildPid, {shutdown, Term}} -> 1715: ok 1716: end; 1717: terminate(_, ChildPid, _, normal) -> 1718: Ref = erlang:monitor(process, ChildPid), 1719: ChildPid ! stop, 1720: receive 1721: {'DOWN', Ref, process, ChildPid, normal} -> 1722: ok 1723: end; 1724: terminate(_, ChildPid, _,abnormal) -> 1725: Ref = erlang:monitor(process, ChildPid), 1726: ChildPid ! die, 1727: receive 1728: {'DOWN', Ref, process, ChildPid, died} -> 1729: ok 1730: end. 1731: 1732: in_child_list([], _) -> 1733: true; 1734: in_child_list([Pid | Rest], Pids) -> 1735: case is_in_child_list(Pid, Pids) of 1736: true -> 1737: in_child_list(Rest, Pids); 1738: false -> 1739: test_server:fail(child_should_be_alive) 1740: end. 1741: not_in_child_list([], _) -> 1742: true; 1743: not_in_child_list([Pid | Rest], Pids) -> 1744: case is_in_child_list(Pid, Pids) of 1745: true -> 1746: test_server:fail(child_should_not_be_alive); 1747: false -> 1748: not_in_child_list(Rest, Pids) 1749: end. 1750: 1751: is_in_child_list(Pid, ChildPids) -> 1752: lists:member(Pid, ChildPids). 1753: 1754: check_exit([]) -> 1755: ok; 1756: check_exit([Pid | Pids]) -> 1757: receive 1758: {'EXIT', Pid, _} -> 1759: check_exit(Pids) 1760: end. 1761: 1762: check_exit_reason(Reason) -> 1763: receive 1764: {'EXIT', _, Reason} -> 1765: ok; 1766: {'EXIT', _, Else} -> 1767: test_server:fail({bad_exit_reason, Else}) 1768: end. 1769: 1770: check_exit_reason(Pid, Reason) -> 1771: receive 1772: {'EXIT', Pid, Reason} -> 1773: ok; 1774: {'EXIT', Pid, Else} -> 1775: test_server:fail({bad_exit_reason, Else}) 1776: end.