File 2741-Allow-changing-the-mode-when-the-system-restarts.patch of Package erlang

From 24fc26590a5dccf0ae118ce38fb5d2f35ae11680 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Valim?= <jose.valim@plataformatec.com.br>
Date: Wed, 20 Nov 2019 19:26:10 +0100
Subject: [PATCH] Allow changing the -mode when the system restarts

In order for Elixir developers to configure their
systems using Elixir syntax, the Elixir application
must be started. However, in order to start the
Elixir application, kernel and stdlib need to be
started, which means we can't configure those
applications.

Elixir addresses this by booting twice. First, the
system boots with a minimal set of apps started, then
it executes the configuration files, writes them to
disk and restarts the system, which will pick up the
new configuration.

The problemw with such approach is that, when running
in embedded mode, starting and shutting down the system
takes a long period of time, due to the loading and
purging of all modules.

This PR addresses the problem by allowing the mode to
be changed to embedded on init:restart/1. This allows
Elixir to first boot in interactive mode, load the
configuration, and then start the system in embedded
mode.

This reduces the boot time in the sample application
that reproduces the problem from 5s to 1s.
---
 erts/doc/src/init.xml          | 16 ++++++++++++++--
 erts/preloaded/src/init.erl    | 37 +++++++++++++++++++++++++++++--------
 lib/kernel/test/init_SUITE.erl | 20 ++++++++++++++++++--
 3 files changed, 61 insertions(+), 12 deletions(-)

diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml
index c824e37976..3129d02449 100644
--- a/erts/doc/src/init.xml
+++ b/erts/doc/src/init.xml
@@ -164,13 +164,25 @@
     <func>
       <name name="restart" arity="0" since=""/>
       <fsummary>Restart the running Erlang node.</fsummary>
+      <desc>
+        <p>The same as
+    <seealso marker="#restart/1"><c>restart([])</c></seealso>.</p>
+      </desc>
+    </func>
+
+    <func>
+      <name name="restart" arity="1" since="OTP 23.0"/>
+      <fsummary>Restart the running Erlang node.</fsummary>
       <desc>
         <p>The system is restarted <em>inside</em> the running Erlang
           node, which means that the emulator is not restarted. All
           applications are taken down smoothly, all code is unloaded,
           and all ports are closed before the system is booted again in
-          the same way as initially started. The same <c>BootArgs</c>
-          are used again.</p>
+          the same way as initially started.</p>
+        <p>The same <c>BootArgs</c> are used when restarting the
+          system unless the <c>mode</c> option is given, allowing the
+          code loading mode to be set to either <c>embedded</c> or
+          <c>interactive</c>. All other <c>BootArgs</c> remain the same.</p>
         <p>To limit the shutdown time, the time <c>init</c> is allowed
           to spend taking down applications, command-line flag
           <c>-shutdown_time</c> is to be used.</p>
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index 5ea67347ec..76077880b6 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -48,7 +48,7 @@
 
 -module(init).
 
--export([restart/0,reboot/0,stop/0,stop/1,
+-export([restart/1,restart/0,reboot/0,stop/0,stop/1,
 	 get_status/0,boot/1,get_arguments/0,get_plain_arguments/0,
 	 get_argument/1,script_id/0]).
 
@@ -63,6 +63,7 @@
 -include_lib("kernel/include/file.hrl").
 
 -type internal_status() :: 'starting' | 'started' | 'stopping'.
+-type mode() :: 'embedded' | 'interactive'.
 
 -record(state, {flags = [],
 		args = [],
@@ -164,7 +165,15 @@ request(Req) ->
     end.
 
 -spec restart() -> 'ok'.
-restart() -> init ! {stop,restart}, ok.
+restart() -> restart([]).
+
+-spec restart([{mode, mode()}]) -> 'ok'.
+restart([]) ->
+    init ! {stop,restart}, ok;
+restart([{mode, Mode}]) when Mode =:= embedded; Mode =:= interactive ->
+    init ! {stop,{restart,Mode}}, ok;
+restart(Opts) when is_list(Opts) ->
+    erlang:error(badarg, [Opts]).
 
 -spec reboot() -> 'ok'.
 reboot() -> init ! {stop,reboot}, ok.
@@ -546,11 +555,11 @@ stop(Reason,State) ->
     clear_system(Reason=/=stop,BootPid,State1),
     do_stop(Reason,State1).
 
-do_stop(restart,#state{start = Start, flags = Flags, args = Args}) ->
-    %% Make sure we don't have any outstanding messages before doing the restart.
-    flush(),
-    erts_internal:erase_persistent_terms(),
-    boot(Start,Flags,Args);
+do_stop({restart,Mode},#state{start=Start, flags=Flags0, args=Args}) ->
+    Flags = update_flag(mode, Flags0, atom_to_binary(Mode)),
+    do_restart(Start,Flags,Args);
+do_stop(restart,#state{start=Start, flags=Flags, args=Args}) ->
+    do_restart(Start,Flags,Args);
 do_stop(reboot,_) ->
     halt();
 do_stop(stop,State) ->
@@ -560,6 +569,11 @@ do_stop({stop,Status},State) ->
     stop_heart(State),
     halt(Status).
 
+do_restart(Start,Flags,Args) ->
+    flush(),
+    erts_internal:erase_persistent_terms(),
+    boot(Start,Flags,Args).
+
 clear_system(Unload,BootPid,State) ->
     Heart = get_heart(State#state.kernel),
     Logger = get_logger(State#state.kernel),
@@ -798,7 +812,7 @@ do_boot(Init,Flags,Start) ->
     start_prim_loader(Init, bs2ss(Path), PathFls),
     BootFile = bootfile(Flags,Root),
     BootList = get_boot(BootFile,Root),
-    LoadMode = b2a(get_flag(mode, Flags, false)),
+    LoadMode = b2a(get_flag(mode, Flags, interactive)),
     Deb = b2a(get_flag(init_debug, Flags, false)),
     catch ?ON_LOAD_HANDLER ! {init_debug_flag,Deb},
     BootVars = get_boot_vars(Root, Flags),
@@ -1233,6 +1247,13 @@ get_args([B|Bs], As) ->
     end;
 get_args([], As) -> {reverse(As),[]}.
 
+update_flag(Flag, [{Flag, _} | Flags], Value) ->
+    [{Flag, [Value]} | Flags];
+update_flag(Flag, [Head | Flags], Value) ->
+    [Head | update_flag(Flag, Flags, Value)];
+update_flag(Flag, [], Value) ->
+    [{Flag, [Value]}].
+
 %%
 %% Internal get_flag function, with default value.
 %% Return: true if flag given without args
diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl
index a0154b2694..25e437ce1a 100644
--- a/lib/kernel/test/init_SUITE.erl
+++ b/lib/kernel/test/init_SUITE.erl
@@ -25,7 +25,7 @@
 	 init_per_group/2,end_per_group/2]).
 
 -export([get_arguments/1, get_argument/1, boot_var/1, restart/1,
-	 many_restarts/0, many_restarts/1,
+	 many_restarts/0, many_restarts/1, restart_with_mode/1,
 	 get_plain_arguments/1,
 	 reboot/1, stop_status/1, stop/1, get_status/1, script_id/1,
 	 find_system_processes/0]).
@@ -48,7 +48,7 @@ suite() ->
 
 all() -> 
     [get_arguments, get_argument, boot_var,
-     many_restarts,
+     many_restarts, restart_with_mode,
      get_plain_arguments, restart, stop_status, get_status, script_id,
      {group, boot}].
 
@@ -348,6 +348,22 @@ wait_for(N,Node,EHPid) ->
 	    wait_for(N-1,Node,EHPid)
     end.
 
+restart_with_mode(Config) when is_list(Config) ->
+    %% We cannot use loose_node because it doesn't run in
+    %% embedded mode so we quickly start one that exits after restarting
+    {ok,[[Erl]]} = init:get_argument(progname),
+    ModPath = filename:dirname(code:which(?MODULE)),
+
+    Eval1 = "'Mode=code:get_mode(), io:fwrite(Mode), case Mode of interactive -> init:restart([{mode,embedded}]); embedded -> erlang:halt() end'",
+    Cmd1 = Erl ++ " -mode interactive -noshell -eval " ++ Eval1,
+    "interactiveembedded" = os:cmd(Cmd1),
+
+    Eval2 = "'Mode=code:get_mode(), io:fwrite(Mode), case Mode of embedded -> init:restart([{mode,interactive}]); interactive -> erlang:halt() end'",
+    Cmd2 = Erl ++ " -mode embedded -noshell -eval " ++ Eval2,
+    "embeddedinteractive" = os:cmd(Cmd2),
+
+    ok.
+
 %% ------------------------------------------------
 %% Slave executes erlang:halt() on master nodedown.
 %% Therefore the slave process must be killed
-- 
2.16.4

openSUSE Build Service is sponsored by