File 2331-Check-for-embedded-mode-in-the-client.patch of Package erlang
From e0ea6f5fd2b0a92e4ff8516c2a6f2680b31684fc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Valim?= <jose.valim@dashbit.co>
Date: Tue, 9 Jan 2024 11:44:52 +0100
Subject: [PATCH] Check for embedded mode in the client
This ensures that checking for code:ensure_loaded/1
in embedded mode (and other operations) do not send
additional messages to the code server.
This pull request fully closes #7889. While previous
work has fixed the slow path in the code server, this
pull request makes it so the code server is avoided
altogether.
---
lib/kernel/doc/src/code.xml | 11 +++++++---
lib/kernel/src/code.erl | 29 ++++++++++++++-----------
lib/kernel/src/code_server.erl | 39 +++++++++++++---------------------
3 files changed, 40 insertions(+), 39 deletions(-)
diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml
index 66090cc6fd..7d5f2c10c9 100644
--- a/lib/kernel/doc/src/code.xml
+++ b/lib/kernel/doc/src/code.xml
@@ -602,8 +602,11 @@ ok = code:finish_loading(Prepared),
<desc>
<p>Tries to load a module in the same way as
<seemfa marker="#load_file/1"><c>load_file/1</c></seemfa>,
- unless the module is already loaded.
- However, in embedded mode it does not load a module that is not
+ unless the module is already loaded. If called concurrently,
+ this function ensures only one process attempts to load said
+ module at a given time.</p>
+
+ <p>In embedded mode, it does not load a module that is not
already loaded, but returns <c>{error, embedded}</c> instead.
See <seeerl marker="#error_reasons">Error Reasons for Code-Loading Functions</seeerl> for a description of other possible error reasons.</p>
</desc>
@@ -614,7 +617,9 @@ ok = code:finish_loading(Prepared),
<desc>
<p>Tries to load any modules not already loaded in the list
<c><anno>Modules</anno></c> in the same way as
- <seemfa marker="#load_file/1">load_file/1</seemfa>.</p>
+ <seemfa marker="#load_file/1">load_file/1</seemfa>.
+ Unlike <seemfa marker="#ensure_loaded/1">ensure_loaded/1</seemfa>,
+ modules are loaded even in <c>embedded</c> mode.</p>
<p>Returns <c>ok</c> if successful, or
<c>{error,[{Module,Reason}]}</c> if loading of some modules fails.
See <seeerl marker="#error_reasons">Error Reasons for Code-Loading Functions</seeerl> for a description of other possible error reasons.</p>
diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl
index 7b82b6be9a..32555971dc 100644
--- a/lib/kernel/src/code.erl
+++ b/lib/kernel/src/code.erl
@@ -196,16 +196,21 @@ ensure_loaded(Mod) when is_atom(Mod) ->
case erlang:module_loaded(Mod) of
true -> {module, Mod};
false ->
- case call({get_object_code_for_loading, Mod}) of
- {module, Mod} -> {module, Mod};
- {error, What} -> {error, What};
- {Binary,File,Ref} ->
- case ensure_prepare_loading(Mod, Binary, File) of
- {error,_}=Error ->
- call({load_error, Ref, Mod, Error});
- Prepared ->
- call({load_module, Prepared, Mod, File, false, Ref})
- end
+ case get_mode() of
+ interactive ->
+ case call({get_object_code_for_loading, Mod}) of
+ {module, Mod} -> {module, Mod};
+ {error, What} -> {error, What};
+ {Binary,File,Ref} ->
+ case ensure_prepare_loading(Mod, Binary, File) of
+ {error,_}=Error ->
+ call({load_error, Ref, Mod, Error});
+ Prepared ->
+ call({load_module, Prepared, Mod, File, false, Ref})
+ end
+ end;
+ embedded ->
+ {error, embedded}
end
end.
@@ -313,7 +318,7 @@ all_loaded() -> call(all_loaded).
Filename :: loaded_filename(),
Loaded :: boolean().
all_available() ->
- case code:get_mode() of
+ case get_mode() of
interactive ->
all_available(get_path(), #{});
embedded ->
@@ -520,7 +525,7 @@ replace_path(Name, Dir, Cache) when (is_atom(Name) orelse is_list(Name)),
call({replace_path,Name,Dir,Cache}).
-spec get_mode() -> 'embedded' | 'interactive'.
-get_mode() -> call(get_mode).
+get_mode() -> code_server:get_mode().
-spec clear_cache() -> ok.
clear_cache() -> call(clear_cache).
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index 5315fa1e1a..78e635a1a9 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -22,7 +22,7 @@
%% This file holds the server part of the code_server.
-export([start_link/1,
- call/1, absname/1,
+ call/1, absname/1, get_mode/0,
is_loaded/1, is_sticky/1,
system_code_change/4,
error_msg/2, info_msg/2
@@ -46,7 +46,6 @@
path :: [{file:name_all(), cache | nocache}],
moddb :: ets:table(),
namedb :: ets:table(),
- mode = interactive :: 'interactive' | 'embedded',
on_load = [] :: [on_load_item()],
loading = #{} :: #{module() => [pid()]}}).
-type state() :: #state{}.
@@ -70,6 +69,9 @@ is_loaded(Mod) ->
is_sticky(Mod) ->
is_sticky(Mod, ?moddb).
+get_mode() ->
+ persistent_term:get(?MODULE).
+
%% -----------------------------------------------------------
%% Init the code_server process.
%% -----------------------------------------------------------
@@ -104,9 +106,9 @@ init(Ref, Parent, [Root,Mode]) ->
root = Root,
path = Path,
moddb = Db,
- namedb = create_namedb(Path, Root),
- mode = Mode},
+ namedb = create_namedb(Path, Root)},
+ persistent_term:put(?MODULE, Mode),
Parent ! {Ref,{ok,self()}},
loop(State).
@@ -342,7 +344,7 @@ handle_call({get_object_code,Mod}, _From, St0) when is_atom(Mod) ->
handle_call({get_object_code_for_loading,Mod}, From, St0) when is_atom(Mod) ->
case erlang:module_loaded(Mod) of
true -> {reply, {module, Mod}, St0};
- false when St0#state.mode =:= interactive ->
+ false ->
%% Handles pending on_load events first. If the code is being
%% loaded, finish before adding more entries to the queue.
Action = fun(_, St1) ->
@@ -351,16 +353,12 @@ handle_call({get_object_code_for_loading,Mod}, From, St0) when is_atom(Mod) ->
false -> get_object_code_for_loading(St1, Mod, From)
end
end,
- handle_pending_on_load(Action, Mod, From, St0);
- false -> {reply, {error,embedded}, St0}
+ handle_pending_on_load(Action, Mod, From, St0)
end;
handle_call(stop,_From, S) ->
{stop,normal,stopped,S};
-handle_call(get_mode, _From, S=#state{mode=Mode}) ->
- {reply, Mode, S};
-
handle_call({finish_loading,Prepared,EnsureLoaded}, _From, S) ->
{reply,finish_loading(Prepared, EnsureLoaded, S),S};
@@ -1087,20 +1085,13 @@ del_paths(_,Path,_) ->
{ok,Path}.
try_finish_module(File, Mod, PC, EnsureLoaded, From, St) ->
- Action = case EnsureLoaded of
- false ->
- fun(_, S) -> try_finish_module_1(File, Mod, PC, From, false, S) end;
- _ ->
- fun(_, S0) ->
- case erlang:module_loaded(Mod) of
- true ->
- reply_loading(EnsureLoaded, Mod, {module, Mod}, S0);
- false when S0#state.mode =:= interactive ->
- try_finish_module_1(File, Mod, PC, From, EnsureLoaded, S0);
- false ->
- reply_loading(EnsureLoaded, Mod, {error, embedded}, S0)
- end
- end
+ Action = fun(_, S) ->
+ case (EnsureLoaded =/= false) andalso erlang:module_loaded(Mod) of
+ true ->
+ reply_loading(EnsureLoaded, Mod, {module, Mod}, S);
+ false ->
+ try_finish_module_1(File, Mod, PC, From, EnsureLoaded, S)
+ end
end,
handle_pending_on_load(Action, Mod, From, St).
--
2.35.3