From 2fe03e832adb11c50bcfc62679cf17779b284124 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Wed, 27 Jul 2016 19:45:41 +0200 Subject: Reclaim literal area after purge has completed --- erts/preloaded/src/Makefile | 3 +- erts/preloaded/src/erts.app.src | 2 +- erts/preloaded/src/erts_code_purger.erl | 49 ++++++++- erts/preloaded/src/erts_internal.erl | 11 +- erts/preloaded/src/erts_literal_area_collector.erl | 113 +++++++++++++++++++++ 5 files changed, 167 insertions(+), 11 deletions(-) create mode 100644 erts/preloaded/src/erts_literal_area_collector.erl (limited to 'erts/preloaded/src') diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index 4a447d3a09..df4645070b 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -44,7 +44,8 @@ PRE_LOADED_ERL_MODULES = \ erts_code_purger \ erlang \ erts_internal \ - erl_tracer + erl_tracer \ + erts_literal_area_collector PRE_LOADED_BEAM_MODULES = \ prim_eval diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src index e18da28905..7ab06164b4 100644 --- a/erts/preloaded/src/erts.app.src +++ b/erts/preloaded/src/erts.app.src @@ -37,7 +37,7 @@ {registered, []}, {applications, []}, {env, []}, - {runtime_dependencies, ["stdlib-3.0", "kernel-5.0", "sasl-3.0"]} + {runtime_dependencies, ["stdlib-3.0", "kernel-5.0", "sasl-3.0.1"]} ]}. %% vim: ft=erlang diff --git a/erts/preloaded/src/erts_code_purger.erl b/erts/preloaded/src/erts_code_purger.erl index d1e64342e0..f9208624b7 100644 --- a/erts/preloaded/src/erts_code_purger.erl +++ b/erts/preloaded/src/erts_code_purger.erl @@ -28,7 +28,36 @@ start() -> register(erts_code_purger, self()), process_flag(trap_exit, true), - loop(). + try + %% Pass bad arguments to copy_literals() in + %% order to determine purge strategy used + %% by the VM... + Res = erts_internal:copy_literals(4711, badarg), + exit({copy_literals_returned, Res}) + catch + error : badarg -> %% VM use old purge strategy + old_loop(); + error : notsup -> %% VM use new purge strategy + loop(); + Type : Reason -> + %% This should not be possible... + exit({"Unexpected copy_literals() behaviour", + {Type, Reason}}) + end. + +old_loop() -> + _ = receive + {purge,Mod,From,Ref} when is_atom(Mod), is_pid(From) -> + Res = do_old_purge(Mod), + From ! {reply, purge, Res, Ref}; + + {soft_purge,Mod,From,Ref} when is_atom(Mod), is_pid(From) -> + Res = do_old_soft_purge(Mod), + From ! {reply, soft_purge, Res, Ref}; + + _Other -> ignore + end, + old_loop(). loop() -> _ = receive @@ -61,7 +90,7 @@ purge(Mod) when is_atom(Mod) -> end. -do_purge(Mod) -> +do_old_purge(Mod) -> case erts_internal:copy_literals(Mod, true) of false -> {false, false}; @@ -72,6 +101,11 @@ do_purge(Mod) -> {WasPurged, DidKill} end. +do_purge(Mod) -> + DidKill = check_proc_code(erlang:processes(), Mod, true), + WasPurged = erts_internal:purge_module(Mod), + {WasPurged, DidKill}. + %% soft_purge(Module) %% Purge old code only if no procs remain that run old code. %% Return true in that case, false if procs remain (in this @@ -86,7 +120,7 @@ soft_purge(Mod) -> end. -do_soft_purge(Mod) -> +do_old_soft_purge(Mod) -> case erts_internal:copy_literals(Mod, true) of false -> true; @@ -102,6 +136,12 @@ do_soft_purge(Mod) -> end end. +do_soft_purge(Mod) -> + case check_proc_code(erlang:processes(), Mod, false) of + false -> false; + true -> erts_internal:purge_module(Mod) + end. + %% %% check_proc_code(Pids, Mod, Hard) - Send asynchronous %% requests to all processes to perform a check_process_code @@ -283,8 +323,7 @@ cpc_sched_kill(Pid, cpc_request(#cpc_static{tag = Tag, module = Mod}, Pid, AllowGc) -> erts_internal:check_process_code(Pid, Mod, [{async, {Tag, Pid, AllowGc}}, - {allow_gc, AllowGc}, - {copy_literals, true}]). + {allow_gc, AllowGc}]). cpc_request_gc(CpcS, [Pid|Pids]) -> cpc_request(CpcS, Pid, true), diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 2459ea2a2c..2bf430d857 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -42,6 +42,7 @@ -export([check_process_code/3]). -export([copy_literals/2]). +-export([release_literal_area_switch/0]). -export([purge_module/1]). -export([flush_monitor_messages/3]). @@ -211,7 +212,6 @@ request_system_task(_Pid, _Prio, _Request) -> erlang:nif_error(undefined). -define(ERTS_CPC_ALLOW_GC, (1 bsl 0)). --define(ERTS_CPC_COPY_LITERALS, (1 bsl 1)). -spec check_process_code(Module, Flags) -> boolean() when Module :: module(), @@ -223,7 +223,7 @@ check_process_code(_Module, _Flags) -> Pid :: pid(), Module :: module(), RequestId :: term(), - Option :: {async, RequestId} | {allow_gc, boolean()} | {copy_literals, boolean()}, + Option :: {async, RequestId} | {allow_gc, boolean()}, OptionList :: [Option], CheckResult :: boolean() | aborted. check_process_code(Pid, Module, OptionList) -> @@ -265,8 +265,6 @@ get_cpc_opts([{async, _ReqId} = AsyncTuple | Options], _OldAsync, Flags) -> get_cpc_opts(Options, AsyncTuple, Flags); get_cpc_opts([{allow_gc, AllowGC} | Options], Async, Flags) -> get_cpc_opts(Options, Async, cpc_flags(Flags, ?ERTS_CPC_ALLOW_GC, AllowGC)); -get_cpc_opts([{copy_literals, CopyLit} | Options], Async, Flags) -> - get_cpc_opts(Options, Async, cpc_flags(Flags, ?ERTS_CPC_COPY_LITERALS, CopyLit)); get_cpc_opts([], Async, Flags) -> {Async, Flags}. @@ -281,6 +279,11 @@ cpc_flags(OldFlags, Bit, false) -> copy_literals(_Mod, _Bool) -> erlang:nif_error(undefined). +-spec release_literal_area_switch() -> 'true' | 'false'. + +release_literal_area_switch() -> + erlang:nif_error(undefined). + -spec purge_module(Module) -> boolean() when Module :: module(). purge_module(_Module) -> diff --git a/erts/preloaded/src/erts_literal_area_collector.erl b/erts/preloaded/src/erts_literal_area_collector.erl new file mode 100644 index 0000000000..3befad8dfb --- /dev/null +++ b/erts/preloaded/src/erts_literal_area_collector.erl @@ -0,0 +1,113 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(erts_literal_area_collector). + +-export([start/0]). + +%% Currently we only allow two outstanding literal +%% copying jobs that garbage collect in order to +%% copy the literals. Maybe we could allow more +%% than two outstanding processes, but for now we +%% play it safe... +-define(MAX_GC_OUTSTND, 2). + +%% +%% The erts_literal_area_collector is started at +%% VM boot by the VM. It is a spawned as a system +%% process, i.e, the whole VM will terminate if +%% this process terminates. +%% +start() -> + process_flag(trap_exit, true), + msg_loop(undefined, 0, 0, []). + +%% +%% The VM will send us a 'copy_literals' message +%% when it has a new literal area that needs to +%% be handled is added. We will also be informed +%% about more areas when we call +%% erts_internal:release_literal_area_switch(). +%% +msg_loop(Area, Outstnd, GcOutstnd, NeedGC) -> + receive + + %% A new area to handle has arrived... + copy_literals when Outstnd == 0 -> + switch_area(); + + %% Process (_Pid) has completed the request... + {copy_literals, {Area, _GcAllowed, _Pid}, ok} when Outstnd == 1 -> + switch_area(); %% Last process completed... + {copy_literals, {Area, false, _Pid}, ok} -> + msg_loop(Area, Outstnd-1, GcOutstnd, NeedGC); + {copy_literals, {Area, true, _Pid}, ok} when NeedGC == [] -> + msg_loop(Area, Outstnd-1, GcOutstnd-1, []); + {copy_literals, {Area, true, _Pid}, ok} -> + send_copy_req(hd(NeedGC), Area, true), + msg_loop(Area, Outstnd-1, GcOutstnd, tl(NeedGC)); + + %% Process (Pid) failed to complete the request + %% since it needs to garbage collect in order to + %% complete the request... + {copy_literals, {Area, false, Pid}, need_gc} when GcOutstnd < ?MAX_GC_OUTSTND -> + send_copy_req(Pid, Area, true), + msg_loop(Area, Outstnd, GcOutstnd+1, NeedGC); + {copy_literals, {Area, false, Pid}, need_gc} -> + msg_loop(Area, Outstnd, GcOutstnd, [Pid|NeedGC]); + + %% Not handled message regarding the area that we + %% currently are working with. Crash the VM so + %% we notice this bug... + {copy_literals, {Area, _, _}, _} = Msg when erlang:is_reference(Area) -> + exit({not_handled_message, Msg}); + + %% Unexpected garbage message. Get rid of it... + _Ignore -> + msg_loop(Area, Outstnd, GcOutstnd, NeedGC) + + end. + +switch_area() -> + Res = erts_internal:release_literal_area_switch(), + erlang:garbage_collect(), %% Almost no live data now... + case Res of + false -> + %% No more areas to handle... + msg_loop(undefined, 0, 0, []); + true -> + %% Send requests to all processes to copy + %% all live data they have referring to the + %% literal area that is to be released... + Area = make_ref(), + Outstnd = send_copy_reqs(erlang:processes(), Area, false), + msg_loop(Area, Outstnd, 0, []) + end. + +send_copy_reqs(Ps, Area, GC) -> + send_copy_reqs(Ps, Area, GC, 0). + +send_copy_reqs([], _Area, _GC, N) -> + N; +send_copy_reqs([P|Ps], Area, GC, N) -> + send_copy_req(P, Area, GC), + send_copy_reqs(Ps, Area, GC, N+1). + +send_copy_req(P, Area, GC) -> + erts_internal:request_system_task(P, normal, {copy_literals, {Area, GC, P}, GC}). -- cgit v1.2.3