17
17
-export ([start_link /1 ]).
18
18
-export ([init /1 , handle_call /3 , handle_cast /2 , handle_info /2 , terminate /2 ]).
19
19
20
- -export ([start_child /4 , stop_child /2 , start_children /4 , stop_children /3 , terminate_all_children /1 ]).
20
+ -export ([start_child /4 , stop_child /2 , child_up /2 ,
21
+ start_children /4 , stop_children /3 , terminate_all_children /1 ]).
21
22
22
23
-export ([get_all_children /1 ]).
23
24
24
25
-record (state , {
25
26
index :: non_neg_integer (),
26
27
table :: ets :table (),
28
+ waiting = #{} :: #{reference () := pid ()},
27
29
tasks = #{} :: #{reference () := pid ()}
28
30
}).
29
31
-type state () :: # state {}.
@@ -39,6 +41,10 @@ start_link(N) ->
39
41
start_child (Sup , Scenario , Id , ScenarioState ) ->
40
42
gen_server :cast (Sup , {start_child , Scenario , Id , ScenarioState }).
41
43
44
+ -spec child_up (pid (), amoc_scenario :user_id ()) -> any ().
45
+ child_up (Sup , Id ) ->
46
+ gen_server :cast (Sup , {child_up , self (), Id }).
47
+
42
48
-spec start_children (pid (), amoc :scenario (), [amoc_scenario :user_id ()], any ()) -> ok .
43
49
start_children (Sup , Scenario , UserIds , ScenarioState ) ->
44
50
gen_server :cast (Sup , {start_children , Scenario , UserIds , ScenarioState }).
@@ -84,10 +90,13 @@ handle_call(get_all_children, _From, #state{table = Table} = State) ->
84
90
{stop_children , non_neg_integer (), boolean ()} |
85
91
terminate_all_children .
86
92
handle_cast ({start_child , Scenario , Id , ScenarioState }, State ) ->
87
- do_start_child (Scenario , Id , ScenarioState , State ),
88
- {noreply , State };
93
+ NewState = do_start_child (Scenario , [ Id ] , ScenarioState , State ),
94
+ {noreply , NewState };
89
95
handle_cast ({start_children , Scenario , Ids , ScenarioState }, State ) ->
90
- [ do_start_child (Scenario , Id , ScenarioState , State ) || Id <- Ids ],
96
+ NewState = do_start_child (Scenario , Ids , ScenarioState , State ),
97
+ {noreply , NewState };
98
+ handle_cast ({child_up , Pid , Id }, # state {index = N , table = Tid } = State ) ->
99
+ handle_up_user (Tid , Pid , Id , N ),
91
100
{noreply , State };
92
101
handle_cast ({stop_children , 0 , _ }, State ) ->
93
102
{noreply , State };
@@ -102,19 +111,26 @@ handle_cast({stop_children, Int, ForceRemove}, #state{table = Table} = State) ->
102
111
{noreply , NewState };
103
112
handle_cast (terminate_all_children , State ) ->
104
113
NewState = do_terminate_all_my_children (State ),
105
- {noreply , NewState };
106
- handle_cast (_Msg , State ) ->
107
- {noreply , State }.
114
+ {noreply , NewState }.
108
115
109
116
% % @private
110
117
-spec handle_info (Request , state ()) -> {noreply , state ()} when
111
118
Request :: {child_up , pid (), amoc_scenario :user_id ()} |
112
119
{'DOWN' , reference (), process , pid (), term ()} |
113
120
{'EXIT' , pid (), term ()}.
114
- handle_info ({'DOWN' , Ref , process , _Pid , _Reason }, # state {tasks = Tasks } = State ) ->
115
- {noreply , State # state {tasks = maps :remove (Ref , Tasks )}};
116
- handle_info ({'EXIT' , Pid , _Reason }, # state {index = N , table = Table } = State ) ->
117
- handle_down_user (Table , Pid , N ),
121
+ handle_info ({'DOWN' , Ref , process , Pid , _Reason },
122
+ # state {waiting = Waiting , tasks = Tasks } = State ) ->
123
+ case {Waiting , Tasks } of
124
+ {#{Pid := Ref }, _ } ->
125
+ flush_exit (Pid ),
126
+ {noreply , State # state {waiting = maps :remove (Pid , Waiting )}};
127
+ {_ , #{Ref := Pid }} ->
128
+ {noreply , State # state {tasks = maps :remove (Ref , Tasks )}};
129
+ _ ->
130
+ {noreply , State }
131
+ end ;
132
+ handle_info ({'EXIT' , Pid , _Reason }, # state {index = N , table = Tid } = State ) ->
133
+ handle_down_user (Tid , Pid , N ),
118
134
{noreply , State };
119
135
handle_info (_Info , State ) ->
120
136
{noreply , State }.
@@ -126,14 +142,19 @@ terminate(_Reason, State) ->
126
142
127
143
% % Helpers
128
144
129
- -spec do_start_child (module (), amoc_scenario :user_id (), term (), state ()) -> any ().
130
- do_start_child (Scenario , Id , ScenarioState , # state {index = N , table = Table }) ->
131
- case amoc_user :start_link (Scenario , Id , ScenarioState ) of
132
- {ok , Pid } ->
133
- handle_up_user (Table , Pid , Id , N );
134
- _ ->
135
- ok
136
- end .
145
+ -spec flush_exit (pid ()) -> ok .
146
+ flush_exit (Pid ) ->
147
+ unlink (Pid ),
148
+ receive {'EXIT' , Pid , _ } -> ok after 0 -> ok end .
149
+
150
+ -spec do_start_child (amoc :scenario (), [amoc_scenario :user_id ()], term (), state ()) -> state ().
151
+ do_start_child (Scenario , Ids , ScenarioState , # state {waiting = Waiting } = State ) ->
152
+ Fun = fun (Id , Acc ) ->
153
+ {Pid , Ref } = amoc_user :start_link (Scenario , Id , ScenarioState ),
154
+ Acc #{Pid => Ref }
155
+ end ,
156
+ NewWaiting = lists :foldl (Fun , Waiting , Ids ),
157
+ State # state {waiting = NewWaiting }.
137
158
138
159
-spec handle_up_user (ets :table (), pid (), amoc_scenario :user_id (), non_neg_integer ()) -> any ().
139
160
handle_up_user (Table , Pid , Id , SupNum ) ->
@@ -156,7 +177,7 @@ maybe_track_task_to_stop_my_children(State, Pids, false) ->
156
177
State ;
157
178
maybe_track_task_to_stop_my_children (# state {tasks = Tasks } = State , Pids , true ) ->
158
179
{Pid , Ref } = spawn_monitor (shutdown_and_kill_after_timeout_fun (Pids )),
159
- State # state {tasks = Tasks #{Pid => Ref }}.
180
+ State # state {tasks = Tasks #{Ref => Pid }}.
160
181
161
182
-spec shutdown_and_kill_after_timeout_fun (pid () | [pid ()]) -> fun (() -> term ()).
162
183
shutdown_and_kill_after_timeout_fun ([_ | _ ] = Pids ) ->
0 commit comments