File 9182-make-application-stop-a-request.patch of Package erlang

From 69b713a82acf1b6a4377b8f07d299ee613744d19 Mon Sep 17 00:00:00 2001
From: loscher <loscher@meta.com>
Date: Mon, 19 Jan 2026 06:22:35 -0800
Subject: [PATCH 2/3] make application stop a request

---
 lib/kernel/src/application_controller.erl | 113 ++++++++++++++++------
 lib/kernel/test/application_SUITE.erl     |   6 +-
 2 files changed, 87 insertions(+), 32 deletions(-)

diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl
index cb533188c5..fcd5b1d456 100644
--- a/lib/kernel/src/application_controller.erl
+++ b/lib/kernel/src/application_controller.erl
@@ -29,6 +29,7 @@
 	 load_application/1, unload_application/1,
 	 start_application/2, start_application_request/2,
 	 start_boot_application/2, stop_application/1,
+	 stop_application_request/1,
 	 control_application/1, is_running/1,
 	 change_application_data/2, prep_config_change/0, config_change/1,
 	 which_applications/0, which_applications/1,
@@ -143,7 +144,7 @@
 -type appname() :: atom().
 
 -record(state, {loading = [], starting = [], start_p_false = [], running = [],
-		control = [], started = [], start_req = [], conf_data}).
+		control = [], started = [], start_req = [], stop_req = [], conf_data}).
 -type state() :: #state{}.
 
 %%-----------------------------------------------------------------
@@ -160,6 +161,7 @@
 %%                 permission = false)
 %% conf_data   = [{AppName, Env}]
 %% start_req   = [{AppName, From}] - list of all start requests
+%% stop_req    = [{AppName, From}] - list of all stop requests
 %% Id          = AMPid | undefined | {distributed, Node}
 %% Env         = [{Key, Value}]
 %%-----------------------------------------------------------------
@@ -282,6 +284,9 @@ start_boot_application(Application, RestartType) ->
 stop_application(AppName) ->
     call({stop_application, AppName}, infinity).
 
+stop_application_request(AppName) ->
+    gen_server:send_request(?AC, {stop_application, AppName}).
+
 %%-----------------------------------------------------------------
 %% Returns: [{Name, Descr, Vsn}]
 %%-----------------------------------------------------------------
@@ -838,25 +843,31 @@ handle_call({permit_application, AppName, Bool}, From, S) ->
 	    end
     end;
 
-handle_call({stop_application, AppName}, _From, S) ->
-    #state{running = Running, started = Started} = S,
-    case lists:keyfind(AppName, 1, Running) of
-	{_AppName, Id} ->
-	    {_AppName2, Type} = lists:keyfind(AppName, 1, Started),
-	    stop_appl(AppName, Id, Type),
-	    NRunning = keydelete(AppName, 1, Running),
-	    NStarted = keydelete(AppName, 1, Started),
-	    cntrl(AppName, S, {ac_application_stopped, AppName}),
-	    {reply, ok, S#state{running = NRunning, started = NStarted}};
+handle_call({stop_application, AppName}, From, S) ->
+    #state{running = Running, starting = _Starting, started = Started,
+	   start_req = _Start_req, stop_req = Stop_req} = S,
+    %% Check if already stopping
+    case lists:keyfind(AppName, 1, Stop_req) of
 	false ->
-	    case lists:keymember(AppName, 1, Started) of
-		true ->
-		    NStarted = keydelete(AppName, 1, Started),
-		    cntrl(AppName, S, {ac_application_stopped, AppName}),
-		    {reply, ok, S#state{started = NStarted}};
+	    case lists:keyfind(AppName, 1, Running) of
+		{_AppName, Id} ->
+		    {_AppName2, Type} = lists:keyfind(AppName, 1, Started),
+		    spawn_stopper(AppName, Id, Type),
+		    {noreply, S#state{stop_req = [{AppName, From} | Stop_req]}};
 		false ->
-		    {reply, {error, {not_started, AppName}}, S}
-	    end
+		    case lists:keymember(AppName, 1, Started) of
+			true ->
+			    NStarted = keydelete(AppName, 1, Started),
+			    cntrl(AppName, S, {ac_application_stopped, AppName}),
+			    {reply, ok, S#state{started = NStarted}};
+			false ->
+			    {reply, {error, {not_started, AppName}}, S}
+		    end
+	    end;
+	{AppName, _FromX} ->
+	    %% Already stopping, add requester to list
+	    SS = S#state{stop_req = [{AppName, From} | Stop_req]},
+	    {noreply, SS}
     end;
 
 handle_call({change_application_data, Applications, Config}, _From, S) ->
@@ -960,11 +971,23 @@ handle_call(info, _From, S) ->
 	     {starting, S#state.starting}],
     {reply, Reply, S}.
 
--spec handle_cast({'application_started', appname(), _}, state()) ->
+-spec handle_cast({'application_started', appname(), _} |
+                  {'application_stopped', appname()}, state()) ->
         {'noreply', state()} | {'stop', string(), state()}.
 
 handle_cast({application_started, AppName, Res}, S) ->
-    handle_application_started(AppName, Res, S).
+    handle_application_started(AppName, Res, S);
+
+handle_cast({application_stopped, AppName}, S) ->
+    handle_application_stopped(S,AppName).
+
+handle_application_stopped(S,AppName) ->
+    #state{running = Running, started = Started, stop_req = Stop_req} = S,
+    NRunning = keydelete(AppName, 1, Running),
+    NStarted = keydelete(AppName, 1, Started),
+    cntrl(AppName, S, {ac_application_stopped, AppName}),
+    Stop_reqN = reply_to_stop_requester(AppName, Stop_req),
+    {noreply, S#state{running = NRunning, started = NStarted, stop_req = Stop_reqN}}.
 
 handle_application_started(AppName, Res, S) ->
     #state{starting = Starting, running = Running, started = Started,
@@ -1209,17 +1232,23 @@ handle_info({'EXIT', Pid, Reason}, S) ->
     NewS = S#state{running = NRunning},
     case lists:keyfind(Pid, 2, S#state.running) of
 	{AppName, _AmPid} ->
-	    cntrl(AppName, S, {ac_application_stopped, AppName}),
-	    case lists:keyfind(AppName, 1, S#state.started) of
-		{_AppName, temporary} ->
-		    info_exited(AppName, Reason, temporary),
-		    {noreply, NewS};
-		{_AppName, transient} when Reason =:= normal ->
-		    info_exited(AppName, Reason, transient),
-		    {noreply, NewS};
-		{_AppName, Type} ->
-		    info_exited(AppName, Reason, Type),
-		    {stop, to_string({application_terminated, AppName, Reason}), NewS}
+	    %% check wether the application is in the process of being stopped
+	    %% and if so, ignore the message
+	    case lists:keyfind(AppName, 1, S#state.stop_req) of
+		{_AppName, _Req} -> {noreply, S};
+		false ->
+		    cntrl(AppName, S, {ac_application_stopped, AppName}),
+		    case lists:keyfind(AppName, 1, S#state.started) of
+			{_AppName, temporary} ->
+			    info_exited(AppName, Reason, temporary),
+		    	    {noreply, NewS};
+			{_AppName, transient} when Reason =:= normal ->
+		    	    info_exited(AppName, Reason, transient),
+		    	    {noreply, NewS};
+			{_AppName, Type} ->
+		    	    info_exited(AppName, Reason, Type),
+		    	    {stop, to_string({application_terminated, AppName, Reason}), NewS}
+	    	    end
 	    end;
 	false ->
 	    {noreply, S#state{control = del_cntrl(S#state.control, Pid)}}
@@ -1418,6 +1447,12 @@ spawn_starter(Appl, S, Type) ->
             application_master:start_link(Appl#appl.appl_data, Type)
     end.
 
+spawn_stopper(AppName, Id, Type) ->
+    spawn(fun() ->
+		  stop_appl(AppName, Id, Type),
+		  gen_server:cast(?AC, {application_stopped, AppName})
+	  end).
+
 reply(undefined, _Reply) ->
     ok;
 reply(From, Reply) -> gen_server:reply(From, Reply).
@@ -2292,6 +2327,22 @@ reply_to_requester(AppName, Start_req, Res) ->
 		[],
 		Start_req).
 
+%%-----------------------------------------------------------------
+%% Reply to all processes waiting this application to be stopped.
+%%-----------------------------------------------------------------
+reply_to_stop_requester(AppName, Stop_req) ->
+    lists:foldl(fun(Sp, AccIn) ->
+			case Sp of
+			    {AppName, From} ->
+				reply(From, ok),
+				AccIn;
+			    _ ->
+				[Sp | AccIn]
+			end
+		end,
+		[],
+		Stop_req).
+
 
 %%-----------------------------------------------------------------
 %% Update the environment variable permission for an application.
diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl
index 03401e702b..829adf7c51 100644
--- a/lib/kernel/test/application_SUITE.erl
+++ b/lib/kernel/test/application_SUITE.erl
@@ -2657,8 +2657,12 @@ shutdown_deadlock(Config) when is_list(Config) ->
 	    erlang:display({self(), "Sending Continue", Server}),
 	    Server ! continue
     end,
+    %% Wait for stop to complete before unloading
+    receive
+        {stop, ok} -> ok
+    end,
     [_|_] = application:which_applications(),
-    application:unload(deadlock), % clean up!
+    ok = application:unload(deadlock), % clean up!
     ok.
 
 
-- 
2.51.0

openSUSE Build Service is sponsored by