1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(erts_literal_area_collector).
21
22-export([start/0, send_copy_request/3, release_area_switch/0]).
23
24%% Currently we only allow two outstanding literal
25%% copying jobs that garbage collect in order to
26%% copy the literals. Maybe we could allow more
27%% than two outstanding processes, but for now we
28%% play it safe...
29-define(MAX_GC_OUTSTND, 2).
30
31%%
32%% The erts_literal_area_collector is started at
33%% VM boot by the VM. It is a spawned as a system
34%% process, i.e, the whole VM will terminate if
35%% this process terminates.
36%%
37start() ->
38    process_flag(trap_exit, true),
39    msg_loop(undefined, 0, 0, []).
40
41%%
42%% The VM will send us a 'copy_literals' message
43%% when it has a new literal area that needs to
44%% be handled is added. We will also be informed
45%% about more areas when we call release_area_switch().
46%%
47msg_loop(Area, Outstnd, GcOutstnd, NeedGC) ->
48
49    HibernateTmo =
50        if Area =:= undefined ->
51                60_000;
52           true ->
53                infinity
54        end,
55
56    receive
57
58	%% A new area to handle has arrived...
59	copy_literals when Outstnd == 0 ->
60	    switch_area();
61
62	%% Process (_Pid) has completed the request...
63	{copy_literals, {Area, _GcAllowed, _Pid}, ok} when Outstnd == 1 ->
64	    switch_area(); %% Last process completed...
65	{copy_literals, {Area, false, _Pid}, ok} ->
66	    msg_loop(Area, Outstnd-1, GcOutstnd, NeedGC);
67	{copy_literals, {Area, true, _Pid}, ok} when NeedGC == [] ->
68	    msg_loop(Area, Outstnd-1, GcOutstnd-1, []);
69	{copy_literals, {Area, true, _Pid}, ok} ->
70	    erts_literal_area_collector:send_copy_request(hd(NeedGC), Area, true),
71	    msg_loop(Area, Outstnd-1, GcOutstnd, tl(NeedGC));
72
73	%% Process (Pid) failed to complete the request
74	%% since it needs to garbage collect in order to
75	%% complete the request...
76	{copy_literals, {Area, false, Pid}, need_gc} when GcOutstnd < ?MAX_GC_OUTSTND ->
77	    erts_literal_area_collector:send_copy_request(Pid, Area, true),
78	    msg_loop(Area, Outstnd, GcOutstnd+1, NeedGC);
79	{copy_literals, {Area, false, Pid}, need_gc} ->
80	    msg_loop(Area, Outstnd, GcOutstnd, [Pid|NeedGC]);
81
82	%% Not handled message regarding the area that we
83	%% currently are working with. Crash the VM so
84	%% we notice this bug...
85	{copy_literals, {Area, _, _}, _} = Msg when erlang:is_reference(Area) ->
86	    exit({not_handled_message, Msg});
87
88	%% Unexpected garbage message. Get rid of it...
89	_Ignore ->
90	    msg_loop(Area, Outstnd, GcOutstnd, NeedGC)
91    after HibernateTmo ->
92            %% We hibernate in order to clear the heap completely.
93            erlang:hibernate(?MODULE, start, [])
94    end.
95
96switch_area() ->
97    Res = erts_literal_area_collector:release_area_switch(),
98    case Res of
99	false ->
100	    %% No more areas to handle...
101	    msg_loop(undefined, 0, 0, []);
102	true ->
103	    %% Send requests to all processes to copy
104	    %% all live data they have referring to the
105	    %% literal area that is to be released...
106	    Area = make_ref(),
107	    Outstnd = send_copy_reqs(erlang:processes(), Area, false),
108	    msg_loop(Area, Outstnd, 0, [])
109    end.
110
111send_copy_reqs(Ps, Area, GC) ->
112    send_copy_reqs(Ps, Area, GC, 0).
113
114send_copy_reqs([], _Area, _GC, N) ->
115    N;
116send_copy_reqs([P|Ps], Area, GC, N) ->
117    erts_literal_area_collector:send_copy_request(P, Area, GC),
118    send_copy_reqs(Ps, Area, GC, N+1).
119
120-spec release_area_switch() -> boolean().
121
122release_area_switch() ->
123    erlang:nif_error(undef). %% Implemented in beam_bif_load.c
124
125-spec send_copy_request(To, AreaId, GcAllowed) -> 'ok' when
126      To :: pid(),
127      AreaId :: term(),
128      GcAllowed :: boolean().
129
130send_copy_request(_To, _AreaId, _GcAllowed) ->
131    erlang:nif_error(undef). %% Implemented in beam_bif_load.c
132