Several bug fixes found when running tests
Validate delay value
This commit is contained in:
		
							parent
							
								
									db7dfdf0cf
								
							
						
					
					
						commit
						111124bc05
					
				|  | @ -3,7 +3,7 @@ | ||||||
| %% | %% | ||||||
| %% 1) the module name is supervisor2 | %% 1) the module name is supervisor2 | ||||||
| %% | %% | ||||||
| %% 2) a internal_find_child/2 utility function has been added | %% 2) a find_child/2 utility function has been added | ||||||
| %% | %% | ||||||
| %% 3) Added an 'intrinsic' restart type. Like the transient type, this | %% 3) Added an 'intrinsic' restart type. Like the transient type, this | ||||||
| %%    type means the child should only be restarted if the child exits | %%    type means the child should only be restarted if the child exits | ||||||
|  | @ -69,8 +69,8 @@ | ||||||
| 	 start_child/2, restart_child/2, | 	 start_child/2, restart_child/2, | ||||||
| 	 delete_child/2, terminate_child/2, | 	 delete_child/2, terminate_child/2, | ||||||
| 	 which_children/1, count_children/1, | 	 which_children/1, count_children/1, | ||||||
|      find_child/2, | 	 check_childspecs/1, get_childspec/2, | ||||||
| 	 check_childspecs/1, get_childspec/2]). |      find_child/2]). | ||||||
| 
 | 
 | ||||||
| %% Internal exports | %% Internal exports | ||||||
| -export([init/1, handle_call/3, handle_cast/2, handle_info/2, | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, | ||||||
|  | @ -179,9 +179,10 @@ | ||||||
| 
 | 
 | ||||||
| -define(is_simple(State), State#state.strategy=:=simple_one_for_one). | -define(is_simple(State), State#state.strategy=:=simple_one_for_one). | ||||||
| -define(is_temporary(_Child_), _Child_#child.restart_type=:=temporary). | -define(is_temporary(_Child_), _Child_#child.restart_type=:=temporary). | ||||||
| -define(is_transient(_Child_), _Child_#child.restart_type=:=transient). | -define(is_permanent(_Child_), ((_Child_#child.restart_type=:=permanent) orelse | ||||||
| -define(is_permanent(_Child_), _Child_#child.restart_type=:=permanent). |                                 (is_tuple(_Child_#child.restart_type) andalso | ||||||
| -define(is_intrinsic(_Child_), _Child_#child.restart_type=:=intrinsic). |                                  tuple_size(_Child_#child.restart_type) =:= 2 andalso | ||||||
|  |                                  element(1, _Child_#child.restart_type) =:= permanent))). | ||||||
| 
 | 
 | ||||||
| -define(is_explicit_restart(R), | -define(is_explicit_restart(R), | ||||||
|         R == {shutdown, restart}). |         R == {shutdown, restart}). | ||||||
|  | @ -637,8 +638,9 @@ handle_info({'EXIT', Pid, Reason}, State) -> | ||||||
| handle_info({delayed_restart, {Reason, Child}}, State) when ?is_simple(State) -> | handle_info({delayed_restart, {Reason, Child}}, State) when ?is_simple(State) -> | ||||||
|     try_restart(Reason, Child, State#state{restarts = []});  %% [1] |     try_restart(Reason, Child, State#state{restarts = []});  %% [1] | ||||||
| handle_info({delayed_restart, {Reason, Child}}, State) -> | handle_info({delayed_restart, {Reason, Child}}, State) -> | ||||||
|     case internal_find_child(Child#child.id, State) of |     ChildId = Child#child.id, | ||||||
|         {value, Child1} -> |     case internal_find_child(ChildId, State) of | ||||||
|  |         {ok, Child1} -> | ||||||
|             try_restart(Reason, Child1, State#state{restarts = []}); %% [1] |             try_restart(Reason, Child1, State#state{restarts = []}); %% [1] | ||||||
|         _What -> |         _What -> | ||||||
|             {noreply, State} |             {noreply, State} | ||||||
|  | @ -775,10 +777,10 @@ try_restart(Reason, Child, State) -> | ||||||
|         {shutdown, State2} -> {stop, shutdown, State2} |         {shutdown, State2} -> {stop, shutdown, State2} | ||||||
|     end. |     end. | ||||||
| 
 | 
 | ||||||
| do_restart(Reason, Child, State) when ?is_permanent(Child) -> | do_restart(Reason, Child=#child{restart_type=permanent}, State) -> % is_permanent | ||||||
|     ?report_error(child_terminated, Reason, Child, State#state.name), |     ?report_error(child_terminated, Reason, Child, State#state.name), | ||||||
|     restart(Child, State); |     restart(Child, State); | ||||||
| do_restart(Reason, Child=#child{restart_type={transient,_Delay}}, State) -> % is_permanent_delay | do_restart(Reason, Child=#child{restart_type={permanent,_Delay}}, State) -> % is_permanent_delay | ||||||
|     ?report_error(child_terminated, Reason, Child, State#state.name), |     ?report_error(child_terminated, Reason, Child, State#state.name), | ||||||
|     do_restart_delay(Reason, Child, State); |     do_restart_delay(Reason, Child, State); | ||||||
| do_restart(normal, Child, State) -> | do_restart(normal, Child, State) -> | ||||||
|  | @ -790,7 +792,7 @@ do_restart(shutdown, Child, State) -> | ||||||
| do_restart({shutdown, _Term}, Child, State) -> | do_restart({shutdown, _Term}, Child, State) -> | ||||||
|     NState = del_child(Child, State), |     NState = del_child(Child, State), | ||||||
|     {ok, NState}; |     {ok, NState}; | ||||||
| do_restart(Reason, Child, State) when ?is_transient(Child) -> | do_restart(Reason, Child=#child{restart_type=transient}, State) -> % is_transient | ||||||
|     ?report_error(child_terminated, Reason, Child, State#state.name), |     ?report_error(child_terminated, Reason, Child, State#state.name), | ||||||
|     restart(Child, State); |     restart(Child, State); | ||||||
| do_restart(Reason, Child=#child{restart_type={transient,_Delay}}, State) -> % is_transient_delay | do_restart(Reason, Child=#child{restart_type={transient,_Delay}}, State) -> % is_transient_delay | ||||||
|  | @ -798,12 +800,12 @@ do_restart(Reason, Child=#child{restart_type={transient,_Delay}}, State) -> % is | ||||||
|     restart_if_explicit_or_abnormal(defer_to_restart_delay(Reason), |     restart_if_explicit_or_abnormal(defer_to_restart_delay(Reason), | ||||||
|                                     fun delete_child_and_continue/2, |                                     fun delete_child_and_continue/2, | ||||||
|                                     Reason, Child, State); |                                     Reason, Child, State); | ||||||
| do_restart(Reason, Child, State) when ?is_intrinsic(Child) -> | do_restart(Reason, Child=#child{restart_type=intrinsic}, State) -> % is_intrinsic | ||||||
|     ?report_error(child_terminated, Reason, Child, State#state.name), |     ?report_error(child_terminated, Reason, Child, State#state.name), | ||||||
|     restart_if_explicit_or_abnormal(fun restart/2, |     restart_if_explicit_or_abnormal(fun restart/2, | ||||||
|                                     fun delete_child_and_stop/2, |                                     fun delete_child_and_stop/2, | ||||||
|                                     Reason, Child, State); |                                     Reason, Child, State); | ||||||
| do_restart(Reason, Child=#child{restart_type={transient,_Delay}}, State) -> % is_intrinsic_delay | do_restart(Reason, Child=#child{restart_type={intrinsic,_Delay}}, State) -> % is_intrinsic_delay | ||||||
|     ?report_error(child_terminated, Reason, Child, State#state.name), |     ?report_error(child_terminated, Reason, Child, State#state.name), | ||||||
|     restart_if_explicit_or_abnormal(defer_to_restart_delay(Reason), |     restart_if_explicit_or_abnormal(defer_to_restart_delay(Reason), | ||||||
|                                     fun delete_child_and_stop/2, |                                     fun delete_child_and_stop/2, | ||||||
|  | @ -834,26 +836,29 @@ is_abnormal_termination(shutdown)      -> false; | ||||||
| is_abnormal_termination({shutdown, _}) -> false; | is_abnormal_termination({shutdown, _}) -> false; | ||||||
| is_abnormal_termination(_Other)        -> true. | is_abnormal_termination(_Other)        -> true. | ||||||
| 
 | 
 | ||||||
| do_restart_delay(Reason, Child0=#child{restart_type={_RestartType,Delay}}, State0) -> | do_restart_delay(Reason, | ||||||
|  |                  Child = #child{id = ChildId, | ||||||
|  |                                 pid = ChildPid0, | ||||||
|  |                                 restart_type = {_RestartType, Delay}}, | ||||||
|  |                  State0) -> | ||||||
|     case add_restart(State0) of |     case add_restart(State0) of | ||||||
|         {ok, State1} -> |         {ok, State1} -> | ||||||
|             Strategy = State1#state.strategy, |             Strategy = State1#state.strategy, | ||||||
|             maybe_restart(Strategy, Child0, State1); |             maybe_restart(Strategy, Child, State1); | ||||||
|         {terminate, _State1} -> |         {terminate, State1} -> | ||||||
|             %% we've reached the max restart intensity, but the |             %% we've reached the max restart intensity, but the | ||||||
|             %% add_restart will have added to the restarts |             %% add_restart will have added to the restarts | ||||||
|             %% field. Given we don't want to die here, we need to go |             %% field. Given we don't want to die here, we need to go | ||||||
|             %% back to the old restarts field otherwise we'll never |             %% back to the old restarts field otherwise we'll never | ||||||
|             %% attempt to restart later, which is why we ignore |             %% attempt to restart later, which is why we ignore | ||||||
|             %% NState for this clause. |             %% NState for this clause. | ||||||
|             Msg = {delayed_restart, {Reason, Child0}}, |             Msg = {delayed_restart, {Reason, Child}}, | ||||||
|             _TRef = erlang:send_after(trunc(Delay*1000), self(), Msg), |             _TRef = erlang:send_after(trunc(Delay*1000), self(), Msg), | ||||||
|             Pid0 = Child0#child.pid, |             ChildPid1 = restarting(ChildPid0), | ||||||
|             Pid1 = restarting(Pid0), |  | ||||||
|             Child1 = Child0#child{pid=Pid1}, |  | ||||||
|             % Note: State0 is intentionally used here |             % Note: State0 is intentionally used here | ||||||
|             State1 = replace_child(Child1, State0), |             % TODO LRB | ||||||
|             {ok, State1} |             State2 = set_pid(ChildPid1, ChildId, State1), | ||||||
|  |             {ok, State2} | ||||||
|     end. |     end. | ||||||
| 
 | 
 | ||||||
| maybe_restart(Strategy, Child, State) -> | maybe_restart(Strategy, Child, State) -> | ||||||
|  | @ -1290,16 +1295,6 @@ set_pid(Pid, Id, {Ids, Db}) -> | ||||||
|     NewDb = maps:update_with(Id, fun(Child) -> Child#child{pid=Pid} end, Db), |     NewDb = maps:update_with(Id, fun(Child) -> Child#child{pid=Pid} end, Db), | ||||||
|     {Ids,NewDb}. |     {Ids,NewDb}. | ||||||
| 
 | 
 | ||||||
| -spec replace_child(child(), state()) -> state(). |  | ||||||
| replace_child(Child, State) -> |  | ||||||
|     Chs = do_replace_child(Child, State#state.children), |  | ||||||
|     State#state{children = Chs}. |  | ||||||
| 
 |  | ||||||
| do_replace_child(Child, [Ch|Chs]) when Ch#child.id =:= Child#child.id -> |  | ||||||
|     [Child | Chs]; |  | ||||||
| do_replace_child(Child, [Ch|Chs]) -> |  | ||||||
|     [Ch|do_replace_child(Child, Chs)]. |  | ||||||
| 
 |  | ||||||
| %% Remove the Id and the child record from the process state | %% Remove the Id and the child record from the process state | ||||||
| -spec remove_child(child_id(), state()) -> state(). | -spec remove_child(child_id(), state()) -> state(). | ||||||
| remove_child(Id, #state{children={Ids,Db}} = State) -> | remove_child(Id, #state{children={Ids,Db}} = State) -> | ||||||
|  | @ -1494,11 +1489,19 @@ validFunc({M, F, A}) when is_atom(M), | ||||||
| validFunc(Func)                      -> throw({invalid_mfa, Func}). | validFunc(Func)                      -> throw({invalid_mfa, Func}). | ||||||
| 
 | 
 | ||||||
| validRestartType(permanent)           -> true; | validRestartType(permanent)           -> true; | ||||||
|  | validRestartType({permanent, Delay})  -> validDelay(Delay); | ||||||
| validRestartType(temporary)           -> true; | validRestartType(temporary)           -> true; | ||||||
| validRestartType(transient)           -> true; | validRestartType(transient)           -> true; | ||||||
|  | validRestartType({transient, Delay})  -> validDelay(Delay); | ||||||
| validRestartType(intrinsic)           -> true; | validRestartType(intrinsic)           -> true; | ||||||
|  | validRestartType({intrinsic, Delay})  -> validDelay(Delay); | ||||||
| validRestartType(RestartType) -> throw({invalid_restart_type, RestartType}). | validRestartType(RestartType) -> throw({invalid_restart_type, RestartType}). | ||||||
| 
 | 
 | ||||||
|  | validDelay(Delay) when is_number(Delay), Delay >= 0 -> | ||||||
|  |     true; | ||||||
|  | validDelay(What) -> | ||||||
|  |     throw({invalid_delay, What}). | ||||||
|  | 
 | ||||||
| validShutdown(Shutdown) | validShutdown(Shutdown) | ||||||
|   when is_integer(Shutdown), Shutdown > 0 -> true; |   when is_integer(Shutdown), Shutdown > 0 -> true; | ||||||
| validShutdown(infinity)             -> true; | validShutdown(infinity)             -> true; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue