aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/doc/src/erlang.xml6
-rw-r--r--erts/emulator/beam/atom.names2
-rw-r--r--erts/emulator/beam/bif.c19
-rw-r--r--erts/emulator/beam/bif.tab1
-rw-r--r--erts/emulator/beam/erl_process.c23
-rw-r--r--erts/emulator/beam/ops.tab4
-rw-r--r--erts/emulator/test/gc_SUITE.erl103
-rw-r--r--erts/preloaded/src/erlang.erl35
-rw-r--r--erts/preloaded/src/erts_internal.erl7
9 files changed, 174 insertions, 26 deletions
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 2b7a3b85e5..90f2de594b 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -1604,6 +1604,12 @@ true</pre>
the form <c>{garbage_collect,
<anno>RequestId</anno>, <anno>GCResult</anno>}</c>.
</item>
+
+ <tag><c>{type, 'major' | 'minor'}</c></tag>
+ <item>Triggers garbage collection of requested type. Default value is
+ <c>'major'</c>, which would trigger a fullsweep GC.
+ The option <c>'minor'</c> is considered a hint and may lead to
+ either minor or major GC run.</item>
</taglist>
<p>If <c><anno>Pid</anno></c> equals <c>self()</c>, and
no <c>async</c> option has been passed, the garbage
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index badd69856e..f88aeba49f 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -361,6 +361,7 @@ atom long_schedule
atom low
atom Lt='<'
atom machine
+atom major
atom match
atom match_limit
atom match_limit_recursion
@@ -388,6 +389,7 @@ atom microstate_accounting
atom milli_seconds
atom min_heap_size
atom min_bin_vheap_size
+atom minor
atom minor_version
atom Minus='-'
atom module
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index d9048065c8..84eab5f651 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -3869,7 +3869,24 @@ BIF_RETTYPE garbage_collect_0(BIF_ALIST_0)
{
FLAGS(BIF_P) |= F_NEED_FULLSWEEP;
erts_garbage_collect(BIF_P, 0, NULL, 0);
- BIF_RET(am_true);
+ return am_true;
+}
+
+/*
+ * Pass atom 'minor' for relaxed generational GC run. This is only
+ * recommendation, major run may still be chosen by VM.
+ * Pass atom 'major' for default behaviour - major GC run (fullsweep)
+ */
+BIF_RETTYPE
+erts_internal_garbage_collect_1(BIF_ALIST_1)
+{
+ switch (BIF_ARG_1) {
+ case am_minor: break;
+ case am_major: FLAGS(BIF_P) |= F_NEED_FULLSWEEP; break;
+ default: BIF_ERROR(BIF_P, BADARG);
+ }
+ erts_garbage_collect(BIF_P, 0, NULL, 0);
+ return am_true;
}
/**********************************************************************/
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 978bcd4bba..5bb7db5d47 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -69,6 +69,7 @@ bif erlang:float_to_list/1
bif erlang:float_to_list/2
bif erlang:fun_info/2
bif erlang:garbage_collect/0
+bif erts_internal:garbage_collect/1
bif erlang:get/0
bif erlang:get/1
bif erlang:get_keys/1
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 85c37e08be..a0c5e247d4 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -446,7 +446,8 @@ int erts_system_profile_ts_type = ERTS_TRACE_FLG_NOW_TIMESTAMP;
#endif
typedef enum {
- ERTS_PSTT_GC, /* Garbage Collect */
+ ERTS_PSTT_GC_MAJOR, /* Garbage Collect: Fullsweep */
+ ERTS_PSTT_GC_MINOR, /* Garbage Collect: Generational */
ERTS_PSTT_CPC, /* Check Process Code */
ERTS_PSTT_COHMQ, /* Change off heap message queue */
ERTS_PSTT_FTMQ /* Flush trace msg queue */
@@ -10358,7 +10359,8 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
break;
switch (st->type) {
- case ERTS_PSTT_GC:
+ case ERTS_PSTT_GC_MAJOR:
+ case ERTS_PSTT_GC_MINOR:
if (c_p->flags & F_DISABLE_GC) {
save_gc_task(c_p, st, st_prio);
st = NULL;
@@ -10366,7 +10368,9 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
}
else {
if (!garbage_collected) {
- FLAGS(c_p) |= F_NEED_FULLSWEEP;
+ if (st->type == ERTS_PSTT_GC_MAJOR) {
+ FLAGS(c_p) |= F_NEED_FULLSWEEP;
+ }
reds -= scheduler_gc_proc(c_p, reds);
garbage_collected = 1;
}
@@ -10442,7 +10446,8 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds)
break;
switch (st->type) {
- case ERTS_PSTT_GC:
+ case ERTS_PSTT_GC_MAJOR:
+ case ERTS_PSTT_GC_MINOR:
st_res = am_false;
break;
case ERTS_PSTT_CPC:
@@ -10555,9 +10560,13 @@ erts_internal_request_system_task_3(BIF_ALIST_3)
switch (req_type) {
case am_garbage_collect:
- st->type = ERTS_PSTT_GC;
- noproc_res = am_false;
- if (!rp)
+ switch (st->arg[0]) {
+ case am_minor: st->type = ERTS_PSTT_GC_MINOR; break;
+ case am_major: st->type = ERTS_PSTT_GC_MAJOR; break;
+ default: goto badarg;
+ }
+ noproc_res = am_false;
+ if (!rp)
goto noproc;
break;
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index 879daaca0a..801d43e957 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -811,6 +811,10 @@ call_ext u==0 Bif=u$bif:erlang:garbage_collect/0 => i_call_ext Bif
call_ext_last u==0 Bif=u$bif:erlang:garbage_collect/0 D => i_call_ext_last Bif D
call_ext_only u==0 Bif=u$bif:erlang:garbage_collect/0 => i_call_ext_only Bif
+call_ext u==1 Bif=u$bif:erts_internal:garbage_collect/1 => i_call_ext Bif
+call_ext_last u==1 Bif=u$bif:erts_internal:garbage_collect/1 D => i_call_ext_last Bif D
+call_ext_only u==1 Bif=u$bif:erts_internal:garbage_collect/1 => i_call_ext_only Bif
+
#
# put/2 and erase/1 must be able to do garbage collection, so we must call
# them like functions.
diff --git a/erts/emulator/test/gc_SUITE.erl b/erts/emulator/test/gc_SUITE.erl
index 8a600b7d9f..35dd147550 100644
--- a/erts/emulator/test/gc_SUITE.erl
+++ b/erts/emulator/test/gc_SUITE.erl
@@ -23,15 +23,26 @@
-module(gc_SUITE).
-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
-export([all/0, suite/0]).
--export([grow_heap/1, grow_stack/1, grow_stack_heap/1, max_heap_size/1]).
+-export([
+ grow_heap/1,
+ grow_stack/1,
+ grow_stack_heap/1,
+ max_heap_size/1,
+ minor_major_gc_option_async/1,
+ minor_major_gc_option_self/1
+]).
suite() ->
[{ct_hooks,[ts_install_cth]}].
all() ->
- [grow_heap, grow_stack, grow_stack_heap, max_heap_size].
+ [grow_heap, grow_stack, grow_stack_heap, max_heap_size,
+ minor_major_gc_option_self,
+ minor_major_gc_option_async].
%% Produce a growing list of elements,
@@ -190,3 +201,91 @@ long_receive() ->
after 10000 ->
ok
end.
+
+minor_major_gc_option_self(_Config) ->
+ Endless = fun Endless() ->
+ receive
+ {gc, Type} -> erlang:garbage_collect(self(), [{type, Type}])
+ after 100 -> ok end,
+ Endless()
+ end,
+
+ %% Try as major, a test process will self-trigger GC
+ P1 = spawn(Endless),
+ erlang:garbage_collect(P1, []),
+ erlang:trace(P1, true, [garbage_collection]),
+ P1 ! {gc, major},
+ expect_trace_messages(P1, [gc_major_start, gc_major_end]),
+ erlang:trace(P1, false, [garbage_collection]),
+ erlang:exit(P1, kill),
+
+ %% Try as minor, a test process will self-trigger GC
+ P2 = spawn(Endless),
+ erlang:garbage_collect(P2, []),
+ erlang:trace(P2, true, [garbage_collection]),
+ P2 ! {gc, minor},
+ expect_trace_messages(P2, [gc_minor_start, gc_minor_end]),
+ erlang:trace(P2, false, [garbage_collection]),
+ erlang:exit(P2, kill).
+
+minor_major_gc_option_async(_Config) ->
+ Endless = fun Endless() ->
+ receive after 100 -> ok end,
+ Endless()
+ end,
+
+ %% Try with default option, must be major gc
+ P1 = spawn(Endless),
+ erlang:garbage_collect(P1, []),
+ erlang:trace(P1, true, [garbage_collection]),
+ erlang:garbage_collect(P1, []),
+ expect_trace_messages(P1, [gc_major_start, gc_major_end]),
+ erlang:trace(P1, false, [garbage_collection]),
+ erlang:exit(P1, kill),
+
+ %% Try with the 'major' type
+ P2 = spawn(Endless),
+ erlang:garbage_collect(P2, []),
+ erlang:trace(P2, true, [garbage_collection]),
+ erlang:garbage_collect(P2, [{type, major}]),
+ expect_trace_messages(P2, [gc_major_start, gc_major_end]),
+ erlang:trace(P2, false, [garbage_collection]),
+ erlang:exit(P2, kill),
+
+ %% Try with 'minor' option, once
+ P3 = spawn(Endless),
+ erlang:garbage_collect(P3, []),
+ erlang:trace(P3, true, [garbage_collection]),
+ erlang:garbage_collect(P3, [{type, minor}]),
+ expect_trace_messages(P3, [gc_minor_start, gc_minor_end]),
+ erlang:trace(P3, false, [garbage_collection]),
+ erlang:exit(P3, kill),
+
+ %% Try with 'minor' option, once, async
+ P4 = spawn(Endless),
+ Ref = erlang:make_ref(),
+ erlang:garbage_collect(P4, []),
+ erlang:trace(P4, true, [garbage_collection]),
+ ?assertEqual(async,
+ erlang:garbage_collect(P4, [{type, minor}, {async, Ref}])),
+ expect_trace_messages(P4, [gc_minor_start, gc_minor_end]),
+ erlang:trace(P4, false, [garbage_collection]),
+ receive {garbage_collect, Ref, true} -> ok;
+ Other4 -> ct:pal("Unexpected message: ~p~n"
+ ++ "while waiting for async gc result", [Other4])
+ after 2000 -> ?assert(false)
+ end,
+ erlang:exit(P4, kill).
+
+%% Given a list of atoms, trace tags - receives messages and checks if they are
+%% trace events, and if the tag matches. Else will crash failing the test.
+expect_trace_messages(_Pid, []) -> ok;
+expect_trace_messages(Pid, [Tag | TraceTags]) ->
+ receive
+ {trace, Pid, Tag, _Data} -> ok;
+ AnythingElse ->
+ ct:pal("Unexpected message: ~p~nWhile expected {trace, _, ~p, _}",
+ [AnythingElse, Tag]),
+ ?assert(false)
+ end,
+ expect_trace_messages(Pid, TraceTags).
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index edf79b8f75..dd1156f292 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -875,36 +875,39 @@ garbage_collect(Pid) ->
error:Error -> erlang:error(Error, [Pid])
end.
+-record(gcopt, {
+ async = sync :: sync | {async, _},
+ type = major % default major, can also be minor
+ }).
+
%% garbage_collect/2
-spec garbage_collect(Pid, OptionList) -> GCResult | async when
Pid :: pid(),
RequestId :: term(),
- Option :: {async, RequestId},
+ Option :: {async, RequestId} | {type, 'major' | 'minor'},
OptionList :: [Option],
GCResult :: boolean().
garbage_collect(Pid, OptionList) ->
try
- Async = get_gc_opts(OptionList, sync),
- case Async of
+ GcOpts = get_gc_opts(OptionList, #gcopt{}),
+ case GcOpts#gcopt.async of
{async, ReqId} ->
{priority, Prio} = erlang:process_info(erlang:self(),
priority),
- erts_internal:request_system_task(Pid,
- Prio,
- {garbage_collect, ReqId}),
+ erts_internal:request_system_task(
+ Pid, Prio, {garbage_collect, ReqId, GcOpts#gcopt.type}),
async;
sync ->
case Pid == erlang:self() of
true ->
- erlang:garbage_collect();
+ erts_internal:garbage_collect(GcOpts#gcopt.type);
false ->
{priority, Prio} = erlang:process_info(erlang:self(),
priority),
ReqId = erlang:make_ref(),
- erts_internal:request_system_task(Pid,
- Prio,
- {garbage_collect,
- ReqId}),
+ erts_internal:request_system_task(
+ Pid, Prio,
+ {garbage_collect, ReqId, GcOpts#gcopt.type}),
receive
{garbage_collect, ReqId, GCResult} ->
GCResult
@@ -916,10 +919,12 @@ garbage_collect(Pid, OptionList) ->
end.
% gets async opt and verify valid option list
-get_gc_opts([{async, _ReqId} = AsyncTuple | Options], _OldAsync) ->
- get_gc_opts(Options, AsyncTuple);
-get_gc_opts([], Async) ->
- Async.
+get_gc_opts([{async, _ReqId} = AsyncTuple | Options], GcOpt = #gcopt{}) ->
+ get_gc_opts(Options, GcOpt#gcopt{ async = AsyncTuple });
+get_gc_opts([{type, T} | Options], GcOpt = #gcopt{}) ->
+ get_gc_opts(Options, GcOpt#gcopt{ type = T });
+get_gc_opts([], GcOpt) ->
+ GcOpt.
%% garbage_collect_message_area/0
-spec erlang:garbage_collect_message_area() -> boolean().
diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl
index 2459ea2a2c..6f2e50d1db 100644
--- a/erts/preloaded/src/erts_internal.erl
+++ b/erts/preloaded/src/erts_internal.erl
@@ -38,7 +38,8 @@
-export([system_check/1,
gather_system_check_result/1]).
--export([request_system_task/3]).
+-export([request_system_task/3,
+ garbage_collect/1]).
-export([check_process_code/3]).
-export([copy_literals/2]).
@@ -210,6 +211,10 @@ port_info(_Result, _Item) ->
request_system_task(_Pid, _Prio, _Request) ->
erlang:nif_error(undefined).
+-spec garbage_collect(Mode) -> 'true' when Mode :: 'major' | 'minor'.
+garbage_collect(_Mode) ->
+ erlang:nif_error(undefined).
+
-define(ERTS_CPC_ALLOW_GC, (1 bsl 0)).
-define(ERTS_CPC_COPY_LITERALS, (1 bsl 1)).