aboutsummaryrefslogtreecommitdiffstats
path: root/erts/preloaded
diff options
context:
space:
mode:
Diffstat (limited to 'erts/preloaded')
-rw-r--r--erts/preloaded/Makefile23
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin56292 -> 54452 bytes
-rw-r--r--erts/preloaded/ebin/erl_tracer.beambin0 -> 2188 bytes
-rw-r--r--erts/preloaded/ebin/erlang.beambin97956 -> 103300 bytes
-rw-r--r--erts/preloaded/ebin/erts_code_purger.beambin0 -> 11372 bytes
-rw-r--r--erts/preloaded/ebin/erts_dirty_process_signal_handler.beambin0 -> 2760 bytes
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin4164 -> 16676 bytes
-rw-r--r--erts/preloaded/ebin/erts_literal_area_collector.beambin0 -> 3288 bytes
-rw-r--r--erts/preloaded/ebin/init.beambin48808 -> 51428 bytes
-rw-r--r--erts/preloaded/ebin/otp_ring0.beambin1468 -> 1424 bytes
-rw-r--r--erts/preloaded/ebin/prim_buffer.beambin0 -> 3588 bytes
-rw-r--r--erts/preloaded/ebin/prim_eval.beambin1340 -> 1496 bytes
-rw-r--r--erts/preloaded/ebin/prim_file.beambin44904 -> 27780 bytes
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin73128 -> 79028 bytes
-rw-r--r--erts/preloaded/ebin/prim_zip.beambin23440 -> 22892 bytes
-rw-r--r--erts/preloaded/ebin/zlib.beambin13188 -> 19760 bytes
-rw-r--r--erts/preloaded/src/Makefile34
-rw-r--r--erts/preloaded/src/add_abstract_code35
-rw-r--r--erts/preloaded/src/erl_prim_loader.erl592
-rw-r--r--erts/preloaded/src/erl_tracer.erl65
-rw-r--r--erts/preloaded/src/erlang.erl1165
-rw-r--r--erts/preloaded/src/erts.app.src27
-rw-r--r--erts/preloaded/src/erts_code_purger.erl377
-rw-r--r--erts/preloaded/src/erts_dirty_process_signal_handler.erl103
-rw-r--r--erts/preloaded/src/erts_internal.erl553
-rw-r--r--erts/preloaded/src/erts_literal_area_collector.erl113
-rw-r--r--erts/preloaded/src/init.erl760
-rw-r--r--erts/preloaded/src/otp_ring0.erl21
-rw-r--r--erts/preloaded/src/prim_buffer.erl140
-rw-r--r--erts/preloaded/src/prim_eval.S62
-rw-r--r--erts/preloaded/src/prim_eval.erl21
-rw-r--r--erts/preloaded/src/prim_file.erl2017
-rw-r--r--erts/preloaded/src/prim_inet.erl537
-rw-r--r--erts/preloaded/src/prim_zip.erl33
-rw-r--r--erts/preloaded/src/zip_internal.hrl23
-rw-r--r--erts/preloaded/src/zlib.erl752
36 files changed, 4463 insertions, 2990 deletions
diff --git a/erts/preloaded/Makefile b/erts/preloaded/Makefile
index 31fdeb96c5..e8935d4410 100644
--- a/erts/preloaded/Makefile
+++ b/erts/preloaded/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2009. All Rights Reserved.
+# Copyright Ericsson AB 2008-2016. All Rights Reserved.
#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
+# 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%
#
diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index 10138cab22..0f5f5036f0 100644
--- a/erts/preloaded/ebin/erl_prim_loader.beam
+++ b/erts/preloaded/ebin/erl_prim_loader.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam
new file mode 100644
index 0000000000..6017112dac
--- /dev/null
+++ b/erts/preloaded/ebin/erl_tracer.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index 7a3d94dd16..bd8cc7d7e0 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam
new file mode 100644
index 0000000000..c899b69a2c
--- /dev/null
+++ b/erts/preloaded/ebin/erts_code_purger.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam
new file mode 100644
index 0000000000..9490a56758
--- /dev/null
+++ b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam
index 9b364d8b21..15c59de80a 100644
--- a/erts/preloaded/ebin/erts_internal.beam
+++ b/erts/preloaded/ebin/erts_internal.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_literal_area_collector.beam b/erts/preloaded/ebin/erts_literal_area_collector.beam
new file mode 100644
index 0000000000..e650a6b5af
--- /dev/null
+++ b/erts/preloaded/ebin/erts_literal_area_collector.beam
Binary files differ
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index a190129b2c..858a9dc63e 100644
--- a/erts/preloaded/ebin/init.beam
+++ b/erts/preloaded/ebin/init.beam
Binary files differ
diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam
index 621f036f33..0d194896c7 100644
--- a/erts/preloaded/ebin/otp_ring0.beam
+++ b/erts/preloaded/ebin/otp_ring0.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_buffer.beam b/erts/preloaded/ebin/prim_buffer.beam
new file mode 100644
index 0000000000..4ad1380d0b
--- /dev/null
+++ b/erts/preloaded/ebin/prim_buffer.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam
index 6695f4cd15..2ae18846bf 100644
--- a/erts/preloaded/ebin/prim_eval.beam
+++ b/erts/preloaded/ebin/prim_eval.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam
index 5e9230826d..df611f2bb0 100644
--- a/erts/preloaded/ebin/prim_file.beam
+++ b/erts/preloaded/ebin/prim_file.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index 83230d42ae..4a345f8152 100644
--- a/erts/preloaded/ebin/prim_inet.beam
+++ b/erts/preloaded/ebin/prim_inet.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam
index b3abba3223..4923cadbdc 100644
--- a/erts/preloaded/ebin/prim_zip.beam
+++ b/erts/preloaded/ebin/prim_zip.beam
Binary files differ
diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam
index 7006764d96..07e7e97814 100644
--- a/erts/preloaded/ebin/zlib.beam
+++ b/erts/preloaded/ebin/zlib.beam
Binary files differ
diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile
index 4ea2d41075..4333f6643a 100644
--- a/erts/preloaded/src/Makefile
+++ b/erts/preloaded/src/Makefile
@@ -1,18 +1,19 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2013. All Rights Reserved.
+# Copyright Ericsson AB 2008-2018. All Rights Reserved.
#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
+# 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%
#
@@ -35,13 +36,18 @@ include $(ERL_TOP)/lib/kernel/vsn.mk
PRE_LOADED_ERL_MODULES = \
erl_prim_loader \
init \
+ prim_buffer \
prim_file \
prim_inet \
zlib \
prim_zip \
otp_ring0 \
+ erts_code_purger \
erlang \
- erts_internal
+ erts_internal \
+ erl_tracer \
+ erts_literal_area_collector \
+ erts_dirty_process_signal_handler
PRE_LOADED_BEAM_MODULES = \
prim_eval
@@ -68,7 +74,7 @@ KERNEL_SRC=$(ERL_TOP)/lib/kernel/src
KERNEL_INCLUDE=$(ERL_TOP)/lib/kernel/include
STDLIB_INCLUDE=$(ERL_TOP)/lib/stdlib/include
-ERL_COMPILE_FLAGS += +warn_obsolete_guard +debug_info -I$(KERNEL_SRC) -I$(KERNEL_INCLUDE)
+ERL_COMPILE_FLAGS += +debug_info -I$(KERNEL_SRC) -I$(KERNEL_INCLUDE)
debug opt: $(TARGET_FILES)
@@ -111,7 +117,7 @@ prim_eval.beam: prim_eval.S prim_eval.abstr
# Include dependencies -- list below added by PaN
$(EBIN)/erl_prim_loader.beam: $(KERNEL_SRC)/inet_boot.hrl $(KERNEL_INCLUDE)/file.hrl
-$(EBIN)/prim_file.beam: $(KERNEL_INCLUDE)/file.hrl
+$(EBIN)/prim_file.beam: $(KERNEL_SRC)/file_int.hrl $(KERNEL_INCLUDE)/file.hrl
$(EBIN)/prim_inet.beam: $(KERNEL_SRC)/inet_int.hrl $(KERNEL_INCLUDE)/inet_sctp.hrl
$(EBIN)/prim_zip.beam: zip_internal.hrl $(KERNEL_INCLUDE)/file.hrl $(STDLIB_INCLUDE)/zip.hrl
$(EBIN)/init.erl: $(KERNEL_INCLUDE)/file.hrl
diff --git a/erts/preloaded/src/add_abstract_code b/erts/preloaded/src/add_abstract_code
index 211a60c930..9040199417 100644
--- a/erts/preloaded/src/add_abstract_code
+++ b/erts/preloaded/src/add_abstract_code
@@ -4,18 +4,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2017. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -27,12 +28,12 @@
main([BeamFile,AbstrFile]) ->
{ok,_,Chunks0} = beam_lib:all_chunks(BeamFile),
{ok,Abstr} = file:consult(AbstrFile),
- Chunks1 = lists:keyreplace("Abst", 1, Chunks0,
- {"Abst",term_to_binary({raw_abstract_v1,Abstr})}),
- {"CInf",CInf0} = lists:keyfind("CInf", 1, Chunks1),
- CInf = fix_options(CInf0),
- Chunks = lists:keyreplace("CInf", 1, Chunks1, {"CInf",CInf}),
- {ok,Module} = beam_lib:build_module(Chunks),
+ {"CInf",CInf0} = lists:keyfind("CInf", 1, Chunks0),
+ {CInf, COpts} = fix_options(CInf0),
+ Chunks1 = lists:keyreplace("CInf", 1, Chunks0, {"CInf",CInf}),
+ Chunks2 = lists:keyreplace("Dbgi", 1, Chunks1,
+ {"Dbgi",term_to_binary({debug_info_v1,erl_abstract_code,{Abstr, COpts}})}),
+ {ok,Module} = beam_lib:build_module(Chunks2),
ok = file:write_file(BeamFile, Module),
init:stop().
@@ -41,4 +42,4 @@ fix_options(CInf0) ->
{options,Opts0} = lists:keyfind(options, 1, CInf1),
Opts = Opts0 -- [from_asm],
CInf = lists:keyreplace(options, 1, CInf1, {options,Opts}),
- term_to_binary(CInf).
+ {term_to_binary(CInf), Opts}.
diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl
index 6b86a427ba..ae5f86e017 100644
--- a/erts/preloaded/src/erl_prim_loader.erl
+++ b/erts/preloaded/src/erl_prim_loader.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -41,7 +42,7 @@
-include("inet_boot.hrl").
%% Public
--export([start/3, set_path/1, get_path/0, get_file/1, get_files/2,
+-export([start/0, set_path/1, get_path/0, get_file/1,
list_dir/1, read_file_info/1, read_link_info/1, get_cwd/0, get_cwd/1]).
%% Used by erl_boot_server
@@ -49,30 +50,31 @@
prim_read_file_info/3, prim_get_cwd/2]).
%% Used by escript and code
--export([set_primary_archive/4, release_archives/0]).
+-export([set_primary_archive/4]).
+
+%% Used by test suites
+-export([purge_archive_cache/0]).
+
+%% Used by init and the code server.
+-export([get_modules/2,get_modules/3, is_basename/1]).
-include_lib("kernel/include/file.hrl").
-type host() :: atom().
-record(prim_state, {debug :: boolean(),
- cache,
primary_archive}).
-type prim_state() :: #prim_state{}.
-record(state,
{loader :: 'efile' | 'inet',
hosts = [] :: [host()], % hosts list (to boot from)
- id, % not used any more?
data :: 'noport' | port(), % data port etc
- timeout :: timeout(), % idle timeout
- %% Number of timeouts before archives are released
- n_timeouts :: non_neg_integer(),
- multi_get = false :: boolean(),
+ timeout :: timeout(), % idle timeout
prim_state :: prim_state()}). % state for efile code loader
--define(IDLE_TIMEOUT, 60000). %% tear inet connection after 1 minutes
--define(N_TIMEOUTS, 6). %% release efile archive after 6 minutes
+-define(EFILE_IDLE_TIMEOUT, (6*60*1000)). %purge archives
+-define(INET_IDLE_TIMEOUT, (60*1000)). %tear down connection timeout
%% Defines for inet as prim_loader
-define(INET_FAMILY, inet).
@@ -102,26 +104,13 @@ debug(#prim_state{debug = Deb}, Term) ->
%%% Interface Functions.
%%% --------------------------------------------------------
--spec start(Id, Loader, Hosts) ->
+-spec start() ->
{'ok', Pid} | {'error', What} when
- Id :: term(),
- Loader :: atom() | string(),
- Hosts :: Host | [Host],
- Host :: host(),
Pid :: pid(),
What :: term().
-start(Id, Pgm, Hosts) when is_atom(Hosts) ->
- start(Id, Pgm, [Hosts]);
-start(Id, Pgm0, Hosts) ->
- Pgm = if
- is_atom(Pgm0) ->
- atom_to_list(Pgm0);
- true ->
- Pgm0
- end,
+start() ->
Self = self(),
- Pid = spawn_link(fun() -> start_it(Pgm, Id, Self, Hosts) end),
- register(erl_prim_loader, Pid),
+ Pid = spawn_link(fun() -> start_it(Self) end),
receive
{Pid,ok} ->
{ok,Pid};
@@ -129,29 +118,41 @@ start(Id, Pgm0, Hosts) ->
{error,Reason}
end.
-%% Hosts must be a list of form ['1.2.3.4' ...]
-start_it("inet", Id, Pid, Hosts) ->
+start_it(Parent) ->
process_flag(trap_exit, true),
- ?dbg(inet, {Id,Pid,Hosts}),
+ register(erl_prim_loader, self()),
+ Loader = case init:get_argument(loader) of
+ {ok,[[Loader0]]} ->
+ Loader0;
+ error ->
+ "efile"
+ end,
+ case Loader of
+ "efile" -> start_efile(Parent);
+ "inet" -> start_inet(Parent)
+ end.
+
+%% Hosts must be a list of form ['1.2.3.4' ...]
+start_inet(Parent) ->
+ Hosts = case init:get_argument(hosts) of
+ {ok,[Hosts0]} -> Hosts0;
+ _ -> []
+ end,
AL = ipv4_list(Hosts),
?dbg(addresses, AL),
{ok,Tcp} = find_master(AL),
- init_ack(Pid),
+ init_ack(Parent),
PS = prim_init(),
State = #state {loader = inet,
hosts = AL,
- id = Id,
data = Tcp,
- timeout = ?IDLE_TIMEOUT,
- n_timeouts = ?N_TIMEOUTS,
+ timeout = ?INET_IDLE_TIMEOUT,
prim_state = PS},
- loop(State, Pid, []);
+ loop(State, Parent, []).
-start_it("efile", Id, Pid, _Hosts) ->
- process_flag(trap_exit, true),
- {ok, Port} = prim_file:start(),
+start_efile(Parent) ->
%% Check that we started in a valid directory.
- case prim_file:get_cwd(Port) of
+ case prim_file:get_cwd() of
{error, _} ->
%% At this point in the startup, we have no error_logger at all.
Report = "Invalid current directory or invalid filename "
@@ -159,20 +160,14 @@ start_it("efile", Id, Pid, _Hosts) ->
erlang:display(Report),
exit({error, invalid_current_directory});
_ ->
- init_ack(Pid)
+ init_ack(Parent)
end,
- MultiGet = case erlang:system_info(thread_pool_size) of
- 0 -> false;
- _ -> true
- end,
PS = prim_init(),
State = #state {loader = efile,
- id = Id,
- data = Port,
- timeout = infinity,
- multi_get = MultiGet,
+ data = noport,
+ timeout = ?EFILE_IDLE_TIMEOUT,
prim_state = PS},
- loop(State, Pid, []).
+ loop(State, Parent, []).
init_ack(Pid) ->
Pid ! {self(),ok},
@@ -197,20 +192,6 @@ get_file(File) when is_atom(File) ->
get_file(File) ->
check_file_result(get_file, File, request({get_file,File})).
--spec get_files([{atom(), string()}],
- fun((atom(),binary(),string()) -> 'ok' | {'error', atom()})) ->
- 'ok' | {'error', atom()}.
-get_files(ModFiles, Fun) ->
- case request({get_files,{ModFiles,Fun}}) of
- E = {error,_M} ->
- E;
- {error,Reason,M} ->
- check_file_result(get_files, M, {error,Reason}),
- {error,M};
- ok ->
- ok
- end.
-
-spec list_dir(Dir) -> {'ok', Filenames} | 'error' when
Dir :: string(),
Filenames :: [Filename :: string()].
@@ -249,10 +230,30 @@ set_primary_archive(File, ArchiveBin, FileInfo, ParserFun)
when is_list(File), is_binary(ArchiveBin), is_record(FileInfo, file_info) ->
request({set_primary_archive, File, ArchiveBin, FileInfo, ParserFun}).
--spec release_archives() -> 'ok' | {'error', _}.
+%% NOTE: Does not close the primary archive. Only closes all
+%% open zip files kept in the cache. Should be called before an archive
+%% file is to be removed (for example in the test suites).
+
+-spec purge_archive_cache() -> 'ok' | {'error', _}.
+purge_archive_cache() ->
+ request(purge_archive_cache).
+
+-spec get_modules([module()],
+ fun((atom(), string(), binary()) ->
+ {'ok',any()} | {'error',any()})) ->
+ {'ok',{[any()],[any()]}}.
+
+get_modules(Modules, Fun) ->
+ request({get_modules,{Modules,Fun}}).
+
+-spec get_modules([module()],
+ fun((atom(), string(), binary()) ->
+ {'ok',any()} | {'error',any()}),
+ [string()]) ->
+ {'ok',{[any()],[any()]}}.
-release_archives() ->
- request(release_archives).
+get_modules(Modules, Fun, Path) ->
+ request({get_modules,{Modules,Fun,Path}}).
request(Req) ->
Loader = whereis(erl_prim_loader),
@@ -298,8 +299,12 @@ check_file_result(Func, Target, {error,Reason}) ->
end,
%% this is equal to calling error_logger:error_report/1 which
%% we don't want to do from code_server during system boot
- error_logger ! {notify,{error_report,group_leader(),
- {self(),std_error,Report}}},
+ logger ! {log,error,#{label=>{?MODULE,file_error},report=>Report},
+ #{pid=>self(),
+ gl=>group_leader(),
+ time=>erlang:monotonic_time(microsecond),
+ error_logger=>#{tag=>error_report,
+ type=>std_error}}},
error
end;
check_file_result(_, _, Other) ->
@@ -309,76 +314,63 @@ check_file_result(_, _, Other) ->
%%% The main loop.
%%% --------------------------------------------------------
-loop(State, Parent, Paths) ->
+loop(St0, Parent, Paths) ->
receive
+ {Pid,{set_path,NewPaths}} when is_pid(Pid) ->
+ Pid ! {self(),ok},
+ loop(St0, Parent, to_strs(NewPaths));
{Pid,Req} when is_pid(Pid) ->
- %% erlang:display(Req),
- {Resp,State2,Paths2} =
- case Req of
- {set_path,NewPaths} ->
- {ok,State,to_strs(NewPaths)};
- {get_path,_} ->
- {{ok,Paths},State,Paths};
- {get_file,File} ->
- {Res,State1} = handle_get_file(State, Paths, File),
- {Res,State1,Paths};
- {get_files,{ModFiles,Fun}} ->
- {Res,State1} = handle_get_files(State, ModFiles, Paths, Fun),
- {Res,State1,Paths};
- {list_dir,Dir} ->
- {Res,State1} = handle_list_dir(State, Dir),
- {Res,State1,Paths};
- {read_file_info,File} ->
- {Res,State1} = handle_read_file_info(State, File),
- {Res,State1,Paths};
- {read_link_info,File} ->
- {Res,State1} = handle_read_link_info(State, File),
- {Res,State1,Paths};
- {get_cwd,[]} ->
- {Res,State1} = handle_get_cwd(State, []),
- {Res,State1,Paths};
- {get_cwd,[_]=Args} ->
- {Res,State1} = handle_get_cwd(State, Args),
- {Res,State1,Paths};
- {set_primary_archive,File,ArchiveBin,FileInfo,ParserFun} ->
- {Res,State1} =
- handle_set_primary_archive(State, File,
- ArchiveBin, FileInfo,
- ParserFun),
- {Res,State1,Paths};
- release_archives ->
- {Res,State1} = handle_release_archives(State),
- {Res,State1,Paths};
- _Other ->
- {ignore,State,Paths}
- end,
- if Resp =:= ignore -> ok;
- true -> Pid ! {self(),Resp}, ok
- end,
- if
- is_record(State2, state) ->
- loop(State2, Parent, Paths2);
- true ->
- exit({bad_state, Req, State2})
+ case handle_request(Req, Paths, St0) of
+ ignore ->
+ ok;
+ {Resp,#state{}=St1} ->
+ Pid ! {self(),Resp},
+ loop(St1, Parent, Paths);
+ {_,State2,_} ->
+ exit({bad_state,Req,State2})
end;
{'EXIT',Parent,W} ->
- _State1 = handle_stop(State),
+ _ = handle_stop(St0),
exit(W);
{'EXIT',P,W} ->
- State1 = handle_exit(State, P, W),
- loop(State1, Parent, Paths);
+ St1 = handle_exit(St0, P, W),
+ loop(St1, Parent, Paths);
_Message ->
- loop(State, Parent, Paths)
- after State#state.timeout ->
- State1 = handle_timeout(State, Parent),
- loop(State1, Parent, Paths)
+ loop(St0, Parent, Paths)
+ after St0#state.timeout ->
+ St1 = handle_timeout(St0, Parent),
+ loop(St1, Parent, Paths)
+ end.
+
+handle_request(Req, Paths, St0) ->
+ case Req of
+ {get_path,_} ->
+ {{ok,Paths},St0};
+ {get_file,File} ->
+ handle_get_file(St0, Paths, File);
+ {get_modules,{Modules,Fun}} ->
+ handle_get_modules(St0, Modules, Fun, Paths);
+ {get_modules,{Modules,Fun,ModPaths}} ->
+ handle_get_modules(St0, Modules, Fun, ModPaths);
+ {list_dir,Dir} ->
+ handle_list_dir(St0, Dir);
+ {read_file_info,File} ->
+ handle_read_file_info(St0, File);
+ {read_link_info,File} ->
+ handle_read_link_info(St0, File);
+ {get_cwd,[]} ->
+ handle_get_cwd(St0, []);
+ {get_cwd,[_]=Args} ->
+ handle_get_cwd(St0, Args);
+ {set_primary_archive,File,ArchiveBin,FileInfo,ParserFun} ->
+ handle_set_primary_archive(St0, File, ArchiveBin,
+ FileInfo, ParserFun);
+ purge_archive_cache ->
+ handle_purge_archive_cache(St0);
+ _ ->
+ ignore
end.
-handle_get_files(State = #state{multi_get = true}, ModFiles, Paths, Fun) ->
- ?SAFE2(efile_multi_get_file_from_port(State, ModFiles, Paths, Fun), State);
-handle_get_files(State, _ModFiles, _Paths, _Fun) -> % no multi get
- {{error,no_multi_get},State}.
-
handle_get_file(State = #state{loader = efile}, Paths, File) ->
?SAFE2(efile_get_file_from_port(State, File, Paths), State);
handle_get_file(State = #state{loader = inet}, Paths, File) ->
@@ -387,8 +379,9 @@ handle_get_file(State = #state{loader = inet}, Paths, File) ->
handle_set_primary_archive(State= #state{loader = efile}, File, ArchiveBin, FileInfo, ParserFun) ->
?SAFE2(efile_set_primary_archive(State, File, ArchiveBin, FileInfo, ParserFun), State).
-handle_release_archives(State= #state{loader = efile}) ->
- ?SAFE2(efile_release_archives(State), State).
+handle_purge_archive_cache(#state{loader = efile}=State) ->
+ prim_purge_cache(),
+ {ok,State}.
handle_list_dir(State = #state{loader = efile}, Dir) ->
?SAFE2(efile_list_dir(State, Dir), State);
@@ -411,12 +404,12 @@ handle_get_cwd(State = #state{loader = inet}, Drive) ->
?SAFE2(inet_get_cwd(State, Drive), State).
handle_stop(State = #state{loader = efile}) ->
- efile_stop_port(State);
+ State;
handle_stop(State = #state{loader = inet}) ->
inet_stop_port(State).
-handle_exit(State = #state{loader = efile}, Who, Reason) ->
- efile_exit_port(State, Who, Reason);
+handle_exit(State = #state{loader = efile}, _Who, _Reason) ->
+ State;
handle_exit(State = #state{loader = inet}, Who, Reason) ->
inet_exit_port(State, Who, Reason).
@@ -429,53 +422,6 @@ handle_timeout(State = #state{loader = inet}, Parent) ->
%%% Functions which handle efile as prim_loader (default).
%%% --------------------------------------------------------
-%%% Reading many files in parallel is an optimization.
-%%% See also comment in init.erl.
-
-%% -> {ok,State} | {{error,Module},State} | {{error,Reason,Module},State}
-efile_multi_get_file_from_port(State, ModFiles, Paths, Fun) ->
- Ref = make_ref(),
- %% More than 200 processes is no gain.
- Max = erlang:min(200, erlang:system_info(thread_pool_size)),
- efile_multi_get_file_from_port2(ModFiles, 0, Max, State, Paths, Fun, Ref, ok).
-
-efile_multi_get_file_from_port2([MF | MFs], Out, Max, State, Paths, Fun, Ref, Ret) when Out < Max ->
- Self = self(),
- _Pid = spawn(fun() -> efile_par_get_file(Ref, State, MF, Paths, Self, Fun) end),
- efile_multi_get_file_from_port2(MFs, Out+1, Max, State, Paths, Fun, Ref, Ret);
-efile_multi_get_file_from_port2(MFs, Out, Max, _State, Paths, Fun, Ref, Ret) when Out > 0 ->
- receive
- {Ref, ok, State1} ->
- efile_multi_get_file_from_port2(MFs, Out-1, Max, State1, Paths, Fun, Ref, Ret);
- {Ref, {error,_Mod} = Error, State1} ->
- efile_multi_get_file_from_port2(MFs, Out-1, Max, State1, Paths, Fun, Ref, Error);
- {Ref, MF, {error,emfile,State1}} ->
- %% Max can take negative values. Out cannot.
- efile_multi_get_file_from_port2([MF | MFs], Out-1, Max-1, State1, Paths, Fun, Ref, Ret);
- {Ref, {M,_F}, {error,Error,State1}} ->
- efile_multi_get_file_from_port2(MFs, Out-1, 0, State1, Paths, Fun, Ref, {error,Error,M})
- end;
-efile_multi_get_file_from_port2(_MFs, 0, _Max, State, _Paths, _Fun, _Ref, Ret) ->
- {Ret,State}.
-
-efile_par_get_file(Ref, State, {Mod,File} = MF, Paths, Pid, Fun) ->
- %% One port for each file read in "parallel":
- case prim_file:start() of
- {ok, Port} ->
- Port0 = State#state.data,
- State1 = State#state{data = Port},
- R = case efile_get_file_from_port(State1, File, Paths) of
- {{error,Reason},State2} ->
- {Ref,MF,{error,Reason,State2}};
- {{ok,BinFile,Full},State2} ->
- %% Fun(...) -> ok | {error,Mod}
- {Ref,Fun(Mod, BinFile, Full),State2#state{data=Port0}}
- end,
- prim_file:close(Port),
- Pid ! R;
- {error, Error} ->
- Pid ! {Ref,MF,{error,Error,State}}
- end.
%% -> {{ok,BinFile,File},State} | {{error,Reason},State}
efile_get_file_from_port(State, File, Paths) ->
@@ -520,10 +466,6 @@ efile_set_primary_archive(#state{prim_state = PS} = State, File,
FileInfo, ParserFun),
{Res,State#state{prim_state = PS2}}.
-efile_release_archives(#state{prim_state = PS} = State) ->
- {Res, PS2} = prim_release_archives(PS),
- {Res,State#state{prim_state = PS2}}.
-
efile_list_dir(#state{prim_state = PS} = State, Dir) ->
{Res, PS2} = prim_list_dir(PS, Dir),
{Res, State#state{prim_state = PS2}}.
@@ -536,24 +478,127 @@ efile_get_cwd(#state{prim_state = PS} = State, Drive) ->
{Res, PS2} = prim_get_cwd(PS, Drive),
{Res, State#state{prim_state = PS2}}.
-efile_stop_port(#state{data=Port}=State) ->
- prim_file:close(Port),
- State#state{data=noport}.
-
-efile_exit_port(State, Port, Reason) when State#state.data =:= Port ->
- exit({port_died,Reason});
-efile_exit_port(State, _Port, _Reason) ->
+efile_timeout_handler(State, _Parent) ->
+ prim_purge_cache(),
State.
-efile_timeout_handler(#state{n_timeouts = N} = State, _Parent) ->
- if
- N =< 0 ->
- {_Res, State2} = efile_release_archives(State),
- State2#state{n_timeouts = ?N_TIMEOUTS};
- true ->
- State#state{n_timeouts = N - 1}
+%%% --------------------------------------------------------
+%%% Read and process severals modules in parallel.
+%%% --------------------------------------------------------
+
+handle_get_modules(#state{loader=efile}=St, Ms, Process, Paths) ->
+ Primary = (St#state.prim_state)#prim_state.primary_archive,
+ Res = case efile_any_archives(Paths, Primary) of
+ false ->
+ efile_get_mods_par(Ms, Process, Paths);
+ true ->
+ Get = fun efile_get_file_from_port/3,
+ gm_get_mods(St, Get, Ms, Process, Paths)
+ end,
+ {Res,St};
+handle_get_modules(#state{loader=inet}=St, Ms, Process, Paths) ->
+ Get = fun inet_get_file_from_port/3,
+ {gm_get_mods(St, Get, Ms, Process, Paths),St}.
+
+efile_get_mods_par(Ms, Process, Paths) ->
+ Self = self(),
+ Ref = make_ref(),
+ GmSpawn = fun() ->
+ efile_gm_spawn({Self,Ref}, Ms, Process, Paths)
+ end,
+ _ = spawn_link(GmSpawn),
+ N = length(Ms),
+ efile_gm_recv(N, Ref, [], []).
+
+efile_any_archives([H|T], Primary) ->
+ case name_split(Primary, H) of
+ {file,_} -> efile_any_archives(T, Primary);
+ {archive,_,_} -> true
+ end;
+efile_any_archives([], _) ->
+ false.
+
+efile_gm_recv(0, _Ref, Succ, Fail) ->
+ {ok,{Succ,Fail}};
+efile_gm_recv(N, Ref, Succ, Fail) ->
+ receive
+ {Ref,Mod,{ok,Res}} ->
+ efile_gm_recv(N-1, Ref, [{Mod,Res}|Succ], Fail);
+ {Ref,Mod,{error,Res}} ->
+ efile_gm_recv(N-1, Ref, Succ, [{Mod,Res}|Fail])
+ end.
+
+efile_gm_spawn(ParentRef, Ms, Process, Paths) ->
+ efile_gm_spawn_1(0, Ms, ParentRef, Process, Paths).
+
+efile_gm_spawn_1(N, Ms, ParentRef, Process, Paths) when N >= 32 ->
+ receive
+ {'DOWN',_,process,_,_} ->
+ efile_gm_spawn_1(N-1, Ms, ParentRef, Process, Paths)
+ end;
+efile_gm_spawn_1(N, [M|Ms], ParentRef, Process, Paths) ->
+ Get = fun() -> efile_gm_get(Paths, M, ParentRef, Process) end,
+ _ = spawn_monitor(Get),
+ efile_gm_spawn_1(N+1, Ms, ParentRef, Process, Paths);
+efile_gm_spawn_1(_, [], _, _, _) ->
+ ok.
+
+efile_gm_get(Paths, Mod, ParentRef, Process) ->
+ File = atom_to_list(Mod) ++ init:objfile_extension(),
+ efile_gm_get_1(Paths, File, Mod, ParentRef, Process).
+
+efile_gm_get_1([P|Ps], File0, Mod, {Parent,Ref}=PR, Process) ->
+ File = join(P, File0),
+ try prim_file:read_file(File) of
+ {ok,Bin} ->
+ Res = gm_process(Mod, File, Bin, Process),
+ Parent ! {Ref,Mod,Res};
+ Error ->
+ _ = check_file_result(get_modules, File, Error),
+ efile_gm_get_1(Ps, File0, Mod, PR, Process)
+ catch
+ _:Reason ->
+ Res = {error,{crash,Reason}},
+ Parent ! {Ref,Mod,Res}
+ end;
+efile_gm_get_1([], _, Mod, {Parent,Ref}, _Process) ->
+ Parent ! {Ref,Mod,{error,enoent}}.
+
+gm_get_mods(St, Get, Ms, Process, Paths) ->
+ gm_get_mods(St, Get, Ms, Process, Paths, [], []).
+
+gm_get_mods(St, Get, [M|Ms], Process, Paths, Succ, Fail) ->
+ File = atom_to_list(M) ++ init:objfile_extension(),
+ case gm_arch_get(St, Get, M, File, Paths, Process) of
+ {ok,Res} ->
+ gm_get_mods(St, Get, Ms, Process, Paths,
+ [{M,Res}|Succ], Fail);
+ {error,Res} ->
+ gm_get_mods(St, Get, Ms, Process, Paths,
+ Succ, [{M,Res}|Fail])
+ end;
+gm_get_mods(_St, _Get, [], _, _, Succ, Fail) ->
+ {ok,{Succ,Fail}}.
+
+gm_arch_get(St, Get, Mod, File, Paths, Process) ->
+ case Get(St, File, Paths) of
+ {{error,_}=E,_} ->
+ E;
+ {{ok,Bin,Full},_} ->
+ gm_process(Mod, Full, Bin, Process)
end.
+gm_process(Mod, File, Bin, Process) ->
+ try Process(Mod, File, Bin) of
+ {ok,_}=Res -> Res;
+ {error,_}=Res -> Res;
+ Other -> {error,{bad_return,Other}}
+ catch
+ _:Error ->
+ {error,{crash,Error}}
+ end.
+
+
%%% --------------------------------------------------------
%%% Functions which handle inet prim_loader
%%% --------------------------------------------------------
@@ -693,7 +738,7 @@ inet_get_file_from_port1(_File, [], State) ->
inet_send_and_rcv(Msg, Tag, State) when State#state.data =:= noport ->
{ok,Tcp} = find_master(State#state.hosts), %% reconnect
inet_send_and_rcv(Msg, Tag, State#state{data = Tcp,
- timeout = ?IDLE_TIMEOUT});
+ timeout = ?INET_IDLE_TIMEOUT});
inet_send_and_rcv(Msg, Tag, #state{data = Tcp, timeout = Timeout} = State) ->
prim_inet:send(Tcp, term_to_binary(Msg)),
receive
@@ -818,32 +863,19 @@ prim_init() ->
end,
cache_new(#prim_state{debug = Deb}).
-prim_release_archives(PS) ->
- debug(PS, release_archives),
- {Res, PS2} = prim_do_release_archives(PS, get(), []),
- debug(PS2, {return, Res}),
- {Res, PS2}.
-
-prim_do_release_archives(PS, [{ArchiveFile, DictVal} | KeyVals], Acc) ->
- Res =
- case DictVal of
- {primary, _PrimZip, _FI, _ParserFun} ->
- ok; % Keep primary archive
- {Cache, _FI} ->
- debug(PS, {release, cache, ArchiveFile}),
- erase(ArchiveFile),
- clear_cache(ArchiveFile, Cache)
- end,
- case Res of
- ok ->
- prim_do_release_archives(PS, KeyVals, Acc);
- {error, Reason} ->
- prim_do_release_archives(PS, KeyVals, [{ArchiveFile, Reason} | Acc])
- end;
-prim_do_release_archives(PS, [], []) ->
- {ok, PS#prim_state{primary_archive = undefined}};
-prim_do_release_archives(PS, [], Errors) ->
- {{error, Errors}, PS#prim_state{primary_archive = undefined}}.
+prim_purge_cache() ->
+ do_prim_purge_cache(get()).
+
+do_prim_purge_cache([{Key,Val}|T]) ->
+ case Val of
+ {Cache,_FI} ->
+ catch clear_cache(Key, Cache);
+ _ ->
+ ok
+ end,
+ do_prim_purge_cache(T);
+do_prim_purge_cache([]) ->
+ ok.
prim_set_primary_archive(PS, undefined, undefined, undefined, _ParserFun) ->
debug(PS, {set_primary_archive, clean}),
@@ -1286,70 +1318,62 @@ path_join([Path],Acc) ->
path_join([Path|Paths],Acc) ->
path_join(Paths,"/" ++ reverse(Path) ++ Acc).
-name_split(ArchiveFile, File0) ->
- File = absname(File0),
- do_name_split(ArchiveFile, File).
-
-do_name_split(undefined, File) ->
+name_split(undefined, File) ->
%% Ignore primary archive
- case string_split(File, init:archive_extension(), []) of
+ RevExt = reverse(init:archive_extension()),
+ case archive_split(File, RevExt, []) of
no_split ->
- %% Plain file
{file, File};
- {split, _RevArchiveBase, RevArchiveFile, []} ->
- %% Top dir in archive
- ArchiveFile = reverse(RevArchiveFile),
- {archive, ArchiveFile, []};
- {split, _RevArchiveBase, RevArchiveFile, [$/ | FileInArchive]} ->
- %% File in archive
- ArchiveFile = reverse(RevArchiveFile),
- {archive, ArchiveFile, FileInArchive};
- {split, _RevArchiveBase, _RevArchiveFile, _FileInArchive} ->
- %% False match. Assume plain file
- {file, File}
+ Archive ->
+ Archive
end;
-do_name_split(ArchiveFile, File) ->
+name_split(ArchiveFile, File0) ->
%% Look first in primary archive
- case string_match(real_path(File), ArchiveFile, []) of
+ File = absname(File0),
+ case string_match(real_path(File), ArchiveFile) of
no_match ->
%% Archive or plain file
- do_name_split(undefined, File);
- {match, _RevPrimArchiveFile, FileInArchive} ->
+ name_split(undefined, File);
+ {match, FileInArchive} ->
%% Primary archive
{archive, ArchiveFile, FileInArchive}
end.
-string_match([Char | File], [Char | Archive], RevTop) ->
- string_match(File, Archive, [Char | RevTop]);
-string_match([] = File, [], RevTop) ->
- {match, RevTop, File};
-string_match([$/ | File], [], RevTop) ->
- {match, RevTop, File};
-string_match(_File, _Archive, _RevTop) ->
+string_match([Char | File], [Char | Archive]) ->
+ string_match(File, Archive);
+string_match([] = File, []) ->
+ {match, File};
+string_match([$/ | File], []) ->
+ {match, File};
+string_match(_File, _Archive) ->
no_match.
-string_split([Char | File], [Char | Ext] = FullExt, RevTop) ->
- RevTop2 = [Char | RevTop],
- string_split2(File, Ext, RevTop, RevTop2, File, FullExt, RevTop2);
-string_split([Char | File], Ext, RevTop) ->
- string_split(File, Ext, [Char | RevTop]);
-string_split([], _Ext, _RevTop) ->
- no_split.
-
-string_split2([Char | File], [Char | Ext], RevBase, RevTop, SaveFile, SaveExt, SaveTop) ->
- string_split2(File, Ext, RevBase, [Char | RevTop], SaveFile, SaveExt, SaveTop);
-string_split2(File, [], RevBase, RevTop, _SaveFile, _SaveExt, _SaveTop) ->
- {split, RevBase, RevTop, File};
-string_split2(_, _Ext, _RevBase, _RevTop, SaveFile, SaveExt, SaveTop) ->
- string_split(SaveFile, SaveExt, SaveTop).
+archive_split("/"++File, RevExt, Acc) ->
+ case is_prefix(RevExt, Acc) of
+ false ->
+ archive_split(File, RevExt, [$/|Acc]);
+ true ->
+ ArchiveFile = absname(reverse(Acc)),
+ {archive, ArchiveFile, File}
+ end;
+archive_split([H|T], RevExt, Acc) ->
+ archive_split(T, RevExt, [H|Acc]);
+archive_split([], RevExt, Acc) ->
+ case is_prefix(RevExt, Acc) of
+ false ->
+ no_split;
+ true ->
+ ArchiveFile = absname(reverse(Acc)),
+ {archive, ArchiveFile, []}
+ end.
+
+is_prefix([H|T1], [H|T2]) -> is_prefix(T1, T2);
+is_prefix([_|_], _) -> false;
+is_prefix([], _ ) -> true.
%% Parse list of ipv4 addresses
ipv4_list([H | T]) ->
- IPV = if is_atom(H) -> ipv4_address(atom_to_list(H));
- is_list(H) -> ipv4_address(H);
- true -> {error,einal}
- end,
- case IPV of
+ case ipv4_address(H) of
{ok,IP} -> [IP | ipv4_list(T)];
_ -> ipv4_list(T)
end;
@@ -1414,8 +1438,6 @@ absname_vr([Drive, $\: | NameRest], _) ->
%% Assumes normalized name
pathtype(Name) when is_list(Name) ->
case erlang:system_info(os_type) of
- {ose, _} ->
- unix_pathtype(Name);
{unix, _} ->
unix_pathtype(Name);
{win32, _} ->
diff --git a/erts/preloaded/src/erl_tracer.erl b/erts/preloaded/src/erl_tracer.erl
new file mode 100644
index 0000000000..c810069d17
--- /dev/null
+++ b/erts/preloaded/src/erl_tracer.erl
@@ -0,0 +1,65 @@
+-module(erl_tracer).
+
+-export([enabled/3, trace/5, on_load/0]).
+
+-type tracee() :: port() | pid() | undefined.
+
+-type trace_tag_running_ports() :: in | out | in_exiting | out_exiting | out_exited.
+-type trace_tag_running_procs() :: in | out | in_exiting | out_exiting | out_exited.
+-type trace_tag_send() :: send | send_to_non_existing_process.
+-type trace_tag_receive() :: 'receive'.
+-type trace_tag_call() :: call | return_to | return_from | exception_from.
+-type trace_tag_procs() :: spawn | spawned | exit | link | unlink
+ | getting_linked | getting_unlinked
+ | register | unregister.
+-type trace_tag_ports() :: open | closed | link | unlink
+ | getting_linked | getting_unlinked.
+-type trace_tag_gc() :: gc_minor_start | gc_minor_end
+ | gc_major_start | gc_major_end.
+
+-type trace_tag() :: trace_tag_send()
+ | trace_tag_receive()
+ | trace_tag_call()
+ | trace_tag_procs()
+ | trace_tag_ports()
+ | trace_tag_running_procs()
+ | trace_tag_running_ports()
+ | trace_tag_gc().
+
+-type trace_opts() :: #{ extra => term(), match_spec_result => term(),
+ scheduler_id => non_neg_integer(),
+ timestamp => timestamp | cpu_timestamp |
+ monotonic | strict_monotonic }.
+-type tracer_state() :: term().
+
+on_load() ->
+ case erlang:load_nif(atom_to_list(?MODULE), 0) of
+ ok -> ok
+ end.
+
+%%%
+%%% NIF placeholders
+%%%
+
+%% This suppression is needed as trace_tag gets collapsed to atom()
+-dialyzer({no_contracts, enabled/3}).
+
+-spec enabled(Tag :: trace_status,
+ TracerState :: tracer_state(),
+ Tracee :: tracee()) ->
+ trace | remove;
+ (Tag :: trace_tag() | seq_trace,
+ TracerState :: tracer_state(),
+ Tracee :: tracee()) ->
+ trace | discard.
+enabled(_, _, _) ->
+ erlang:nif_error(nif_not_loaded).
+
+-spec trace(Tag :: trace_tag() | seq_trace,
+ TracerState :: tracer_state(),
+ Tracee :: tracee(),
+ Msg :: term(),
+ Opts :: trace_opts()) -> any().
+
+trace(_, _, _, _, _) ->
+ erlang:nif_error(nif_not_loaded).
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 611a568014..1ed6b6b284 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -30,25 +31,26 @@
-export([localtime_to_universaltime/1]).
-export([suspend_process/1]).
-export([min/2, max/2]).
--export([dlink/1, dunlink/1, dsend/2, dsend/3, dgroup_leader/2,
- dexit/2, dmonitor_node/3, dmonitor_p/2]).
+-export([dmonitor_node/3]).
-export([delay_trap/2]).
-export([set_cookie/2, get_cookie/0]).
-export([nodes/0]).
-export([integer_to_list/2]).
-export([integer_to_binary/2]).
--export([flush_monitor_message/2]).
-export([set_cpu_topology/1, format_cpu_topology/1]).
--export([await_proc_exit/3]).
-export([memory/0, memory/1]).
-export([alloc_info/1, alloc_sizes/1]).
--export([gather_sched_wall_time_result/1,
- await_sched_wall_time_modifications/2,
- gather_gc_info_result/1]).
+-export([gather_gc_info_result/1]).
+
+-export([dist_ctrl_input_handler/2,
+ dist_ctrl_put_data/2,
+ dist_ctrl_get_data/1,
+ dist_ctrl_get_data_notification/1,
+ dist_get_stat/1]).
--deprecated([hash/2]).
+-deprecated([get_stacktrace/0,now/0]).
%% Get rid of autoimports of spawn to avoid clashes with ourselves.
-compile({no_auto_import,[spawn_link/1]}).
@@ -58,12 +60,43 @@
-compile({no_auto_import,[spawn_opt/5]}).
-export_type([timestamp/0]).
+-export_type([time_unit/0]).
+-export_type([deprecated_time_unit/0]).
-type ext_binary() :: binary().
-type timestamp() :: {MegaSecs :: non_neg_integer(),
Secs :: non_neg_integer(),
MicroSecs :: non_neg_integer()}.
+-type time_unit() ::
+ pos_integer()
+ | 'second'
+ | 'millisecond'
+ | 'microsecond'
+ | 'nanosecond'
+ | 'native'
+ | 'perf_counter'
+ | deprecated_time_unit().
+
+%% Deprecated symbolic units...
+-type deprecated_time_unit() ::
+ 'seconds'
+ | 'milli_seconds'
+ | 'micro_seconds'
+ | 'nano_seconds'.
+
+-opaque prepared_code() :: reference().
+-export_type([prepared_code/0]).
+
+-opaque nif_resource() :: reference().
+-export_type([nif_resource/0]).
+
+-opaque dist_handle() :: atom().
+-export_type([dist_handle/0]).
+
+-type iovec() :: [binary()].
+-export_type([iovec/0]).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Native code BIF stubs and their types
%% (BIF's actually implemented in this module goes last in the file)
@@ -81,30 +114,34 @@
-export([binary_to_list/3, binary_to_term/1, binary_to_term/2]).
-export([bit_size/1, bitsize/1, bitstring_to_list/1]).
-export([bump_reductions/1, byte_size/1, call_on_load_function/1]).
--export([cancel_timer/1, check_old_code/1, check_process_code/2,
+-export([cancel_timer/1, cancel_timer/2, ceil/1,
+ check_old_code/1, check_process_code/2,
check_process_code/3, crc32/1]).
-export([crc32/2, crc32_combine/3, date/0, decode_packet/3]).
-export([delete_element/2]).
-export([delete_module/1, demonitor/1, demonitor/2, display/1]).
--export([display_nl/0, display_string/1, dist_exit/3, erase/0, erase/1]).
--export([error/1, error/2, exit/1, exit/2, external_size/1]).
+-export([display_nl/0, display_string/1, erase/0, erase/1]).
+-export([error/1, error/2, exit/1, exit/2, exit_signal/2, external_size/1]).
-export([external_size/2, finish_after_on_load/2, finish_loading/1, float/1]).
-export([float_to_binary/1, float_to_binary/2,
- float_to_list/1, float_to_list/2]).
+ float_to_list/1, float_to_list/2, floor/1]).
-export([fun_info/2, fun_info_mfa/1, fun_to_list/1, function_exported/3]).
-export([garbage_collect/0, garbage_collect/1, garbage_collect/2]).
--export([garbage_collect_message_area/0, get/0, get/1, get_keys/1]).
+-export([garbage_collect_message_area/0, get/0, get/1, get_keys/0, get_keys/1]).
-export([get_module_info/1, get_stacktrace/0, group_leader/0]).
--export([group_leader/2, halt/0, halt/1, halt/2, hash/2, hibernate/3]).
+-export([group_leader/2]).
+-export([halt/0, halt/1, halt/2,
+ has_prepared_code_on_load/1, hibernate/3]).
-export([insert_element/3]).
-export([integer_to_binary/1, integer_to_list/1]).
--export([iolist_size/1, iolist_to_binary/1]).
--export([is_alive/0, is_builtin/3, is_process_alive/1, length/1, link/1]).
+-export([iolist_size/1, iolist_to_binary/1, iolist_to_iovec/1]).
+-export([is_alive/0, is_builtin/3, is_map_key/2, is_process_alive/1, length/1, link/1]).
-export([list_to_atom/1, list_to_binary/1]).
-export([list_to_bitstring/1, list_to_existing_atom/1, list_to_float/1]).
-export([list_to_integer/1, list_to_integer/2]).
--export([list_to_pid/1, list_to_tuple/1, loaded/0]).
--export([localtime/0, make_ref/0, map_size/1, match_spec_test/3, md5/1, md5_final/1]).
+-export([list_to_pid/1, list_to_port/1, list_to_ref/1, list_to_tuple/1, loaded/0]).
+-export([localtime/0, make_ref/0]).
+-export([map_size/1, map_get/2, match_spec_test/3, md5/1, md5_final/1]).
-export([md5_init/0, md5_update/2, module_loaded/1, monitor/2]).
-export([monitor_node/2, monitor_node/3, nif_error/1, nif_error/2]).
-export([node/0, node/1, now/0, phash/2, phash2/1, phash2/2]).
@@ -112,13 +149,19 @@
-export([port_connect/2, port_control/3, port_get_data/1]).
-export([port_set_data/2, port_to_list/1, ports/0]).
-export([posixtime_to_universaltime/1, pre_loaded/0, prepare_loading/2]).
+-export([monotonic_time/0, monotonic_time/1]).
+-export([system_time/0, system_time/1]).
+-export([convert_time_unit/3]).
+-export([unique_integer/0, unique_integer/1]).
+-export([time_offset/0, time_offset/1, timestamp/0]).
-export([process_display/2]).
-export([process_flag/3, process_info/1, processes/0, purge_module/1]).
--export([put/2, raise/3, read_timer/1, ref_to_list/1, register/2]).
--export([registered/0, resume_process/1, round/1, self/0, send_after/3]).
+-export([put/2, raise/3, read_timer/1, read_timer/2, ref_to_list/1, register/2]).
+-export([send_after/3, send_after/4, start_timer/3, start_timer/4]).
+-export([registered/0, resume_process/1, round/1, self/0]).
-export([seq_trace/2, seq_trace_print/1, seq_trace_print/2, setnode/2]).
-export([setnode/3, size/1, spawn/3, spawn_link/3, split_binary/2]).
--export([start_timer/3, suspend_process/2, system_monitor/0]).
+-export([suspend_process/2, system_monitor/0]).
-export([system_monitor/1, system_monitor/2, system_profile/0]).
-export([system_profile/2, throw/1, time/0, trace/3, trace_delivered/1]).
-export([trace_info/2, trunc/1, tuple_size/1, universaltime/0]).
@@ -169,6 +212,8 @@
'receive' |
'print' |
'timestamp' |
+ 'monotonic_timestamp' |
+ 'strict_monotonic_timestamp' |
'label' |
'serial'.
@@ -182,7 +227,10 @@
'exclusive' |
'runnable_ports' |
'runnable_procs' |
- 'scheduler'.
+ 'scheduler' |
+ 'timestamp' |
+ 'monotonic_timestamp' |
+ 'strict_monotonic_timestamp'.
-type system_monitor_option() ::
'busy_port' |
@@ -206,25 +254,32 @@
send |
'receive' |
procs |
+ ports |
call |
- silent |
+ arity |
return_to |
+ silent |
running |
exiting |
+ running_procs |
+ running_ports |
garbage_collection |
timestamp |
cpu_timestamp |
- arity |
+ monotonic_timestamp |
+ strict_monotonic_timestamp |
set_on_spawn |
set_on_first_spawn |
set_on_link |
set_on_first_link |
- {tracer, pid() | port()}.
+ {tracer, pid() | port()} |
+ {tracer, module(), term()}.
-type trace_info_item_result() ::
{traced, global | local | false | undefined} |
{match_spec, trace_match_spec() | false | undefined} |
{meta, pid() | port() | false | undefined | []} |
+ {meta, module(), term() } |
{meta_match_spec, trace_match_spec() | false | undefined} |
{call_count, non_neg_integer() | boolean() | undefined} |
{call_time, [{pid(), non_neg_integer(),
@@ -242,12 +297,15 @@
running |
garbage_collection |
timestamp |
+ monotonic_timestamp |
+ strict_monotonic_timestamp |
arity.
-type trace_info_return() ::
undefined |
{flags, [trace_info_flag()]} |
{tracer, pid() | port() | []} |
+ {tracer, module(), term()} |
trace_info_item_result() |
{all, [ trace_info_item_result() ] | false | undefined}.
@@ -365,9 +423,11 @@ binary_to_term(_Binary) ->
erlang:nif_error(undefined).
%% binary_to_term/2
--spec binary_to_term(Binary, Opts) -> term() when
+-spec binary_to_term(Binary, Opts) -> term() | {term(), Used} when
Binary :: ext_binary(),
- Opts :: [safe].
+ Opt :: safe | used,
+ Opts :: [Opt],
+ Used :: pos_integer().
binary_to_term(_Binary, _Opts) ->
erlang:nif_error(undefined).
@@ -410,12 +470,34 @@ call_on_load_function(_P1) ->
erlang:nif_error(undefined).
%% cancel_timer/1
--spec erlang:cancel_timer(TimerRef) -> Time | false when
+-spec erlang:cancel_timer(TimerRef) -> Result when
TimerRef :: reference(),
- Time :: non_neg_integer().
+ Time :: non_neg_integer(),
+ Result :: Time | false.
+
cancel_timer(_TimerRef) ->
erlang:nif_error(undefined).
+%% cancel_timer/2
+-spec erlang:cancel_timer(TimerRef, Options) -> Result | ok when
+ TimerRef :: reference(),
+ Async :: boolean(),
+ Info :: boolean(),
+ Option :: {async, Async} | {info, Info},
+ Options :: [Option],
+ Time :: non_neg_integer(),
+ Result :: Time | false.
+
+cancel_timer(_TimerRef, _Options) ->
+ erlang:nif_error(undefined).
+
+%% ceil/1
+%% Shadowed by erl_bif_types: erlang:ceil/1
+-spec ceil(Number) -> integer() when
+ Number :: number().
+ceil(_) ->
+ erlang:nif_error(undef).
+
%% check_old_code/1
-spec check_old_code(Module) -> boolean() when
Module :: module().
@@ -429,7 +511,7 @@ check_old_code(_Module) ->
CheckResult :: boolean().
check_process_code(Pid, Module) ->
try
- erlang:check_process_code(Pid, Module, [{allow_gc, true}])
+ erts_internal:check_process_code(Pid, Module, [{allow_gc, true}])
catch
error:Error -> erlang:error(Error, [Pid, Module])
end.
@@ -444,51 +526,11 @@ check_process_code(Pid, Module) ->
CheckResult :: boolean() | aborted.
check_process_code(Pid, Module, OptionList) ->
try
- {Async, AllowGC} = get_cpc_opts(OptionList, sync, true),
- case Async of
- {async, ReqId} ->
- {priority, Prio} = erlang:process_info(erlang:self(),
- priority),
- erts_internal:request_system_task(Pid,
- Prio,
- {check_process_code,
- ReqId,
- Module,
- AllowGC}),
- async;
- sync ->
- case Pid == erlang:self() of
- true ->
- erts_internal:check_process_code(Module,
- [{allow_gc, AllowGC}]);
- false ->
- {priority, Prio} = erlang:process_info(erlang:self(),
- priority),
- ReqId = erlang:make_ref(),
- erts_internal:request_system_task(Pid,
- Prio,
- {check_process_code,
- ReqId,
- Module,
- AllowGC}),
- receive
- {check_process_code, ReqId, CheckResult} ->
- CheckResult
- end
- end
- end
+ erts_internal:check_process_code(Pid, Module, OptionList)
catch
error:Error -> erlang:error(Error, [Pid, Module, OptionList])
end.
-% gets async and allow_gc opts and verify valid option list
-get_cpc_opts([{async, _ReqId} = AsyncTuple | Options], _OldAsync, AllowGC) ->
- get_cpc_opts(Options, AsyncTuple, AllowGC);
-get_cpc_opts([{allow_gc, AllowGC} | Options], Async, _OldAllowGC) ->
- get_cpc_opts(Options, Async, AllowGC);
-get_cpc_opts([], Async, AllowGC) ->
- {Async, AllowGC}.
-
%% crc32/1
-spec erlang:crc32(Data) -> non_neg_integer() when
Data :: iodata().
@@ -658,14 +700,6 @@ display_nl() ->
display_string(_P1) ->
erlang:nif_error(undefined).
-%% dist_exit/3
--spec erlang:dist_exit(P1, P2, P3) -> true when
- P1 :: pid(),
- P2 :: kill | noconnection | normal,
- P3 :: pid() | port().
-dist_exit(_P1, _P2, _P3) ->
- erlang:nif_error(undefined).
-
%% dt_append_vm_tag_data/1
-spec erlang:dt_append_vm_tag_data(IoData) -> IoDataRet when
IoData :: iodata(),
@@ -751,6 +785,13 @@ exit(_Reason) ->
exit(_Pid, _Reason) ->
erlang:nif_error(undefined).
+%% exit_signal/2
+-spec erlang:exit_signal(Pid, Reason) -> true when
+ Pid :: pid() | port(),
+ Reason :: term().
+exit_signal(_Pid, _Reason) ->
+ erlang:nif_error(undefined).
+
%% external_size/1
-spec erlang:external_size(Term) -> non_neg_integer() when
Term :: term().
@@ -765,9 +806,9 @@ external_size(_Term, _Options) ->
erlang:nif_error(undefined).
%% finish_loading/2
--spec erlang:finish_loading(PreparedCodeBinaries) -> ok | Error when
- PreparedCodeBinaries :: [PreparedCodeBinary],
- PreparedCodeBinary :: binary(),
+-spec erlang:finish_loading(PreparedCodeList) -> ok | Error when
+ PreparedCodeList :: [PreparedCode],
+ PreparedCode :: prepared_code(),
ModuleList :: [module()],
Error :: {not_purged,ModuleList} | {on_load,ModuleList}.
finish_loading(_List) ->
@@ -819,6 +860,13 @@ float_to_list(_Float) ->
float_to_list(_Float, _Options) ->
erlang:nif_error(undefined).
+%% floor/1
+%% Shadowed by erl_bif_types: erlang:floor/1
+-spec floor(Number) -> integer() when
+ Number :: number().
+floor(_) ->
+ erlang:nif_error(undef).
+
%% fun_info/2
-spec erlang:fun_info(Fun, Item) -> {Item, Info} when
Fun :: function(),
@@ -853,7 +901,7 @@ function_exported(_Module, _Function, _Arity) ->
%% garbage_collect/0
-spec garbage_collect() -> true.
garbage_collect() ->
- erlang:nif_error(undefined).
+ erts_internal:garbage_collect(major).
%% garbage_collect/1
-spec garbage_collect(Pid) -> GCResult when
@@ -866,36 +914,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
@@ -907,10 +958,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().
@@ -931,6 +984,12 @@ get() ->
get(_Key) ->
erlang:nif_error(undefined).
+%% get_keys/0
+-spec get_keys() -> [Key] when
+ Key :: term().
+get_keys() ->
+ erlang:nif_error(undefined).
+
%% get_keys/1
-spec get_keys(Val) -> [Key] when
Val :: term(),
@@ -939,9 +998,10 @@ get_keys(_Val) ->
erlang:nif_error(undefined).
%% get_module_info/1
--spec erlang:get_module_info(P1) -> [{atom(), [{atom(), term()}]}] when
- P1 :: atom().
-get_module_info(_P1) ->
+-spec erlang:get_module_info(Module) -> [{Item, term()}] when
+ Item :: module | exports | attributes | compile | native | md5,
+ Module :: atom().
+get_module_info(_Module) ->
erlang:nif_error(undefined).
%% get_stacktrace/0
@@ -958,21 +1018,33 @@ group_leader() ->
-spec group_leader(GroupLeader, Pid) -> true when
GroupLeader :: pid(),
Pid :: pid().
-group_leader(_GroupLeader, _Pid) ->
- erlang:nif_error(undefined).
+group_leader(GroupLeader, Pid) ->
+ case case erts_internal:group_leader(GroupLeader, Pid) of
+ false ->
+ Ref = erlang:make_ref(),
+ erts_internal:group_leader(GroupLeader,
+ Pid,
+ Ref),
+ receive {Ref, MsgRes} -> MsgRes end;
+ Res ->
+ Res
+ end of
+ true -> true;
+ Error -> erlang:error(Error, [GroupLeader, Pid])
+ end.
%% halt/0
%% Shadowed by erl_bif_types: erlang:halt/0
-spec halt() -> no_return().
halt() ->
- erlang:nif_error(undefined).
+ erlang:halt(0, []).
%% halt/1
%% Shadowed by erl_bif_types: erlang:halt/1
-spec halt(Status) -> no_return() when
Status :: non_neg_integer() | 'abort' | string().
-halt(_Status) ->
- erlang:nif_error(undefined).
+halt(Status) ->
+ erlang:halt(Status, []).
%% halt/2
%% Shadowed by erl_bif_types: erlang:halt/2
@@ -983,11 +1055,10 @@ halt(_Status) ->
halt(_Status, _Options) ->
erlang:nif_error(undefined).
-%% hash/2
--spec erlang:hash(Term, Range) -> pos_integer() when
- Term :: term(),
- Range :: pos_integer().
-hash(_Term, _Range) ->
+%% has_prepared_code_on_load/1
+-spec erlang:has_prepared_code_on_load(PreparedCode) -> boolean() when
+ PreparedCode :: prepared_code().
+has_prepared_code_on_load(_PreparedCode) ->
erlang:nif_error(undefined).
%% hibernate/3
@@ -1031,6 +1102,12 @@ iolist_size(_Item) ->
iolist_to_binary(_IoListOrBinary) ->
erlang:nif_error(undefined).
+%% iolist_to_iovec/1
+-spec erlang:iolist_to_iovec(IoListOrBinary) -> iovec() when
+ IoListOrBinary :: iolist() | binary().
+iolist_to_iovec(_IoListOrBinary) ->
+ erlang:nif_error(undefined).
+
%% is_alive/0
-spec is_alive() -> boolean().
is_alive() ->
@@ -1044,6 +1121,13 @@ is_alive() ->
is_builtin(_Module, _Function, _Arity) ->
erlang:nif_error(undefined).
+%% Shadowed by erl_bif_types: erlang:is_map_key/2
+-spec is_map_key(Key, Map) -> boolean() when
+ Key :: term(),
+ Map :: map().
+is_map_key(_,_) ->
+ erlang:nif_error(undef).
+
%% is_process_alive/1
-spec is_process_alive(Pid) -> boolean() when
Pid :: pid().
@@ -1112,6 +1196,18 @@ list_to_integer(_String,_Base) ->
list_to_pid(_String) ->
erlang:nif_error(undefined).
+%% list_to_port/1
+-spec list_to_port(String) -> port() when
+ String :: string().
+list_to_port(_String) ->
+ erlang:nif_error(undefined).
+
+%% list_to_ref/1
+-spec list_to_ref(String) -> reference() when
+ String :: string().
+list_to_ref(_String) ->
+ erlang:nif_error(undefined).
+
%% list_to_tuple/1
-spec list_to_tuple(List) -> tuple() when
List :: [term()].
@@ -1141,11 +1237,19 @@ make_ref() ->
map_size(_Map) ->
erlang:nif_error(undefined).
+%% Shadowed by erl_bif_types: erlang:map_get/2
+-spec map_get(Key, Map) -> Value when
+ Map :: map(),
+ Key :: any(),
+ Value :: any().
+map_get(_Key, _Map) ->
+ erlang:nif_error(undefined).
+
%% match_spec_test/3
--spec erlang:match_spec_test(P1, P2, P3) -> TestResult when
- P1 :: [term()] | tuple(),
- P2 :: term(),
- P3 :: table | trace,
+-spec erlang:match_spec_test(MatchAgainst, MatchSpec, Type) -> TestResult when
+ MatchAgainst :: [term()] | tuple(),
+ MatchSpec :: term(),
+ Type :: table | trace,
TestResult :: {ok, term(), [return_trace], [ {error | warning, string()} ]} | {error, [ {error | warning, string()} ]}.
match_spec_test(_P1, _P2, _P3) ->
erlang:nif_error(undefined).
@@ -1184,13 +1288,20 @@ md5_update(_Context, _Data) ->
module_loaded(_Module) ->
erlang:nif_error(undefined).
+-type registered_name() :: atom().
+-type registered_process_identifier() :: registered_name() | {registered_name(), node()}.
+-type monitor_process_identifier() :: pid() | registered_process_identifier().
+-type monitor_port_identifier() :: port() | registered_name().
+
%% monitor/2
--spec monitor(Type, Item) -> MonitorRef when
- Type :: process,
- Item :: pid() | RegName | {RegName, Node},
- RegName :: module(),
- Node :: node(),
- MonitorRef :: reference().
+-spec monitor
+ (process, monitor_process_identifier()) -> MonitorRef
+ when MonitorRef :: reference();
+ (port, monitor_port_identifier()) -> MonitorRef
+ when MonitorRef :: reference();
+ (time_offset, clock_service) -> MonitorRef
+ when MonitorRef :: reference().
+
monitor(_Type, _Item) ->
erlang:nif_error(undefined).
@@ -1276,7 +1387,7 @@ pid_to_list(_Pid) ->
erlang:nif_error(undefined).
%% port_to_list/1
--spec erlang:port_to_list(Port) -> string() when
+-spec port_to_list(Port) -> string() when
Port :: port().
port_to_list(_Port) ->
erlang:nif_error(undefined).
@@ -1292,11 +1403,111 @@ ports() ->
posixtime_to_universaltime(_P1) ->
erlang:nif_error(undefined).
+-spec erlang:unique_integer(ModifierList) -> integer() when
+ ModifierList :: [Modifier],
+ Modifier :: positive | monotonic.
+
+unique_integer(_ModifierList) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:unique_integer() -> integer().
+
+unique_integer() ->
+ erlang:nif_error(undefined).
+
+-spec erlang:monotonic_time() -> integer().
+
+monotonic_time() ->
+ erlang:nif_error(undefined).
+
+-spec erlang:monotonic_time(Unit) -> integer() when
+ Unit :: time_unit().
+
+monotonic_time(_Unit) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:system_time() -> integer().
+
+system_time() ->
+ erlang:nif_error(undefined).
+
+-spec erlang:system_time(Unit) -> integer() when
+ Unit :: time_unit().
+
+system_time(_Unit) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:convert_time_unit(Time, FromUnit, ToUnit) -> ConvertedTime when
+ Time :: integer(),
+ ConvertedTime :: integer(),
+ FromUnit :: time_unit(),
+ ToUnit :: time_unit().
+
+convert_time_unit(Time, FromUnit, ToUnit) ->
+ try
+ FU = case FromUnit of
+ native -> erts_internal:time_unit();
+ perf_counter -> erts_internal:perf_counter_unit();
+ nanosecond -> 1000*1000*1000;
+ microsecond -> 1000*1000;
+ millisecond -> 1000;
+ second -> 1;
+
+ %% Deprecated symbolic units...
+ nano_seconds -> 1000*1000*1000;
+ micro_seconds -> 1000*1000;
+ milli_seconds -> 1000;
+ seconds -> 1;
+
+ _ when FromUnit > 0 -> FromUnit
+ end,
+ TU = case ToUnit of
+ native -> erts_internal:time_unit();
+ perf_counter -> erts_internal:perf_counter_unit();
+ nanosecond -> 1000*1000*1000;
+ microsecond -> 1000*1000;
+ millisecond -> 1000;
+ second -> 1;
+
+ %% Deprecated symbolic units...
+ nano_seconds -> 1000*1000*1000;
+ micro_seconds -> 1000*1000;
+ milli_seconds -> 1000;
+ seconds -> 1;
+
+ _ when ToUnit > 0 -> ToUnit
+ end,
+ case Time < 0 of
+ true -> TU*Time - (FU - 1);
+ false -> TU*Time
+ end div FU
+ catch
+ _ : _ ->
+ erlang:error(badarg, [Time, FromUnit, ToUnit])
+ end.
+
+-spec erlang:time_offset() -> integer().
+
+time_offset() ->
+ erlang:nif_error(undefined).
+
+-spec erlang:time_offset(Unit) -> integer() when
+ Unit :: time_unit().
+
+time_offset(_Unit) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:timestamp() -> Timestamp when
+ Timestamp :: timestamp().
+
+timestamp() ->
+ erlang:nif_error(undefined).
+
%% prepare_loading/2
-spec erlang:prepare_loading(Module, Code) -> PreparedCode | {error, Reason} when
Module :: module(),
Code :: binary(),
- PreparedCode :: binary(),
+ PreparedCode :: prepared_code(),
Reason :: bad_file.
prepare_loading(_Module, _Code) ->
erlang:nif_error(undefined).
@@ -1310,8 +1521,21 @@ pre_loaded() ->
-spec erlang:process_display(Pid, Type) -> true when
Pid :: pid(),
Type :: backtrace.
-process_display(_Pid, _Type) ->
- erlang:nif_error(undefined).
+process_display(Pid, Type) ->
+ case case erts_internal:process_display(Pid, Type) of
+ Ref when erlang:is_reference(Ref) ->
+ receive
+ {Ref, Res} ->
+ Res
+ end;
+ Res ->
+ Res
+ end of
+ badarg ->
+ erlang:error(badarg, [Pid, Type]);
+ Result ->
+ Result
+ end.
%% process_flag/3
-spec process_flag(Pid, Flag, Value) -> OldValue when
@@ -1319,8 +1543,15 @@ process_display(_Pid, _Type) ->
Flag :: save_calls,
Value :: non_neg_integer(),
OldValue :: non_neg_integer().
-process_flag(_Pid, _Flag, _Value) ->
- erlang:nif_error(undefined).
+process_flag(Pid, Flag, Value) ->
+ case case erts_internal:process_flag(Pid, Flag, Value) of
+ Ref when erlang:is_reference(Ref) ->
+ receive {Ref, Res} -> Res end;
+ Res -> Res
+ end of
+ badarg -> erlang:error(badarg, [Pid, Flag, Value]);
+ Result -> Result
+ end.
%% process_info/1
-spec process_info(Pid) -> Info when
@@ -1338,8 +1569,16 @@ processes() ->
%% purge_module/1
-spec purge_module(Module) -> true when
Module :: atom().
-purge_module(_Module) ->
- erlang:nif_error(undefined).
+purge_module(Module) when erlang:is_atom(Module) ->
+ case erts_code_purger:purge(Module) of
+ {false, _} ->
+ erlang:error(badarg, [Module]);
+ {true, _} ->
+ true
+ end;
+purge_module(Arg) ->
+ erlang:error(badarg, [Arg]).
+
%% put/2
-spec put(Key, Val) -> term() when
@@ -1357,13 +1596,28 @@ raise(_Class, _Reason, _Stacktrace) ->
erlang:nif_error(undefined).
%% read_timer/1
--spec erlang:read_timer(TimerRef) -> non_neg_integer() | false when
- TimerRef :: reference().
+-spec erlang:read_timer(TimerRef) -> Result when
+ TimerRef :: reference(),
+ Time :: non_neg_integer(),
+ Result :: Time | false.
+
read_timer(_TimerRef) ->
erlang:nif_error(undefined).
+%% read_timer/2
+-spec erlang:read_timer(TimerRef, Options) -> Result | ok when
+ TimerRef :: reference(),
+ Async :: boolean(),
+ Option :: {async, Async},
+ Options :: [Option],
+ Time :: non_neg_integer(),
+ Result :: Time | false.
+
+read_timer(_TimerRef, _Options) ->
+ erlang:nif_error(undefined).
+
%% ref_to_list/1
--spec erlang:ref_to_list(Ref) -> string() when
+-spec ref_to_list(Ref) -> string() when
Ref :: reference().
ref_to_list(_Ref) ->
erlang:nif_error(undefined).
@@ -1406,9 +1660,23 @@ self() ->
Dest :: pid() | atom(),
Msg :: term(),
TimerRef :: reference().
+
send_after(_Time, _Dest, _Msg) ->
erlang:nif_error(undefined).
+%% send_after/4
+-spec erlang:send_after(Time, Dest, Msg, Options) -> TimerRef when
+ Time :: integer(),
+ Dest :: pid() | atom(),
+ Msg :: term(),
+ Options :: [Option],
+ Abs :: boolean(),
+ Option :: {abs, Abs},
+ TimerRef :: reference().
+
+send_after(_Time, _Dest, _Msg, _Options) ->
+ erlang:nif_error(undefined).
+
%% seq_trace/2
-spec erlang:seq_trace(P1, P2) -> seq_trace_info_returns() | {term(), term(), term(), term(), term()} when
P1 :: atom(),
@@ -1437,12 +1705,26 @@ setnode(_P1, _P2) ->
erlang:nif_error(undefined).
%% setnode/3
--spec erlang:setnode(P1, P2, P3) -> true when
- P1 :: atom(),
- P2 :: port(),
- P3 :: {term(), term(), term(), term()}.
-setnode(_P1, _P2, _P3) ->
- erlang:nif_error(undefined).
+-spec erlang:setnode(Node, DistCtrlr, Opts) -> dist_handle() when
+ Node :: atom(),
+ DistCtrlr :: port() | pid(),
+ Opts :: {integer(), integer(), atom(), atom()}.
+setnode(Node, DistCtrlr, {Flags, Ver, IC, OC} = Opts) when erlang:is_atom(IC),
+ erlang:is_atom(OC) ->
+ case case erts_internal:create_dist_channel(Node, DistCtrlr,
+ Flags, Ver) of
+ {ok, DH} -> DH;
+ {message, Ref} -> receive {Ref, Res} -> Res end;
+ Err -> Err
+ end of
+ Error when erlang:is_atom(Error) ->
+ erlang:error(Error, [Node, DistCtrlr, Opts]);
+ DHandle ->
+ DHandle
+ end;
+setnode(Node, DistCtrlr, Opts) ->
+ erlang:error(badarg, [Node, DistCtrlr, Opts]).
+
%% size/1
%% Shadowed by erl_bif_types: erlang:size/1
@@ -1480,16 +1762,53 @@ split_binary(_Bin, _Pos) ->
Dest :: pid() | atom(),
Msg :: term(),
TimerRef :: reference().
+
start_timer(_Time, _Dest, _Msg) ->
erlang:nif_error(undefined).
+%% start_timer/4
+-spec erlang:start_timer(Time, Dest, Msg, Options) -> TimerRef when
+ Time :: integer(),
+ Dest :: pid() | atom(),
+ Msg :: term(),
+ Options :: [Option],
+ Abs :: boolean(),
+ Option :: {abs, Abs},
+ TimerRef :: reference().
+
+start_timer(_Time, _Dest, _Msg, _Options) ->
+ erlang:nif_error(undefined).
+
%% suspend_process/2
-spec erlang:suspend_process(Suspendee, OptList) -> boolean() when
Suspendee :: pid(),
OptList :: [Opt],
- Opt :: unless_suspending | asynchronous.
-suspend_process(_Suspendee, _OptList) ->
- erlang:nif_error(undefined).
+ Opt :: unless_suspending | asynchronous | {asynchronous, term()}.
+suspend_process(Suspendee, OptList) ->
+ case case erts_internal:suspend_process(Suspendee, OptList) of
+ Ref when erlang:is_reference(Ref) ->
+ receive {Ref, Res} -> Res end;
+ Res ->
+ Res
+ end of
+ true -> true;
+ false -> false;
+ Error -> erlang:error(Error, [Suspendee, OptList])
+ end.
+
+-spec erlang:suspend_process(Suspendee) -> 'true' when
+ Suspendee :: pid().
+suspend_process(Suspendee) ->
+ case case erts_internal:suspend_process(Suspendee, []) of
+ Ref when erlang:is_reference(Ref) ->
+ receive {Ref, Res} -> Res end;
+ Res ->
+ Res
+ end of
+ true -> true;
+ false -> erlang:error(internal_error, [Suspendee]);
+ Error -> erlang:error(Error, [Suspendee])
+ end.
%% system_monitor/0
-spec erlang:system_monitor() -> MonSettings when
@@ -1548,12 +1867,35 @@ time() ->
erlang:nif_error(undefined).
%% trace/3
--spec erlang:trace(PidSpec, How, FlagList) -> integer() when
- PidSpec :: pid() | existing | new | all,
+-spec erlang:trace(PidPortSpec, How, FlagList) -> integer() when
+ PidPortSpec :: pid() | port()
+ | all | processes | ports
+ | existing | existing_processes | existing_ports
+ | new | new_processes | new_ports,
How :: boolean(),
FlagList :: [trace_flag()].
-trace(_PidSpec, _How, _FlagList) ->
- erlang:nif_error(undefined).
+trace(PidPortSpec, How, FlagList) ->
+ %% Make sure that we have loaded the tracer module
+ case lists:keyfind(tracer, 1, FlagList) of
+ {tracer, Module, State} when erlang:is_atom(Module) ->
+ case erlang:module_loaded(Module) of
+ false ->
+ Module:enabled(trace_status, erlang:self(), State);
+ true ->
+ ok
+ end;
+ _ ->
+ ignore
+ end,
+
+ try erts_internal:trace(PidPortSpec, How, FlagList) of
+ Res -> Res
+ catch E:R ->
+ {_, [_ | CST]} = erlang:process_info(
+ erlang:self(), current_stacktrace),
+ erlang:raise(
+ E, R, [{?MODULE, trace, [PidPortSpec, How, FlagList], []} | CST])
+ end.
%% trace_delivered/1
-spec erlang:trace_delivered(Tracee) -> Ref when
@@ -1563,14 +1905,16 @@ trace_delivered(_Tracee) ->
erlang:nif_error(undefined).
%% trace_info/2
--spec erlang:trace_info(PidOrFunc, Item) -> Res when
- PidOrFunc :: pid() | new | {Module, Function, Arity} | on_load,
+-spec erlang:trace_info(PidPortFuncEvent, Item) -> Res when
+ PidPortFuncEvent :: pid() | port() | new | new_processes | new_ports
+ | {Module, Function, Arity} | on_load | send | 'receive',
Module :: module(),
Function :: atom(),
Arity :: arity(),
- Item :: flags | tracer | traced | match_spec | meta | meta_match_spec | call_count | call_time | all,
+ Item :: flags | tracer | traced | match_spec
+ | meta | meta_match_spec | call_count | call_time | all,
Res :: trace_info_return().
-trace_info(_PidOrFunc, _Item) ->
+trace_info(_PidPortFuncEvent, _Item) ->
erlang:nif_error(undefined).
%% trunc/1
@@ -1649,10 +1993,12 @@ element(_N, _Tuple) ->
erlang:nif_error(undefined).
%% Not documented
+-type module_info_key() :: attributes | compile | exports | functions | md5
+ | module | native | native_addresses | nifs.
-spec erlang:get_module_info(Module, Item) -> ModuleInfo when
Module :: atom(),
- Item :: module | imports | exports | functions | attributes | compile | native_addresses,
- ModuleInfo :: atom() | [] | [{atom(), arity()}] | [{atom(), term()}] | [{atom(), arity(), integer()}].
+ Item :: module_info_key(),
+ ModuleInfo :: term().
get_module_info(_Module, _Item) ->
erlang:nif_error(undefined).
@@ -1778,8 +2124,8 @@ load_module(Mod, Code) ->
case erlang:prepare_loading(Mod, Code) of
{error,_}=Error ->
Error;
- Bin when erlang:is_binary(Bin) ->
- case erlang:finish_loading([Bin]) of
+ Prep when erlang:is_reference(Prep) ->
+ case erlang:finish_loading([Prep]) of
ok ->
{module,Mod};
{Error,[Mod]} ->
@@ -1805,6 +2151,7 @@ localtime_to_universaltime(_Localtime, _IsDst) ->
%% CHECK! Why the strange very thorough specification of the error
%% condition with disallowed arity in erl_bif_types?
%% Not documented
+%% Shadowed by erl_bif_types: erlang:make_fun/3
-spec erlang:make_fun(Module, Function, Arity) -> function() when
Module :: atom(),
Function :: atom(),
@@ -1844,7 +2191,7 @@ nodes(_Arg) ->
| stream
| {line, L :: non_neg_integer()}
| {cd, Dir :: string() | binary()}
- | {env, Env :: [{Name :: string(), Val :: string() | false}]}
+ | {env, Env :: [{Name :: os:env_var_name(), Val :: os:env_var_value() | false}]}
| {args, [string() | binary()]}
| {arg0, string() | binary()}
| exit_status
@@ -1857,12 +2204,21 @@ nodes(_Arg) ->
| eof
| {parallelism, Boolean :: boolean()}
| hide.
-open_port(_PortName,_PortSettings) ->
- erlang:nif_error(undefined).
+open_port(PortName, PortSettings) ->
+ case case erts_internal:open_port(PortName, PortSettings) of
+ Ref when erlang:is_reference(Ref) -> receive {Ref, Res} -> Res end;
+ Res -> Res
+ end of
+ Port when erlang:is_port(Port) -> Port;
+ Error -> erlang:error(Error, [PortName, PortSettings])
+ end.
-type priority_level() ::
low | normal | high | max.
+-type message_queue_data() ::
+ off_heap | on_heap.
+
-spec process_flag(trap_exit, Boolean) -> OldBoolean when
Boolean :: boolean(),
OldBoolean :: boolean();
@@ -1875,6 +2231,12 @@ open_port(_PortName,_PortSettings) ->
(min_bin_vheap_size, MinBinVHeapSize) -> OldMinBinVHeapSize when
MinBinVHeapSize :: non_neg_integer(),
OldMinBinVHeapSize :: non_neg_integer();
+ (max_heap_size, MaxHeapSize) -> OldMaxHeapSize when
+ MaxHeapSize :: max_heap_size(),
+ OldMaxHeapSize :: max_heap_size();
+ (message_queue_data, MQD) -> OldMQD when
+ MQD :: message_queue_data(),
+ OldMQD :: message_queue_data();
(priority, Level) -> OldLevel when
Level :: priority_level(),
OldLevel :: priority_level();
@@ -1901,6 +2263,7 @@ process_flag(_Flag, _Value) ->
dictionary |
error_handler |
garbage_collection |
+ garbage_collection_info |
group_leader |
heap_size |
initial_call |
@@ -1913,6 +2276,7 @@ process_flag(_Flag, _Value) ->
min_bin_vheap_size |
monitored_by |
monitors |
+ message_queue_data |
priority |
reductions |
registered_name |
@@ -1940,6 +2304,7 @@ process_flag(_Flag, _Value) ->
{dictionary, Dictionary :: [{Key :: term(), Value :: term()}]} |
{error_handler, Module :: module()} |
{garbage_collection, GCInfo :: [{atom(),non_neg_integer()}]} |
+ {garbage_collection_info, GCInfo :: [{atom(),non_neg_integer()}]} |
{group_leader, GroupLeader :: pid()} |
{heap_size, Size :: non_neg_integer()} |
{initial_call, mfa()} |
@@ -1950,13 +2315,15 @@ process_flag(_Flag, _Value) ->
{messages, MessageQueue :: [term()]} |
{min_heap_size, MinHeapSize :: non_neg_integer()} |
{min_bin_vheap_size, MinBinVHeapSize :: non_neg_integer()} |
- {monitored_by, Pids :: [pid()]} |
+ {max_heap_size, MaxHeapSize :: max_heap_size()} |
+ {monitored_by, MonitoredBy :: [pid() | port() | nif_resource()]} |
{monitors,
- Monitors :: [{process, Pid :: pid() |
+ Monitors :: [{process | port, Pid :: pid() | port() |
{RegName :: atom(), Node :: node()}}]} |
+ {message_queue_data, MQD :: message_queue_data()} |
{priority, Level :: priority_level()} |
{reductions, Number :: non_neg_integer()} |
- {registered_name, Atom :: atom()} |
+ {registered_name, [] | (Atom :: atom())} |
{sequential_trace_token, [] | (SequentialTraceToken :: term())} |
{stack_size, Size :: non_neg_integer()} |
{status, Status :: exiting | garbage_collecting | waiting | running | runnable | suspended} |
@@ -2008,6 +2375,8 @@ send(_Dest,_Msg,_Options) ->
('receive') -> {'receive', boolean()};
(print) -> {print, boolean()};
(timestamp) -> {timestamp, boolean()};
+ (monotonic_timestamp) -> {timestamp, boolean()};
+ (strict_monotonic_timestamp) -> {strict_monotonic_timestamp, boolean()};
(label) -> [] | {label, non_neg_integer()};
(serial) -> [] | {serial, {non_neg_integer(), non_neg_integer()}}.
seq_trace_info(_What) ->
@@ -2031,11 +2400,16 @@ setelement(_Index, _Tuple1, _Value) ->
| {priority, Level :: priority_level()}
| {fullsweep_after, Number :: non_neg_integer()}
| {min_heap_size, Size :: non_neg_integer()}
+ | {max_heap_size, Size :: max_heap_size()}
| {min_bin_vheap_size, VSize :: non_neg_integer()}.
spawn_opt(_Tuple) ->
erlang:nif_error(undefined).
--spec statistics(context_switches) -> {ContextSwitches,0} when
+-spec statistics(active_tasks) -> [ActiveTasks] when
+ ActiveTasks :: non_neg_integer();
+ (active_tasks_all) -> [ActiveTasks] when
+ ActiveTasks :: non_neg_integer();
+ (context_switches) -> {ContextSwitches,0} when
ContextSwitches :: non_neg_integer();
(exact_reductions) -> {Total_Exact_Reductions,
Exact_Reductions_Since_Last_Call} when
@@ -2047,11 +2421,26 @@ spawn_opt(_Tuple) ->
(io) -> {{input, Input}, {output, Output}} when
Input :: non_neg_integer(),
Output :: non_neg_integer();
+ (microstate_accounting) -> [MSAcc_Thread] | undefined when
+ MSAcc_Thread :: #{ type := MSAcc_Thread_Type,
+ id := MSAcc_Thread_Id,
+ counters := MSAcc_Counters},
+ MSAcc_Thread_Type :: async | aux | dirty_io_scheduler
+ | dirty_cpu_scheduler | poll | scheduler,
+ MSAcc_Thread_Id :: non_neg_integer(),
+ MSAcc_Counters :: #{ MSAcc_Thread_State => non_neg_integer() },
+ MSAcc_Thread_State :: alloc | aux | bif | busy_wait | check_io |
+ emulator | ets | gc | gc_fullsweep | nif |
+ other | port | send | sleep | timers;
(reductions) -> {Total_Reductions,
Reductions_Since_Last_Call} when
Total_Reductions :: non_neg_integer(),
Reductions_Since_Last_Call :: non_neg_integer();
(run_queue) -> non_neg_integer();
+ (run_queue_lengths) -> [RunQueueLength] when
+ RunQueueLength :: non_neg_integer();
+ (run_queue_lengths_all) -> [RunQueueLength] when
+ RunQueueLength :: non_neg_integer();
(runtime) -> {Total_Run_Time, Time_Since_Last_Call} when
Total_Run_Time :: non_neg_integer(),
Time_Since_Last_Call :: non_neg_integer();
@@ -2059,6 +2448,18 @@ spawn_opt(_Tuple) ->
SchedulerId :: pos_integer(),
ActiveTime :: non_neg_integer(),
TotalTime :: non_neg_integer();
+ (scheduler_wall_time_all) -> [{SchedulerId, ActiveTime, TotalTime}] | undefined when
+ SchedulerId :: pos_integer(),
+ ActiveTime :: non_neg_integer(),
+ TotalTime :: non_neg_integer();
+ (total_active_tasks) -> ActiveTasks when
+ ActiveTasks :: non_neg_integer();
+ (total_active_tasks_all) -> ActiveTasks when
+ ActiveTasks :: non_neg_integer();
+ (total_run_queue_lengths) -> TotalRunQueueLengths when
+ TotalRunQueueLengths :: non_neg_integer();
+ (total_run_queue_lengths_all) -> TotalRunQueueLengths when
+ TotalRunQueueLengths :: non_neg_integer();
(wall_clock) -> {Total_Wallclock_Time,
Wallclock_Time_Since_Last_Call} when
Total_Wallclock_Time :: non_neg_integer(),
@@ -2092,9 +2493,16 @@ subtract(_,_) ->
OldDirtyCPUSchedulersOnline when
DirtyCPUSchedulersOnline :: pos_integer(),
OldDirtyCPUSchedulersOnline :: pos_integer();
+ (erts_alloc, {Alloc, F, V}) -> ok | notsup when
+ Alloc :: atom(),
+ F :: atom(),
+ V :: integer();
(fullsweep_after, Number) -> OldNumber when
Number :: non_neg_integer(),
OldNumber :: non_neg_integer();
+ (microstate_accounting, Action) -> OldState when
+ Action :: true | false | reset,
+ OldState :: true | false;
(min_heap_size, MinHeapSize) -> OldMinHeapSize when
MinHeapSize :: non_neg_integer(),
OldMinHeapSize :: non_neg_integer();
@@ -2102,9 +2510,12 @@ subtract(_,_) ->
OldMinBinVHeapSize when
MinBinVHeapSize :: non_neg_integer(),
OldMinBinVHeapSize :: non_neg_integer();
+ (max_heap_size, MaxHeapSize) -> OldMaxHeapSize when
+ MaxHeapSize :: max_heap_size(),
+ OldMaxHeapSize :: max_heap_size();
(multi_scheduling, BlockState) -> OldBlockState when
- BlockState :: block | unblock,
- OldBlockState :: block | unblock | enabled;
+ BlockState :: block | unblock | block_normal | unblock_normal,
+ OldBlockState :: blocked | disabled | enabled;
(scheduler_bind_type, How) -> OldBindType when
How :: scheduler_bind_type() | default_bind,
OldBindType :: scheduler_bind_type();
@@ -2118,9 +2529,11 @@ subtract(_,_) ->
(trace_control_word, TCW) -> OldTCW when
TCW :: non_neg_integer(),
OldTCW :: non_neg_integer();
+ (time_offset, finalize) -> OldState when
+ OldState :: preliminary | final | volatile;
%% These are deliberately not documented
(internal_cpu_topology, term()) -> term();
- (sequential_tracer, pid() | port() | false) -> pid() | port() | false;
+ (sequential_tracer, pid() | port() | {module(), term()} | false) -> pid() | port() | false;
(1,0) -> true.
system_flag(_Flag, _Value) ->
@@ -2135,7 +2548,7 @@ term_to_binary(_Term) ->
Term :: term(),
Options :: [compressed |
{compressed, Level :: 0..9} |
- {minor_version, Version :: 0..1} ].
+ {minor_version, Version :: 0..2} ].
term_to_binary(_Term, _Options) ->
erlang:nif_error(undefined).
@@ -2145,35 +2558,70 @@ term_to_binary(_Term, _Options) ->
tl(_List) ->
erlang:nif_error(undefined).
+-type match_variable() :: atom(). % Approximation of '$1' | '$2' | ...
-type trace_pattern_mfa() ::
{atom(),atom(),arity() | '_'} | on_load.
-type trace_match_spec() ::
- [{[term()] | '_' ,[term()],[term()]}].
+ [{[term()] | '_' | match_variable() ,[term()],[term()]}].
-spec erlang:trace_pattern(MFA, MatchSpec) -> non_neg_integer() when
- MFA :: trace_pattern_mfa(),
+ MFA :: trace_pattern_mfa() | send | 'receive',
MatchSpec :: (MatchSpecList :: trace_match_spec())
| boolean()
| restart
| pause.
-trace_pattern(_MFA, _MatchSpec) ->
- erlang:nif_error(undefined).
+trace_pattern(MFA, MatchSpec) ->
+ try erts_internal:trace_pattern(MFA, MatchSpec, []) of
+ Res -> Res
+ catch E:R ->
+ {_, [_ | CST]} = erlang:process_info(
+ erlang:self(), current_stacktrace),
+ erlang:raise(
+ E, R, [{?MODULE, trace_pattern, [MFA, MatchSpec], []} | CST])
+ end.
-type trace_pattern_flag() ::
global | local |
meta | {meta, Pid :: pid()} |
+ {meta, TracerModule :: module(), TracerState :: term()} |
call_count |
call_time.
--spec erlang:trace_pattern(MFA, MatchSpec, FlagList) -> non_neg_integer() when
+-spec erlang:trace_pattern(send, MatchSpec, []) -> non_neg_integer() when
+ MatchSpec :: (MatchSpecList :: trace_match_spec())
+ | boolean();
+ ('receive', MatchSpec, []) -> non_neg_integer() when
+ MatchSpec :: (MatchSpecList :: trace_match_spec())
+ | boolean();
+ (MFA, MatchSpec, FlagList) -> non_neg_integer() when
MFA :: trace_pattern_mfa(),
MatchSpec :: (MatchSpecList :: trace_match_spec())
| boolean()
| restart
| pause,
FlagList :: [ trace_pattern_flag() ].
-trace_pattern(_MFA, _MatchSpec, _FlagList) ->
- erlang:nif_error(undefined).
+trace_pattern(MFA, MatchSpec, FlagList) ->
+ %% Make sure that we have loaded the tracer module
+ case lists:keyfind(meta, 1, FlagList) of
+ {meta, Module, State} when erlang:is_atom(Module) ->
+ case erlang:module_loaded(Module) of
+ false ->
+ Module:enabled(trace_status, erlang:self(), State);
+ true ->
+ ok
+ end;
+ _ ->
+ ignore
+ end,
+
+ try erts_internal:trace_pattern(MFA, MatchSpec, FlagList) of
+ Res -> Res
+ catch E:R ->
+ {_, [_ | CST]} = erlang:process_info(
+ erlang:self(), current_stacktrace),
+ erlang:raise(
+ E, R, [{?MODULE, trace_pattern, [MFA, MatchSpec, FlagList], []} | CST])
+ end.
%% Shadowed by erl_bif_types: erlang:tuple_to_list/1
-spec tuple_to_list(Tuple) -> [term()] when
@@ -2205,12 +2653,14 @@ tuple_to_list(_Tuple) ->
Settings :: [{Subsystem :: atom(),
[{Parameter :: atom(),
Value :: term()}]}];
- (alloc_util_allocators) -> [Alloc] when
- Alloc :: atom();
({allocator, Alloc}) -> [_] when %% More or less anything
Alloc :: atom();
+ (alloc_util_allocators) -> [Alloc] when
+ Alloc :: atom();
({allocator_sizes, Alloc}) -> [_] when %% More or less anything
Alloc :: atom();
+ (atom_count) -> pos_integer();
+ (atom_limit) -> pos_integer();
(build_type) -> opt | debug | purify | quantify | purecov |
gcov | valgrind | gprof | lcnt | frmptr;
(c_compiler_used) -> {atom(), term()};
@@ -2222,6 +2672,7 @@ tuple_to_list(_Tuple) ->
CpuTopology :: cpu_topology();
(creation) -> integer();
(debug_compiled) -> boolean();
+ (delayed_node_table_gc) -> infinity | non_neg_integer();
(dirty_cpu_schedulers) -> non_neg_integer();
(dirty_cpu_schedulers_online) -> non_neg_integer();
(dirty_io_schedulers) -> non_neg_integer();
@@ -2230,10 +2681,12 @@ tuple_to_list(_Tuple) ->
(dist_ctrl) -> {Node :: node(),
ControllingEntity :: port() | pid()};
(driver_version) -> string();
- (dynamic_trace) -> none | dtrace | systemtap;
+ (dynamic_trace) -> none | dtrace | systemtap;
(dynamic_trace_probes) -> boolean();
+ (end_time) -> non_neg_integer();
(elib_malloc) -> false;
(eager_check_io) -> boolean();
+ (ets_count) -> pos_integer();
(ets_limit) -> pos_integer();
(fullsweep_after) -> {fullsweep_after, non_neg_integer()};
(garbage_collection) -> [{atom(), integer()}];
@@ -2246,14 +2699,20 @@ tuple_to_list(_Tuple) ->
logical_processors_available |
logical_processors_online) -> unknown | pos_integer();
(machine) -> string();
+ (max_heap_size) -> {max_heap_size, MaxHeapSize :: max_heap_size()};
+ (message_queue_data) -> message_queue_data();
(min_heap_size) -> {min_heap_size, MinHeapSize :: pos_integer()};
(min_bin_vheap_size) -> {min_bin_vheap_size,
MinBinVHeapSize :: pos_integer()};
(modified_timing_level) -> integer() | undefined;
- (multi_scheduling) -> disabled | blocked | enabled;
- (multi_scheduling_blockers) -> [PID :: pid()];
+ (multi_scheduling) -> disabled | blocked | blocked_normal | enabled;
+ (multi_scheduling_blockers) -> [Pid :: pid()];
(nif_version) -> string();
+ (normal_multi_scheduling_blockers) -> [Pid :: pid()];
(otp_release) -> string();
+ (os_monotonic_time_source) -> [{atom(),term()}];
+ (os_system_time_source) -> [{atom(),term()}];
+ (port_parallelism) -> boolean();
(port_count) -> non_neg_integer();
(port_limit) -> pos_integer();
(process_count) -> pos_integer();
@@ -2271,15 +2730,20 @@ tuple_to_list(_Tuple) ->
(scheduler_id) -> SchedulerId :: pos_integer();
(schedulers | schedulers_online) -> pos_integer();
(smp_support) -> boolean();
+ (start_time) -> integer();
(system_version) -> string();
(system_architecture) -> string();
(threads) -> boolean();
(thread_pool_size) -> non_neg_integer();
+ (time_correction) -> true | false;
+ (time_offset) -> preliminary | final | volatile;
+ (time_warp_mode) -> no_time_warp | single_time_warp | multi_time_warp;
(tolerant_timeofday) -> enabled | disabled;
(trace_control_word) -> non_neg_integer();
(update_cpu_info) -> changed | unchanged;
(version) -> string();
- (wordsize | {wordsize, internal} | {wordsize, external}) -> 4 | 8.
+ (wordsize | {wordsize, internal} | {wordsize, external}) -> 4 | 8;
+ (overview) -> boolean().
system_info(_Item) ->
erlang:nif_error(undefined).
@@ -2296,6 +2760,7 @@ universaltime_to_localtime(_Universaltime) ->
%%--------------------------------------------------------------------------
+%% Shadowed by erl_bif_types: erlang:apply/2
-spec apply(Fun, Args) -> term() when
Fun :: function(),
Args :: [term()].
@@ -2374,14 +2839,27 @@ spawn_monitor(M, F, A) when erlang:is_atom(M),
spawn_monitor(M, F, A) ->
erlang:error(badarg, [M,F,A]).
+
+-type max_heap_size() ::
+ Size :: non_neg_integer()
+ %% TODO change size => to := when -type maps support is finalized
+ | #{ size => non_neg_integer(),
+ kill => boolean(),
+ error_logger => boolean() }.
+
+-type spawn_opt_option() ::
+ link
+ | monitor
+ | {priority, Level :: priority_level()}
+ | {fullsweep_after, Number :: non_neg_integer()}
+ | {min_heap_size, Size :: non_neg_integer()}
+ | {min_bin_vheap_size, VSize :: non_neg_integer()}
+ | {max_heap_size, Size :: max_heap_size()}
+ | {message_queue_data, MQD :: message_queue_data()}.
+
-spec spawn_opt(Fun, Options) -> pid() | {pid(), reference()} when
Fun :: function(),
- Options :: [Option],
- Option :: link | monitor
- | {priority, Level :: priority_level()}
- | {fullsweep_after, Number :: non_neg_integer()}
- | {min_heap_size, Size :: non_neg_integer()}
- | {min_bin_vheap_size, VSize :: non_neg_integer()}.
+ Options :: [spawn_opt_option()].
spawn_opt(F, O) when erlang:is_function(F) ->
spawn_opt(erlang, apply, [F, []], O);
spawn_opt({M,F}=MF, O) when erlang:is_atom(M), erlang:is_atom(F) ->
@@ -2394,12 +2872,7 @@ spawn_opt(F, O) ->
-spec spawn_opt(Node, Fun, Options) -> pid() | {pid(), reference()} when
Node :: node(),
Fun :: function(),
- Options :: [Option],
- Option :: link | monitor
- | {priority, Level :: priority_level()}
- | {fullsweep_after, Number :: non_neg_integer()}
- | {min_heap_size, Size :: non_neg_integer()}
- | {min_bin_vheap_size, VSize :: non_neg_integer()}.
+ Options :: [spawn_opt_option()].
spawn_opt(N, F, O) when N =:= erlang:node() ->
spawn_opt(F, O);
spawn_opt(N, F, O) when erlang:is_function(F) ->
@@ -2486,12 +2959,7 @@ spawn_link(N,M,F,A) ->
Module :: module(),
Function :: atom(),
Args :: [term()],
- Options :: [Option],
- Option :: link | monitor
- | {priority, Level :: priority_level()}
- | {fullsweep_after, Number :: non_neg_integer()}
- | {min_heap_size, Size :: non_neg_integer()}
- | {min_bin_vheap_size, VSize :: non_neg_integer()}.
+ Options :: [spawn_opt_option()].
spawn_opt(M, F, A, Opts) ->
case catch erlang:spawn_opt({M,F,A,Opts}) of
{'EXIT',{Reason,_}} ->
@@ -2506,12 +2974,7 @@ spawn_opt(M, F, A, Opts) ->
Module :: module(),
Function :: atom(),
Args :: [term()],
- Options :: [Option],
- Option :: link | monitor
- | {priority, Level :: priority_level()}
- | {fullsweep_after, Number :: non_neg_integer()}
- | {min_heap_size, Size :: non_neg_integer()}
- | {min_bin_vheap_size, VSize :: non_neg_integer()}.
+ Options :: [spawn_opt_option()].
spawn_opt(N, M, F, A, O) when N =:= erlang:node(),
erlang:is_atom(M), erlang:is_atom(F),
erlang:is_list(A), erlang:is_list(O) ->
@@ -2640,15 +3103,6 @@ send_nosuspend(Pid, Msg, Opts) ->
localtime_to_universaltime(Localtime) ->
erlang:localtime_to_universaltime(Localtime, undefined).
--spec erlang:suspend_process(Suspendee) -> 'true' when
- Suspendee :: pid().
-suspend_process(P) ->
- case catch erlang:suspend_process(P, []) of
- {'EXIT', {Reason, _}} -> erlang:error(Reason, [P]);
- {'EXIT', Reason} -> erlang:error(Reason, [P]);
- Res -> Res
- end.
-
%%
%% Port BIFs
%%
@@ -2806,6 +3260,9 @@ port_info(Port) ->
(Port, monitors) -> {monitors, Monitors} | 'undefined' when
Port :: port() | atom(),
Monitors :: [{process, pid()}];
+ (Port, monitored_by) -> {monitored_by, MonitoredBy} | 'undefined' when
+ Port :: port() | atom(),
+ MonitoredBy :: [pid()];
(Port, name) -> {name, Name} | 'undefined' when
Port :: port() | atom(),
Name :: string();
@@ -2848,33 +3305,51 @@ port_get_data(_Port) ->
erlang:nif_error(undefined).
%%
-%% If the emulator wants to perform a distributed command and
-%% a connection is not established to the actual node the following
-%% functions are called in order to set up the connection and then
-%% reactivate the command.
+%% Distribution channel management
%%
--spec erlang:dlink(pid() | port()) -> 'true'.
-dlink(Pid) ->
- case net_kernel:connect(erlang:node(Pid)) of
- true -> erlang:link(Pid);
- false -> erlang:dist_exit(erlang:self(), noconnection, Pid), true
- end.
+-spec erlang:dist_ctrl_input_handler(DHandle, InputHandler) -> 'ok' when
+ DHandle :: dist_handle(),
+ InputHandler :: pid().
-%% Can this ever happen?
--spec erlang:dunlink(identifier()) -> 'true'.
-dunlink(Pid) ->
- case net_kernel:connect(erlang:node(Pid)) of
- true -> erlang:unlink(Pid);
- false -> true
- end.
+dist_ctrl_input_handler(_DHandle, _InputHandler) ->
+ erlang:nif_error(undefined).
-dmonitor_node(Node, Flag, []) ->
- case net_kernel:connect(Node) of
- true -> erlang:monitor_node(Node, Flag, []);
- false -> erlang:self() ! {nodedown, Node}, true
- end;
+-spec erlang:dist_ctrl_put_data(DHandle, Data) -> 'ok' when
+ DHandle :: dist_handle(),
+ Data :: iodata().
+
+dist_ctrl_put_data(_DHandle, _Data) ->
+ erlang:nif_error(undefined).
+-spec erlang:dist_ctrl_get_data(DHandle) -> Data | 'none' when
+ DHandle :: dist_handle(),
+ Data :: iodata().
+
+dist_ctrl_get_data(_DHandle) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:dist_ctrl_get_data_notification(DHandle) -> 'ok' when
+ DHandle :: dist_handle().
+
+dist_ctrl_get_data_notification(_DHandle) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:dist_get_stat(DHandle) -> Res when
+ DHandle :: dist_handle(),
+ InputPackets :: non_neg_integer(),
+ OutputPackets :: non_neg_integer(),
+ PendingOutputPackets :: boolean(),
+ Res :: {'ok', InputPackets, OutputPackets, PendingOutputPackets}.
+
+dist_get_stat(_DHandle) ->
+ erlang:nif_error(undefined).
+
+
+dmonitor_node(Node, _Flag, []) ->
+ %% Only called when auto-connect attempt failed early in VM
+ erlang:self() ! {nodedown, Node},
+ true;
dmonitor_node(Node, Flag, Opts) ->
case lists:member(allow_passive_connect, Opts) of
true ->
@@ -2886,72 +3361,6 @@ dmonitor_node(Node, Flag, Opts) ->
dmonitor_node(Node,Flag,[])
end.
-dgroup_leader(Leader, Pid) ->
- case net_kernel:connect(erlang:node(Pid)) of
- true -> erlang:group_leader(Leader, Pid);
- false -> true %% bad arg ?
- end.
-
-dexit(Pid, Reason) ->
- case net_kernel:connect(erlang:node(Pid)) of
- true -> erlang:exit(Pid, Reason);
- false -> true
- end.
-
-dsend(Pid, Msg) when erlang:is_pid(Pid) ->
- case net_kernel:connect(erlang:node(Pid)) of
- true -> erlang:send(Pid, Msg);
- false -> Msg
- end;
-dsend(Port, Msg) when erlang:is_port(Port) ->
- case net_kernel:connect(erlang:node(Port)) of
- true -> erlang:send(Port, Msg);
- false -> Msg
- end;
-dsend({Name, Node}, Msg) ->
- case net_kernel:connect(Node) of
- true -> erlang:send({Name,Node}, Msg);
- false -> Msg;
- ignored -> Msg % Not distributed.
- end.
-
-dsend(Pid, Msg, Opts) when erlang:is_pid(Pid) ->
- case net_kernel:connect(erlang:node(Pid)) of
- true -> erlang:send(Pid, Msg, Opts);
- false -> ok
- end;
-dsend(Port, Msg, Opts) when erlang:is_port(Port) ->
- case net_kernel:connect(erlang:node(Port)) of
- true -> erlang:send(Port, Msg, Opts);
- false -> ok
- end;
-dsend({Name, Node}, Msg, Opts) ->
- case net_kernel:connect(Node) of
- true -> erlang:send({Name,Node}, Msg, Opts);
- false -> ok;
- ignored -> ok % Not distributed.
- end.
-
--spec erlang:dmonitor_p('process', pid() | {atom(),atom()}) -> reference().
-dmonitor_p(process, ProcSpec) ->
- %% ProcSpec = pid() | {atom(),atom()}
- %% ProcSpec CANNOT be an atom because a locally registered process
- %% is never handled here.
- Node = case ProcSpec of
- {S,N} when erlang:is_atom(S),
- erlang:is_atom(N),
- N =/= erlang:node() -> N;
- _ when erlang:is_pid(ProcSpec) -> erlang:node(ProcSpec)
- end,
- case net_kernel:connect(Node) of
- true ->
- erlang:monitor(process, ProcSpec);
- false ->
- Ref = erlang:make_ref(),
- erlang:self() ! {'DOWN', Ref, process, ProcSpec, noconnection},
- Ref
- end.
-
%%
%% Trap function used when modified timing has been enabled.
%%
@@ -3041,16 +3450,6 @@ integer_to_binary(I0, Base, R0) ->
true -> integer_to_binary(I1, Base, R1)
end.
-%% erlang:flush_monitor_message/2 is for internal use only!
-%%
-%% erlang:demonitor(Ref, [flush]) traps to
-%% erlang:flush_monitor_message(Ref, Res) when
-%% it needs to flush a monitor message.
-flush_monitor_message(Ref, Res) when erlang:is_reference(Ref),
- erlang:is_atom(Res) ->
- receive {_, Ref, _, _, _} -> ok after 0 -> ok end,
- Res.
-
-record(cpu, {node = -1,
processor = -1,
processor_node = -1,
@@ -3194,33 +3593,6 @@ rvrs(Xs) -> rvrs(Xs, []).
rvrs([],Ys) -> Ys;
rvrs([X|Xs],Ys) -> rvrs(Xs, [X|Ys]).
-%% erlang:await_proc_exit/3 is for internal use only!
-%%
-%% BIFs that need to await a specific process exit before
-%% returning traps to erlang:await_proc_exit/3.
-%%
-%% NOTE: This function is tightly coupled to
-%% the implementation of the
-%% erts_bif_prep_await_proc_exit_*()
-%% functions in bif.c. Do not make
-%% any changes to it without reading
-%% the comment about them in bif.c!
--spec erlang:await_proc_exit(dst(), 'apply' | 'data' | 'reason', term()) -> term().
-await_proc_exit(Proc, Op, Data) ->
- Mon = erlang:monitor(process, Proc),
- receive
- {'DOWN', Mon, process, _Proc, Reason} ->
- case Op of
- apply ->
- {M, F, A} = Data,
- erlang:apply(M, F, A);
- data ->
- Data;
- reason ->
- Reason
- end
- end.
-
-spec min(Term1, Term2) -> Minimum when
Term1 :: term(),
Term2 :: term(),
@@ -3244,11 +3616,9 @@ max(A, _) -> A.
%%
-type memory_type() :: 'total' | 'processes' | 'processes_used' | 'system'
- | 'atom' | 'atom_used' | 'binary' | 'code' | 'ets'
- | 'low' | 'maximum'.
+ | 'atom' | 'atom_used' | 'binary' | 'code' | 'ets'.
-define(CARRIER_ALLOCS, [mseg_alloc]).
--define(LOW_ALLOCS, [ll_low_alloc, std_low_alloc]).
-define(ALL_NEEDED_ALLOCS, (erlang:system_info(alloc_util_allocators)
-- ?CARRIER_ALLOCS)).
@@ -3260,9 +3630,7 @@ max(A, _) -> A.
atom_used = 0,
binary = 0,
code = 0,
- ets = 0,
- low = 0,
- maximum = 0}).
+ ets = 0}).
-spec erlang:memory() -> [{Type, Size}] when
Type :: memory_type(),
@@ -3272,14 +3640,6 @@ memory() ->
notsup ->
erlang:error(notsup);
Mem ->
- InstrTail = case Mem#memory.maximum of
- 0 -> [];
- _ -> [{maximum, Mem#memory.maximum}]
- end,
- Tail = case Mem#memory.low of
- 0 -> InstrTail;
- _ -> [{low, Mem#memory.low} | InstrTail]
- end,
[{total, Mem#memory.total},
{processes, Mem#memory.processes},
{processes_used, Mem#memory.processes_used},
@@ -3288,7 +3648,7 @@ memory() ->
{atom_used, Mem#memory.atom_used},
{binary, Mem#memory.binary},
{code, Mem#memory.code},
- {ets, Mem#memory.ets} | Tail]
+ {ets, Mem#memory.ets}]
end.
-spec erlang:memory(Type :: memory_type()) -> non_neg_integer();
@@ -3376,16 +3736,6 @@ need_mem_info(binary) ->
{false, [binary_alloc], true, false};
need_mem_info(ets) ->
{true, [ets_alloc], true, false};
-need_mem_info(low) ->
- LowAllocs = ?LOW_ALLOCS -- ?CARRIER_ALLOCS,
- {_, _, FeatureList, _} = erlang:system_info(allocator),
- AlcUAllocs = case LowAllocs -- FeatureList of
- [] -> LowAllocs;
- _ -> []
- end,
- {false, AlcUAllocs, true, true};
-need_mem_info(maximum) ->
- {true, [], true, true};
need_mem_info(_) ->
{false, [], false, true}.
@@ -3398,8 +3748,6 @@ get_memval(atom_used, #memory{atom_used = V}) -> V;
get_memval(binary, #memory{binary = V}) -> V;
get_memval(code, #memory{code = V}) -> V;
get_memval(ets, #memory{ets = V}) -> V;
-get_memval(low, #memory{low = V}) -> V;
-get_memval(maximum, #memory{maximum = V}) -> V;
get_memval(_, #memory{}) -> 0.
memory_is_supported() ->
@@ -3413,15 +3761,14 @@ memory_is_supported() ->
get_blocks_size([{blocks_size, Sz, _, _} | Rest], Acc) ->
get_blocks_size(Rest, Acc+Sz);
-get_blocks_size([{_, _, _, _} | Rest], Acc) ->
- get_blocks_size(Rest, Acc);
get_blocks_size([{blocks_size, Sz} | Rest], Acc) ->
get_blocks_size(Rest, Acc+Sz);
-get_blocks_size([{_, _} | Rest], Acc) ->
+get_blocks_size([_ | Rest], Acc) ->
get_blocks_size(Rest, Acc);
get_blocks_size([], Acc) ->
Acc.
+
blocks_size([{Carriers, SizeList} | Rest], Acc) when Carriers == mbcs;
Carriers == mbcs_pool;
Carriers == sbcs ->
@@ -3432,9 +3779,13 @@ blocks_size([], Acc) ->
Acc.
get_fix_proc([{ProcType, A1, U1}| Rest], {A0, U0}) when ProcType == proc;
- ProcType == monitor_sh;
- ProcType == nlink_sh;
- ProcType == msg_ref ->
+ ProcType == monitor;
+ ProcType == link;
+ ProcType == msg_ref;
+ ProcType == ll_ptimer;
+ ProcType == hl_ptimer;
+ ProcType == bif_timer;
+ ProcType == accessor_bif_timer ->
get_fix_proc(Rest, {A0+A1, U0+U1});
get_fix_proc([_|Rest], Acc) ->
get_fix_proc(Rest, Acc);
@@ -3451,16 +3802,6 @@ fix_proc([_ | Rest], Acc) ->
fix_proc([], Acc) ->
Acc.
-is_low_alloc(_A, []) ->
- false;
-is_low_alloc(A, [A|_As]) ->
- true;
-is_low_alloc(A, [_A|As]) ->
- is_low_alloc(A, As).
-
-is_low_alloc(A) ->
- is_low_alloc(A, ?LOW_ALLOCS).
-
au_mem_data(notsup, _) ->
notsup;
au_mem_data(_, [{_, false} | _]) ->
@@ -3513,16 +3854,11 @@ au_mem_data(#memory{total = Tot,
Rest)
end;
au_mem_data(#memory{total = Tot,
- system = Sys,
- low = Low} = Mem,
- [{A, _, Data} | Rest]) ->
+ system = Sys} = Mem,
+ [{_, _, Data} | Rest]) ->
Sz = blocks_size(Data, 0),
au_mem_data(Mem#memory{total = Tot+Sz,
- system = Sys+Sz,
- low = case is_low_alloc(A) of
- true -> Low+Sz;
- false -> Low
- end},
+ system = Sys+Sz},
Rest);
au_mem_data(EMD, []) ->
EMD.
@@ -3544,10 +3880,6 @@ receive_emd(Ref) ->
receive_emd(Ref, #memory{}, erlang:system_info(schedulers)).
aa_mem_data(#memory{} = Mem,
- [{maximum, Max} | Rest]) ->
- aa_mem_data(Mem#memory{maximum = Max},
- Rest);
-aa_mem_data(#memory{} = Mem,
[{total, Tot} | Rest]) ->
aa_mem_data(Mem#memory{total = Tot,
system = 0}, % system will be adjusted later
@@ -3572,7 +3904,6 @@ aa_mem_data(#memory{processes = Proc,
processes_used = ProcU,
system = Sys} = Mem,
[{ProcData, Sz} | Rest]) when ProcData == bif_timer;
- ProcData == link_lh;
ProcData == process_table ->
aa_mem_data(Mem#memory{processes = Proc+Sz,
processes_used = ProcU+Sz,
@@ -3657,37 +3988,6 @@ receive_allocator(Ref, N, Acc) ->
receive_allocator(Ref, N-1, insert_info(InfoList, Acc))
end.
--spec erlang:await_sched_wall_time_modifications(Ref, Result) -> boolean() when
- Ref :: reference(),
- Result :: boolean().
-
-await_sched_wall_time_modifications(Ref, Result) ->
- sched_wall_time(Ref, erlang:system_info(schedulers)),
- Result.
-
--spec erlang:gather_sched_wall_time_result(Ref) -> [{pos_integer(),
- non_neg_integer(),
- non_neg_integer()}] when
- Ref :: reference().
-
-gather_sched_wall_time_result(Ref) when erlang:is_reference(Ref) ->
- sched_wall_time(Ref, erlang:system_info(schedulers), []).
-
-sched_wall_time(_Ref, 0) ->
- ok;
-sched_wall_time(Ref, N) ->
- receive Ref -> sched_wall_time(Ref, N-1) end.
-
-sched_wall_time(_Ref, 0, Acc) ->
- Acc;
-sched_wall_time(Ref, N, undefined) ->
- receive {Ref, _} -> sched_wall_time(Ref, N-1, undefined) end;
-sched_wall_time(Ref, N, Acc) ->
- receive
- {Ref, undefined} -> sched_wall_time(Ref, N-1, undefined);
- {Ref, SWT} -> sched_wall_time(Ref, N-1, [SWT|Acc])
- end.
-
-spec erlang:gather_gc_info_result(Ref) ->
{number(),number(),0} when Ref :: reference().
@@ -3701,4 +4001,3 @@ gc_info(Ref, N, {OrigColls,OrigRecl}) ->
{Ref, {_,Colls, Recl}} ->
gc_info(Ref, N-1, {Colls+OrigColls,Recl+OrigRecl})
end.
-
diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src
index 345a6ae3be..8c34c99a98 100644
--- a/erts/preloaded/src/erts.app.src
+++ b/erts/preloaded/src/erts.app.src
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2018. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -26,6 +27,8 @@
erts_internal,
init,
otp_ring0,
+ erts_code_purger,
+ prim_buffer,
prim_eval,
prim_file,
prim_inet,
@@ -35,7 +38,7 @@
{registered, []},
{applications, []},
{env, []},
- {runtime_dependencies, ["stdlib-2.0", "kernel-3.0", "sasl-2.4"]}
+ {runtime_dependencies, ["stdlib-3.5", "kernel-6.1", "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
new file mode 100644
index 0000000000..c41532ed87
--- /dev/null
+++ b/erts/preloaded/src/erts_code_purger.erl
@@ -0,0 +1,377 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016-2018. 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_code_purger).
+
+%% Purpose : Implement system process erts_code_purger
+%% to handle code module purging.
+
+-export([start/0, purge/1, soft_purge/1, pending_purge_lambda/3,
+ finish_after_on_load/2]).
+
+-spec start() -> no_return().
+start() ->
+ register(erts_code_purger, self()),
+ process_flag(trap_exit, true),
+ wait_for_request().
+
+wait_for_request() ->
+ handle_request(receive Msg -> Msg end, []).
+
+handle_request({purge, Mod, From, Ref}, Reqs) when is_atom(Mod), is_pid(From) ->
+ {Res, NewReqs} = do_purge(Mod, Reqs),
+ From ! {reply, purge, Res, Ref},
+ check_requests(NewReqs);
+handle_request({soft_purge, Mod, From, Ref}, Reqs) when is_atom(Mod), is_pid(From) ->
+ {Res, NewReqs} = do_soft_purge(Mod, Reqs),
+ From ! {reply, soft_purge, Res, Ref},
+ check_requests(NewReqs);
+handle_request({finish_after_on_load, {Mod,Keep}, From, Ref}, Reqs)
+ when is_atom(Mod), is_boolean(Keep), is_pid(From) ->
+ NewReqs = do_finish_after_on_load(Mod, Keep, Reqs),
+ From ! {reply, finish_after_on_load, ok, Ref},
+ check_requests(NewReqs);
+handle_request({test_purge, Mod, From, Type, Ref}, Reqs) when is_atom(Mod), is_pid(From) ->
+ NewReqs = do_test_purge(Mod, From, Type, Ref, Reqs),
+ check_requests(NewReqs);
+handle_request(_Garbage, Reqs) ->
+ check_requests(Reqs).
+
+check_requests([]) ->
+ wait_for_request();
+check_requests([R|Rs]) ->
+ handle_request(R, Rs).
+
+%%
+%% Processes that tries to call a fun that belongs to
+%% a module that currently is being purged will end
+%% up here (pending_purge_lambda) in a suspended state.
+%% When the purge operation completes or aborts (soft
+%% purge that failed) these processes will be resumed.
+%%
+pending_purge_lambda(_Module, Fun, Args) ->
+ %%
+ %% When the process is resumed, the following
+ %% scenarios exist:
+ %% * The code that the fun refers to is still
+ %% there due to a failed soft purge. The
+ %% call to the fun will succeed via apply/2.
+ %% * The code was purged, and a current version
+ %% of the module is loaded which does not
+ %% contain this fun. The call will result
+ %% in an exception being raised.
+ %% * The code was purged, and no current
+ %% version of the module is loaded. An attempt
+ %% to load the module (via the error_handler)
+ %% will be made. This may or may not succeed.
+ %% If the module is loaded, it may or may
+ %% not contain the fun. The call will
+ %% succeed if the error_handler was able
+ %% to load the module and loaded module
+ %% contains this fun; otherwise, an exception
+ %% will be raised.
+ %%
+ apply(Fun, Args).
+
+%% purge(Module)
+%% Kill all processes running code from *old* Module, and then purge the
+%% module. Return {WasOld, DidKill}:
+%% {false, false} there was no old module to purge
+%% {true, false} module purged, no process killed
+%% {true, true} module purged, at least one process killed
+
+purge(Mod) when is_atom(Mod) ->
+ Ref = make_ref(),
+ erts_code_purger ! {purge, Mod, self(), Ref},
+ receive
+ {reply, purge, Result, Ref} ->
+ Result
+ end.
+
+do_purge(Mod, Reqs) ->
+ case erts_internal:purge_module(Mod, prepare) of
+ false ->
+ {{false, false}, Reqs};
+ true ->
+ {DidKill, NewReqs} = check_proc_code(erlang:processes(),
+ Mod, true, Reqs),
+ true = erts_internal:purge_module(Mod, complete),
+ {{true, DidKill}, NewReqs}
+ end.
+
+%% 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
+%% case old code is not purged)
+
+soft_purge(Mod) ->
+ Ref = make_ref(),
+ erts_code_purger ! {soft_purge, Mod, self(), Ref},
+ receive
+ {reply, soft_purge, Result, Ref} ->
+ Result
+ end.
+
+do_soft_purge(Mod, Reqs) ->
+ case erts_internal:purge_module(Mod, prepare) of
+ false ->
+ {true, Reqs};
+ true ->
+ {PurgeOp, NewReqs} = check_proc_code(erlang:processes(),
+ Mod, false, Reqs),
+ {erts_internal:purge_module(Mod, PurgeOp), NewReqs}
+ end.
+
+%% finish_after_on_load(Module, Keep)
+%% Finish after running on_load function. If Keep is false,
+%% purge the code for the on_load function.
+
+finish_after_on_load(Mod, Keep) ->
+ Ref = make_ref(),
+ erts_code_purger ! {finish_after_on_load, {Mod,Keep}, self(), Ref},
+ receive
+ {reply, finish_after_on_load, Result, Ref} ->
+ Result
+ end.
+
+do_finish_after_on_load(Mod, Keep, Reqs) ->
+ erlang:finish_after_on_load(Mod, Keep),
+ case Keep of
+ true ->
+ Reqs;
+ false ->
+ case erts_internal:purge_module(Mod, prepare_on_load) of
+ false ->
+ Reqs;
+ true ->
+ {_DidKill, NewReqs} =
+ check_proc_code(erlang:processes(),
+ Mod, true, Reqs),
+ true = erts_internal:purge_module(Mod, complete),
+ NewReqs
+ end
+ end.
+
+
+%%
+%% check_proc_code(Pids, Mod, Hard, Preqs) - Send asynchronous
+%% requests to all processes to perform a check_process_code
+%% operation. Each process will check their own state and
+%% reply with the result. If 'Hard' equals
+%% - true, processes that refer 'Mod' will be killed. If
+%% any processes were killed true is returned; otherwise,
+%% false.
+%% - false, and any processes refer 'Mod', 'abort' will
+%% be returned; otherwise, 'complete'.
+%%
+%% We only allow ?MAX_CPC_NO_OUTSTANDING_KILLS
+%% outstanding kills. This both in order to avoid flooding
+%% our message queue with 'DOWN' messages and limiting the
+%% amount of memory used to keep references to all
+%% outstanding kills.
+%%
+
+-define(MAX_CPC_NO_OUTSTANDING_KILLS, 10).
+
+-record(cpc_static, {hard, module, tag, purge_requests}).
+
+-record(cpc_kill, {outstanding = [],
+ no_outstanding = 0,
+ waiting = [],
+ killed = false}).
+
+check_proc_code(Pids, Mod, Hard, PReqs) ->
+ Tag = erlang:make_ref(),
+ CpcS = #cpc_static{hard = Hard,
+ module = Mod,
+ tag = Tag,
+ purge_requests = PReqs},
+ cpc_receive(CpcS, cpc_init(CpcS, Pids, 0), #cpc_kill{}, []).
+
+cpc_receive(#cpc_static{hard = true} = CpcS,
+ 0,
+ #cpc_kill{outstanding = [], waiting = [], killed = Killed},
+ PReqs) ->
+ %% No outstanding cpc requests. We did a hard check, so result is
+ %% whether or not we killed any processes...
+ cpc_result(CpcS, PReqs, Killed);
+cpc_receive(#cpc_static{hard = false} = CpcS, 0, _KillState, PReqs) ->
+ %% No outstanding cpc requests and we did a soft check that succeeded...
+ cpc_result(CpcS, PReqs, complete);
+cpc_receive(#cpc_static{tag = Tag} = CpcS, NoReq, KillState0, PReqs) ->
+ receive
+ {check_process_code, {Tag, _Pid}, false} ->
+ %% Process not referring the module; done with this process...
+ cpc_receive(CpcS, NoReq-1, KillState0, PReqs);
+ {check_process_code, {Tag, Pid}, true} ->
+ %% Process referring the module...
+ case CpcS#cpc_static.hard of
+ false ->
+ %% ... and soft check. The whole operation failed so
+ %% no point continuing; fail straight away. Garbage
+ %% messages from this session will be ignored
+ %% by following sessions...
+ cpc_result(CpcS, PReqs, abort);
+ true ->
+ %% ... and hard check; schedule kill of it...
+ KillState1 = cpc_sched_kill(Pid, KillState0),
+ cpc_receive(CpcS, NoReq-1, KillState1, PReqs)
+ end;
+ {'DOWN', MonRef, process, _, _} ->
+ KillState1 = cpc_handle_down(MonRef, KillState0),
+ cpc_receive(CpcS, NoReq, KillState1, PReqs);
+ PReq when element(1, PReq) == purge;
+ element(1, PReq) == soft_purge;
+ element(1, PReq) == test_purge ->
+ %% A new purge request; save it until later...
+ cpc_receive(CpcS, NoReq, KillState0, [PReq | PReqs]);
+ _Garbage ->
+ %% Garbage message; ignore it...
+ cpc_receive(CpcS, NoReq, KillState0, PReqs)
+ end.
+
+cpc_result(#cpc_static{purge_requests = PReqs}, NewPReqs, Res) ->
+ {Res, PReqs ++ cpc_reverse(NewPReqs)}.
+
+cpc_reverse([_] = L) -> L;
+cpc_reverse(Xs) -> cpc_reverse(Xs, []).
+
+cpc_reverse([], Ys) -> Ys;
+cpc_reverse([X|Xs], Ys) -> cpc_reverse(Xs, [X|Ys]).
+
+cpc_handle_down(R, #cpc_kill{outstanding = Rs,
+ no_outstanding = N} = KillState0) ->
+ try
+ NewOutst = cpc_list_rm(R, Rs),
+ KillState1 = KillState0#cpc_kill{outstanding = NewOutst,
+ no_outstanding = N-1},
+ cpc_sched_kill_waiting(KillState1)
+ catch
+ throw : undefined -> %% Triggered by garbage message...
+ KillState0
+ end.
+
+cpc_list_rm(_R, []) ->
+ throw(undefined);
+cpc_list_rm(R, [R|Rs]) ->
+ Rs;
+cpc_list_rm(R0, [R1|Rs]) ->
+ [R1|cpc_list_rm(R0, Rs)].
+
+cpc_sched_kill_waiting(#cpc_kill{waiting = []} = KillState) ->
+ KillState;
+cpc_sched_kill_waiting(#cpc_kill{outstanding = Rs,
+ no_outstanding = N,
+ waiting = [P|Ps]} = KillState) ->
+ R = erlang:monitor(process, P),
+ exit(P, kill),
+ KillState#cpc_kill{outstanding = [R|Rs],
+ no_outstanding = N+1,
+ waiting = Ps,
+ killed = true}.
+
+cpc_sched_kill(Pid, #cpc_kill{no_outstanding = N, waiting = Pids} = KillState)
+ when N >= ?MAX_CPC_NO_OUTSTANDING_KILLS ->
+ KillState#cpc_kill{waiting = [Pid|Pids]};
+cpc_sched_kill(Pid,
+ #cpc_kill{outstanding = Rs, no_outstanding = N} = KillState) ->
+ R = erlang:monitor(process, Pid),
+ exit(Pid, kill),
+ KillState#cpc_kill{outstanding = [R|Rs],
+ no_outstanding = N+1,
+ killed = true}.
+
+cpc_request(#cpc_static{tag = Tag, module = Mod}, Pid) ->
+ erts_internal:check_process_code(Pid, Mod, [{async, {Tag, Pid}}]).
+
+cpc_init(_CpcS, [], NoReqs) ->
+ NoReqs;
+cpc_init(CpcS, [Pid|Pids], NoReqs) ->
+ cpc_request(CpcS, Pid),
+ cpc_init(CpcS, Pids, NoReqs+1).
+
+% end of check_proc_code() implementation.
+
+%%
+%% FOR TESTING ONLY
+%%
+%% do_test_purge() is for testing only. The purge is done
+%% as usual, but the tester can control when to enter the
+%% specific phases.
+%%
+do_test_purge(Mod, From, true, Ref, Reqs) ->
+ {Res, NewReqs} = do_test_hard_purge(Mod, From, Ref, Reqs),
+ From ! {test_purge, Res, Ref},
+ NewReqs;
+do_test_purge(Mod, From, false, Ref, Reqs) ->
+ {Res, NewReqs} = do_test_soft_purge(Mod, From, Ref, Reqs),
+ From ! {test_purge, Res, Ref},
+ NewReqs;
+do_test_purge(_, _, _, _, Reqs) ->
+ Reqs.
+
+do_test_soft_purge(Mod, From, Ref, Reqs) ->
+ PrepRes = erts_internal:purge_module(Mod, prepare),
+ TestRes = test_progress(started, From, Ref, ok),
+ case PrepRes of
+ false ->
+ _ = test_progress(continued, From, Ref, TestRes),
+ {true, Reqs};
+ true ->
+ {PurgeOp, NewReqs} = check_proc_code(erlang:processes(),
+ Mod, false, Reqs),
+ _ = test_progress(continued, From, Ref, TestRes),
+ {erts_internal:purge_module(Mod, PurgeOp), NewReqs}
+ end.
+
+do_test_hard_purge(Mod, From, Ref, Reqs) ->
+ PrepRes = erts_internal:purge_module(Mod, prepare),
+ TestRes = test_progress(started, From, Ref, ok),
+ case PrepRes of
+ false ->
+ _ = test_progress(continued, From, Ref, TestRes),
+ {{false, false}, Reqs};
+ true ->
+ {DidKill, NewReqs} = check_proc_code(erlang:processes(),
+ Mod, true, Reqs),
+ _ = test_progress(continued, From, Ref, TestRes),
+ true = erts_internal:purge_module(Mod, complete),
+ {{true, DidKill}, NewReqs}
+ end.
+
+test_progress(_State, _From, _Ref, died) ->
+ %% Test process died; continue so we wont
+ %% leave the system in an inconsistent
+ %% state...
+ died;
+test_progress(started, From, Ref, ok) ->
+ From ! {started, Ref},
+ Mon = erlang:monitor(process, From),
+ receive
+ {'DOWN', Mon, process, From, _} -> died;
+ {continue, Ref} -> erlang:demonitor(Mon, [flush]), ok
+ end;
+test_progress(continued, From, Ref, ok) ->
+ From ! {continued, Ref},
+ Mon = erlang:monitor(process, From),
+ receive
+ {'DOWN', Mon, process, From, _} -> died;
+ {complete, Ref} -> erlang:demonitor(Mon, [flush]), ok
+ end.
+
diff --git a/erts/preloaded/src/erts_dirty_process_signal_handler.erl b/erts/preloaded/src/erts_dirty_process_signal_handler.erl
new file mode 100644
index 0000000000..381f81ef14
--- /dev/null
+++ b/erts/preloaded/src/erts_dirty_process_signal_handler.erl
@@ -0,0 +1,103 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018. 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_dirty_process_signal_handler).
+
+-export([start/0]).
+
+%%
+%% The erts_dirty_process_signal_handler 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().
+
+msg_loop() ->
+ _ = receive
+ Request ->
+ try
+ handle_request(Request)
+ catch
+ _ : _ ->
+ %% Ignore all failures;
+ %% someone passed us garbage...
+ ok
+ end
+ end,
+ msg_loop().
+
+handle_request(Pid) when is_pid(Pid) ->
+ handle_incoming_signals(Pid, 0);
+handle_request({Requester, Target, Prio,
+ {SysTaskOp, ReqId, Arg} = Op} = Request) ->
+ case handle_sys_task(Requester, Target, SysTaskOp, ReqId, Arg, 0) of
+ done ->
+ ok;
+ busy ->
+ self() ! Request;
+ normal ->
+ %% Target has stopped executing dirty since the
+ %% initial request was made. Dispatch the
+ %% request to target and let it handle it itself...
+ case erts_internal:request_system_task(Requester,
+ Target,
+ Prio,
+ Op) of
+ ok ->
+ ok;
+ dirty_execution ->
+ %% Ahh... It began executing dirty again...
+ handle_request(Request)
+ end
+ end;
+handle_request(_Garbage) ->
+ ignore.
+
+%%
+%% ----------------------------------------------------------------------------
+%%
+
+handle_incoming_signals(Pid, 5) ->
+ self() ! Pid; %% Work with other requests for a while...
+handle_incoming_signals(Pid, N) ->
+ case erts_internal:dirty_process_handle_signals(Pid) of
+ more -> handle_incoming_signals(Pid, N+1);
+ _Res -> ok
+ end.
+
+handle_sys_task(Requester, Target, check_process_code, ReqId, Module, N) ->
+ case erts_internal:check_dirty_process_code(Target, Module) of
+ Bool when Bool == true; Bool == false ->
+ Requester ! {check_process_code, ReqId, Bool},
+ done;
+ busy ->
+ case N > 5 of
+ true ->
+ busy;
+ false ->
+ handle_sys_task(Requester, Target, check_process_code,
+ ReqId, Module, N+1)
+ end;
+ Res ->
+ Res
+ end.
diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl
index 2c5bd82cf0..88f47e917b 100644
--- a/erts/preloaded/src/erts_internal.erl
+++ b/erts/preloaded/src/erts_internal.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2018. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -30,13 +31,64 @@
-export([await_port_send_result/3]).
-export([cmp_term/2]).
--export([map_to_tuple_keys/1]).
--export([port_command/3, port_connect/2, port_close/1,
+-export([map_to_tuple_keys/1, term_type/1, map_hashmap_children/1,
+ map_next/3]).
+-export([open_port/2, port_command/3, port_connect/2, port_close/1,
port_control/3, port_call/3, port_info/1, port_info/2]).
--export([request_system_task/3]).
+-export([system_check/1,
+ gather_system_check_result/1]).
+
+-export([request_system_task/3, request_system_task/4]).
+-export([garbage_collect/1]).
+
+-export([check_process_code/3]).
+-export([check_dirty_process_code/2]).
+-export([is_process_executing_dirty/1]).
+-export([dirty_process_handle_signals/1]).
+
+-export([release_literal_area_switch/0]).
+-export([purge_module/2]).
+
+-export([flush_monitor_messages/3]).
+
+-export([await_result/1, gather_io_bytes/2]).
+
+-export([time_unit/0, perf_counter_unit/0]).
+
+-export([is_system_process/1]).
+
+-export([await_microstate_accounting_modifications/3,
+ gather_microstate_accounting_result/2]).
+
+-export([trace/3, trace_pattern/3]).
+
+-export([dist_ctrl_put_data/2]).
--export([check_process_code/2]).
+-export([get_dflags/0]).
+-export([new_connection/1]).
+-export([abort_connection/2]).
+
+-export([scheduler_wall_time/1, system_flag_scheduler_wall_time/1,
+ gather_sched_wall_time_result/1,
+ await_sched_wall_time_modifications/2]).
+
+-export([group_leader/2, group_leader/3]).
+
+%% Auto import name clash
+-export([check_process_code/1]).
+
+-export([is_process_alive/1, is_process_alive/2]).
+
+-export([gather_alloc_histograms/1, gather_carrier_info/1]).
+
+-export([suspend_process/2]).
+
+-export([process_display/2]).
+
+-export([process_flag/3]).
+
+-export([create_dist_channel/4]).
%%
%% Await result of send to port
@@ -49,9 +101,43 @@ await_port_send_result(Ref, Busy, Ok) ->
end.
%%
+%% Await result...
+%%
+
+await_result(Ref) when is_reference(Ref) ->
+ receive
+ {Ref, Result} ->
+ Result
+ end.
+
+%%
+%% statistics(io) end up in gather_io_bytes/2
+%%
+
+gather_io_bytes(Ref, No) when is_reference(Ref),
+ is_integer(No),
+ No > 0 ->
+ gather_io_bytes(Ref, No, 0, 0).
+
+gather_io_bytes(_Ref, 0, InAcc, OutAcc) ->
+ {{input, InAcc}, {output, OutAcc}};
+gather_io_bytes(Ref, No, InAcc, OutAcc) ->
+ receive
+ {Ref, _SchedId, In, Out} ->
+ gather_io_bytes(Ref, No-1, InAcc + In, OutAcc + Out)
+ end.
+
+%%
%% Statically linked port NIFs
%%
+-spec erts_internal:open_port(PortName, PortSettings) -> Result when
+ PortName :: tuple(),
+ PortSettings :: term(),
+ Result :: port() | reference() | atom().
+open_port(_PortName, _PortSettings) ->
+ erlang:nif_error(undefined).
+
-spec erts_internal:port_command(Port, Data, OptionList) -> Result when
Port :: port() | atom(),
Data :: iodata(),
@@ -147,20 +233,131 @@ port_info(_Result, _Item) ->
-spec request_system_task(Pid, Prio, Request) -> 'ok' when
Prio :: 'max' | 'high' | 'normal' | 'low',
- Request :: {'garbage_collect', term()}
- | {'check_process_code', term(), module(), boolean()},
+ Type :: 'major' | 'minor',
+ Request :: {'garbage_collect', term(), Type}
+ | {'check_process_code', term(), module()}
+ | {'copy_literals', term(), boolean()},
Pid :: pid().
request_system_task(_Pid, _Prio, _Request) ->
erlang:nif_error(undefined).
--spec check_process_code(Module, OptionList) -> boolean() when
+-spec request_system_task(RequesterPid, TargetPid, Prio, Request) -> 'ok' | 'dirty_execution' when
+ Prio :: 'max' | 'high' | 'normal' | 'low',
+ Request :: {'garbage_collect', term()}
+ | {'check_process_code', term(), module()}
+ | {'copy_literals', term(), boolean()},
+ RequesterPid :: pid(),
+ TargetPid :: pid().
+
+request_system_task(_RequesterPid, _TargetPid, _Prio, _Request) ->
+ erlang:nif_error(undefined).
+
+-spec garbage_collect(Mode) -> 'true' when Mode :: 'major' | 'minor'.
+
+garbage_collect(_Mode) ->
+ erlang:nif_error(undefined).
+
+-spec check_process_code(Module) -> boolean() when
+ Module :: module().
+check_process_code(_Module) ->
+ erlang:nif_error(undefined).
+
+-spec check_process_code(Pid, Module, OptionList) -> CheckResult | async when
+ Pid :: pid(),
Module :: module(),
- Option :: {allow_gc, boolean()},
- OptionList :: [Option].
-check_process_code(_Module, _OptionList) ->
+ RequestId :: term(),
+ Option :: {async, RequestId} | {allow_gc, boolean()},
+ OptionList :: [Option],
+ CheckResult :: boolean() | aborted.
+check_process_code(Pid, Module, OptionList) ->
+ Async = get_cpc_opts(OptionList, sync),
+ case Async of
+ {async, ReqId} ->
+ {priority, Prio} = erlang:process_info(erlang:self(),
+ priority),
+ erts_internal:request_system_task(Pid,
+ Prio,
+ {check_process_code,
+ ReqId,
+ Module}),
+ async;
+ sync ->
+ case Pid == erlang:self() of
+ true ->
+ erts_internal:check_process_code(Module);
+ false ->
+ {priority, Prio} = erlang:process_info(erlang:self(),
+ priority),
+ ReqId = erlang:make_ref(),
+ erts_internal:request_system_task(Pid,
+ Prio,
+ {check_process_code,
+ ReqId,
+ Module}),
+ receive
+ {check_process_code, ReqId, CheckResult} ->
+ CheckResult
+ end
+ end
+ end.
+
+% gets async opt and verify valid option list
+get_cpc_opts([{async, _ReqId} = AsyncTuple | Options], _OldAsync) ->
+ get_cpc_opts(Options, AsyncTuple);
+get_cpc_opts([{allow_gc, AllowGC} | Options], Async) when AllowGC == true;
+ AllowGC == false ->
+ get_cpc_opts(Options, Async);
+get_cpc_opts([], Async) ->
+ Async.
+
+-spec check_dirty_process_code(Pid, Module) -> Result when
+ Result :: boolean() | 'normal' | 'busy',
+ Pid :: pid(),
+ Module :: module().
+check_dirty_process_code(_Pid,_Module) ->
+ erlang:nif_error(undefined).
+
+-spec is_process_executing_dirty(Pid) -> 'true' | 'false' when
+ Pid :: pid().
+is_process_executing_dirty(_Pid) ->
+ erlang:nif_error(undefined).
+
+-spec dirty_process_handle_signals(Pid) -> Res when
+ Pid :: pid(),
+ Res :: 'false' | 'true' | 'noproc' | 'normal' | 'more' | 'ok'.
+
+dirty_process_handle_signals(_Pid) ->
+ erlang:nif_error(undefined).
+
+-spec release_literal_area_switch() -> 'true' | 'false'.
+
+release_literal_area_switch() ->
erlang:nif_error(undefined).
+-spec purge_module(Module, Op) -> boolean() when
+ Module :: module(),
+ Op :: 'prepare' | 'prepare_on_load' | 'abort' | 'complete'.
+purge_module(_Module, _Op) ->
+ erlang:nif_error(undefined).
+
+-spec system_check(Type) -> 'ok' when
+ Type :: 'schedulers'.
+
+system_check(_Type) ->
+ erlang:nif_error(undefined).
+
+gather_system_check_result(Ref) when is_reference(Ref) ->
+ gather_system_check_result(Ref, erlang:system_info(schedulers)).
+
+gather_system_check_result(_Ref, 0) ->
+ ok;
+gather_system_check_result(Ref, N) ->
+ receive
+ Ref ->
+ gather_system_check_result(Ref, N - 1)
+ end.
+
%% term compare where integer() < float() = true
-spec cmp_term(A,B) -> Result when
@@ -178,3 +375,319 @@ cmp_term(_A,_B) ->
map_to_tuple_keys(_M) ->
erlang:nif_error(undefined).
+
+%% return the internal term type
+-spec term_type(T) -> Type when
+ T :: term(),
+ Type :: 'flatmap' | 'hashmap' | 'hashmap_node'
+ | 'fixnum' | 'bignum' | 'hfloat'
+ | 'list' | 'tuple' | 'export' | 'fun'
+ | 'refc_binary' | 'heap_binary' | 'sub_binary'
+ | 'reference' | 'external_reference'
+ | 'pid' | 'external_pid' | 'port' | 'external_port'
+ | 'atom' | 'catch' | 'nil'.
+
+term_type(_T) ->
+ erlang:nif_error(undefined).
+
+%% return the internal hashmap sub-nodes from
+%% a hashmap node
+-spec map_hashmap_children(M) -> Children when
+ M :: map(), %% hashmap node
+ Children :: [map() | nonempty_improper_list(term(),term())].
+
+map_hashmap_children(_M) ->
+ erlang:nif_error(undefined).
+
+%% return the next assoc in the iterator and a new iterator
+-spec map_next(I, M, A) -> {K,V,NI} | list() when
+ I :: non_neg_integer(),
+ M :: map(),
+ K :: term(),
+ V :: term(),
+ A :: iterator | list(),
+ NI :: maps:iterator().
+
+map_next(_I, _M, _A) ->
+ erlang:nif_error(undefined).
+
+-spec erts_internal:flush_monitor_messages(Ref, Multi, Res) -> term() when
+ Ref :: reference(),
+ Multi :: boolean(),
+ Res :: term().
+
+%% erlang:demonitor(Ref, [flush]) traps to
+%% erts_internal:flush_monitor_messages(Ref, Res) when
+%% it needs to flush monitor messages.
+flush_monitor_messages(Ref, Multi, Res) when is_reference(Ref) ->
+ receive
+ {_, Ref, _, _, _} ->
+ case Multi of
+ false ->
+ Res;
+ _ ->
+ flush_monitor_messages(Ref, Multi, Res)
+ end
+ after 0 ->
+ Res
+ end.
+
+-spec erts_internal:time_unit() -> pos_integer().
+
+time_unit() ->
+ erlang:nif_error(undefined).
+
+-spec erts_internal:perf_counter_unit() -> pos_integer().
+
+perf_counter_unit() ->
+ erlang:nif_error(undefined).
+
+-spec erts_internal:is_system_process(Pid) -> boolean() when
+ Pid :: pid().
+
+is_system_process(_Pid) ->
+ erlang:nif_error(undefined).
+
+-spec await_microstate_accounting_modifications(Ref, Result, Threads) -> boolean() when
+ Ref :: reference(),
+ Result :: boolean(),
+ Threads :: pos_integer().
+
+await_microstate_accounting_modifications(Ref, Result, Threads) ->
+ _ = microstate_accounting(Ref,Threads),
+ Result.
+
+-spec gather_microstate_accounting_result(Ref, Threads) -> [#{}] when
+ Ref :: reference(),
+ Threads :: pos_integer().
+
+gather_microstate_accounting_result(Ref, Threads) ->
+ microstate_accounting(Ref, Threads).
+
+microstate_accounting(_Ref, 0) ->
+ [];
+microstate_accounting(Ref, Threads) ->
+ receive
+ Ref -> microstate_accounting(Ref, Threads - 1);
+ {Ref, Res} ->
+ [Res | microstate_accounting(Ref, Threads - 1)]
+ end.
+
+-spec trace(PidPortSpec, How, FlagList) -> integer() when
+ PidPortSpec :: pid() | port()
+ | all | processes | ports
+ | existing | existing_processes | existing_ports
+ | new | new_processes | new_ports,
+ How :: boolean(),
+ FlagList :: [].
+trace(_PidSpec, _How, _FlagList) ->
+ erlang:nif_error(undefined).
+
+-type match_variable() :: atom(). % Approximation of '$1' | '$2' | ...
+-type trace_pattern_mfa() ::
+ {atom(),atom(),arity() | '_'} | on_load.
+-type trace_match_spec() ::
+ [{[term()] | '_' | match_variable() ,[term()],[term()]}].
+
+-spec trace_pattern(MFA, MatchSpec, FlagList) -> non_neg_integer() when
+ MFA :: trace_pattern_mfa(),
+ MatchSpec :: (MatchSpecList :: trace_match_spec())
+ | boolean()
+ | restart
+ | pause,
+ FlagList :: [ ].
+trace_pattern(_MFA, _MatchSpec, _FlagList) ->
+ erlang:nif_error(undefined).
+
+-spec dist_ctrl_put_data(DHandle, Data) -> 'ok' when
+ DHandle :: erlang:dist_handle(),
+ Data :: iolist().
+
+dist_ctrl_put_data(DHandle, IoList) ->
+ %%
+ %% Helper for erlang:dist_ctrl_put_data/2
+ %%
+ %% erlang:dist_ctrl_put_data/2 traps to
+ %% this function if second argument is
+ %% a list...
+ %%
+ try
+ Binary = erlang:iolist_to_binary(IoList),
+ %% Restart erlang:dist_ctrl_put_data/2
+ %% with the iolist converted to a binary...
+ erlang:dist_ctrl_put_data(DHandle, Binary)
+ catch
+ Class : Reason ->
+ %% Throw exception as if thrown from
+ %% erlang:dist_ctrl_put_data/2 ...
+ RootST = try erlang:error(Reason)
+ catch
+ error:Reason:ST ->
+ case ST of
+ [] -> [];
+ [_|T] -> T
+ end
+ end,
+ StackTrace = [{erlang, dist_ctrl_put_data,
+ [DHandle, IoList], []}
+ | RootST],
+ erlang:raise(Class, Reason, StackTrace)
+ end.
+
+
+-spec erts_internal:get_dflags() -> {erts_dflags, integer(), integer(),
+ integer(), integer(), integer()}.
+get_dflags() ->
+ erlang:nif_error(undefined).
+
+-spec erts_internal:new_connection(Node) -> ConnId when
+ Node :: atom(),
+ ConnId :: {integer(), erlang:dist_handle()}.
+new_connection(_Node) ->
+ erlang:nif_error(undefined).
+
+-spec erts_internal:abort_connection(Node, ConnId) -> boolean() when
+ Node :: atom(),
+ ConnId :: {integer(), erlang:dist_handle()}.
+abort_connection(_Node, _ConnId) ->
+ erlang:nif_error(undefined).
+
+%% Scheduler wall time
+
+-spec erts_internal:system_flag_scheduler_wall_time(Enable) -> boolean() when
+ Enable :: boolean().
+
+system_flag_scheduler_wall_time(Bool) ->
+ kernel_refc:scheduler_wall_time(Bool).
+
+
+-spec erts_internal:await_sched_wall_time_modifications(Ref, Result) -> boolean() when
+ Ref :: reference(),
+ Result :: boolean().
+
+-spec erts_internal:scheduler_wall_time(Enable) -> boolean() when
+ Enable :: boolean().
+
+scheduler_wall_time(_Enable) ->
+ erlang:nif_error(undefined).
+
+await_sched_wall_time_modifications(Ref, Result) ->
+ sched_wall_time(Ref, erlang:system_info(schedulers)),
+ Result.
+
+-spec erts_internal:gather_sched_wall_time_result(Ref) -> [{pos_integer(),
+ non_neg_integer(),
+ non_neg_integer()}] when
+ Ref :: reference().
+
+gather_sched_wall_time_result(Ref) when erlang:is_reference(Ref) ->
+ sched_wall_time(Ref, erlang:system_info(schedulers), []).
+
+sched_wall_time(_Ref, 0) ->
+ ok;
+sched_wall_time(Ref, N) ->
+ receive Ref -> sched_wall_time(Ref, N-1) end.
+
+sched_wall_time(_Ref, 0, Acc) ->
+ Acc;
+sched_wall_time(Ref, N, undefined) ->
+ receive {Ref, _} -> sched_wall_time(Ref, N-1, undefined) end;
+sched_wall_time(Ref, N, Acc) ->
+ receive
+ {Ref, undefined} -> sched_wall_time(Ref, N-1, undefined);
+ {Ref, SWTL} when erlang:is_list(SWTL) -> sched_wall_time(Ref, N-1, Acc ++ SWTL);
+ {Ref, SWT} -> sched_wall_time(Ref, N-1, [SWT|Acc])
+ end.
+
+-spec erts_internal:group_leader(GL, Pid) -> true | false | badarg when
+ GL :: pid(),
+ Pid :: pid().
+
+group_leader(_GL, _Pid) ->
+ erlang:nif_error(undefined).
+
+-spec erts_internal:group_leader(GL, Pid, Ref) -> ok when
+ GL :: pid(),
+ Pid :: pid(),
+ Ref :: reference().
+
+group_leader(_GL, _Pid, _Ref) ->
+ erlang:nif_error(undefined).
+
+-spec erts_internal:is_process_alive(Pid, Ref) -> 'ok' when
+ Pid :: pid(),
+ Ref :: reference().
+
+is_process_alive(_Pid, _Ref) ->
+ erlang:nif_error(undefined).
+
+-spec erts_internal:is_process_alive(Pid) -> boolean() when
+ Pid :: pid().
+
+is_process_alive(Pid) ->
+ Ref = make_ref(),
+ erts_internal:is_process_alive(Pid, Ref),
+ receive
+ {Ref, Res} ->
+ Res
+ end.
+
+-spec gather_alloc_histograms({Type, SchedId, HistWidth, HistStart, Ref}) -> MsgCount when
+ Type :: atom(),
+ SchedId :: non_neg_integer(),
+ HistWidth :: non_neg_integer(),
+ HistStart :: non_neg_integer(),
+ Ref :: reference(),
+ MsgCount :: non_neg_integer().
+
+gather_alloc_histograms(_) ->
+ erlang:nif_error(undef).
+
+-spec gather_carrier_info({Type, SchedId, HistWidth, HistStart, Ref}) -> MsgCount when
+ Type :: atom(),
+ SchedId :: non_neg_integer(),
+ HistWidth :: non_neg_integer(),
+ HistStart :: non_neg_integer(),
+ Ref :: reference(),
+ MsgCount :: non_neg_integer().
+
+gather_carrier_info(_) ->
+ erlang:nif_error(undef).
+
+-spec suspend_process(Suspendee, OptList) -> Result when
+ Result :: boolean() | 'badarg' | reference(),
+ Suspendee :: pid(),
+ OptList :: [Opt],
+ Opt :: unless_suspending | asynchronous | {asynchronous, term()}.
+
+suspend_process(_Suspendee, _OptList) ->
+ erlang:nif_error(undefined).
+
+%% process_display/2
+-spec process_display(Pid, Type) -> 'true' | 'badarg' | reference() when
+ Pid :: pid(),
+ Type :: backtrace.
+process_display(_Pid, _Type) ->
+ erlang:nif_error(undefined).
+
+%% process_flag/3
+-spec process_flag(Pid, Flag, Value) -> OldValue | 'badarg' | reference() when
+ Pid :: pid(),
+ Flag :: save_calls,
+ Value :: non_neg_integer(),
+ OldValue :: non_neg_integer().
+process_flag(_Pid, _Flag, _Value) ->
+ erlang:nif_error(undefined).
+
+-spec create_dist_channel(Node, DistCtrlr, Flags, Ver) -> Result when
+ Node :: atom(),
+ DistCtrlr :: port() | pid(),
+ Flags :: integer(),
+ Ver :: integer(),
+ Result :: {'ok', erlang:dist_handle()}
+ | {'message', reference()}
+ | 'badarg'
+ | 'system_limit'.
+
+create_dist_channel(_Node, _DistCtrlr, _Flags, _Ver) ->
+ erlang:nif_error(undefined).
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}).
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index e95e11b3e6..253fcf7a1f 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -22,7 +23,6 @@
%% a local file or distributed from another erlang node.
%%
%% Flags:
-%% -id Identity : identity of the system.
%% -boot File : Absolute file name of the boot script.
%% -boot_var Var Value
%% : $Var in the boot script is expanded to
@@ -32,8 +32,8 @@
%% (Optional - default efile)
%% -hosts [Node] : List of hosts from which we can boot.
%% (Mandatory if -loader inet)
-%% -mode embedded : Load all modules at startup, no automatic loading
-%% -mode interactive : Auto load modules (default system behaviour).
+%% -mode interactive : Auto load modules not needed at startup (default system behaviour).
+%% -mode embedded : Load all modules in the boot script, disable auto loading.
%% -path : Override path in bootfile.
%% -pa Path+ : Add my own paths first.
%% -pz Path+ : Add my own paths last.
@@ -41,6 +41,7 @@
%% -s : Start own processes.
%%
%% Experimental flags:
+%% -profile_boot : Use an 'eprof light' to profile boot sequence
%% -init_debug : Activate debug printouts in init
%% -loader_debug : Activate debug printouts in erl_prim_loader
%% -code_path_choice : strict | relaxed
@@ -74,8 +75,22 @@
subscribed = []}).
-type state() :: #state{}.
+%% Data for eval_script/2.
+-record(es,
+ {init,
+ debug,
+ path,
+ pa,
+ pz,
+ path_choice,
+ prim_load,
+ load_mode,
+ vars
+ }).
+
-define(ON_LOAD_HANDLER, init__boot__on_load_handler).
+
debug(false, _) -> ok;
debug(_, T) -> erlang:display(T).
@@ -116,7 +131,7 @@ bs2ss(L) ->
get_status() ->
request(get_status).
--spec fetch_loaded() -> [atom()].
+-spec fetch_loaded() -> [{module(),file:filename()}].
fetch_loaded() ->
request(fetch_loaded).
@@ -159,18 +174,46 @@ stop() -> init ! {stop,stop}, ok.
-spec stop(Status) -> 'ok' when
Status :: non_neg_integer() | string().
-stop(Status) -> init ! {stop,{stop,Status}}, ok.
+stop(Status) when is_integer(Status), Status >= 0 ->
+ stop_1(Status);
+stop(Status) when is_list(Status) ->
+ case is_bytelist(Status) of
+ true ->
+ stop_1(Status);
+ false ->
+ erlang:error(badarg)
+ end;
+stop(_) ->
+ erlang:error(badarg).
+
+is_bytelist([B|Bs]) when is_integer(B), B >= 0, B < 256 -> is_bytelist(Bs);
+is_bytelist([]) -> true;
+is_bytelist(_) -> false.
+
+%% Note that we check the type of Status beforehand to ensure that
+%% the call to halt(Status) by the init process cannot fail
+stop_1(Status) -> init ! {stop,{stop,Status}}, ok.
-spec boot(BootArgs) -> no_return() when
BootArgs :: [binary()].
boot(BootArgs) ->
register(init, self()),
process_flag(trap_exit, true),
- start_on_load_handler_process(),
+
+ %% Load the static nifs
+ zlib:on_load(),
+ erl_tracer:on_load(),
+ prim_buffer:on_load(),
+ prim_file:on_load(),
+
{Start0,Flags,Args} = parse_boot_args(BootArgs),
+ %% We don't get to profile parsing of BootArgs
+ case b2a(get_flag(profile_boot, Flags, false)) of
+ false -> ok;
+ true -> debug_profile_start()
+ end,
Start = map(fun prepare_run_args/1, Start0),
- Flags0 = flags_to_atoms_again(Flags),
- boot(Start,Flags0,Args).
+ boot(Start, Flags, Args).
prepare_run_args({eval, [Expr]}) ->
{eval,Expr};
@@ -202,16 +245,6 @@ map(_F, []) ->
map(F, [X|Rest]) ->
[F(X) | map(F, Rest)].
-flags_to_atoms_again([]) ->
- [];
-flags_to_atoms_again([{F0,L0}|Rest]) ->
- L = L0,
- F = b2a(F0),
- [{F,L}|flags_to_atoms_again(Rest)];
-flags_to_atoms_again([{F0}|Rest]) ->
- F = b2a(F0),
- [{F}|flags_to_atoms_again(Rest)].
-
-spec code_path_choice() -> 'relaxed' | 'strict'.
code_path_choice() ->
case get_argument(code_path_choice) of
@@ -224,6 +257,7 @@ code_path_choice() ->
end.
boot(Start,Flags,Args) ->
+ start_on_load_handler_process(),
BootPid = do_boot(Flags,Start),
State = #state{flags = Flags,
args = Args,
@@ -232,21 +266,30 @@ boot(Start,Flags,Args) ->
boot_loop(BootPid,State).
%%% Convert a term to a printable string, if possible.
-to_string(X) when is_list(X) -> % assume string
+to_string(X, D) when is_list(X), D < 4 -> % assume string
F = flatten(X, []),
case printable_list(F) of
- true -> F;
- false -> ""
+ true when length(F) > 0 -> F;
+ _false ->
+ List = [to_string(E, D+1) || E <- X],
+ flatten(["[",join(List),"]"], [])
end;
-to_string(X) when is_atom(X) ->
+to_string(X, _D) when is_list(X) ->
+ "[_]";
+to_string(X, _D) when is_atom(X) ->
atom_to_list(X);
-to_string(X) when is_pid(X) ->
+to_string(X, _D) when is_pid(X) ->
pid_to_list(X);
-to_string(X) when is_float(X) ->
+to_string(X, _D) when is_float(X) ->
float_to_list(X);
-to_string(X) when is_integer(X) ->
+to_string(X, _D) when is_integer(X) ->
integer_to_list(X);
-to_string(_X) ->
+to_string(X, D) when is_tuple(X), D < 4 ->
+ List = [to_string(E, D+1) || E <- tuple_to_list(X)],
+ flatten(["{",join(List),"}"], []);
+to_string(X, _D) when is_tuple(X) ->
+ "{_}";
+to_string(_X, _D) ->
"". % can't do anything with it
%% This is an incorrect and narrow definition of printable characters.
@@ -260,6 +303,13 @@ printable_list([$\t|T]) -> printable_list(T);
printable_list([]) -> true;
printable_list(_) -> false.
+join([] = T) ->
+ T;
+join([_Elem] = T) ->
+ T;
+join([Elem|T]) ->
+ [Elem,","|join(T)].
+
flatten([H|T], Tail) when is_list(H) ->
flatten(H, flatten(T, Tail));
flatten([H|T], Tail) ->
@@ -268,26 +318,17 @@ flatten([], Tail) ->
Tail.
things_to_string([X|Rest]) ->
- " (" ++ to_string(X) ++ ")" ++ things_to_string(Rest);
+ " (" ++ to_string(X, 0) ++ ")" ++ things_to_string(Rest);
things_to_string([]) ->
"".
halt_string(String, List) ->
- HaltString = String ++ things_to_string(List),
- if
- length(HaltString)<199 -> HaltString;
- true -> first198(HaltString, 198)
- end.
-
-first198([H|T], N) when N>0 ->
- [H|first198(T, N-1)];
-first198(_, 0) ->
- [].
+ String ++ things_to_string(List).
%% String = string()
-%% List = [string() | atom() | pid() | number()]
-%% Any other items in List, such as tuples, are ignored when creating
-%% the string used as argument to erlang:halt/1.
+%% List = [string() | atom() | pid() | number() | list() | tuple()]
+%% Items in List are truncated if found to be too large
+-spec crash(_, _) -> no_return().
crash(String, List) ->
halt(halt_string(String, List)).
@@ -295,9 +336,9 @@ crash(String, List) ->
-spec boot_loop(pid(), state()) -> no_return().
boot_loop(BootPid, State) ->
receive
- {BootPid,loaded,ModLoaded} ->
- Loaded = State#state.loaded,
- boot_loop(BootPid,State#state{loaded = [ModLoaded|Loaded]});
+ {BootPid,loaded,NewlyLoaded} ->
+ Loaded = NewlyLoaded ++ State#state.loaded,
+ boot_loop(BootPid, State#state{loaded = Loaded});
{BootPid,started,KernelPid} ->
boot_loop(BootPid, new_kernelpid(KernelPid, BootPid, State));
{BootPid,progress,started} ->
@@ -336,12 +377,25 @@ boot_loop(BootPid, State) ->
end.
ensure_loaded(Module, Loaded) ->
- File = concat([Module,objfile_extension()]),
- case catch load_mod(Module,File) of
- {ok, FullName} ->
- {{module, Module}, [{Module, FullName}|Loaded]};
- Res ->
- {Res, Loaded}
+ case erlang:module_loaded(Module) of
+ true ->
+ {{module, Module}, Loaded};
+ false ->
+ do_ensure_loaded(Module, Loaded)
+ end.
+
+do_ensure_loaded(Module, Loaded) ->
+ File = atom_to_list(Module) ++ objfile_extension(),
+ case erl_prim_loader:get_file(File) of
+ {ok,BinCode,FullName} ->
+ case do_load_module(Module, BinCode) of
+ ok ->
+ {{module, Module}, [{Module, FullName}|Loaded]};
+ error ->
+ {error, [{Module, FullName}|Loaded]}
+ end;
+ Error ->
+ {Error, Loaded}
end.
%% Tell subscribed processes the system has started.
@@ -387,9 +441,6 @@ loop(State) ->
Loaded = State#state.loaded, %% boot_loop but is handled here
From ! {init,Loaded}, %% anyway.
loop(State);
- {From, {ensure_loaded, _}} ->
- From ! {init, not_allowed},
- loop(State);
Msg ->
loop(handle_msg(Msg,State))
end.
@@ -429,10 +480,17 @@ do_handle_msg(Msg,State) ->
From ! {init,ok},
{new_state,State#state{subscribed = [Pid|Subscribed]}}
end;
+ {From, {ensure_loaded, _}} ->
+ From ! {init, not_allowed};
X ->
case whereis(user) of
undefined ->
- catch error_logger ! {info, self(), {self(), X, []}};
+ Time = erlang:monotonic_time(microsecond),
+ catch logger ! {log, info, "init got unexpected: ~p", [X],
+ #{pid=>self(),
+ gl=>self(),
+ time=>Time,
+ error_logger=>#{tag=>info_msg}}};
User ->
User ! X,
ok
@@ -450,9 +508,9 @@ do_handle_msg(Msg,State) ->
%%% -------------------------------------------------
make_permanent(Boot,Config,Flags0,State) ->
- case set_flag('-boot',Boot,Flags0) of
+ case set_flag(boot, Boot, Flags0) of
{ok,Flags1} ->
- case set_flag('-config',Config,Flags1) of
+ case set_flag(config, Config, Flags1) of
{ok,Flags} ->
{ok,State#state{flags = Flags}};
Error ->
@@ -492,6 +550,8 @@ stop(Reason,State) ->
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(),
boot(Start,Flags,Args);
do_stop(reboot,_) ->
halt();
@@ -504,8 +564,18 @@ do_stop({stop,Status},State) ->
clear_system(BootPid,State) ->
Heart = get_heart(State#state.kernel),
- shutdown_pids(Heart,BootPid,State),
- unload(Heart).
+ Logger = get_logger(State#state.kernel),
+ shutdown_pids(Heart,Logger,BootPid,State),
+ unload(Heart),
+ kill_em([Logger]),
+ do_unload([logger_server]).
+
+flush() ->
+ receive
+ _M -> flush()
+ after 0 ->
+ ok
+ end.
stop_heart(State) ->
case get_heart(State#state.kernel) of
@@ -518,19 +588,26 @@ stop_heart(State) ->
shutdown_kernel_pid(Pid, BootPid, self(), State)
end.
-shutdown_pids(Heart,BootPid,State) ->
+shutdown_pids(Heart,Logger,BootPid,State) ->
Timer = shutdown_timer(State#state.flags),
catch shutdown(State#state.kernel,BootPid,Timer,State),
- kill_all_pids(Heart), % Even the shutdown timer.
- kill_all_ports(Heart),
+ kill_all_pids(Heart,Logger), % Even the shutdown timer.
+ kill_all_ports(Heart), % Logger has no ports
flush_timout(Timer).
-get_heart([{heart,Pid}|_Kernel]) -> Pid;
-get_heart([_|Kernel]) -> get_heart(Kernel);
-get_heart(_) -> false.
+get_heart(Kernel) ->
+ get_kernelpid(heart,Kernel).
+
+get_logger(Kernel) ->
+ get_kernelpid(logger,Kernel).
+
+get_kernelpid(Name,[{Name,Pid}|_Kernel]) -> Pid;
+get_kernelpid(Name,[_|Kernel]) -> get_kernelpid(Name,Kernel);
+get_kernelpid(_,_) -> false.
-shutdown([{heart,_Pid}|Kernel],BootPid,Timer,State) ->
+shutdown([{Except,_Pid}|Kernel],BootPid,Timer,State)
+ when Except==heart; Except==logger ->
shutdown(Kernel, BootPid, Timer, State);
shutdown([{_Name,Pid}|Kernel],BootPid,Timer,State) ->
shutdown_kernel_pid(Pid, BootPid, Timer, State),
@@ -582,27 +659,25 @@ resend(_) ->
%%
%% Kill all existing pids in the system (except init and heart).
-kill_all_pids(Heart) ->
- case get_pids(Heart) of
+kill_all_pids(Heart,Logger) ->
+ case get_pids(Heart,Logger) of
[] ->
ok;
Pids ->
kill_em(Pids),
- kill_all_pids(Heart) % Continue until all are really killed.
+ kill_all_pids(Heart,Logger) % Continue until all are really killed.
end.
-%% All except zombies.
-alive_processes() ->
- [P || P <- processes(), erlang:is_process_alive(P)].
-
-get_pids(Heart) ->
- Pids = alive_processes(),
- delete(Heart,self(),Pids).
-
-delete(Heart,Init,[Heart|Pids]) -> delete(Heart,Init,Pids);
-delete(Heart,Init,[Init|Pids]) -> delete(Heart,Init,Pids);
-delete(Heart,Init,[Pid|Pids]) -> [Pid|delete(Heart,Init,Pids)];
-delete(_,_,[]) -> [].
+%% All except system processes.
+get_pids(Heart,Logger) ->
+ Pids = [P || P <- processes(), not erts_internal:is_system_process(P)],
+ delete(Heart,Logger,self(),Pids).
+
+delete(Heart,Logger,Init,[Heart|Pids]) -> delete(Heart,Logger,Init,Pids);
+delete(Heart,Logger,Init,[Logger|Pids]) -> delete(Heart,Logger,Init,Pids);
+delete(Heart,Logger,Init,[Init|Pids]) -> delete(Heart,Logger,Init,Pids);
+delete(Heart,Logger,Init,[Pid|Pids]) -> [Pid|delete(Heart,Logger,Init,Pids)];
+delete(_,_,_,[]) -> [].
kill_em([Pid|Pids]) ->
exit(Pid,kill),
@@ -632,9 +707,9 @@ kill_all_ports(_,_) ->
ok.
unload(false) ->
- do_unload(sub(erlang:pre_loaded(),erlang:loaded()));
+ do_unload(sub([logger_server|erlang:pre_loaded()],erlang:loaded()));
unload(_) ->
- do_unload(sub([heart|erlang:pre_loaded()],erlang:loaded())).
+ do_unload(sub([heart,logger_server|erlang:pre_loaded()],erlang:loaded())).
do_unload([M|Mods]) ->
catch erlang:purge_module(M),
@@ -642,16 +717,8 @@ do_unload([M|Mods]) ->
catch erlang:purge_module(M),
do_unload(Mods);
do_unload([]) ->
- purge_all_hipe_refs(),
ok.
-purge_all_hipe_refs() ->
- case erlang:system_info(hipe_architecture) of
- undefined -> ok;
- _ -> hipe_bifs:remove_refs_from(all)
- end.
-
-
sub([H|T],L) -> sub(T,del(H,L));
sub([],L) -> L.
@@ -693,17 +760,15 @@ sleep(T) -> receive after T -> ok end.
%%% The loader shall run for ever!
%%% -------------------------------------------------
-start_prim_loader(Init,Id,Pgm,Nodes,Path,{Pa,Pz}) ->
- case erl_prim_loader:start(Id,Pgm,Nodes) of
- {ok,Pid} when Path =:= false ->
- InitPath = append(Pa,["."|Pz]),
- erl_prim_loader:set_path(InitPath),
- add_to_kernel(Init,Pid),
- Pid;
+start_prim_loader(Init, Path0, {Pa,Pz}) ->
+ Path = case Path0 of
+ false -> Pa ++ ["."|Pz];
+ _ -> Path0
+ end,
+ case erl_prim_loader:start() of
{ok,Pid} ->
erl_prim_loader:set_path(Path),
- add_to_kernel(Init,Pid),
- Pid;
+ add_to_kernel(Init, Pid);
{error,Reason} ->
erlang:display({"cannot start loader",Reason}),
exit(Reason)
@@ -717,13 +782,6 @@ add_to_kernel(Init,Pid) ->
ok
end.
-prim_load_flags(Flags) ->
- PortPgm = get_flag('-loader',Flags,<<"efile">>),
- Hosts = get_flag_list('-hosts', Flags, []),
- Id = get_flag('-id',Flags,none),
- Path = get_flag_list('-path',Flags,false),
- {PortPgm, Hosts, Id, Path}.
-
%%% -------------------------------------------------
%%% The boot process fetches a boot script and loads
%%% all modules specified and starts spec. processes.
@@ -736,46 +794,74 @@ do_boot(Flags,Start) ->
do_boot(Init,Flags,Start) ->
process_flag(trap_exit,true),
- {Pgm0,Nodes,Id,Path} = prim_load_flags(Flags),
- Root = b2s(get_flag('-root',Flags)),
- PathFls = path_flags(Flags),
- Pgm = b2s(Pgm0),
- _Pid = start_prim_loader(Init,b2a(Id),Pgm,bs2as(Nodes),
- bs2ss(Path),PathFls),
+ Root = get_root(Flags),
+ Path = get_flag_list(path, Flags, false),
+ {Pa,Pz} = PathFls = path_flags(Flags),
+ start_prim_loader(Init, bs2ss(Path), PathFls),
BootFile = bootfile(Flags,Root),
BootList = get_boot(BootFile,Root),
- LoadMode = b2a(get_flag('-mode',Flags,false)),
- Deb = b2a(get_flag('-init_debug',Flags,false)),
+ LoadMode = b2a(get_flag(mode, Flags, false)),
+ Deb = b2a(get_flag(init_debug, Flags, false)),
catch ?ON_LOAD_HANDLER ! {init_debug_flag,Deb},
- BootVars = get_flag_args('-boot_var',Flags),
- ParallelLoad =
- (Pgm =:= "efile") and (erlang:system_info(thread_pool_size) > 0),
+ BootVars = get_boot_vars(Root, Flags),
PathChoice = code_path_choice(),
- eval_script(BootList,Init,PathFls,{Root,BootVars},Path,
- {true,LoadMode,ParallelLoad},Deb,PathChoice),
+ Es = #es{init=Init,debug=Deb,path=Path,pa=Pa,pz=Pz,
+ path_choice=PathChoice,
+ prim_load=true,load_mode=LoadMode,
+ vars=BootVars},
+ eval_script(BootList, Es),
%% To help identifying Purify windows that pop up,
%% print the node name into the Purify log.
(catch erlang:system_info({purify, "Node: " ++ atom_to_list(node())})),
- start_em(Start).
+ start_em(Start),
+ case b2a(get_flag(profile_boot,Flags,false)) of
+ false -> ok;
+ true ->
+ debug_profile_format_mfas(debug_profile_mfas()),
+ debug_profile_stop()
+ end,
+ ok.
+
+get_root(Flags) ->
+ case get_argument(root, Flags) of
+ {ok,[[Root]]} ->
+ Root;
+ _ ->
+ exit(no_or_multiple_root_variables)
+ end.
+
+get_boot_vars(Root, Flags) ->
+ BootVars = get_boot_vars_1(#{}, Flags),
+ RootKey = <<"ROOT">>,
+ BootVars#{RootKey=>Root}.
+
+get_boot_vars_1(Vars, [{boot_var,[Key,Value]}|T]) ->
+ get_boot_vars_1(Vars#{Key=>Value}, T);
+get_boot_vars_1(_, [{boot_var,_}|_]) ->
+ exit(invalid_boot_var_argument);
+get_boot_vars_1(Vars, [_|T]) ->
+ get_boot_vars_1(Vars, T);
+get_boot_vars_1(Vars, []) ->
+ Vars.
bootfile(Flags,Root) ->
- b2s(get_flag('-boot',Flags,concat([Root,"/bin/start"]))).
+ b2s(get_flag(boot, Flags, Root++"/bin/start")).
path_flags(Flags) ->
- Pa = append(reverse(get_flag_args('-pa',Flags))),
- Pz = append(get_flag_args('-pz',Flags)),
+ Pa = append(reverse(get_flag_args(pa, Flags))),
+ Pz = append(get_flag_args(pz, Flags)),
{bs2ss(Pa),bs2ss(Pz)}.
get_boot(BootFile0,Root) ->
- BootFile = concat([BootFile0,".boot"]),
+ BootFile = BootFile0 ++ ".boot",
case get_boot(BootFile) of
{ok, CmdList} ->
CmdList;
not_found -> %% Check for default.
- BootF = concat([Root,"/bin/",BootFile]),
+ BootF = Root ++ "/bin/" ++ BootFile,
case get_boot(BootF) of
{ok, CmdList} ->
CmdList;
@@ -809,91 +895,88 @@ get_boot(BootFile) ->
%% boot process hangs (we want to ensure syncronicity).
%%
-eval_script([{progress,Info}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice) ->
- debug(Deb,{progress,Info}),
+eval_script([{progress,Info}=Progress|T], #es{debug=Deb}=Es) ->
+ debug(Deb, Progress),
init ! {self(),progress,Info},
- eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);
-eval_script([{preLoaded,_}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice) ->
- eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);
-eval_script([{path,Path}|CfgL],Init,{Pa,Pz},Vars,false,Ph,Deb,PathChoice) ->
+ eval_script(T, Es);
+eval_script([{preLoaded,_}|T], #es{}=Es) ->
+ eval_script(T, Es);
+eval_script([{path,Path}|T], #es{path=false,pa=Pa,pz=Pz,
+ path_choice=PathChoice,
+ vars=Vars}=Es) ->
RealPath0 = make_path(Pa, Pz, Path, Vars),
RealPath = patch_path(RealPath0, PathChoice),
erl_prim_loader:set_path(RealPath),
- eval_script(CfgL,Init,{Pa,Pz},Vars,false,Ph,Deb,PathChoice);
-eval_script([{path,_}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice) ->
+ eval_script(T, Es);
+eval_script([{path,_}|T], #es{}=Es) ->
%% Ignore, use the command line -path flag.
- eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);
-eval_script([{kernel_load_completed}|CfgL],Init,PathFs,Vars,P,{_,embedded,Par},Deb,PathChoice) ->
- eval_script(CfgL,Init,PathFs,Vars,P,{true,embedded,Par},Deb,PathChoice);
-eval_script([{kernel_load_completed}|CfgL],Init,PathFs,Vars,P,{_,E,Par},Deb,PathChoice) ->
- eval_script(CfgL,Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice);
-eval_script([{primLoad,Mods}|CfgL],Init,PathFs,Vars,P,{true,E,Par},Deb,PathChoice)
+ eval_script(T, Es);
+eval_script([{kernel_load_completed}|T], #es{load_mode=Mode}=Es0) ->
+ Es = case Mode of
+ embedded -> Es0;
+ _ -> Es0#es{prim_load=false}
+ end,
+ eval_script(T, Es);
+eval_script([{primLoad,Mods}|T], #es{init=Init,prim_load=PrimLoad}=Es)
when is_list(Mods) ->
- if
- Par =:= true ->
- par_load_modules(Mods,Init);
+ case PrimLoad of
true ->
- load_modules(Mods)
+ load_modules(Mods, Init);
+ false ->
+ %% Do not load now, code_server does that dynamically!
+ ok
end,
- eval_script(CfgL,Init,PathFs,Vars,P,{true,E,Par},Deb,PathChoice);
-eval_script([{primLoad,_Mods}|CfgL],Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice) ->
- %% Do not load now, code_server does that dynamically!
- eval_script(CfgL,Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice);
-eval_script([{kernelProcess,Server,{Mod,Fun,Args}}|CfgL],Init,
- PathFs,Vars,P,Ph,Deb,PathChoice) ->
- debug(Deb,{start,Server}),
- start_in_kernel(Server,Mod,Fun,Args,Init),
- eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);
-eval_script([{apply,{Mod,Fun,Args}}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice) ->
- debug(Deb,{apply,{Mod,Fun,Args}}),
- apply(Mod,Fun,Args),
- eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);
-eval_script([],_,_,_,_,_,_,_) ->
+ eval_script(T, Es);
+eval_script([{kernelProcess,Server,{Mod,Fun,Args}}|T],
+ #es{init=Init,debug=Deb}=Es) ->
+ debug(Deb, {start,Server}),
+ start_in_kernel(Server, Mod, Fun, Args, Init),
+ eval_script(T, Es);
+eval_script([{apply,{Mod,Fun,Args}}=Apply|T], #es{debug=Deb}=Es) ->
+ debug(Deb, Apply),
+ apply(Mod, Fun, Args),
+ eval_script(T, Es);
+eval_script([], #es{}) ->
ok;
-eval_script(What,_,_,_,_,_,_,_) ->
+eval_script(What, #es{}) ->
exit({'unexpected command in bootfile',What}).
-load_modules([Mod|Mods]) ->
- File = concat([Mod,objfile_extension()]),
- {ok,Full} = load_mod(Mod,File),
- init ! {self(),loaded,{Mod,Full}}, %% Tell init about loaded module
- load_modules(Mods);
-load_modules([]) ->
+load_modules(Mods0, Init) ->
+ Mods = [M || M <- Mods0, not erlang:module_loaded(M)],
+ F = prepare_loading_fun(),
+ case erl_prim_loader:get_modules(Mods, F) of
+ {ok,{Prep0,[]}} ->
+ Prep = [Code || {_,{prepared,Code,_}} <- Prep0],
+ ok = erlang:finish_loading(Prep),
+ Loaded = [{Mod,Full} || {Mod,{_,_,Full}} <- Prep0],
+ Init ! {self(),loaded,Loaded},
+ Beams = [{M,Beam,Full} || {M,{on_load,Beam,Full}} <- Prep0],
+ load_rest(Beams, Init);
+ {ok,{_,[_|_]=Errors}} ->
+ Ms = [M || {M,_} <- Errors],
+ exit({load_failed,Ms})
+ end.
+
+load_rest([{Mod,Beam,Full}|T], Init) ->
+ do_load_module(Mod, Beam),
+ Init ! {self(),loaded,[{Mod,Full}]},
+ load_rest(T, Init);
+load_rest([], _) ->
ok.
-%%% An optimization: erl_prim_loader gets the chance of loading many
-%%% files in parallel, using threads. This will reduce the seek times,
-%%% and loaded code can be processed while other threads are waiting
-%%% for the disk. The optimization is not tried unless the loader is
-%%% "efile" and there is a non-empty pool of threads.
-%%%
-%%% Many threads are needed to get a good result, so it would be
-%%% beneficial to load several applications in parallel. However,
-%%% measurements show that the file system handles one directory at a
-%%% time, regardless if parallel threads are created for files on
-%%% several directories (a guess: writing the meta information when
-%%% the file was last read ('mtime'), forces the file system to sync
-%%% between directories).
-
-par_load_modules(Mods,Init) ->
- Ext = objfile_extension(),
- ModFiles = [{Mod,concat([Mod,Ext])} || Mod <- Mods,
- not erlang:module_loaded(Mod)],
- Self = self(),
- Fun = fun(Mod, BinCode, FullName) ->
- case catch load_mod_code(Mod, BinCode, FullName) of
- {ok, _} ->
- Init ! {Self,loaded,{Mod,FullName}},
- ok;
- _EXIT ->
- {error, Mod}
- end
- end,
- case erl_prim_loader:get_files(ModFiles, Fun) of
- ok ->
- ok;
- {error,Mod} ->
- exit({'cannot load',Mod,get_files})
+prepare_loading_fun() ->
+ fun(Mod, FullName, Beam) ->
+ case erlang:prepare_loading(Mod, Beam) of
+ {error,_}=Error ->
+ Error;
+ Prepared ->
+ case erlang:has_prepared_code_on_load(Prepared) of
+ true ->
+ {ok,{on_load,Beam,FullName}};
+ false ->
+ {ok,{prepared,Prepared,FullName}}
+ end
+ end
end.
make_path(Pa, Pz, Path, Vars) ->
@@ -910,34 +993,25 @@ fix_path([Path|Ps], Vars) ->
fix_path(_, _) ->
[].
-add_var("$ROOT/" ++ Path, {Root,_}) ->
- concat([Root, "/", Path]);
-add_var([$$|Path0], {_,VarList}) ->
- {Var,Path} = extract_var(Path0,[]),
- Value = b2s(get_var_value(list_to_binary(Var),VarList)),
- concat([Value, "/", Path]);
-add_var(Path, _) ->
+add_var("$"++Path0, Vars) ->
+ {Var,Path} = extract_var(Path0, []),
+ Key = list_to_binary(Var),
+ case Vars of
+ #{Key:=Value0} ->
+ Value = b2s(Value0),
+ Value ++ "/" ++ Path;
+ _ ->
+ Error0 = "cannot expand $" ++ Var ++ " in bootfile",
+ Error = list_to_atom(Error0),
+ exit(Error)
+ end;
+add_var(Path, _) ->
Path.
extract_var([$/|Path],Var) -> {reverse(Var),Path};
extract_var([H|T],Var) -> extract_var(T,[H|Var]);
extract_var([],Var) -> {reverse(Var),[]}.
-%% get_var_value(Var, [Vars]) where Vars == [atom()]
-get_var_value(Var,[Vars|VarList]) ->
- case get_var_val(Var,Vars) of
- {ok, Value} ->
- Value;
- _ ->
- get_var_value(Var,VarList)
- end;
-get_var_value(Var,[]) ->
- exit(list_to_atom(concat(["cannot expand \$", Var, " in bootfile"]))).
-
-get_var_val(Var,[Var,Value|_]) -> {ok, Value};
-get_var_val(Var,[_,_|Vars]) -> get_var_val(Var,Vars);
-get_var_val(_,_) -> false.
-
patch_path(Dirs, strict) ->
Dirs;
patch_path(Dirs, relaxed) ->
@@ -1045,55 +1119,29 @@ start_it({eval,Bin}) ->
{ok,Ts,_} = erl_scan:string(Str),
Ts1 = case reverse(Ts) of
[{dot,_}|_] -> Ts;
- TsR -> reverse([{dot,1} | TsR])
+ TsR -> reverse([{dot,erl_anno:new(1)} | TsR])
end,
{ok,Expr} = erl_parse:parse_exprs(Ts1),
{value, _Value, _Bs} = erl_eval:exprs(Expr, erl_eval:new_bindings()),
ok;
start_it([_|_]=MFA) ->
- Ref = make_ref(),
- case catch {Ref,case MFA of
- [M] -> M:start();
- [M,F] -> M:F();
- [M,F|Args] -> M:F(Args) % Args is a list
- end} of
- {Ref,R} ->
- R;
- {'EXIT',Reason} ->
- exit(Reason);
- Other ->
- throw(Other)
+ case MFA of
+ [M] -> M:start();
+ [M,F] -> M:F();
+ [M,F|Args] -> M:F(Args) % Args is a list
end.
-%%
-%% Fetch a module and load it into the system.
-%%
-load_mod(Mod, File) ->
- case erlang:module_loaded(Mod) of
- false ->
- case erl_prim_loader:get_file(File) of
- {ok,BinCode,FullName} ->
- load_mod_code(Mod, BinCode, FullName);
- _ ->
- exit({'cannot load',Mod,get_file})
- end;
- _ -> % Already loaded.
- {ok,File}
- end.
+%% Load a module.
-load_mod_code(Mod, BinCode, FullName) ->
- case erlang:module_loaded(Mod) of
- false ->
- case erlang:load_module(Mod, BinCode) of
- {module,Mod} -> {ok,FullName};
- {error,on_load} ->
- ?ON_LOAD_HANDLER ! {loaded,Mod},
- {ok,FullName};
- Other ->
- exit({'cannot load',Mod,Other})
- end;
- _ -> % Already loaded.
- {ok,FullName}
+do_load_module(Mod, BinCode) ->
+ case erlang:load_module(Mod, BinCode) of
+ {module,Mod} ->
+ ok;
+ {error,on_load} ->
+ ?ON_LOAD_HANDLER ! {loaded,Mod},
+ ok;
+ _ ->
+ error
end.
%% --------------------------------------------------------
@@ -1104,7 +1152,7 @@ load_mod_code(Mod, BinCode, FullName) ->
%% --------------------------------------------------------
shutdown_timer(Flags) ->
- case get_flag('-shutdown_time',Flags,infinity) of
+ case get_flag(shutdown_time, Flags, infinity) of
infinity ->
self();
Time ->
@@ -1154,14 +1202,10 @@ parse_boot_args([B|Bs], Ss, Fs, As) ->
eval_arg ->
{Expr,Rest} = get_args(Bs, []),
parse_boot_args(Rest, [{eval, Expr}|Ss], Fs, As);
- flag ->
+ {flag,A} ->
{F,Rest} = get_args(Bs, []),
- Fl = case F of
- [] -> [B];
- FF -> [B,FF]
- end,
- parse_boot_args(Rest, Ss,
- [list_to_tuple(Fl)|Fs], As);
+ Fl = {A,F},
+ parse_boot_args(Rest, Ss, [Fl|Fs], As);
arg ->
parse_boot_args(Bs, Ss, Fs, [B|As]);
end_args ->
@@ -1175,12 +1219,8 @@ check(<<"-s">>) -> start_arg;
check(<<"-run">>) -> start_arg2;
check(<<"-eval">>) -> eval_arg;
check(<<"--">>) -> end_args;
-check(X) when is_binary(X) ->
- case binary_to_list(X) of
- [$-|_Rest] -> flag;
- _Chars -> arg %Even empty atoms
- end;
-check(_X) -> arg. %This should never occur
+check(<<"-",Flag/binary>>) -> {flag,b2a(Flag)};
+check(_) -> arg.
get_args([B|Bs], As) ->
case check(B) of
@@ -1189,7 +1229,7 @@ get_args([B|Bs], As) ->
start_arg2 -> {reverse(As), [B|Bs]};
eval_arg -> {reverse(As), [B|Bs]};
end_args -> {reverse(As), Bs};
- flag -> {reverse(As), [B|Bs]};
+ {flag,_} -> {reverse(As), [B|Bs]};
arg ->
get_args(Bs, [B|As])
end;
@@ -1201,44 +1241,28 @@ get_args([], As) -> {reverse(As),[]}.
%% atom() if a single arg was given.
%% list(atom()) if several args were given.
%%
-get_flag(F,Flags,Default) ->
- case catch get_flag(F,Flags) of
- {'EXIT',_} ->
- Default;
- Value ->
- Value
- end.
-
-get_flag(F,Flags) ->
- case search(F,Flags) of
- {value,{F,[V]}} ->
+get_flag(F, Flags, Default) ->
+ case lists:keyfind(F, 1, Flags) of
+ {F,[]} ->
+ true;
+ {F,[V]} ->
V;
- {value,{F,V}} ->
+ {F,V} ->
V;
- {value,{F}} -> % Flag given!
- true;
_ ->
- exit(list_to_atom(concat(["no ",F," flag"])))
+ Default
end.
%%
%% Internal get_flag function, with default value.
%% Return: list(atom())
%%
-get_flag_list(F,Flags,Default) ->
- case catch get_flag_list(F,Flags) of
- {'EXIT',_} ->
- Default;
- Value ->
- Value
- end.
-
-get_flag_list(F,Flags) ->
- case search(F,Flags) of
- {value,{F,V}} ->
+get_flag_list(F, Flags, Default) ->
+ case lists:keyfind(F, 1, Flags) of
+ {F,[_|_]=V} ->
V;
_ ->
- exit(list_to_atom(concat(["no ",F," flag"])))
+ Default
end.
%%
@@ -1248,21 +1272,15 @@ get_flag_list(F,Flags) ->
%%
get_flag_args(F,Flags) -> get_flag_args(F,Flags,[]).
-get_flag_args(F,[{F,V}|Flags],Acc) when is_list(V) ->
- get_flag_args(F,Flags,[V|Acc]);
get_flag_args(F,[{F,V}|Flags],Acc) ->
- get_flag_args(F,Flags,[[V]|Acc]);
+ get_flag_args(F,Flags,[V|Acc]);
get_flag_args(F,[_|Flags],Acc) ->
get_flag_args(F,Flags,Acc);
get_flag_args(_,[],Acc) ->
reverse(Acc).
get_arguments([{F,V}|Flags]) ->
- [$-|Fl] = atom_to_list(F),
- [{list_to_atom(Fl),to_strings(V)}|get_arguments(Flags)];
-get_arguments([{F}|Flags]) ->
- [$-|Fl] = atom_to_list(F),
- [{list_to_atom(Fl),[]}|get_arguments(Flags)];
+ [{F,to_strings(V)}|get_arguments(Flags)];
get_arguments([]) ->
[].
@@ -1270,44 +1288,26 @@ to_strings([H|T]) when is_atom(H) -> [atom_to_list(H)|to_strings(T)];
to_strings([H|T]) when is_binary(H) -> [b2s(H)|to_strings(T)];
to_strings([]) -> [].
-get_argument(Arg,Flags) ->
- Args = get_arguments(Flags),
- case get_argument1(Arg,Args) of
- [] ->
- error;
- Value ->
- {ok,Value}
+get_argument(Arg, Flags) ->
+ case get_argument1(Arg, Flags) of
+ [] -> error;
+ Value -> {ok,Value}
end.
-get_argument1(Arg,[{Arg,V}|Args]) ->
- [V|get_argument1(Arg,Args)];
-get_argument1(Arg,[_|Args]) ->
- get_argument1(Arg,Args);
-get_argument1(_,[]) ->
+get_argument1(Arg, [{Arg,V}|Args]) ->
+ [to_strings(V)|get_argument1(Arg, Args)];
+get_argument1(Arg, [_|Args]) ->
+ get_argument1(Arg, Args);
+get_argument1(_, []) ->
[].
set_argument([{Flag,_}|Flags],Flag,Value) ->
[{Flag,[Value]}|Flags];
-set_argument([{Flag}|Flags],Flag,Value) ->
- [{Flag,[Value]}|Flags];
set_argument([Item|Flags],Flag,Value) ->
[Item|set_argument(Flags,Flag,Value)];
set_argument([],Flag,Value) ->
[{Flag,[Value]}].
-concat([A|T]) when is_atom(A) ->
- atom_to_list(A) ++ concat(T);
-concat([C|T]) when is_integer(C), 0 =< C, C =< 255 ->
- [C|concat(T)];
-concat([Bin|T]) when is_binary(Bin) ->
- binary_to_list(Bin) ++ concat(T);
-concat([S|T]) ->
- S ++ concat(T);
-concat([]) ->
- [].
-
-append(L, Z) -> L ++ Z.
-
append([E]) -> E;
append([H|T]) ->
H ++ append(T);
@@ -1322,13 +1322,6 @@ reverse([A, B]) ->
reverse([A, B | L]) ->
lists:reverse(L, [B, A]). % BIF
-search(Key, [H|_T]) when is_tuple(H), element(1, H) =:= Key ->
- {value, H};
-search(Key, [_|T]) ->
- search(Key, T);
-search(_Key, []) ->
- false.
-
-spec objfile_extension() -> nonempty_string().
objfile_extension() ->
".beam".
@@ -1404,3 +1397,64 @@ run_on_load_handlers([M|Ms], Debug) ->
end
end;
run_on_load_handlers([], _) -> ok.
+
+
+%% debug profile (light variant of eprof)
+debug_profile_start() ->
+ _ = erlang:trace_pattern({'_','_','_'},true,[call_time]),
+ _ = erlang:trace_pattern(on_load,true,[call_time]),
+ _ = erlang:trace(all,true,[call]),
+ ok.
+
+debug_profile_stop() ->
+ _ = erlang:trace_pattern({'_','_','_'},false,[call_time]),
+ _ = erlang:trace_pattern(on_load,false,[call_time]),
+ _ = erlang:trace(all,false,[call]),
+ ok.
+
+debug_profile_mfas() ->
+ _ = erlang:trace_pattern({'_','_','_'},pause,[call_time]),
+ _ = erlang:trace_pattern(on_load,pause,[call_time]),
+ MFAs = collect_loaded_mfas() ++ erlang:system_info(snifs),
+ collect_mfas(MFAs,[]).
+
+%% debug_profile_format_mfas should be called at the end of the boot phase
+%% so all pertinent modules should be loaded at that point.
+debug_profile_format_mfas(MFAs0) ->
+ MFAs = lists:sort(MFAs0),
+ lists:foreach(fun({{Us,C},{M,F,A}}) ->
+ Str = io_lib:format("~w:~w/~w", [M,F,A]),
+ io:format(standard_error,"~55s - ~6w : ~w us~n", [Str,C,Us])
+ end, MFAs),
+ ok.
+
+collect_loaded_mfas() ->
+ Ms = [M || M <- [element(1, Mi) || Mi <- code:all_loaded()]],
+ collect_loaded_mfas(Ms,[]).
+
+collect_loaded_mfas([],MFAs) -> MFAs;
+collect_loaded_mfas([M|Ms],MFAs0) ->
+ MFAs = [{M,F,A} || {F,A} <- M:module_info(functions)],
+ collect_loaded_mfas(Ms,MFAs ++ MFAs0).
+
+
+collect_mfas([], Info) -> Info;
+collect_mfas([MFA|MFAs],Info) ->
+ case erlang:trace_info(MFA,call_time) of
+ {call_time, []} ->
+ collect_mfas(MFAs,Info);
+ {call_time, false} ->
+ collect_mfas(MFAs,Info);
+ {call_time, Data} ->
+ case collect_mfa(MFA,Data,0,0) of
+ {{0,_},_} ->
+ %% ignore mfas with zero time
+ collect_mfas(MFAs,Info);
+ MfaData ->
+ collect_mfas(MFAs,[MfaData|Info])
+ end
+ end.
+
+collect_mfa(Mfa,[],Count,Time) -> {{Time,Count},Mfa};
+collect_mfa(Mfa,[{_Pid,C,S,Us}|Data],Count,Time) ->
+ collect_mfa(Mfa,Data,Count + C,Time + S * 1000000 + Us).
diff --git a/erts/preloaded/src/otp_ring0.erl b/erts/preloaded/src/otp_ring0.erl
index 2ccf142f30..62a60fffe2 100644
--- a/erts/preloaded/src/otp_ring0.erl
+++ b/erts/preloaded/src/otp_ring0.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/erts/preloaded/src/prim_buffer.erl b/erts/preloaded/src/prim_buffer.erl
new file mode 100644
index 0000000000..e0d35a6792
--- /dev/null
+++ b/erts/preloaded/src/prim_buffer.erl
@@ -0,0 +1,140 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2017. 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(prim_buffer).
+
+-export([on_load/0]).
+
+%% This is a mutable binary buffer that helps break out buffering logic from
+%% NIFs/drivers, which is often the only thing that prevents the C code from
+%% being reduced to bare system call wrappers.
+%%
+%% All operations in this file are thread-unsafe and risk crashing the emulator
+%% if you're not careful.
+
+-export([new/0, size/1, wipe/1, read/2, read_iovec/2, write/2, skip/2]).
+
+-export([find_byte_index/2]).
+
+-export([try_lock/1, unlock/1]).
+
+-type prim_buffer() :: term().
+
+%% Controls when to copy rather than extract sub-binaries from the buffer,
+%% reducing the risk of small reads keeping a large binary alive.
+-define(COPYING_READ_LIMIT, 512).
+
+%% Reads that fit into heap binaries are always copied since the cost of
+%% peeking binaries that short is largely equivalent to copying.
+-define(ERL_ONHEAP_BIN_LIMIT, 64).
+
+on_load() ->
+ case erlang:load_nif(atom_to_list(?MODULE), 0) of
+ ok -> ok
+ end.
+
+-spec new() -> prim_buffer().
+new() ->
+ erlang:nif_error(undef).
+
+-spec size(Buffer :: prim_buffer()) -> non_neg_integer().
+size(_Buffer) ->
+ erlang:nif_error(undef).
+
+%% Reads data as a binary from the front of the buffer. This will almost always
+%% result in copying so it should be avoided unless you absolutely must have a
+%% binary.
+-spec read(Buffer :: prim_buffer(), Size :: non_neg_integer()) -> binary().
+read(Buffer, Size) when Size =< ?ERL_ONHEAP_BIN_LIMIT ->
+ copying_read(Buffer, Size);
+read(Buffer, Size) when Size > ?ERL_ONHEAP_BIN_LIMIT ->
+ iolist_to_binary(read_iovec(Buffer, Size)).
+
+%% Reads data as an erlang:iovec() binary from the front of the buffer,
+%% avoiding copying if reasonable.
+-spec read_iovec(Buffer, Size) -> IOVec when
+ Buffer :: prim_buffer(),
+ Size :: non_neg_integer(),
+ IOVec :: erlang:iovec().
+read_iovec(Buffer, Size) when Size =< ?ERL_ONHEAP_BIN_LIMIT ->
+ [copying_read(Buffer, Size)];
+read_iovec(Buffer, Size) when Size > ?ERL_ONHEAP_BIN_LIMIT ->
+ Head = peek_head(Buffer),
+ HeadSize = byte_size(Head),
+ if
+ (HeadSize - Size) > ?COPYING_READ_LIMIT, Size =< ?COPYING_READ_LIMIT ->
+ [copying_read(Buffer, Size)];
+ HeadSize > Size ->
+ skip(Buffer, Size),
+ {First, _Rest} = split_binary(Head, Size),
+ [First];
+ HeadSize < Size ->
+ skip(Buffer, HeadSize),
+ [Head | read_iovec(Buffer, Size - HeadSize)];
+ HeadSize =:= Size ->
+ skip(Buffer, Size),
+ [Head]
+ end.
+
+%% Writes an erlang:iovec() to the back of the buffer.
+-spec write(Buffer :: prim_buffer(), IOVec :: erlang:iovec()) -> ok.
+write(_Buffer, _IOVec) ->
+ erlang:nif_error(undef).
+
+%% Removes data from the front of the buffer without reading it.
+-spec skip(Buffer :: prim_buffer(), Size :: non_neg_integer()) -> ok.
+skip(_Buffer, _Size) ->
+ erlang:nif_error(undef).
+
+-spec wipe(Buffer :: prim_buffer()) -> ok.
+wipe(Buffer) ->
+ skip(Buffer, prim_buffer:size(Buffer)).
+
+%% Finds the start-index of the first occurence of Needle, for implementing
+%% read_line and similar.
+-spec find_byte_index(Buffer, Needle) -> Result when
+ Buffer :: prim_buffer(),
+ Needle :: non_neg_integer(),
+ Result :: {ok, non_neg_integer()} |
+ not_found.
+find_byte_index(_Buffer, _Needle) ->
+ erlang:nif_error(undef).
+
+%% Attempts to take a unique lock on the buffer. Failure handling is left to
+%% the user.
+-spec try_lock(Buffer :: prim_buffer()) -> acquired | busy.
+try_lock(_Buffer) ->
+ erlang:nif_error(undef).
+
+-spec unlock(Buffer :: prim_buffer()) -> ok.
+unlock(_Buffer) ->
+ erlang:nif_error(undef).
+
+%% Unexported helper functions:
+
+%% Reads data from the front of the buffer, returning a copy of the data to
+%% avoid holding references.
+-spec copying_read(Buffer :: prim_buffer(), Size :: non_neg_integer()) -> binary().
+copying_read(_Buffer, _Size) ->
+ erlang:nif_error(undef).
+
+%% Returns the binary at the front of the buffer without modifying the buffer.
+-spec peek_head(Buffer :: prim_buffer()) -> binary().
+peek_head(_Buffer) ->
+ erlang:nif_error(undef).
diff --git a/erts/preloaded/src/prim_eval.S b/erts/preloaded/src/prim_eval.S
index 958a79a1da..e4b1560517 100644
--- a/erts/preloaded/src/prim_eval.S
+++ b/erts/preloaded/src/prim_eval.S
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2017. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -25,7 +26,7 @@
{attributes, []}.
-{labels, 10}.
+{labels, 14}.
{function, 'receive', 2, 2}.
@@ -35,6 +36,9 @@
{allocate,2,2}.
{move,{x,1},{y,0}}.
{move,{x,0},{y,1}}.
+ %% Call arg_reg_alloc() in order to ensure
+ %% that def_arg_reg[0] isn't clobbered
+ {call,0,{f,7}}.
{label,3}.
{loop_rec,{f,5},{x,0}}.
{move,{y,1},{x,1}}.
@@ -52,19 +56,43 @@
{deallocate,2}.
return.
-
-{function, module_info, 0, 8}.
+{function, arg_reg_alloc, 0, 7}.
{label,6}.
- {func_info,{atom,prim_eval},{atom,module_info},0}.
+ {func_info,{atom,prim_eval},{atom,arg_reg_alloc},0}.
{label,7}.
+ {allocate,0,0}.
+ {move,{integer,134217727},{x,0}}.
+ {call_ext,1,{extfunc,erlang,bump_reductions,1}}.
+ {move,{atom,true},{x,3}}.
+ {move,{atom,true},{x,4}}.
+ {move,{atom,true},{x,2}}.
+ {move,{atom,true},{x,5}}.
+ {move,{atom,true},{x,1}}.
+ {move,{atom,true},{x,6}}.
+ {move,{atom,true},{x,0}}.
+ {call_last,7,{f,9},0}.
+
+
+{function, arg_reg_alloc, 7, 9}.
+ {label,8}.
+ {func_info,{atom,prim_eval},{atom,arg_reg_alloc},7}.
+ {label,9}.
+ {move,{atom,ok},{x,0}}.
+ return.
+
+
+{function, module_info, 0, 11}.
+ {label,10}.
+ {func_info,{atom,prim_eval},{atom,module_info},0}.
+ {label,11}.
{move,{atom,prim_eval},{x,0}}.
{call_ext_only,1,{extfunc,erlang,get_module_info,1}}.
-{function, module_info, 1, 10}.
- {label,8}.
+{function, module_info, 1, 13}.
+ {label,12}.
{func_info,{atom,prim_eval},{atom,module_info},1}.
- {label,9}.
+ {label,13}.
{move,{x,0},{x,1}}.
{move,{atom,prim_eval},{x,0}}.
{call_ext_only,2,{extfunc,erlang,get_module_info,2}}.
diff --git a/erts/preloaded/src/prim_eval.erl b/erts/preloaded/src/prim_eval.erl
index ec5af8c138..22e924f9e9 100644
--- a/erts/preloaded/src/prim_eval.erl
+++ b/erts/preloaded/src/prim_eval.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl
index 34679404a2..6d85868183 100644
--- a/erts/preloaded/src/prim_file.erl
+++ b/erts/preloaded/src/prim_file.erl
@@ -1,164 +1,63 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2018. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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(prim_file).
-%% Interface module to the file driver.
-
-
-
-%%% Interface towards a single file's contents. Uses ?FD_DRV.
-
-%% Generic file contents operations
--export([open/2, close/1, datasync/1, sync/1, advise/4, position/2, truncate/1,
- write/2, pwrite/2, pwrite/3, read/2, read_line/1, pread/2, pread/3,
- copy/3, sendfile/8, allocate/3]).
+-export([on_load/0]).
-%% Specialized file operations
--export([open/1, open/3]).
--export([read_file/1, read_file/2, write_file/2]).
--export([ipread_s32bu_p32bu/3]).
+-export([open/2, close/1,
+ sync/1, datasync/1, truncate/1, advise/4, allocate/3,
+ read_line/1, read/2, write/2, position/2,
+ pread/2, pread/3, pwrite/2, pwrite/3]).
+%% OTP internal.
+-export([ipread_s32bu_p32bu/3, sendfile/8, altname/1, get_handle/1]).
+-export([read_file/1, write_file/2]).
-%%% Interface towards file system and metadata. Uses ?DRV.
+-export([read_link/1, read_link_all/1,
+ read_link_info/1, read_link_info/2,
+ read_file_info/1, read_file_info/2,
+ write_file_info/2, write_file_info/3]).
-%% Takes an optional port (opens a ?DRV port per default) as first argument.
--export([get_cwd/0, get_cwd/1, get_cwd/2,
- set_cwd/1, set_cwd/2,
- delete/1, delete/2,
- rename/2, rename/3,
- make_dir/1, make_dir/2,
- del_dir/1, del_dir/2,
- read_file_info/1, read_file_info/2, read_file_info/3,
- altname/1, altname/2,
- write_file_info/2, write_file_info/3, write_file_info/4,
- make_link/2, make_link/3,
- make_symlink/2, make_symlink/3,
- read_link/1, read_link/2, read_link_all/1, read_link_all/2,
- read_link_info/1, read_link_info/2, read_link_info/3,
- list_dir/1, list_dir/2, list_dir_all/1, list_dir_all/2]).
-%% How to start and stop the ?DRV port.
--export([start/0, stop/1]).
+-export([list_dir/1, list_dir_all/1]).
-%% Debug exports
--export([open_int/4, open_mode/1, open_mode/4]).
-
-%%%-----------------------------------------------------------------
-%%% Includes and defines
-
--include("file.hrl").
-
--define(DRV, "efile").
--define(FD_DRV, "efile").
+-export([get_cwd/0, get_cwd/1, set_cwd/1,
+ delete/1, rename/2,
+ make_dir/1, del_dir/1,
+ make_link/2, make_symlink/2]).
+-define(MIN_READLINE_SIZE, 256).
-define(LARGEFILESIZE, (1 bsl 63)).
-%% Driver commands
--define(FILE_OPEN, 1).
--define(FILE_READ, 2).
--define(FILE_LSEEK, 3).
--define(FILE_WRITE, 4).
--define(FILE_FSTAT, 5).
--define(FILE_PWD, 6).
--define(FILE_READDIR, 7).
--define(FILE_CHDIR, 8).
--define(FILE_FSYNC, 9).
--define(FILE_MKDIR, 10).
--define(FILE_DELETE, 11).
--define(FILE_RENAME, 12).
--define(FILE_RMDIR, 13).
--define(FILE_TRUNCATE, 14).
--define(FILE_READ_FILE, 15).
--define(FILE_WRITE_INFO, 16).
--define(FILE_LSTAT, 19).
--define(FILE_READLINK, 20).
--define(FILE_LINK, 21).
--define(FILE_SYMLINK, 22).
--define(FILE_CLOSE, 23).
--define(FILE_PWRITEV, 24).
--define(FILE_PREADV, 25).
--define(FILE_SETOPT, 26).
--define(FILE_IPREAD, 27).
--define(FILE_ALTNAME, 28).
--define(FILE_READ_LINE, 29).
--define(FILE_FDATASYNC, 30).
--define(FILE_ADVISE, 31).
--define(FILE_SENDFILE, 32).
--define(FILE_ALLOCATE, 33).
-
-%% Driver responses
--define(FILE_RESP_OK, 0).
--define(FILE_RESP_ERROR, 1).
--define(FILE_RESP_DATA, 2).
--define(FILE_RESP_NUMBER, 3).
--define(FILE_RESP_INFO, 4).
--define(FILE_RESP_NUMERR, 5).
--define(FILE_RESP_LDATA, 6).
--define(FILE_RESP_N2DATA, 7).
--define(FILE_RESP_EOF, 8).
--define(FILE_RESP_FNAME, 9).
--define(FILE_RESP_ALL_DATA, 10).
--define(FILE_RESP_LFNAME, 11).
-
-%% Open modes for the driver's open function.
--define(EFILE_MODE_READ, 1).
--define(EFILE_MODE_WRITE, 2).
--define(EFILE_MODE_READ_WRITE, 3).
--define(EFILE_MODE_APPEND, 4).
--define(EFILE_COMPRESSED, 8).
--define(EFILE_MODE_EXCL, 16).
-%% Note: bit 5 (32) is used internally for VxWorks
--define(EFILE_MODE_SYNC, 64).
-
-%% Use this mask to get just the mode bits to be passed to the driver.
--define(EFILE_MODE_MASK, 127).
-
-%% Seek modes for the driver's seek function.
--define(EFILE_SEEK_SET, 0).
--define(EFILE_SEEK_CUR, 1).
--define(EFILE_SEEK_END, 2).
-
-%% Options
--define(FILE_OPT_DELAYED_WRITE, 0).
--define(FILE_OPT_READ_AHEAD, 1).
-
-%% IPREAD variants
--define(IPREAD_S32BU_P32BU, 0).
-
-%% POSIX file advises
--define(POSIX_FADV_NORMAL, 0).
--define(POSIX_FADV_RANDOM, 1).
--define(POSIX_FADV_SEQUENTIAL, 2).
--define(POSIX_FADV_WILLNEED, 3).
--define(POSIX_FADV_DONTNEED, 4).
--define(POSIX_FADV_NOREUSE, 5).
-
-%% Sendfile flags
--define(EFILE_SENDFILE_USE_THREADS, 1).
+-export([copy/3]).
+
+-include("file_int.hrl").
+-type prim_file_ref() :: term().
%%% BIFs
-export([internal_name2native/1,
internal_native2name/1,
internal_normalize_utf8/1,
- is_translatable/1]).
+ is_translatable/1]).
-type prim_file_name() :: string() | unicode:unicode_binary().
-type prim_file_name_error() :: 'error' | 'ignore' | 'warning'.
@@ -184,1315 +83,745 @@ internal_normalize_utf8(_) ->
is_translatable(_) ->
erlang:nif_error(undefined).
-%%% End of BIFs
-
-%%%-----------------------------------------------------------------
-%%% Functions operating on a file through a handle. ?FD_DRV.
-%%%
-%%% Generic file contents operations.
-%%%
-%%% Supposed to be called by applications through module file.
-
-
-%% Opens a file using the driver port Port. Returns {error, Reason}
-%% | {ok, FileDescriptor}
-open(Port, File, ModeList) when is_port(Port),
- (is_list(File) orelse is_binary(File)),
- is_list(ModeList) ->
- case open_mode(ModeList) of
- {Mode, _Portopts, _Setopts} ->
- open_int(Port, File, Mode, []);
- Reason ->
- {error, Reason}
- end;
-open(_,_,_) ->
- {error, badarg}.
-
-%% Opens a file. Returns {error, Reason} | {ok, FileDescriptor}.
-open(File, ModeList) when (is_list(File) orelse is_binary(File)),
- is_list(ModeList) ->
- case open_mode(ModeList) of
- {Mode, Portopts, Setopts} ->
- open_int({?FD_DRV, Portopts},File, Mode, Setopts);
- Reason ->
- {error, Reason}
- end;
-open(_, _) ->
- {error, badarg}.
+%%
-%% Opens a port that can be used for open/3 or read_file/2.
-%% Returns {ok, Port} | {error, Reason}.
-open(Portopts) when is_list(Portopts) ->
- drv_open(?FD_DRV, [binary|Portopts]);
-open(_) ->
- {error, badarg}.
+%% Returns {error, Reason} | {ok, BytesCopied}
+copy(#file_descriptor{module = ?MODULE} = Source,
+ #file_descriptor{module = ?MODULE} = Dest,
+ Length)
+ when is_integer(Length), Length >= 0;
+ is_atom(Length) ->
+ %% XXX Should be moved down to the driver for optimization.
+ file:copy_opened(Source, Dest, Length).
-open_int({Driver, Portopts}, File, Mode, Setopts) ->
- case drv_open(Driver, Portopts) of
- {ok, Port} ->
- open_int(Port, File, Mode, Setopts);
- {error, _} = Error ->
- Error
- end;
-open_int(Port, File, Mode, Setopts) ->
- M = Mode band ?EFILE_MODE_MASK,
- case drv_command(Port, [<<?FILE_OPEN, M:32>>, pathname(File)]) of
- {ok, Number} ->
- open_int_setopts(Port, Number, Setopts);
- Error ->
- drv_close(Port),
- Error
- end.
+on_load() ->
+ ok = erlang:load_nif(atom_to_list(?MODULE), 0).
-open_int_setopts(Port, Number, []) ->
- {ok, #file_descriptor{module = ?MODULE, data = {Port, Number}}};
-open_int_setopts(Port, Number, [Cmd | Tail]) ->
- case drv_command(Port, Cmd) of
- ok ->
- open_int_setopts(Port, Number, Tail);
- Error ->
- drv_close(Port),
- Error
+open(Name, Modes) ->
+ %% The try/catch pattern seen here is used throughout the file to adhere to
+ %% the public file interface, which has leaked through for ages because of
+ %% "raw files."
+ try open_nif(encode_path(Name), Modes) of
+ {ok, Ref} -> {ok, make_fd(Ref, Modes)};
+ {error, Reason} -> {error, Reason}
+ catch
+ error:badarg -> {error, badarg}
end.
+make_fd(FRef, Modes) ->
+ #file_descriptor{module = ?MODULE, data = build_fd_data(FRef, Modes) }.
-
-%% Returns ok.
-
-close(#file_descriptor{module = ?MODULE, data = {Port, _}}) ->
- case drv_command(Port, <<?FILE_CLOSE>>) of
- ok ->
- drv_close(Port);
- Error ->
- Error
- end;
-%% Closes a port opened with open/1.
-close(Port) when is_port(Port) ->
- drv_close(Port).
-
--define(ADVISE(Offs, Len, Adv),
- <<?FILE_ADVISE, Offs:64/signed, Len:64/signed,
- Adv:32/signed>>).
-
-%% Returns {error, Reason} | ok.
-advise(#file_descriptor{module = ?MODULE, data = {Port, _}},
- Offset, Length, Advise) ->
- case Advise of
- normal ->
- Cmd = ?ADVISE(Offset, Length, ?POSIX_FADV_NORMAL),
- drv_command(Port, Cmd);
- random ->
- Cmd = ?ADVISE(Offset, Length, ?POSIX_FADV_RANDOM),
- drv_command(Port, Cmd);
- sequential ->
- Cmd = ?ADVISE(Offset, Length, ?POSIX_FADV_SEQUENTIAL),
- drv_command(Port, Cmd);
- will_need ->
- Cmd = ?ADVISE(Offset, Length, ?POSIX_FADV_WILLNEED),
- drv_command(Port, Cmd);
- dont_need ->
- Cmd = ?ADVISE(Offset, Length, ?POSIX_FADV_DONTNEED),
- drv_command(Port, Cmd);
- no_reuse ->
- Cmd = ?ADVISE(Offset, Length, ?POSIX_FADV_NOREUSE),
- drv_command(Port, Cmd);
- _ ->
- {error, einval}
+close(Fd) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ close_nif(FRef)
+ catch
+ error:badarg -> {error, badarg}
end.
-%% Returns {error, Reason} | ok.
-allocate(#file_descriptor{module = ?MODULE, data = {Port, _}}, Offset, Length) ->
- Cmd = <<?FILE_ALLOCATE, Offset:64/signed, Length:64/signed>>,
- drv_command(Port, Cmd).
-
-%% Returns {error, Reason} | ok.
-write(#file_descriptor{module = ?MODULE, data = {Port, _}}, Bytes) ->
- case drv_command_nt(Port, [?FILE_WRITE,erlang:dt_prepend_vm_tag_data(Bytes)],undefined) of
- {ok, _Size} ->
- ok;
- Error ->
- Error
+read(Fd, Size) ->
+ try
+ #{ handle := FRef,
+ r_ahead_size := RASz,
+ r_buffer := RBuf } = get_fd_data(Fd),
+ read_1(FRef, RBuf, prim_buffer:size(RBuf), RASz, Size)
+ catch
+ error:badarg -> {error, badarg}
end.
-%% Returns ok | {error, {WrittenCount, Reason}}
-pwrite(#file_descriptor{module = ?MODULE, data = {Port, _}}, L)
- when is_list(L) ->
- pwrite_int(Port, L, 0, [], []).
-
-pwrite_int(_, [], 0, [], []) ->
- ok;
-pwrite_int(Port, [], N, Spec, Data) ->
- Header = list_to_binary([?FILE_PWRITEV, erlang:dt_prepend_vm_tag_data(<<N:32>>) | reverse(Spec)]),
- case drv_command_nt(Port, [Header | reverse(Data)], undefined) of
- {ok, _Size} ->
- ok;
- Error ->
- Error
+-spec read_1(FRef, RBuf, RBufSz, RASz, RSz) -> Result when
+ FRef :: prim_file_ref(),
+ RBuf :: term(),
+ RBufSz :: non_neg_integer(),
+ RASz :: non_neg_integer(),
+ RSz :: non_neg_integer(),
+ Result :: eof | {ok, binary()} | {error, Reason :: atom()}.
+read_1(_FRef, RBuf, RBufSz, _RASz, RSz) when RBufSz >= RSz ->
+ {ok, prim_buffer:read(RBuf, RSz)};
+read_1(FRef, RBuf, RBufSz, RASz, RSz) when RBufSz > 0 ->
+ Buffered = prim_buffer:read(RBuf, RBufSz),
+ case read_1(FRef, RBuf, 0, RASz, RSz - RBufSz) of
+ {ok, Data} ->
+ {ok, <<Buffered/binary, Data/binary>>};
+ eof ->
+ {ok, Buffered};
+ {error, Reason} ->
+ {error, Reason}
end;
-pwrite_int(Port, [{Offs, Bytes} | T], N, Spec, Data)
- when is_integer(Offs) ->
- if
- -(?LARGEFILESIZE) =< Offs, Offs < ?LARGEFILESIZE ->
- pwrite_int(Port, T, N, Spec, Data, Offs, Bytes);
- true ->
- {error, einval}
- end;
-pwrite_int(_, [_|_], _N, _Spec, _Data) ->
- {error, badarg}.
+read_1(FRef, RBuf, RBufSz, RASz, RSz) when RBufSz =:= 0 ->
+ case read_nif(FRef, RASz + RSz) of
+ {ok, Data} when byte_size(Data) > RSz ->
+ {First, Rest} = split_binary(Data, RSz),
+ prim_buffer:write(RBuf, [Rest]),
+ {ok, First};
+ {ok, Data} when byte_size(Data) =< RSz ->
+ {ok, Data};
+ eof ->
+ eof;
+ {error, Reason} ->
+ {error, Reason}
+ end.
-pwrite_int(Port, T, N, Spec, Data, Offs, Bin)
- when is_binary(Bin) ->
- Size = byte_size(Bin),
- pwrite_int(Port, T, N+1,
- [<<Offs:64/signed, Size:64>> | Spec],
- [Bin | Data]);
-pwrite_int(Port, T, N, Spec, Data, Offs, Bytes) ->
- try list_to_binary(Bytes) of
- Bin ->
- pwrite_int(Port, T, N, Spec, Data, Offs, Bin)
+read_line(Fd) ->
+ try
+ #{ handle := FRef,
+ r_ahead_size := RASz,
+ r_buffer := RBuf } = get_fd_data(Fd),
+ SearchResult = prim_buffer:find_byte_index(RBuf, $\n),
+ LineSize = max(?MIN_READLINE_SIZE, RASz),
+ read_line_1(FRef, RBuf, SearchResult, LineSize)
catch
- error:Reason ->
- {error, Reason}
+ error:badarg -> {error, badarg}
end.
-
-
-%% Returns {error, Reason} | ok.
-pwrite(#file_descriptor{module = ?MODULE, data = {Port, _}}, Offs, Bytes)
- when is_integer(Offs) ->
- if
- -(?LARGEFILESIZE) =< Offs, Offs < ?LARGEFILESIZE ->
- case pwrite_int(Port, [], 0, [], [], Offs, Bytes) of
- {error, {_, Reason}} ->
- {error, Reason};
- Result ->
- Result
- end;
- true ->
- {error, einval}
+-spec read_line_1(FRef, RBuf, SearchResult, LineSize) -> Result when
+ FRef :: prim_file_ref(),
+ RBuf :: term(),
+ SearchResult :: not_found | {ok, non_neg_integer()},
+ LineSize :: non_neg_integer(),
+ Result :: eof | {ok, binary()} | {error, Reason :: atom()}.
+read_line_1(FRef, RBuf, not_found, LineSize) ->
+ case read_nif(FRef, LineSize) of
+ {ok, Data} ->
+ prim_buffer:write(RBuf, [Data]),
+ SearchResult = prim_buffer:find_byte_index(RBuf, $\n),
+ read_line_1(FRef, RBuf, SearchResult, LineSize);
+ eof ->
+ case prim_buffer:size(RBuf) of
+ Size when Size > 0 -> {ok, prim_buffer:read(RBuf, Size)};
+ Size when Size =:= 0 -> eof
+ end;
+ {error, Reason} ->
+ {error, Reason}
end;
-pwrite(#file_descriptor{module = ?MODULE}, _, _) ->
- {error, badarg}.
-
+read_line_1(_FRef, RBuf, {ok, LFIndex}, _LineSize) ->
+ %% Translate CRLF into just LF, completely ignoring which encoding is used.
+ CRIndex = (LFIndex - 1),
+ case prim_buffer:read(RBuf, LFIndex + 1) of
+ <<Line:CRIndex/binary, "\r\n">> -> {ok, <<Line/binary, "\n">>};
+ Line -> {ok, Line}
+ end.
-%% Returns {error, Reason} | ok.
-datasync(#file_descriptor{module = ?MODULE, data = {Port, _}}) ->
- drv_command(Port, [?FILE_FDATASYNC]).
-
-%% Returns {error, Reason} | ok.
-sync(#file_descriptor{module = ?MODULE, data = {Port, _}}) ->
- drv_command(Port, [?FILE_FSYNC]).
-
-%% Returns {ok, Data} | eof | {error, Reason}.
-read_line(#file_descriptor{module = ?MODULE, data = {Port, _}}) ->
- case drv_command(Port, <<?FILE_READ_LINE>>) of
- {ok, {0, _Data}} ->
- eof;
- {ok, {_Size, Data}} ->
- {ok, Data};
- {error, enomem} ->
- erlang:garbage_collect(),
- case drv_command(Port, <<?FILE_READ_LINE>>) of
- {ok, {0, _Data}} ->
- eof;
- {ok, {_Size, Data}} ->
- {ok, Data};
- Other ->
- Other
- end;
- Error ->
- Error
+write(Fd, IOData) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ reset_write_position(Fd),
+ write_1(FRef, erlang:iolist_to_iovec(IOData))
+ catch
+ error:badarg -> {error, badarg}
end.
-
-%% Returns {ok, Data} | eof | {error, Reason}.
-read(#file_descriptor{module = ?MODULE, data = {Port, _}}, Size)
- when is_integer(Size), 0 =< Size ->
- if
- Size < ?LARGEFILESIZE ->
- case drv_command(Port, <<?FILE_READ, Size:64>>) of
- {ok, {0, _Data}} when Size =/= 0 ->
- eof;
- {ok, {_Size, Data}} ->
- {ok, Data};
- {error, enomem} ->
- %% Garbage collecting here might help if
- %% the current processes have some old binaries left.
- erlang:garbage_collect(),
- case drv_command(Port, <<?FILE_READ, Size:64>>) of
- {ok, {0, _Data}} when Size =/= 0 ->
- eof;
- {ok, {_Size, Data}} ->
- {ok, Data};
- Other ->
- Other
- end;
- Error ->
- Error
- end;
- true ->
- {error, einval}
+write_1(FRef, IOVec) ->
+ case write_nif(FRef, IOVec) of
+ {continue, Remainder} ->
+ write_1(FRef, Remainder);
+ ok ->
+ ok;
+ {error, Reason} ->
+ {error, Reason}
end.
-%% Returns {ok, [Data|eof, ...]} | {error, Reason}
-pread(#file_descriptor{module = ?MODULE, data = {Port, _}}, L)
- when is_list(L) ->
- pread_int(Port, L, 0, []).
-
-pread_int(_, [], 0, []) ->
- {ok, []};
-pread_int(Port, [], N, Spec) ->
- drv_command_nt(Port, [?FILE_PREADV, erlang:dt_prepend_vm_tag_data(<<0:32, N:32>>) | reverse(Spec)],undefined);
-pread_int(Port, [{Offs, Size} | T], N, Spec)
- when is_integer(Offs), is_integer(Size), 0 =< Size ->
- if
- -(?LARGEFILESIZE) =< Offs, Offs < ?LARGEFILESIZE,
- Size < ?LARGEFILESIZE ->
- pread_int(Port, T, N+1, [<<Offs:64/signed, Size:64>> | Spec]);
- true ->
- {error, einval}
- end;
-pread_int(_, [_|_], _N, _Spec) ->
- {error, badarg}.
-
-
-
-%% Returns {ok, Data} | eof | {error, Reason}.
-pread(#file_descriptor{module = ?MODULE, data = {Port, _}}, Offs, Size)
- when is_integer(Offs), is_integer(Size), 0 =< Size ->
- if
- -(?LARGEFILESIZE) =< Offs, Offs < ?LARGEFILESIZE,
- Size < ?LARGEFILESIZE ->
- case drv_command_nt(Port,
- [?FILE_PREADV, erlang:dt_prepend_vm_tag_data(<<0:32, 1:32,
- Offs:64/signed, Size:64>>)], undefined) of
- {ok, [eof]} ->
- eof;
- {ok, [Data]} ->
- {ok, Data};
- Error ->
- Error
- end;
- true ->
- {error, einval}
- end;
-pread(#file_descriptor{module = ?MODULE, data = {_, _}}, _, _) ->
- {error, badarg}.
-
-
-
-%% Returns {ok, Position} | {error, Reason}.
-position(#file_descriptor{module = ?MODULE, data = {Port, _}}, At) ->
- case lseek_position(At) of
- {Offs, Whence}
- when -(?LARGEFILESIZE) =< Offs, Offs < ?LARGEFILESIZE ->
- drv_command(Port, <<?FILE_LSEEK, Offs:64/signed, Whence:32>>);
- {_, _} ->
- {error, einval};
- Reason ->
- {error, Reason}
+truncate(Fd) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ reset_write_position(Fd),
+ truncate_nif(FRef)
+ catch
+ error:badarg -> {error, badarg}
end.
-%% Returns {error, Reason} | ok.
-truncate(#file_descriptor{module = ?MODULE, data = {Port, _}}) ->
- drv_command(Port, <<?FILE_TRUNCATE>>).
-
-
-
-%% Returns {error, Reason} | {ok, BytesCopied}
-copy(#file_descriptor{module = ?MODULE} = Source,
- #file_descriptor{module = ?MODULE} = Dest,
- Length)
- when is_integer(Length), Length >= 0;
- is_atom(Length) ->
- %% XXX Should be moved down to the driver for optimization.
- file:copy_opened(Source, Dest, Length).
-
-
-
-ipread_s32bu_p32bu(#file_descriptor{module = ?MODULE,
- data = {_, _}} = Handle,
- Offs,
- Infinity) when is_atom(Infinity) ->
- ipread_s32bu_p32bu(Handle, Offs, (1 bsl 31)-1);
-ipread_s32bu_p32bu(#file_descriptor{module = ?MODULE, data = {Port, _}},
- Offs,
- MaxSize)
- when is_integer(Offs), is_integer(MaxSize) ->
- if
- -(?LARGEFILESIZE) =< Offs, Offs < ?LARGEFILESIZE,
- 0 =< MaxSize, MaxSize < (1 bsl 31) ->
- drv_command(Port, <<?FILE_IPREAD, ?IPREAD_S32BU_P32BU,
- Offs:64, MaxSize:32>>);
- true ->
- {error, einval}
- end;
-ipread_s32bu_p32bu(#file_descriptor{module = ?MODULE, data = {_, _}},
- _Offs,
- _MaxSize) ->
- {error, badarg}.
+advise(Fd, Offset, Length, Advise) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ advise_nif(FRef, Offset, Length, Advise)
+ catch
+ error:badarg -> {error, badarg}
+ end.
+allocate(Fd, Offset, Length) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ allocate_nif(FRef, Offset, Length)
+ catch
+ error:badarg -> {error, badarg}
+ end.
+sync(Fd) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ sync_nif(FRef, 0)
+ catch
+ error:badarg -> {error, badarg}
+ end.
-%% Returns {ok, Contents} | {error, Reason}
-read_file(File) when (is_list(File) orelse is_binary(File)) ->
- case drv_open(?FD_DRV, [binary]) of
- {ok, Port} ->
- Result = read_file(Port, File),
- close(Port),
- Result;
- {error, _} = Error ->
- Error
- end;
-read_file(_) ->
- {error, badarg}.
+datasync(Fd) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ sync_nif(FRef, 1)
+ catch
+ error:badarg -> {error, badarg}
+ end.
-%% Takes a Port opened with open/1.
-read_file(Port, File) when is_port(Port),
- (is_list(File) orelse is_binary(File)) ->
- Cmd = [?FILE_READ_FILE | pathname(File)],
- case drv_command(Port, Cmd) of
- {error, enomem} ->
- %% It could possibly help to do a
- %% garbage collection here,
- %% if the file server has some references
- %% to binaries read earlier.
- erlang:garbage_collect(),
- drv_command(Port, Cmd);
- Result ->
- Result
+position(Fd, {cur, Offset}) ->
+ try
+ %% Adjust our current position according to how much we've read ahead.
+ #{ r_buffer := RBuf } = get_fd_data(Fd),
+ position_1(Fd, cur, Offset - prim_buffer:size(RBuf))
+ catch
+ error:badarg -> {error, badarg}
end;
-read_file(_,_) ->
- {error, badarg}.
-
-
-
-%% Returns {error, Reason} | ok.
-write_file(File, Bin) when (is_list(File) orelse is_binary(File)) ->
- case open(File, [binary, write]) of
- {ok, Handle} ->
- Result = write(Handle, Bin),
- close(Handle),
- Result;
- Error ->
- Error
+position(Fd, {Mark, Offset}) ->
+ try
+ position_1(Fd, Mark, Offset)
+ catch
+ error:badarg -> {error, badarg}
end;
-write_file(_, _) ->
- {error, badarg}.
+position(Fd, cur) -> position(Fd, {cur, 0});
+position(Fd, bof) -> position(Fd, {bof, 0});
+position(Fd, eof) -> position(Fd, {eof, 0});
+position(Fd, Offset) -> position(Fd, {bof, Offset}).
+position_1(Fd, Mark, Offset) ->
+ #{ handle := FRef, r_buffer := RBuf } = get_fd_data(Fd),
+ prim_buffer:wipe(RBuf),
+ seek_nif(FRef, Mark, Offset).
-%% Returns {error, Reason} | {ok, BytesCopied}
-%sendfile(_,_,_,_,_,_,_,_,_,_) ->
-% {error, enotsup};
-sendfile(#file_descriptor{module = ?MODULE, data = {Port, _}},
- Dest, Offset, Bytes, _ChunkSize, Headers, Trailers,
- Flags) ->
- case erlang:port_get_data(Dest) of
- Data when Data == inet_tcp; Data == inet6_tcp ->
- ok = inet:lock_socket(Dest,true),
- {ok, DestFD} = prim_inet:getfd(Dest),
- IntFlags = translate_sendfile_flags(Flags),
- try drv_command(Port, [<<?FILE_SENDFILE, DestFD:32,
- IntFlags:8,
- Offset:64/unsigned,
- Bytes:64/unsigned,
- (iolist_size(Headers)):32/unsigned,
- (iolist_size(Trailers)):32/unsigned>>,
- Headers,Trailers])
- after
- ok = inet:lock_socket(Dest,false)
- end;
- _Else ->
- {error,badarg}
+pread(Fd, Offset, Size) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ pread_nif(FRef, Offset, Size)
+ catch
+ error:badarg -> {error, badarg}
end.
-translate_sendfile_flags([{use_threads,true}|T]) ->
- ?EFILE_SENDFILE_USE_THREADS bor translate_sendfile_flags(T);
-translate_sendfile_flags([_|T]) ->
- translate_sendfile_flags(T);
-translate_sendfile_flags([]) ->
- 0.
-
-
-%%%-----------------------------------------------------------------
-%%% Functions operating on files without handle to the file. ?DRV.
-%%%
-%%% Supposed to be called by applications through module file.
-
-
-
-%% Returns {ok, Port}, the Port should be used as first argument in all
-%% the following functions. Returns {error, Reason} upon failure.
-start() ->
- drv_open(?DRV, [binary]).
-
-stop(Port) when is_port(Port) ->
- try erlang:port_close(Port) of
- _ ->
- ok
+pread(Fd, LocNums) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ pread_list(FRef, LocNums, [])
catch
- _:_ ->
- ok
+ error:badarg -> {error, badarg}
end.
+-spec pread_list(FRef, LocNums, ResultList) -> Result when
+ FRef :: prim_file_ref(),
+ LocNums :: list({Offset :: non_neg_integer(),
+ Size :: non_neg_integer()}),
+ ResultList :: list(eof | binary()),
+ Result :: {ok, ResultList} | {error, Reason :: atom()}.
+pread_list(_FRef, [], ResultList) ->
+ {ok, reverse_list(ResultList)};
+pread_list(FRef, [{Offset, Size} | Rest], ResultList) ->
+ case pread_nif(FRef, Offset, Size) of
+ {ok, Data} ->
+ pread_list(FRef, Rest, [Data | ResultList]);
+ eof ->
+ pread_list(FRef, Rest, [eof | ResultList]);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+pwrite(Fd, Offset, IOData) ->
+ try
+ #{ handle := FRef, r_buffer := RBuf } = get_fd_data(Fd),
+ prim_buffer:wipe(RBuf),
+ pwrite_plain(FRef, Offset, erlang:iolist_to_iovec(IOData))
+ catch
+ error:badarg -> {error, badarg}
+ end.
+pwrite_plain(FRef, Offset, IOVec) ->
+ case pwrite_nif(FRef, Offset, IOVec) of
+ {continue, BytesWritten, Remainder} ->
+ pwrite_plain(FRef, Offset + BytesWritten, Remainder);
+ ok ->
+ ok;
+ {error, Reason} ->
+ {error, Reason}
+ end.
-%%% The following functions take an optional Port as first argument.
-%%% If the port is not supplied, a temporary one is opened and then
-%%% closed after the request has been performed.
-
-
-
-%% get_cwd/{0,1,2}
-
-get_cwd() ->
- get_cwd_int(0).
-
-get_cwd(Port) when is_port(Port) ->
- get_cwd_int(Port, 0);
-get_cwd([]) ->
- get_cwd_int(0);
-get_cwd([Letter, $: | _]) when $a =< Letter, Letter =< $z ->
- get_cwd_int(Letter - $a + 1);
-get_cwd([Letter, $: | _]) when $A =< Letter, Letter =< $Z ->
- get_cwd_int(Letter - $A + 1);
-get_cwd([_|_]) ->
- {error, einval};
-get_cwd(_) ->
- {error, badarg}.
-
-get_cwd(Port, []) when is_port(Port) ->
- get_cwd_int(Port, 0);
-get_cwd(Port, [Letter, $: | _])
- when is_port(Port), $a =< Letter, Letter =< $z ->
- get_cwd_int(Port, Letter - $a + 1);
-get_cwd(Port, [Letter, $: | _])
- when is_port(Port), $A =< Letter, Letter =< $Z ->
- get_cwd_int(Port, Letter - $A + 1);
-get_cwd(Port, [_|_]) when is_port(Port) ->
- {error, einval};
-get_cwd(_, _) ->
- {error, badarg}.
-
-get_cwd_int(Drive) ->
- get_cwd_int({?DRV, [binary]}, Drive).
-
-get_cwd_int(Port, Drive) ->
- drv_command(Port, <<?FILE_PWD, Drive>>,
- fun handle_fname_response/1).
-
-
-
-%% set_cwd/{1,2}
+pwrite(Fd, LocBytes) ->
+ try
+ #{ handle := FRef, r_buffer := RBuf } = get_fd_data(Fd),
+ prim_buffer:wipe(RBuf),
+ pwrite_list(FRef, LocBytes, 0)
+ catch
+ error:badarg -> {error, badarg}
+ end.
-set_cwd(Dir) ->
- set_cwd_int({?DRV, [binary]}, Dir).
+-spec pwrite_list(FRef, LocBytes, Successes) -> Result when
+ FRef :: prim_file_ref(),
+ LocBytes :: list({Offset :: non_neg_integer(),
+ IOData :: iodata()}),
+ Successes :: non_neg_integer(),
+ Result :: ok | {error, {Successes, Reason :: atom()}}.
+pwrite_list(_FRef, [], _Successes) ->
+ ok;
+pwrite_list(FRef, [{Offset, IOData} | Rest], Successes) ->
+ case pwrite_plain(FRef, Offset, erlang:iolist_to_iovec(IOData)) of
+ {error, Reason} -> {error, {Successes, Reason}};
+ ok -> pwrite_list(FRef, Rest, Successes + 1)
+ end.
-set_cwd(Port, Dir) when is_port(Port) ->
- set_cwd_int(Port, Dir).
+sendfile(Fd, Socket, Offset, Bytes, _ChunkSize, [], [], _Flags) ->
+ %% There's a very nasty race in here; if we die just prior to duplicating
+ %% the handle down in the sendfile call, it might get reused by something
+ %% entirely different and we'll leak unknown data to the socket until it
+ %% dies soon after.
+ %%
+ %% This bug was inherited from the old driver, except it was vulnerable to
+ %% the bug at any point and not just during setup.
+ %%
+ %% We'll have to live with this until we have a way to unambiguously
+ %% transfer things between drivers or NIFs. Current ideas all fall afoul
+ %% of the Two Generals problem.
+ try
+ advise(Fd, Offset, Bytes, sequential),
+ prim_inet:sendfile(Socket, get_handle(Fd), Offset, Bytes)
+ catch
+ error:badarg -> {error, badarg}
+ end;
+sendfile(_Fd, _Socket, _Offset, _Bytes, _ChunkSize, _Headers, _Trailers, _Flags) ->
+ {error, enotsup}.
-set_cwd_int(Port, Dir) when is_binary(Dir) ->
- case prim_file:is_translatable(Dir) of
- false ->
- {error, no_translation};
- true ->
- drv_command(Port, [?FILE_CHDIR, pathname(Dir)])
+%% Undocumented internal function that reads a data block with indirection.
+%%
+%% This is only used once in DETS and can easily be emulated with pread/2, but
+%% it's pretty performance-sensitive so we've implemented it down in the NIF to
+%% avoid excessive rescheduling.
+-spec ipread_s32bu_p32bu(Fd, Offset, MaxSize) -> Result when
+ Fd :: #file_descriptor{},
+ Offset :: non_neg_integer(),
+ MaxSize :: non_neg_integer() | infinity,
+ Result :: {ok, Size :: non_neg_integer(),
+ Pointer :: non_neg_integer(),
+ Data :: iodata() | eof} |
+ eof |
+ {error, Reason :: atom()}.
+ipread_s32bu_p32bu(Fd, Offset, Infinity) when is_atom(Infinity) ->
+ ipread_s32bu_p32bu(Fd, Offset, (1 bsl 31) - 1);
+ipread_s32bu_p32bu(Fd, Offset, MaxSize)
+ when is_integer(Offset), is_integer(MaxSize) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ ipread_s32bu_p32bu_nif(FRef, Offset, MaxSize)
+ catch
+ error:badarg -> {error, badarg}
end;
-set_cwd_int(Port, Dir) when is_list(Dir) ->
- drv_command(Port, [?FILE_CHDIR, pathname(Dir)]);
-set_cwd_int(_, _) ->
+ipread_s32bu_p32bu(_Fd, _Offset, _MaxSize) ->
{error, badarg}.
-
-
-
-%% delete/{1,2}
-
-delete(File) ->
- delete_int({?DRV, [binary]}, File).
-
-delete(Port, File) when is_port(Port) ->
- delete_int(Port, File).
-
-delete_int(Port, File) ->
- drv_command(Port, [?FILE_DELETE, pathname(File)]).
-
-
-
-%% rename/{2,3}
-
-rename(From, To) ->
- rename_int({?DRV, [binary]}, From, To).
-
-rename(Port, From, To) when is_port(Port) ->
- rename_int(Port, From, To).
-
-rename_int(Port, From, To) ->
- drv_command(Port, [?FILE_RENAME, pathname(From), pathname(To)]).
-
-
-
-%% make_dir/{1,2}
-
-make_dir(Dir) ->
- make_dir_int({?DRV, [binary]}, Dir).
-
-make_dir(Port, Dir) when is_port(Port) ->
- make_dir_int(Port, Dir).
-
-make_dir_int(Port, Dir) ->
- drv_command(Port, [?FILE_MKDIR, pathname(Dir)]).
-
-
-
-%% del_dir/{1,2}
-
-del_dir(Dir) ->
- del_dir_int({?DRV, [binary]}, Dir).
-
-del_dir(Port, Dir) when is_port(Port) ->
- del_dir_int(Port, Dir).
-
-del_dir_int(Port, Dir) ->
- drv_command(Port, [?FILE_RMDIR, pathname(Dir)]).
-
-
-
-%% read_file_info/{1,2,3}
-
-read_file_info(File) ->
- read_file_info_int({?DRV, [binary]}, File, local).
-
-read_file_info(Port, File) when is_port(Port) ->
- read_file_info_int(Port, File, local);
-read_file_info(File, Opts) ->
- read_file_info_int({?DRV, [binary]}, File, plgv(time, Opts, local)).
-
-read_file_info(Port, File, Opts) when is_port(Port) ->
- read_file_info_int(Port, File, plgv(time, Opts, local)).
-
-read_file_info_int(Port, File, TimeType) ->
+ipread_s32bu_p32bu_nif(_FRef, _Offset, _MaxSize) ->
+ erlang:nif_error(undef).
+
+%% Returns the binary representation of the underlying handle, for use in
+%% tricky operations like sendfile/8.
+-spec get_handle(Fd) -> Result when
+ Fd :: #file_descriptor{},
+ Result :: binary() | {error, Reason :: atom()}.
+get_handle(Fd) ->
try
- case drv_command(Port, [?FILE_FSTAT, pathname(File)]) of
- {ok, FI} -> {ok, FI#file_info{
- ctime = from_seconds(FI#file_info.ctime, TimeType),
- mtime = from_seconds(FI#file_info.mtime, TimeType),
- atime = from_seconds(FI#file_info.atime, TimeType)
- }};
- Error -> Error
- end
+ #{ handle := FRef } = get_fd_data(Fd),
+ get_handle_nif(FRef)
catch
- error:_ -> {error, badarg}
+ error:badarg -> {error, badarg}
end.
+%% Resets the write head to the position the user believes we're at, which may
+%% not be the same as the real one when read caching is in effect.
+reset_write_position(Fd) ->
+ #{ r_buffer := RBuf } = Fd#file_descriptor.data,
+ case prim_buffer:size(RBuf) of
+ Size when Size > 0 -> position(Fd, cur);
+ Size when Size =:= 0 -> ok
+ end.
-%% altname/{1,2}
-
-altname(File) ->
- altname_int({?DRV, [binary]}, File).
-
-altname(Port, File) when is_port(Port) ->
- altname_int(Port, File).
-
-altname_int(Port, File) ->
- drv_command(Port, [?FILE_ALTNAME, pathname(File)],
- fun handle_fname_response/1).
-
-%% write_file_info/{2,3,4}
-
-write_file_info(File, Info) ->
- write_file_info_int({?DRV, [binary]}, File, Info, local).
-
-write_file_info(Port, File, Info) when is_port(Port) ->
- write_file_info_int(Port, File, Info, local);
-write_file_info(File, Info, Opts) ->
- write_file_info_int({?DRV, [binary]}, File, Info, plgv(time, Opts, local)).
-
-write_file_info(Port, File, Info, Opts) when is_port(Port) ->
- write_file_info_int(Port, File, Info, plgv(time, Opts, local)).
+get_fd_data(#file_descriptor{ data = Data }) ->
+ #{ owner := Owner } = Data,
+ case self() of
+ Owner -> Data;
+ _ -> error(not_on_controlling_process)
+ end.
-write_file_info_int(Port, File,
- #file_info{mode=Mode,
- uid=Uid,
- gid=Gid,
- atime=Atime0,
- mtime=Mtime0,
- ctime=Ctime0},
- TimeType) ->
+build_fd_data(FRef, Modes) ->
+ Defaults =
+ #{ owner => self(),
+ handle => FRef,
+ r_ahead_size => 0,
+ r_buffer => prim_buffer:new() },
+ fill_fd_option_map(Modes, Defaults).
+
+fill_fd_option_map([], Map) ->
+ Map;
+
+fill_fd_option_map([read_ahead | Modes], Map) ->
+ fill_fd_option_map([{read_ahead, 64 bsl 10} | Modes], Map);
+fill_fd_option_map([{read_ahead, Size} | Modes], Map) ->
+ fill_fd_option_map(Modes, Map#{ r_ahead_size => Size });
+
+fill_fd_option_map([_Ignored | Modes], Map) ->
+ fill_fd_option_map(Modes, Map).
+
+open_nif(_Name, _Modes) ->
+ erlang:nif_error(undef).
+close_nif(_FileRef) ->
+ erlang:nif_error(undef).
+read_nif(_FileRef, _Size) ->
+ erlang:nif_error(undef).
+write_nif(_FileRef, _IOVec) ->
+ erlang:nif_error(undef).
+pread_nif(_FileRef, _Offset, _Size) ->
+ erlang:nif_error(undef).
+pwrite_nif(_FileRef, _Offset, _IOVec) ->
+ erlang:nif_error(undef).
+seek_nif(_FileRef, _Mark, _Offset) ->
+ erlang:nif_error(undef).
+sync_nif(_FileRef, _DataOnly) ->
+ erlang:nif_error(undef).
+advise_nif(_FileRef, _Offset, _Length, _Advise) ->
+ erlang:nif_error(undef).
+allocate_nif(_FileRef, _Offset, _Length) ->
+ erlang:nif_error(undef).
+truncate_nif(_FileRef) ->
+ erlang:nif_error(undef).
+get_handle_nif(_FileRef) ->
+ erlang:nif_error(undef).
- % Atime and/or Mtime might be undefined
- % - use localtime() for atime, if atime is undefined
- % - use atime as mtime if mtime is undefined
- % - use mtime as ctime if ctime is undefined
+%%
+%% Quality-of-life helpers
+%%
+read_file(Filename) ->
+ %% We're doing this operation in the NIF to avoid excessive rescheduling.
try
- Atime = file_info_validate_atime(Atime0, TimeType),
- Mtime = file_info_validate_mtime(Mtime0, Atime),
- Ctime = file_info_validate_ctime(Ctime0, Mtime),
-
- drv_command(Port, [?FILE_WRITE_INFO,
- int_to_int32bytes(Mode),
- int_to_int32bytes(Uid),
- int_to_int32bytes(Gid),
- int_to_int64bytes(to_seconds(Atime, TimeType)),
- int_to_int64bytes(to_seconds(Mtime, TimeType)),
- int_to_int64bytes(to_seconds(Ctime, TimeType)),
- pathname(File)])
+ read_file_nif(encode_path(Filename))
catch
- error:_ -> {error, badarg}
+ error:badarg -> {error, badarg}
+ end.
+read_file_nif(_Filename) ->
+ erlang:nif_error(undef).
+
+write_file(Filename, Bytes) ->
+ write_file(Filename, Bytes, []).
+write_file(Filename, Bytes, Modes) ->
+ case open(Filename, [write, binary | Modes]) of
+ {ok, Fd} ->
+ Result = write(Fd, Bytes),
+ close(Fd),
+ Result;
+ {error, Reason} ->
+ {error, Reason}
end.
+%%
+%% Filesystem operations
+%%
-file_info_validate_atime(Atime, _) when Atime =/= undefined -> Atime;
-file_info_validate_atime(undefined, local) -> erlang:localtime();
-file_info_validate_atime(undefined, universal) -> erlang:universaltime();
-file_info_validate_atime(undefined, posix) -> erlang:universaltime_to_posixtime(erlang:universaltime()).
-
-file_info_validate_mtime(undefined, Atime) -> Atime;
-file_info_validate_mtime(Mtime, _) -> Mtime.
-
-file_info_validate_ctime(undefined, Mtime) -> Mtime;
-file_info_validate_ctime(Ctime, _) -> Ctime.
-
-%% make_link/{2,3}
-
-make_link(Old, New) ->
- make_link_int({?DRV, [binary]}, Old, New).
-
-make_link(Port, Old, New) when is_port(Port) ->
- make_link_int(Port, Old, New).
-
-make_link_int(Port, Old, New) ->
- drv_command(Port, [?FILE_LINK, pathname(Old), pathname(New)]).
-
-
-
-%% make_symlink/{2,3}
-
-make_symlink(Old, New) ->
- make_symlink_int({?DRV, [binary]}, Old, New).
-
-make_symlink(Port, Old, New) when is_port(Port) ->
- make_symlink_int(Port, Old, New).
-
-make_symlink_int(Port, Old, New) ->
- drv_command(Port, [?FILE_SYMLINK, pathname(Old), pathname(New)]).
-
-
-
-%% read_link/{2,3}
-
-read_link(Link) ->
- read_link_int({?DRV, [binary]}, Link).
-
-read_link(Port, Link) when is_port(Port) ->
- read_link_int(Port, Link).
-
-read_link_int(Port, Link) ->
- drv_command(Port, [?FILE_READLINK, pathname(Link)],
- fun handle_fname_response/1).
-
-%% read_link_all/{2,3}
-
-read_link_all(Link) ->
- read_link_all_int({?DRV, [binary]}, Link).
-
-read_link_all(Port, Link) when is_port(Port) ->
- read_link_all_int(Port, Link).
-
-read_link_all_int(Port, Link) ->
- drv_command(Port, [?FILE_READLINK, pathname(Link)],
- fun handle_fname_response_all/1).
-
+read_link(Name) -> read_link_1(Name, false).
+read_link_all(Name) -> read_link_1(Name, true).
+read_link_1(Name, AcceptRawNames) ->
+ try read_link_nif(encode_path(Name)) of
+ {ok, RawName} -> translate_raw_name(RawName, AcceptRawNames);
+ {error, Reason} -> {error, Reason}
+ catch
+ error:badarg -> {error, badarg}
+ end.
-%% read_link_info/{2,3}
+translate_raw_name(RawName, SilentFailure) ->
+ case decode_path(RawName) of
+ Converted when is_list(Converted) -> {ok, Converted};
+ {error, _Reason} when SilentFailure =:= false -> {error, einval};
+ {error, _Reason} when SilentFailure =:= true -> {ok, RawName}
+ end.
-read_link_info(Link) ->
- read_link_info_int({?DRV, [binary]}, Link, local).
+list_dir(Name) -> list_dir_1(Name, true).
+list_dir_all(Name) -> list_dir_1(Name, false).
-read_link_info(Port, Link) when is_port(Port) ->
- read_link_info_int(Port, Link, local);
+list_dir_1(Name, SkipInvalid) ->
+ try list_dir_nif(encode_path(Name)) of
+ {ok, RawNames} -> list_dir_convert(RawNames, SkipInvalid, []);
+ {error, Reason} -> {error, Reason}
+ catch
+ error:badarg -> {error, badarg}
+ end.
-read_link_info(Link, Opts) ->
- read_link_info_int({?DRV, [binary]}, Link, plgv(time, Opts, local)).
+list_dir_convert([], _SkipInvalid, Result) ->
+ {ok, Result};
+list_dir_convert([RawName | Rest], SkipInvalid, Result) ->
+ case decode_path(RawName) of
+ Converted when is_list(Converted) ->
+ list_dir_convert(Rest, SkipInvalid, [Converted | Result]);
+ {error, _} when SkipInvalid =:= false ->
+ list_dir_convert(Rest, SkipInvalid, [RawName | Result]);
+
+ %% If the filename cannot be converted, return error or ignore with
+ %% optional error logger warning depending on +fn{u|a}{i|e|w} emulator
+ %% switches.
+ {error, ignore} ->
+ list_dir_convert(Rest, SkipInvalid, Result);
+ {error, warning} ->
+ %% this is equal to calling error_logger:warning_msg/2 which
+ %% we don't want to do from code_server during system boot
+ logger ! {log,warning,"Non-unicode filename ~p ignored\n", [RawName],
+ #{pid=>self(),
+ gl=>group_leader(),
+ time=>erlang:system_time(microsecond),
+ error_logger=>#{tag=>warning_msg}}},
+ list_dir_convert(Rest, SkipInvalid, Result);
+ {error, _} ->
+ {error, {no_translation, RawName}}
+ end.
-read_link_info(Port, Link, Opts) when is_port(Port) ->
- read_link_info_int(Port, Link, plgv(time, Opts, local)).
+read_file_info(Filename) ->
+ read_info_1(Filename, 1, local).
+read_file_info(Filename, Opts) ->
+ read_info_1(Filename, 1, proplist_get_value(time, Opts, local)).
+read_link_info(Name) ->
+ read_info_1(Name, 0, local).
+read_link_info(Name, Opts) ->
+ read_info_1(Name, 0, proplist_get_value(time, Opts, local)).
-read_link_info_int(Port, Link, TimeType) ->
+read_info_1(Name, FollowLinks, TimeType) ->
try
- case drv_command(Port, [?FILE_LSTAT, pathname(Link)]) of
- {ok, FI} -> {ok, FI#file_info{
- ctime = from_seconds(FI#file_info.ctime, TimeType),
- mtime = from_seconds(FI#file_info.mtime, TimeType),
- atime = from_seconds(FI#file_info.atime, TimeType)
- }};
- Error -> Error
- end
+ case read_info_nif(encode_path(Name), FollowLinks) of
+ {error, Reason} ->
+ {error, Reason};
+ FileInfo ->
+ CTime = from_posix_seconds(FileInfo#file_info.ctime, TimeType),
+ MTime = from_posix_seconds(FileInfo#file_info.mtime, TimeType),
+ ATime = from_posix_seconds(FileInfo#file_info.atime, TimeType),
+ {ok, FileInfo#file_info{ ctime = CTime,
+ mtime = MTime,
+ atime = ATime }}
+ end
catch
- error:_ -> {error, badarg}
+ error:_ -> {error, badarg}
end.
-%% list_dir/{1,2}
-
-list_dir(Dir) ->
- list_dir_int({?DRV, [binary]}, Dir).
-
-list_dir(Port, Dir) when is_port(Port) ->
- list_dir_int(Port, Dir).
-
-list_dir_int(Port, Dir) ->
- drv_command(Port, [?FILE_READDIR, pathname(Dir)],
- fun(P) ->
- case list_dir_response(P, []) of
- {ok, RawNames} ->
- try
- {ok, list_dir_convert(RawNames)}
- catch
- throw:Reason ->
- Reason
- end;
- Error ->
- Error
- end
- end).
-
-list_dir_all(Dir) ->
- list_dir_all_int({?DRV, [binary]}, Dir).
-
-list_dir_all(Port, Dir) when is_port(Port) ->
- list_dir_all_int(Port, Dir).
-
-list_dir_all_int(Port, Dir) ->
- drv_command(Port, [?FILE_READDIR, pathname(Dir)],
- fun(P) ->
- case list_dir_response(P, []) of
- {ok, RawNames} ->
- {ok, list_dir_convert_all(RawNames)};
- Error ->
- Error
- end
- end).
-
-list_dir_response(Port, Acc0) ->
- case drv_get_response(Port) of
- {lfname, []} ->
- {ok, Acc0};
- {lfname, Names} ->
- Acc = [Name || <<L:16,Name:L/binary>> <= Names] ++ Acc0,
- list_dir_response(Port, Acc);
- Error ->
- Error
+write_file_info(Filename, Info) ->
+ write_file_info_1(Filename, Info, local).
+write_file_info(Filename, Info, Opts) ->
+ write_file_info_1(Filename, Info, proplist_get_value(time, Opts, local)).
+
+write_file_info_1(Filename, Info, TimeType) ->
+ #file_info{ mode = Modes,
+ uid = Uid,
+ gid = Gid,
+ atime = ATime0,
+ mtime = MTime0,
+ ctime = CTime0} = Info,
+ try
+ % ATime and/or MTime might be undefined
+ % - use localtime() for atime, if atime is undefined
+ % - use atime as mtime if mtime is undefined
+ % - use mtime as ctime if ctime is undefined
+ ATime = file_info_convert_atime(ATime0, TimeType),
+ MTime = file_info_convert_mtime(MTime0, ATime, TimeType),
+ CTime = file_info_convert_ctime(CTime0, MTime, TimeType),
+ EncodedName = encode_path(Filename),
+
+ %% This is a bit ugly but we need to handle partial failures the same
+ %% way the old driver did.
+ throw_on_error(set_owner(EncodedName, Uid, Gid)),
+ throw_on_error(set_permissions(EncodedName, Modes)),
+ throw_on_error(set_time(EncodedName, ATime, MTime, CTime))
+ catch
+ throw:Reason -> {error, Reason};
+ error:_ -> {error, badarg}
end.
-list_dir_convert([Name|Names]) ->
- %% If the filename cannot be converted, return error or ignore
- %% with optional error logger warning, depending on +fn{u|a}{i|e|w}
- %% emulator switches.
- case prim_file:internal_native2name(Name) of
- {error, warning} ->
- error_logger:warning_msg("Non-unicode filename ~p ignored\n",
- [Name]),
- list_dir_convert(Names);
- {error, ignore} ->
- list_dir_convert(Names);
- {error, error} ->
- throw({error, {no_translation, Name}});
- Converted when is_list(Converted) ->
- [Converted|list_dir_convert(Names)]
- end;
-list_dir_convert([]) -> [].
-
-list_dir_convert_all([Name|Names]) ->
- %% If the filename cannot be converted, retain the filename as
- %% a binary.
- case prim_file:internal_native2name(Name) of
- {error, _} ->
- [Name|list_dir_convert_all(Names)];
- Converted when is_list(Converted) ->
- [Converted|list_dir_convert_all(Names)]
- end;
-list_dir_convert_all([]) -> [].
-
-%%%-----------------------------------------------------------------
-%%% Functions to communicate with the driver
-
-handle_fname_response(Port) ->
- case drv_get_response(Port) of
- {fname, Name} ->
- case prim_file:internal_native2name(Name) of
- {error, warning} ->
- error_logger:warning_msg("Non-unicode filename ~p "
- "ignored when reading link\n",
- [Name]),
- {error, einval};
- {error, _} ->
- {error, einval};
- Converted when is_list(Converted) ->
- {ok, Converted}
- end;
- Error ->
- Error
- end.
+set_owner(EncodedName, Uid, undefined) ->
+ set_owner(EncodedName, Uid, -1);
+set_owner(EncodedName, undefined, Gid) ->
+ set_owner(EncodedName, -1, Gid);
+set_owner(EncodedName, Uid, Gid) ->
+ set_owner_nif(EncodedName, Uid, Gid).
+set_owner_nif(_Path, _Uid, _Gid) ->
+ erlang:nif_error(undef).
-handle_fname_response_all(Port) ->
- case drv_get_response(Port) of
- {fname, Name} ->
- case prim_file:internal_native2name(Name) of
- {error, _} ->
- {ok, Name};
- Converted when is_list(Converted) ->
- {ok, Converted}
- end;
- Error ->
- Error
+set_permissions(_EncodedName, undefined) ->
+ ok;
+set_permissions(EncodedName, Permissions) ->
+ set_permissions_nif(EncodedName, Permissions).
+set_permissions_nif(_Path, _Permissions) ->
+ erlang:nif_error(undef).
+
+set_time(EncodedName, ATime, MTime, CTime) ->
+ set_time_nif(EncodedName, ATime, MTime, CTime).
+set_time_nif(_Path, _ATime, _MTime, _CTime) ->
+ erlang:nif_error(undef).
+
+throw_on_error(ok) -> ok;
+throw_on_error({error, enotsup}) -> ok;
+throw_on_error({error, Reason}) -> throw(Reason).
+
+file_info_convert_atime(ATime, TimeType) when ATime =/= undefined ->
+ to_posix_seconds(ATime, TimeType);
+file_info_convert_atime(undefined, local) ->
+ to_posix_seconds(erlang:localtime(), local);
+file_info_convert_atime(undefined, universal) ->
+ to_posix_seconds(erlang:universaltime(), universal);
+file_info_convert_atime(undefined, posix) ->
+ erlang:universaltime_to_posixtime(erlang:universaltime()).
+
+file_info_convert_mtime(undefined, ATime, _TimeType) ->
+ ATime;
+file_info_convert_mtime(MTime, _ATime, TimeType) ->
+ to_posix_seconds(MTime, TimeType).
+
+file_info_convert_ctime(undefined, MTime, _TimeType) ->
+ MTime;
+file_info_convert_ctime(CTime, _MTime, TimeType) ->
+ to_posix_seconds(CTime, TimeType).
+
+%% This is only relevant on Windows, so we assume that format to simplify the
+%% internals.
+get_cwd([Letter, $:]) when Letter >= $A, Letter =< $Z ->
+ get_dcwd(Letter - $A + 1);
+get_cwd([Letter, $:]) when Letter >= $a, Letter =< $z ->
+ get_dcwd(Letter - $a + 1);
+get_cwd([_|_]) ->
+ {error, einval};
+get_cwd(_) ->
+ {error, badarg}.
+get_dcwd(Index) ->
+ try get_device_cwd_nif(Index) of
+ {ok, RawPath} -> {ok, decode_path(RawPath)};
+ {error, Reason} -> {error, Reason}
+ catch
+ error:badarg -> {error, badarg}
end.
-%% Opens a driver port and converts any problems into {error, emfile}.
-%% Returns {ok, Port} when successful.
-
-drv_open(Driver, Portopts) ->
- try erlang:open_port({spawn_driver, Driver}, Portopts) of
- Port ->
- {ok, Port}
+get_cwd() ->
+ try get_cwd_nif() of
+ {ok, RawPath} -> {ok, decode_path(RawPath)};
+ {error, Reason} -> {error, Reason}
catch
- error:Reason ->
- {error, Reason}
+ error:badarg -> {error, badarg}
end.
-
-
-
-%% Closes a port in a safe way. Returns ok.
-
-drv_close(Port) ->
- Save = erlang:dt_spread_tag(false),
+set_cwd(Path) ->
try
- try erlang:port_close(Port) catch error:_ -> ok end,
- receive %% Ugly workaround in case the caller==owner traps exits
- {'EXIT', Port, _Reason} ->
- ok
- after 0 ->
- ok
- end
- after
- erlang:dt_restore_tag(Save)
+ case is_path_translatable(Path) of
+ true -> set_cwd_nif(encode_path(Path));
+ false -> {error, no_translation}
+ end
+ catch
+ error:badarg -> {error, badarg}
end.
-
-
-%% Issues a command to a port and gets the response.
-%% If Port is {Driver, Portopts} a port is first opened and
-%% then closed after the result has been received.
-%% Returns {ok, Result} or {error, Reason}.
-
-drv_command(Port, Command) ->
- drv_command(Port, Command, undefined).
-
-drv_command(Port, Command, R) when is_binary(Command) ->
- drv_command(Port, Command, true, R);
-drv_command(Port, Command, R) ->
- try erlang:iolist_size(Command) of
- _ ->
- drv_command(Port, Command, true, R)
+delete(Path) ->
+ try
+ del_file_nif(encode_path(Path))
catch
- error:Reason ->
- {error, Reason}
+ error:badarg -> {error, badarg}
end.
-drv_command(Port, Command, Validated, R) when is_port(Port) ->
- Save = erlang:dt_spread_tag(false),
- try erlang:port_command(Port, erlang:dt_append_vm_tag_data(Command)) of
- true ->
- drv_get_response(Port, R)
+rename(Source, Destination) ->
+ try
+ rename_nif(encode_path(Source), encode_path(Destination))
catch
- %% If the Command is valid, knowing that the port is a port,
- %% a badarg error must mean it is a dead port, that is:
- %% a currently invalid filehandle, -> einval, not badarg.
- error:badarg when Validated ->
- {error, einval};
- error:badarg ->
- try erlang:iolist_size(Command) of
- _ -> % Valid
- {error, einval}
- catch
- error:_ ->
- {error, badarg}
- end;
- error:Reason ->
- {error, Reason}
- after
- erlang:dt_restore_tag(Save)
- end;
-drv_command({Driver, Portopts}, Command, Validated, R) ->
- case drv_open(Driver, Portopts) of
- {ok, Port} ->
- Result = drv_command(Port, Command, Validated, R),
- drv_close(Port),
- Result;
- Error ->
- Error
+ error:badarg -> {error, badarg}
end.
-drv_command_nt(Port, Command, R) when is_port(Port) ->
- Save = erlang:dt_spread_tag(false),
- try erlang:port_command(Port, Command) of
- true ->
- drv_get_response(Port, R)
+make_dir(Path) ->
+ try
+ make_dir_nif(encode_path(Path))
catch
- error:badarg ->
- try erlang:iolist_size(Command) of
- _ -> % Valid
- {error, einval}
- catch
- error:_ ->
- {error, badarg}
- end;
- error:Reason ->
- {error, Reason}
- after
- erlang:dt_restore_tag(Save)
+ error:badarg -> {error, badarg}
end.
-
-
-
-%% Receives the response from a driver port.
-%% Returns: {ok, ListOrBinary}|{error, Reason}
-
-drv_get_response(Port, undefined) ->
- drv_get_response(Port);
-drv_get_response(Port, Fun) when is_function(Fun, 1) ->
- Fun(Port).
-
-drv_get_response(Port) ->
- erlang:bump_reductions(100),
- receive
- {Port, {data, [Response|Rest] = Data}} ->
- try translate_response(Response, Rest)
- catch
- error:Reason ->
- {error, {bad_response_from_port, Data,
- {Reason, erlang:get_stacktrace()}}}
- end;
- {'EXIT', Port, Reason} ->
- {error, {port_died, Reason}}
+del_dir(Path) ->
+ try
+ del_dir_nif(encode_path(Path))
+ catch
+ error:badarg -> {error, badarg}
end.
-
-
-%%%-----------------------------------------------------------------
-%%% Utility functions.
-
-%% Converts a list of mode atoms into a mode word for the driver.
-%% Returns {Mode, Portopts, Setopts} where Portopts is a list of
-%% options for erlang:open_port/2 and Setopts is a list of
-%% setopt commands to send to the port, or error Reason upon failure.
-
-open_mode(List) when is_list(List) ->
- case open_mode(List, 0, [], []) of
- {Mode, Portopts, Setopts} when Mode band
- (?EFILE_MODE_READ bor ?EFILE_MODE_WRITE)
- =:= 0 ->
- {Mode bor ?EFILE_MODE_READ, Portopts, Setopts};
- Other ->
- Other
+make_link(Existing, New) ->
+ try
+ make_hard_link_nif(encode_path(Existing), encode_path(New))
+ catch
+ error:badarg -> {error, badarg}
end.
-
-open_mode([raw|Rest], Mode, Portopts, Setopts) ->
- open_mode(Rest, Mode, Portopts, Setopts);
-open_mode([read|Rest], Mode, Portopts, Setopts) ->
- open_mode(Rest, Mode bor ?EFILE_MODE_READ, Portopts, Setopts);
-open_mode([write|Rest], Mode, Portopts, Setopts) ->
- open_mode(Rest, Mode bor ?EFILE_MODE_WRITE, Portopts, Setopts);
-open_mode([binary|Rest], Mode, Portopts, Setopts) ->
- open_mode(Rest, Mode, [binary | Portopts], Setopts);
-open_mode([compressed|Rest], Mode, Portopts, Setopts) ->
- open_mode(Rest, Mode bor ?EFILE_COMPRESSED, Portopts, Setopts);
-open_mode([append|Rest], Mode, Portopts, Setopts) ->
- open_mode(Rest, Mode bor ?EFILE_MODE_APPEND bor ?EFILE_MODE_WRITE,
- Portopts, Setopts);
-open_mode([exclusive|Rest], Mode, Portopts, Setopts) ->
- open_mode(Rest, Mode bor ?EFILE_MODE_EXCL, Portopts, Setopts);
-open_mode([sync|Rest], Mode, Portopts, Setopts) ->
- open_mode(Rest, Mode bor ?EFILE_MODE_SYNC, Portopts, Setopts);
-open_mode([delayed_write|Rest], Mode, Portopts, Setopts) ->
- open_mode([{delayed_write, 64*1024, 2000}|Rest], Mode,
- Portopts, Setopts);
-open_mode([{delayed_write, Size, Delay}|Rest], Mode, Portopts, Setopts)
- when is_integer(Size), 0 =< Size, is_integer(Delay), 0 =< Delay ->
- if
- Size < ?LARGEFILESIZE, Delay < 1 bsl 64 ->
- open_mode(Rest, Mode, Portopts,
- [<<?FILE_SETOPT, ?FILE_OPT_DELAYED_WRITE,
- Size:64, Delay:64>>
- | Setopts]);
- true ->
- einval
- end;
-open_mode([read_ahead|Rest], Mode, Portopts, Setopts) ->
- open_mode([{read_ahead, 64*1024}|Rest], Mode, Portopts, Setopts);
-open_mode([{read_ahead, Size}|Rest], Mode, Portopts, Setopts)
- when is_integer(Size), 0 =< Size ->
- if
- Size < ?LARGEFILESIZE ->
- open_mode(Rest, Mode, Portopts,
- [<<?FILE_SETOPT, ?FILE_OPT_READ_AHEAD,
- Size:64>> | Setopts]);
- true ->
- einval
- end;
-open_mode([], Mode, Portopts, Setopts) ->
- {Mode, reverse(Portopts), reverse(Setopts)};
-open_mode(_, _Mode, _Portopts, _Setopts) ->
- badarg.
-
-
-
-%% Converts a position tuple {bof, X} | {cur, X} | {eof, X} into
-%% {Offset, OriginCode} for the driver.
-%% Returns badarg upon failure.
-
-lseek_position(Pos)
- when is_integer(Pos) ->
- lseek_position({bof, Pos});
-lseek_position(bof) ->
- lseek_position({bof, 0});
-lseek_position(cur) ->
- lseek_position({cur, 0});
-lseek_position(eof) ->
- lseek_position({eof, 0});
-lseek_position({bof, Offset})
- when is_integer(Offset) ->
- {Offset, ?EFILE_SEEK_SET};
-lseek_position({cur, Offset})
- when is_integer(Offset) ->
- {Offset, ?EFILE_SEEK_CUR};
-lseek_position({eof, Offset})
- when is_integer(Offset) ->
- {Offset, ?EFILE_SEEK_END};
-lseek_position(_) ->
- badarg.
-
-
-
-%% Translates the response from the driver into
-%% {ok, Result} or {error, Reason}.
-
-translate_response(?FILE_RESP_OK, []) ->
- ok;
-translate_response(?FILE_RESP_ERROR, List) when is_list(List) ->
- {error, list_to_atom(List)};
-translate_response(?FILE_RESP_NUMBER, List) ->
- {N, []} = get_uint64(List),
- {ok, N};
-translate_response(?FILE_RESP_DATA, List) ->
- {_N, _Data} = ND = get_uint64(List),
- {ok, ND};
-translate_response(?FILE_RESP_INFO, List) when is_list(List) ->
- {ok, transform_info(List)};
-translate_response(?FILE_RESP_NUMERR, L0) ->
- {N, L1} = get_uint64(L0),
- {error, {N, list_to_atom(L1)}};
-translate_response(?FILE_RESP_LDATA, List) ->
- {ok, transform_ldata(List)};
-translate_response(?FILE_RESP_N2DATA,
- <<Offset:64, 0:64, Size:64>>) ->
- {ok, {Size, Offset, eof}};
-translate_response(?FILE_RESP_N2DATA,
- [<<Offset:64, 0:64, Size:64>> | <<>>]) ->
- {ok, {Size, Offset, eof}};
-translate_response(?FILE_RESP_N2DATA = X,
- [<<_:64, 0:64, _:64>> | _] = Data) ->
- {error, {bad_response_from_port, [X | Data]}};
-translate_response(?FILE_RESP_N2DATA = X,
- [<<_:64, _:64, _:64>> | <<>>] = Data) ->
- {error, {bad_response_from_port, [X | Data]}};
-translate_response(?FILE_RESP_N2DATA,
- [<<Offset:64, _ReadSize:64, Size:64>> | D]) ->
- {ok, {Size, Offset, D}};
-translate_response(?FILE_RESP_N2DATA = X, L0) when is_list(L0) ->
- {Offset, L1} = get_uint64(L0),
- {ReadSize, L2} = get_uint64(L1),
- {Size, L3} = get_uint64(L2),
- case {ReadSize, L3} of
- {0, []} ->
- {ok, {Size, Offset, eof}};
- {0, _} ->
- {error, {bad_response_from_port, [X | L0]}};
- {_, []} ->
- {error, {bad_response_from_port, [X | L0]}};
- _ ->
- {ok, {Size, Offset, L3}}
- end;
-translate_response(?FILE_RESP_EOF, []) ->
- eof;
-translate_response(?FILE_RESP_FNAME, Data) ->
- {fname, Data};
-translate_response(?FILE_RESP_LFNAME, Data) ->
- {lfname, Data};
-translate_response(?FILE_RESP_ALL_DATA, Data) ->
- {ok, Data};
-translate_response(X, Data) ->
- {error, {bad_response_from_port, [X | Data]}}.
-
-transform_info([
- Hsize1, Hsize2, Hsize3, Hsize4,
- Lsize1, Lsize2, Lsize3, Lsize4,
- Type1, Type2, Type3, Type4,
- Atime1, Atime2, Atime3, Atime4, Atime5, Atime6, Atime7, Atime8,
- Mtime1, Mtime2, Mtime3, Mtime4, Mtime5, Mtime6, Mtime7, Mtime8,
- Ctime1, Ctime2, Ctime3, Ctime4, Ctime5, Ctime6, Ctime7, Ctime8,
- Mode1, Mode2, Mode3, Mode4,
- Links1, Links2, Links3, Links4,
- Major1, Major2, Major3, Major4,
- Minor1, Minor2, Minor3, Minor4,
- Inode1, Inode2, Inode3, Inode4,
- Uid1, Uid2, Uid3, Uid4,
- Gid1, Gid2, Gid3, Gid4,
- Access1,Access2,Access3,Access4]) ->
- #file_info {
- size = uint32(Hsize1,Hsize2,Hsize3,Hsize4)*16#100000000 + uint32(Lsize1,Lsize2,Lsize3,Lsize4),
- type = file_type(uint32(Type1,Type2,Type3,Type4)),
- access = file_access(uint32(Access1,Access2,Access3,Access4)),
- atime = sint64(Atime1, Atime2, Atime3, Atime4, Atime5, Atime6, Atime7, Atime8),
- mtime = sint64(Mtime1, Mtime2, Mtime3, Mtime4, Mtime5, Mtime6, Mtime7, Mtime8),
- ctime = sint64(Ctime1, Ctime2, Ctime3, Ctime4, Ctime5, Ctime6, Ctime7, Ctime8),
- mode = uint32(Mode1,Mode2,Mode3,Mode4),
- links = uint32(Links1,Links2,Links3,Links4),
- major_device = uint32(Major1,Major2,Major3,Major4),
- minor_device = uint32(Minor1,Minor2,Minor3,Minor4),
- inode = uint32(Inode1,Inode2,Inode3,Inode4),
- uid = uint32(Uid1,Uid2,Uid3,Uid4),
- gid = uint32(Gid1,Gid2,Gid3,Gid4)
- }.
-
-
-file_type(1) -> device;
-file_type(2) -> directory;
-file_type(3) -> regular;
-file_type(4) -> symlink;
-file_type(_) -> other.
-
-file_access(0) -> none;
-file_access(1) -> write;
-file_access(2) -> read;
-file_access(3) -> read_write.
-
-int_to_int32bytes(Int) when is_integer(Int) ->
- <<Int:32>>;
-int_to_int32bytes(undefined) ->
- <<-1:32>>.
-
-int_to_int64bytes(Int) when is_integer(Int) ->
- <<Int:64/signed>>.
-
-
-sint64(I1,I2,I3,I4,I5,I6,I7,I8) when I1 > 127 ->
- ((I1 bsl 56) bor (I2 bsl 48) bor (I3 bsl 40) bor (I4 bsl 32) bor
- (I5 bsl 24) bor (I6 bsl 16) bor (I7 bsl 8) bor I8) - (1 bsl 64);
-sint64(I1,I2,I3,I4,I5,I6,I7,I8) ->
- ((I1 bsl 56) bor (I2 bsl 48) bor (I3 bsl 40) bor (I4 bsl 32) bor
- (I5 bsl 24) bor (I6 bsl 16) bor (I7 bsl 8) bor I8).
-
-
-uint32(X1,X2,X3,X4) ->
- (X1 bsl 24) bor (X2 bsl 16) bor (X3 bsl 8) bor X4.
-
-get_uint64(L0) ->
- {X1, L1} = get_uint32(L0),
- {X2, L2} = get_uint32(L1),
- {(X1 bsl 32) bor X2, L2}.
-
-get_uint32([X1,X2,X3,X4|List]) ->
- {(((((X1 bsl 8) bor X2) bsl 8) bor X3) bsl 8) bor X4, List}.
-
-
-%% Binary mode
-transform_ldata(<<0:32, 0:32>>) ->
- [];
-transform_ldata([<<0:32, N:32, Sizes/binary>> | Datas]) ->
- transform_ldata(N, Sizes, Datas, []);
-%% List mode
-transform_ldata([_,_,_,_,_,_,_,_|_] = L0) ->
- {0, L1} = get_uint32(L0),
- {N, L2} = get_uint32(L1),
- transform_ldata(N, L2, []).
-
-%% List mode
-transform_ldata(0, List, Sizes) ->
- transform_ldata(0, List, reverse(Sizes), []);
-transform_ldata(N, L0, Sizes) ->
- {Size, L1} = get_uint64(L0),
- transform_ldata(N-1, L1, [Size | Sizes]).
-
-%% Binary mode
-transform_ldata(1, <<0:64>>, <<>>, R) ->
- reverse(R, [eof]);
-transform_ldata(1, <<Size:64>>, Data, R)
- when byte_size(Data) =:= Size ->
- reverse(R, [Data]);
-transform_ldata(N, <<0:64, Sizes/binary>>, [<<>> | Datas], R) ->
- transform_ldata(N-1, Sizes, Datas, [eof | R]);
-transform_ldata(N, <<Size:64, Sizes/binary>>, [Data | Datas], R)
- when byte_size(Data) =:= Size ->
- transform_ldata(N-1, Sizes, Datas, [Data | R]);
-%% List mode
-transform_ldata(0, [], [], R) ->
- reverse(R);
-transform_ldata(0, List, [0 | Sizes], R) ->
- transform_ldata(0, List, Sizes, [eof | R]);
-transform_ldata(0, List, [Size | Sizes], R) ->
- {Front, Rear} = lists_split(List, Size),
- transform_ldata(0, Rear, Sizes, [Front | R]).
-
-lists_split(List, 0) when is_list(List) ->
- {[], List};
-lists_split(List, N) when is_list(List), is_integer(N), N < 0 ->
- erlang:error(badarg, [List, N]);
-lists_split(List, N) when is_list(List), is_integer(N) ->
- case lists_split(List, N, []) of
- premature_end_of_list ->
- erlang:error(badarg, [List, N]);
- Result ->
- Result
+make_symlink(Existing, New) ->
+ try
+ make_soft_link_nif(encode_path(Existing), encode_path(New))
+ catch
+ error:badarg -> {error, badarg}
end.
-lists_split(List, 0, Rev) ->
- {reverse(Rev), List};
-lists_split([], _, _) ->
- premature_end_of_list;
-lists_split([Hd | Tl], N, Rev) ->
- lists_split(Tl, N-1, [Hd | Rev]).
-
-%% We KNOW that lists:reverse/2 is a BIF.
-
-reverse(X) -> lists:reverse(X, []).
-reverse(L, T) -> lists:reverse(L, T).
+altname(Path) ->
+ try altname_nif(encode_path(Path)) of
+ {ok, RawPath} -> {ok, decode_path(RawPath)};
+ Other -> Other
+ catch
+ error:badarg -> {error, badarg}
+ end.
-% Will add zero termination too
-% The 'EXIT' tuple from a bad argument will eventually generate an error
-% in list_to_binary, which is caught and generates the {error,badarg} return
-pathname(File) ->
- (catch prim_file:internal_name2native(File)).
+list_dir_nif(_Path) ->
+ erlang:nif_error(undef).
+read_link_nif(_Path) ->
+ erlang:nif_error(undef).
+read_info_nif(_Path, _FollowLinks) ->
+ erlang:nif_error(undef).
+make_hard_link_nif(_Existing, _New) ->
+ erlang:nif_error(undef).
+make_soft_link_nif(_Existing, _New) ->
+ erlang:nif_error(undef).
+rename_nif(_Source, _Destination) ->
+ erlang:nif_error(undef).
+make_dir_nif(_Path) ->
+ erlang:nif_error(undef).
+del_file_nif(_Path) ->
+ erlang:nif_error(undef).
+del_dir_nif(_Path) ->
+ erlang:nif_error(undef).
+get_device_cwd_nif(_DevicePath) ->
+ erlang:nif_error(undef).
+set_cwd_nif(_Path) ->
+ erlang:nif_error(undef).
+get_cwd_nif() ->
+ erlang:nif_error(undef).
+altname_nif(_Path) ->
+ erlang:nif_error(undef).
+%%
+%% General helper functions.
+%%
-%% proplist:get_value/3
-plgv(K, [{K, V}|_], _) -> V;
-plgv(K, [_|KVs], D) -> plgv(K, KVs, D);
-plgv(_, [], D) -> D.
+%% We know for certain that lists:reverse/2 is a BIF, so it's safe to use it
+%% even though this module is preloaded.
+reverse_list(List) -> lists:reverse(List, []).
+
+proplist_get_value(_Key, [], Default) ->
+ Default;
+proplist_get_value(Key, [{Key, Value} | _Rest], _Default) ->
+ Value;
+proplist_get_value(Key, [Key | _Rest], _Default) ->
+ true;
+proplist_get_value(Key, [_Other | Rest], Default) ->
+ proplist_get_value(Key, Rest, Default).
+
+encode_path(Path) ->
+ prim_file:internal_name2native(Path).
+decode_path(NativePath) when is_binary(NativePath) ->
+ prim_file:internal_native2name(NativePath).
+
+is_path_translatable(Path) when is_list(Path) ->
+ true;
+is_path_translatable(Path) ->
+ prim_file:is_translatable(Path).
-%%
%% We don't actually want this here
+%%
%% We want to use posix time in all prim but erl_prim_loader makes that tricky
%% It is probably needed to redo the whole erl_prim_loader
-from_seconds(Seconds, posix) when is_integer(Seconds) ->
+from_posix_seconds(Seconds, posix) when is_integer(Seconds) ->
Seconds;
-from_seconds(Seconds, universal) when is_integer(Seconds) ->
+from_posix_seconds(Seconds, universal) when is_integer(Seconds) ->
erlang:posixtime_to_universaltime(Seconds);
-from_seconds(Seconds, local) when is_integer(Seconds) ->
+from_posix_seconds(Seconds, local) when is_integer(Seconds) ->
erlang:universaltime_to_localtime(erlang:posixtime_to_universaltime(Seconds)).
-to_seconds(Seconds, posix) when is_integer(Seconds) ->
+to_posix_seconds(Seconds, posix) when is_integer(Seconds) ->
Seconds;
-to_seconds({_,_} = Datetime, universal) ->
+to_posix_seconds({_,_} = Datetime, universal) ->
erlang:universaltime_to_posixtime(Datetime);
-to_seconds({_,_} = Datetime, local) ->
+to_posix_seconds({_,_} = Datetime, local) ->
erlang:universaltime_to_posixtime(erlang:localtime_to_universaltime(Datetime)).
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index 79ff013c77..8169943dde 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2018. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -28,9 +29,9 @@
-export([open/3, open/4, fdopen/4, fdopen/5, close/1]).
-export([bind/3, listen/1, listen/2, peeloff/2]).
-export([connect/3, connect/4, async_connect/4]).
--export([accept/1, accept/2, async_accept/2]).
+-export([accept/1, accept/2, accept/3, async_accept/2]).
-export([shutdown/2]).
--export([send/2, send/3, sendto/4, sendmsg/3]).
+-export([send/2, send/3, sendto/4, sendmsg/3, sendfile/4]).
-export([recv/2, recv/3, async_recv/3]).
-export([unrecv/2]).
-export([recvfrom/2, recvfrom/3]).
@@ -69,11 +70,13 @@ open(Protocol, Family, Type) ->
open(Protocol, Family, Type, Opts) ->
open(Protocol, Family, Type, Opts, ?INET_REQ_OPEN, []).
+%% FDOPEN(tcp|udp|sctp, inet|inet6|local, stream|dgram|seqpacket, integer())
+
fdopen(Protocol, Family, Type, Fd) when is_integer(Fd) ->
fdopen(Protocol, Family, Type, Fd, true).
fdopen(Protocol, Family, Type, Fd, Bound)
- when is_integer(Fd), Bound == true orelse Bound == false ->
+ when is_integer(Fd), is_boolean(Bound) ->
open(Protocol, Family, Type, [], ?INET_REQ_FDOPEN,
[?int32(Fd), enc_value_2(bool, Bound)]).
@@ -103,8 +106,9 @@ open(Protocol, Family, Type, Opts, Req, Data) ->
error:system_limit -> {error, system_limit}
end.
-enc_family(inet) -> ?INET_AF_INET;
-enc_family(inet6) -> ?INET_AF_INET6.
+enc_family(inet) -> ?INET_AF_INET;
+enc_family(inet6) -> ?INET_AF_INET6;
+enc_family(local) -> ?INET_AF_LOCAL.
enc_type(stream) -> ?INET_TYPE_STREAM;
enc_type(dgram) -> ?INET_TYPE_DGRAM;
@@ -127,37 +131,18 @@ drv2protocol(_) -> undefined.
%% TODO: shutdown equivalent for SCTP
%%
shutdown(S, read) when is_port(S) ->
- shutdown_2(S, 0);
+ shutdown_1(S, 0);
shutdown(S, write) when is_port(S) ->
shutdown_1(S, 1);
shutdown(S, read_write) when is_port(S) ->
shutdown_1(S, 2).
shutdown_1(S, How) ->
- case subscribe(S, [subs_empty_out_q]) of
- {ok,[{subs_empty_out_q,N}]} when N > 0 ->
- shutdown_pend_loop(S, N); %% wait for pending output to be sent
- _Other -> ok
- end,
- shutdown_2(S, How).
-
-shutdown_2(S, How) ->
case ctl_cmd(S, ?TCP_REQ_SHUTDOWN, [How]) of
{ok, []} -> ok;
{error,_}=Error -> Error
end.
-shutdown_pend_loop(S, N0) ->
- receive
- {empty_out_q,S} -> ok
- after ?INET_CLOSE_TIMEOUT ->
- case getstat(S, [send_pend]) of
- {ok,[{send_pend,N0}]} -> ok;
- {ok,[{send_pend,N}]} -> shutdown_pend_loop(S, N);
- _ -> ok
- end
- end.
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% CLOSE(insock()) -> ok
@@ -165,11 +150,16 @@ shutdown_pend_loop(S, N0) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
close(S) when is_port(S) ->
- case subscribe(S, [subs_empty_out_q]) of
- {ok, [{subs_empty_out_q,N}]} when N > 0 ->
- close_pend_loop(S, N); %% wait for pending output to be sent
+ case getopt(S, linger) of
+ {ok,{true,0}} ->
+ close_port(S);
_ ->
- close_port(S)
+ case subscribe(S, [subs_empty_out_q]) of
+ {ok, [{subs_empty_out_q,N}]} when N > 0 ->
+ close_pend_loop(S, N); %% wait for pending output to be sent
+ _ ->
+ close_port(S)
+ end
end.
close_pend_loop(S, N) ->
@@ -202,41 +192,52 @@ close_port(S) ->
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-bind(S,IP,Port) when is_port(S), is_integer(Port), Port >= 0, Port =< 65535 ->
- case ctl_cmd(S,?INET_REQ_BIND,enc_value(set, addr, {IP,Port})) of
- {ok, [P1,P0]} -> {ok, ?u16(P1, P0)};
- {error,_}=Error -> Error
- end;
-
%% Multi-homed "bind": sctp_bindx(). The Op is 'add' or 'remove'.
%% If no addrs are specified, it just does nothing.
%% Function returns {ok, S} on success, unlike TCP/UDP "bind":
-bind(S, Op, Addrs) when is_port(S), is_list(Addrs) ->
- case Op of
- add ->
- bindx(S, 1, Addrs);
- remove ->
- bindx(S, 0, Addrs);
- _ -> {error, einval}
+bind(S, add, Addrs) when is_port(S), is_list(Addrs) ->
+ bindx(S, 1, Addrs);
+bind(S, remove, Addrs) when is_port(S), is_list(Addrs) ->
+ bindx(S, 0, Addrs);
+bind(S, Addr, _) when is_port(S), tuple_size(Addr) =:= 2 ->
+ case type_value(set, addr, Addr) of
+ true ->
+ case ctl_cmd(S,?INET_REQ_BIND,enc_value(set, addr, Addr)) of
+ {ok, [P1,P0]} -> {ok, ?u16(P1, P0)};
+ {error, _} = Error -> Error
+ end;
+ false ->
+ {error, einval}
end;
-bind(_, _, _) -> {error, einval}.
+bind(S, IP, Port) ->
+ bind(S, {IP, Port}, 0).
bindx(S, AddFlag, Addrs) ->
case getprotocol(S) of
sctp ->
- %% Really multi-homed "bindx". Stringified args:
- %% [AddFlag, (AddrBytes see enc_value_2(addr,X))+]:
- Args =
- [?int8(AddFlag)|
- [enc_value(set, addr, {IP,Port}) ||
- {IP, Port} <- Addrs]],
- case ctl_cmd(S, ?SCTP_REQ_BINDX, Args) of
- {ok,_} -> {ok, S};
- {error,_}=Error -> Error
+ case bindx_check_addrs(Addrs) of
+ true ->
+ %% Really multi-homed "bindx". Stringified args:
+ %% [AddFlag, (AddrBytes see enc_value_2(addr,X))+]:
+ Args =
+ [?int8(AddFlag)|
+ [enc_value(set, addr, Addr) || Addr <- Addrs]],
+ case ctl_cmd(S, ?SCTP_REQ_BINDX, Args) of
+ {ok, _} -> {ok, S};
+ {error, _}=Error -> Error
+ end;
+ false ->
+ {error, einval}
end;
- _ -> {error, einval}
+ _ ->
+ {error, einval}
end.
+bindx_check_addrs([Addr|Addrs]) ->
+ type_value(set, addr, Addr) andalso bindx_check_addrs(Addrs);
+bindx_check_addrs([]) ->
+ true.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% CONNECT(insock(), IP, Port [,Timeout]) -> ok | {error, Reason}
@@ -245,7 +246,7 @@ bindx(S, AddFlag, Addrs) ->
%% if timeout is given:
%% timeout < 0 -> infinity
%% 0 -> immediate connect (mostly works for loopback)
-%% > 0 -> wait for timout ms if not connected then
+%% > 0 -> wait for timeout ms if not connected then
%% return {error, timeout}
%%
%% ASYNC_CONNECT(insock(), IP, Port, Timeout) -> {ok, S, Ref} | {error, Reason}
@@ -255,14 +256,24 @@ bindx(S, AddFlag, Addrs) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% For TCP, UDP or SCTP sockets.
%%
-connect(S, IP, Port) -> connect0(S, IP, Port, -1).
-connect(S, IP, Port, infinity) -> connect0(S, IP, Port, -1);
-connect(S, IP, Port, Time) -> connect0(S, IP, Port, Time).
+connect(S, IP, Port) ->
+ connect(S, IP, Port, infinity).
+%%
+connect(S, Addr, _, Time) when is_port(S), tuple_size(Addr) =:= 2 ->
+ case type_value(set, addr, Addr) of
+ true when Time =:= infinity ->
+ connect0(S, Addr, -1);
+ true when is_integer(Time) ->
+ connect0(S, Addr, Time);
+ false ->
+ {error, einval}
+ end;
+connect(S, IP, Port, Time) ->
+ connect(S, {IP, Port}, 0, Time).
-connect0(S, IP, Port, Time) when is_port(S), Port > 0, Port =< 65535,
- is_integer(Time) ->
- case async_connect(S, IP, Port, Time) of
+connect0(S, Addr, Time) ->
+ case async_connect0(S, Addr, Time) of
{ok, S, Ref} ->
receive
{inet_async, S, Ref, Status} ->
@@ -271,23 +282,41 @@ connect0(S, IP, Port, Time) when is_port(S), Port > 0, Port =< 65535,
Error -> Error
end.
+
+async_connect(S, Addr, _, Time) when is_port(S), tuple_size(Addr) =:= 2 ->
+ case type_value(set, addr, Addr) of
+ true when Time =:= infinity ->
+ async_connect0(S, Addr, -1);
+ true when is_integer(Time) ->
+ async_connect0(S, Addr, Time);
+ false ->
+ {error, einval}
+ end;
+%%
async_connect(S, IP, Port, Time) ->
- case ctl_cmd(S, ?INET_REQ_CONNECT,
- [enc_time(Time),?int16(Port),ip_to_bytes(IP)]) of
+ async_connect(S, {IP, Port}, 0, Time).
+
+async_connect0(S, Addr, Time) ->
+ case ctl_cmd(
+ S, ?INET_REQ_CONNECT,
+ [enc_time(Time),enc_value(set, addr, Addr)])
+ of
{ok, [R1,R0]} -> {ok, S, ?u16(R1,R0)};
- {error,_}=Error -> Error
+ {error, _}=Error -> Error
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
-%% ACCEPT(insock() [,Timeout] ) -> {ok,insock()} | {error, Reason}
+%% ACCEPT(insock() [,Timeout][,FamilyOpts] ) -> {ok,insock()} | {error, Reason}
%%
%% accept incoming connection on listen socket
%% if timeout is given:
%% timeout < 0 -> infinity
%% 0 -> immediate accept (poll)
-%% > 0 -> wait for timout ms for accept if no accept then
+%% > 0 -> wait for timeout ms for accept if no accept then
%% return {error, timeout}
+%% FamilyOpts are address family specific options to copy from
+%% listen socket to accepted socket
%%
%% ASYNC_ACCEPT(insock(), Timeout)
%%
@@ -298,17 +327,22 @@ async_connect(S, IP, Port, Time) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% For TCP sockets only.
%%
-accept(L) -> accept0(L, -1).
+accept(L) -> accept0(L, -1, []).
+
+accept(L, infinity) -> accept0(L, -1, []);
+accept(L, FamilyOpts) when is_list(FamilyOpts) -> accept0(L, -1, FamilyOpts);
+accept(L, Time) -> accept0(L, Time, []).
-accept(L, infinity) -> accept0(L, -1);
-accept(L, Time) -> accept0(L, Time).
+accept(L, infinity, FamilyOpts) -> accept0(L, -1, FamilyOpts);
+accept(L, Time, FamilyOpts) -> accept0(L, Time, FamilyOpts).
-accept0(L, Time) when is_port(L), is_integer(Time) ->
+accept0(L, Time, FamilyOpts)
+ when is_port(L), is_integer(Time), is_list(FamilyOpts) ->
case async_accept(L, Time) of
{ok, Ref} ->
receive
{inet_async, L, Ref, {ok,S}} ->
- accept_opts(L, S);
+ accept_opts(L, S, FamilyOpts);
{inet_async, L, Ref, Error} ->
Error
end;
@@ -316,15 +350,22 @@ accept0(L, Time) when is_port(L), is_integer(Time) ->
end.
%% setup options from listen socket on the connected socket
-accept_opts(L, S) ->
- case getopts(L, [active, nodelay, keepalive, delay_send, priority, tos]) of
+accept_opts(L, S, FamilyOpts) ->
+ case
+ getopts(
+ L,
+ [active, nodelay, keepalive, delay_send, priority]
+ ++ FamilyOpts)
+ of
{ok, Opts} ->
- case setopts(S, Opts) of
- ok -> {ok, S};
- Error -> close(S), Error
- end;
- Error ->
- close(S), Error
+ case setopts(S, Opts) of
+ ok ->
+ {ok, S};
+ Error1 ->
+ close(S), Error1
+ end;
+ Error2 ->
+ close(S), Error2
end.
async_accept(L, Time) ->
@@ -413,20 +454,34 @@ send(S, Data) ->
%% "sendto" is for UDP. IP and Port are set by the caller to 0 if the socket
%% is known to be connected.
-sendto(S, IP, Port, Data) when is_port(S), Port >= 0, Port =< 65535 ->
- ?DBG_FORMAT("prim_inet:sendto(~p, ~p, ~p, ~p)~n", [S,IP,Port,Data]),
- try erlang:port_command(S, [?int16(Port),ip_to_bytes(IP),Data]) of
- true ->
- receive
- {inet_reply,S,Reply} ->
- ?DBG_FORMAT("prim_inet:sendto() -> ~p~n", [Reply]),
- Reply
- end
- catch
- error:_ ->
- ?DBG_FORMAT("prim_inet:sendto() -> {error,einval}~n", []),
- {error,einval}
- end.
+sendto(S, Addr, _, Data) when is_port(S), tuple_size(Addr) =:= 2 ->
+ case type_value(set, addr, Addr) of
+ true ->
+ ?DBG_FORMAT("prim_inet:sendto(~p, ~p, ~p)~n", [S,Addr,Data]),
+ try
+ erlang:port_command(S, [enc_value(set, addr, Addr),Data])
+ of
+ true ->
+ receive
+ {inet_reply,S,Reply} ->
+ ?DBG_FORMAT(
+ "prim_inet:sendto() -> ~p~n", [Reply]),
+ Reply
+ end
+ catch
+ error:_ ->
+ ?DBG_FORMAT(
+ "prim_inet:sendto() -> {error,einval}~n", []),
+ {error,einval}
+ end;
+ false ->
+ ?DBG_FORMAT(
+ "prim_inet:sendto() -> {error,einval}~n", []),
+ {error,einval}
+ end;
+sendto(S, IP, Port, Data) ->
+ sendto(S, {IP, Port}, 0, Data).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
@@ -449,6 +504,55 @@ sendmsg(S, #sctp_sndrcvinfo{}=SRI, Data) when is_port(S) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
+%% SENDFILE(outsock(), Fd, Offset, Length) -> {ok,BytesSent} | {error, Reason}
+%%
+%% send Length data bytes from a file handle, to a socket, starting at Offset
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% "sendfile" is for TCP:
+
+sendfile(S, FileHandle, Offset, Length)
+ when not is_port(S);
+ not is_binary(FileHandle);
+ not is_integer(Offset);
+ not is_integer(Length) ->
+ {error, badarg};
+sendfile(S, FileHandle, Offset, Length) ->
+ case erlang:port_info(S, connected) of
+ {connected, Pid} when Pid =:= self() ->
+ sendfile_1(S, FileHandle, Offset, Length);
+ {connected, Pid} when Pid =/= self() ->
+ {error, not_owner};
+ _Other ->
+ {error, einval}
+ end.
+
+sendfile_1(S, FileHandle, Offset, 0) ->
+ sendfile_1(S, FileHandle, Offset, (1 bsl 63) - 1);
+sendfile_1(_S, _FileHandle, Offset, Length) when
+ Offset < 0; Offset > ((1 bsl 63) - 1);
+ Length < 0; Length > ((1 bsl 63) - 1) ->
+ {error, einval};
+sendfile_1(S, FileHandle, Offset, Length) ->
+ Args = [FileHandle,
+ ?int64(Offset),
+ ?int64(Length)],
+ case ctl_cmd(S, ?TCP_REQ_SENDFILE, Args) of
+ {ok, []} ->
+ receive
+ {sendfile, S, {ok, SentLow, SentHigh}} ->
+ {ok, SentLow bor (SentHigh bsl 32)};
+ {sendfile, S, {error, Reason}} ->
+ {error, Reason};
+ {'EXIT', S, _Reason} ->
+ {error, closed}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
%% RECV(insock(), Length, [Timeout]) -> {ok,Data} | {error, Reason}
%%
%% receive Length data bytes from a socket
@@ -497,29 +601,46 @@ async_recv(S, Length, Time) ->
%% oriented: preserved here only for API compatibility.
%%
recvfrom(S, Length) ->
- recvfrom0(S, Length, -1).
+ recvfrom(S, Length, infinity).
-recvfrom(S, Length, infinity) ->
+recvfrom(S, Length, infinity) when is_port(S) ->
recvfrom0(S, Length, -1);
-recvfrom(S, Length, Time) when is_integer(Time), Time < 16#ffffffff ->
- recvfrom0(S, Length, Time);
-recvfrom(_, _, _) -> {error,einval}.
+recvfrom(S, Length, Time) when is_port(S) ->
+ if
+ is_integer(Time), 0 =< Time, Time < 16#ffffffff ->
+ recvfrom0(S, Length, Time);
+ true ->
+ {error, einval}
+ end.
recvfrom0(S, Length, Time)
- when is_port(S), is_integer(Length), Length >= 0, Length =< 16#ffffffff ->
+ when is_integer(Length), 0 =< Length, Length =< 16#ffffffff ->
case ctl_cmd(S, ?PACKET_REQ_RECV,[enc_time(Time),?int32(Length)]) of
{ok,[R1,R0]} ->
Ref = ?u16(R1,R0),
receive
% Success, UDP:
- {inet_async, S, Ref, {ok, [F,P1,P0 | AddrData]}} ->
- {IP,Data} = get_ip(F, AddrData),
- {ok, {IP, ?u16(P1,P0), Data}};
+ {inet_async, S, Ref, {ok, {[F | AddrData], AncData}}} ->
+ %% With ancillary data
+ case get_addr(F, AddrData) of
+ {{Family, _} = Addr, Data} when is_atom(Family) ->
+ {ok, {Addr, 0, AncData, Data}};
+ {{IP, Port}, Data} ->
+ {ok, {IP, Port, AncData, Data}}
+ end;
+ {inet_async, S, Ref, {ok, [F | AddrData]}} ->
+ %% Without ancillary data
+ case get_addr(F, AddrData) of
+ {{Family, _} = Addr, Data} when is_atom(Family) ->
+ {ok, {Addr, 0, Data}};
+ {{IP, Port}, Data} ->
+ {ok, {IP, Port, Data}}
+ end;
% Success, SCTP:
{inet_async, S, Ref, {ok, {[F,P1,P0 | Addr], AncData, DE}}} ->
- {IP, _} = get_ip(F, Addr),
- {ok, {IP, ?u16(P1,P0), AncData, DE}};
+ {IP, _} = get_ip(F, Addr),
+ {ok, {IP, ?u16(P1, P0), AncData, DE}};
% Back-end error:
{inet_async, S, Ref, Error={error, _}} ->
@@ -538,21 +659,26 @@ recvfrom0(_, _, _) -> {error,einval}.
peername(S) when is_port(S) ->
case ctl_cmd(S, ?INET_REQ_PEER, []) of
- {ok, [F, P1,P0 | Addr]} ->
- {IP, _} = get_ip(F, Addr),
- {ok, { IP, ?u16(P1, P0) }};
- {error,_}=Error -> Error
+ {ok, [F | Addr]} ->
+ {A, _} = get_addr(F, Addr),
+ {ok, A};
+ {error, _} = Error -> Error
end.
-setpeername(S, {IP,Port}) when is_port(S) ->
- case ctl_cmd(S, ?INET_REQ_SETPEER, [?int16(Port),ip_to_bytes(IP)]) of
- {ok,[]} -> ok;
- {error,_}=Error -> Error
- end;
setpeername(S, undefined) when is_port(S) ->
case ctl_cmd(S, ?INET_REQ_SETPEER, []) of
- {ok,[]} -> ok;
- {error,_}=Error -> Error
+ {ok, []} -> ok;
+ {error, _} = Error -> Error
+ end;
+setpeername(S, Addr) when is_port(S) ->
+ case type_value(set, addr, Addr) of
+ true ->
+ case ctl_cmd(S, ?INET_REQ_SETPEER, enc_value(set, addr, Addr)) of
+ {ok, []} -> ok;
+ {error, _} = Error -> Error
+ end;
+ false ->
+ {error, einval}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -593,21 +719,28 @@ peernames(S, AssocId)
sockname(S) when is_port(S) ->
case ctl_cmd(S, ?INET_REQ_NAME, []) of
- {ok, [F, P1, P0 | Addr]} ->
- {IP, _} = get_ip(F, Addr),
- {ok, { IP, ?u16(P1, P0) }};
- {error,_}=Error -> Error
+ {ok, [F | Addr]} ->
+ {A, _} = get_addr(F, Addr),
+ {ok, A};
+ {error, _} = Error -> Error
end.
-setsockname(S, {IP,Port}) when is_port(S) ->
- case ctl_cmd(S, ?INET_REQ_SETNAME, [?int16(Port),ip_to_bytes(IP)]) of
- {ok,[]} -> ok;
- {error,_}=Error -> Error
- end;
setsockname(S, undefined) when is_port(S) ->
case ctl_cmd(S, ?INET_REQ_SETNAME, []) of
- {ok,[]} -> ok;
- {error,_}=Error -> Error
+ {ok, []} -> ok;
+ {error, _} = Error -> Error
+ end;
+setsockname(S, Addr) when is_port(S) ->
+ case type_value(set, addr, Addr) of
+ true ->
+ case
+ ctl_cmd(S, ?INET_REQ_SETNAME, enc_value(set, addr, Addr))
+ of
+ {ok, []} -> ok;
+ {error, _} = Error -> Error
+ end;
+ false ->
+ {error, einval}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1135,6 +1268,12 @@ enc_opt(sndbuf) -> ?INET_OPT_SNDBUF;
enc_opt(recbuf) -> ?INET_OPT_RCVBUF;
enc_opt(priority) -> ?INET_OPT_PRIORITY;
enc_opt(tos) -> ?INET_OPT_TOS;
+enc_opt(tclass) -> ?INET_OPT_TCLASS;
+enc_opt(recvtos) -> ?INET_OPT_RECVTOS;
+enc_opt(recvtclass) -> ?INET_OPT_RECVTCLASS;
+enc_opt(pktoptions) -> ?INET_OPT_PKTOPTIONS;
+enc_opt(ttl) -> ?INET_OPT_TTL;
+enc_opt(recvttl) -> ?INET_OPT_RECVTTL;
enc_opt(nodelay) -> ?TCP_OPT_NODELAY;
enc_opt(multicast_if) -> ?UDP_OPT_MULTICAST_IF;
enc_opt(multicast_ttl) -> ?UDP_OPT_MULTICAST_TTL;
@@ -1159,7 +1298,10 @@ enc_opt(delay_send) -> ?INET_LOPT_TCP_DELAY_SEND;
enc_opt(packet_size) -> ?INET_LOPT_PACKET_SIZE;
enc_opt(read_packets) -> ?INET_LOPT_READ_PACKETS;
enc_opt(netns) -> ?INET_LOPT_NETNS;
+enc_opt(show_econnreset) -> ?INET_LOPT_TCP_SHOW_ECONNRESET;
+enc_opt(line_delimiter) -> ?INET_LOPT_LINE_DELIM;
enc_opt(raw) -> ?INET_OPT_RAW;
+enc_opt(bind_to_device) -> ?INET_OPT_BIND_TO_DEVICE;
% Names of SCTP opts:
enc_opt(sctp_rtoinfo) -> ?SCTP_OPT_RTOINFO;
enc_opt(sctp_associnfo) -> ?SCTP_OPT_ASSOCINFO;
@@ -1192,7 +1334,13 @@ dec_opt(?INET_OPT_SNDBUF) -> sndbuf;
dec_opt(?INET_OPT_RCVBUF) -> recbuf;
dec_opt(?INET_OPT_PRIORITY) -> priority;
dec_opt(?INET_OPT_TOS) -> tos;
+dec_opt(?INET_OPT_TCLASS) -> tclass;
dec_opt(?TCP_OPT_NODELAY) -> nodelay;
+dec_opt(?INET_OPT_RECVTOS) -> recvtos;
+dec_opt(?INET_OPT_RECVTCLASS) -> recvtclass;
+dec_opt(?INET_OPT_PKTOPTIONS) -> pktoptions;
+dec_opt(?INET_OPT_TTL) -> ttl;
+dec_opt(?INET_OPT_RECVTTL) -> recvttl;
dec_opt(?UDP_OPT_MULTICAST_IF) -> multicast_if;
dec_opt(?UDP_OPT_MULTICAST_TTL) -> multicast_ttl;
dec_opt(?UDP_OPT_MULTICAST_LOOP) -> multicast_loop;
@@ -1216,7 +1364,10 @@ dec_opt(?INET_LOPT_TCP_DELAY_SEND) -> delay_send;
dec_opt(?INET_LOPT_PACKET_SIZE) -> packet_size;
dec_opt(?INET_LOPT_READ_PACKETS) -> read_packets;
dec_opt(?INET_LOPT_NETNS) -> netns;
+dec_opt(?INET_LOPT_TCP_SHOW_ECONNRESET) -> show_econnreset;
+dec_opt(?INET_LOPT_LINE_DELIM) -> line_delimiter;
dec_opt(?INET_OPT_RAW) -> raw;
+dec_opt(?INET_OPT_BIND_TO_DEVICE) -> bind_to_device;
dec_opt(I) when is_integer(I) -> undefined.
@@ -1264,6 +1415,12 @@ type_opt_1(sndbuf) -> int;
type_opt_1(recbuf) -> int;
type_opt_1(priority) -> int;
type_opt_1(tos) -> int;
+type_opt_1(tclass) -> int;
+type_opt_1(recvtos) -> bool;
+type_opt_1(recvtclass) -> bool;
+type_opt_1(pktoptions) -> opts;
+type_opt_1(ttl) -> int;
+type_opt_1(recvttl) -> bool;
type_opt_1(nodelay) -> bool;
type_opt_1(ipv6_v6only) -> bool;
%% multicast
@@ -1298,6 +1455,7 @@ type_opt_1(packet) ->
{httph_bin,?TCP_PB_HTTPH_BIN},
{ssl, ?TCP_PB_SSL_TLS}, % obsolete
{ssl_tls, ?TCP_PB_SSL_TLS}]};
+type_opt_1(line_delimiter) -> int;
type_opt_1(mode) ->
{enum,[{list, ?INET_MODE_LIST},
{binary, ?INET_MODE_BINARY}]};
@@ -1315,6 +1473,8 @@ type_opt_1(delay_send) -> bool;
type_opt_1(packet_size) -> uint;
type_opt_1(read_packets) -> uint;
type_opt_1(netns) -> binary;
+type_opt_1(show_econnreset) -> bool;
+type_opt_1(bind_to_device) -> binary;
%%
%% SCTP options (to be set). If the type is a record type, the corresponding
%% record signature is returned, otherwise, an "elementary" type tag
@@ -1503,14 +1663,49 @@ type_value_2(uint8, X) when X band 16#ff =:= X -> true;
type_value_2(time, infinity) -> true;
type_value_2(time, X) when is_integer(X), X >= 0 -> true;
type_value_2(ip,{A,B,C,D}) when ?ip(A,B,C,D) -> true;
+%%
type_value_2(addr, {any,Port}) ->
type_value_2(uint16, Port);
type_value_2(addr, {loopback,Port}) ->
type_value_2(uint16, Port);
-type_value_2(addr, {{A,B,C,D},Port}) when ?ip(A,B,C,D) ->
+type_value_2(addr, {IP,_} = Addr) when tuple_size(IP) =:= 4 ->
+ type_value_2(addr, {inet,Addr});
+type_value_2(addr, {IP,_} = Addr) when tuple_size(IP) =:= 8 ->
+ type_value_2(addr, {inet6,Addr});
+type_value_2(addr, {Local,_}) when is_list(Local); is_binary(Local) ->
+ type_value_2(addr, {local,Local});
+%%
+type_value_2(addr, {Family,{Tag,Port}})
+ when (Family =:= inet orelse Family =:= inet6) andalso
+ (Tag =:= any orelse Tag =:= loopback) ->
+ type_value_2(uint16, Port);
+type_value_2(addr, {inet,{{A,B,C,D},Port}})
+ when ?ip(A,B,C,D) ->
type_value_2(uint16, Port);
-type_value_2(addr, {{A,B,C,D,E,F,G,H},Port}) when ?ip6(A,B,C,D,E,F,G,H) ->
+type_value_2(addr, {inet6,{{A,B,C,D,E,F,G,H},Port}})
+ when ?ip6(A,B,C,D,E,F,G,H) ->
type_value_2(uint16, Port);
+type_value_2(addr, {local,Addr}) ->
+ if
+ is_binary(Addr) ->
+ byte_size(Addr) =< 255;
+ true ->
+ try
+ %% We either get a badarg from byte_size
+ %% or from characters_to_binary
+ byte_size(
+ unicode:characters_to_binary(
+ Addr, file:native_name_encoding()))
+ of
+ N when N =< 255 ->
+ true;
+ _ ->
+ false
+ catch error:badarg ->
+ false
+ end
+ end;
+%%
type_value_2(ether,[X1,X2,X3,X4,X5,X6])
when ?ether(X1,X2,X3,X4,X5,X6) -> true;
type_value_2({enum,List}, Enum) ->
@@ -1618,6 +1813,7 @@ enc_value_2(time, Val) -> ?int32(Val);
enc_value_2(ip,{A,B,C,D}) -> [A,B,C,D];
enc_value_2(ip, any) -> [0,0,0,0];
enc_value_2(ip, loopback) -> [127,0,0,1];
+%%
enc_value_2(addr, {any,Port}) ->
[?INET_AF_ANY|?int16(Port)];
enc_value_2(addr, {loopback,Port}) ->
@@ -1626,6 +1822,35 @@ enc_value_2(addr, {IP,Port}) when tuple_size(IP) =:= 4 ->
[?INET_AF_INET,?int16(Port)|ip4_to_bytes(IP)];
enc_value_2(addr, {IP,Port}) when tuple_size(IP) =:= 8 ->
[?INET_AF_INET6,?int16(Port)|ip6_to_bytes(IP)];
+enc_value_2(addr, {File,_}) when is_list(File); is_binary(File) ->
+ [?INET_AF_LOCAL,iolist_size(File)|File];
+%%
+enc_value_2(addr, {inet,{any,Port}}) ->
+ [?INET_AF_INET,?int16(Port),0,0,0,0];
+enc_value_2(addr, {inet,{loopback,Port}}) ->
+ [?INET_AF_INET,?int16(Port),127,0,0,1];
+enc_value_2(addr, {inet,{IP,Port}}) ->
+ [?INET_AF_INET,?int16(Port)|ip4_to_bytes(IP)];
+enc_value_2(addr, {inet6,{any,Port}}) ->
+ [?INET_AF_INET6,?int16(Port),0,0,0,0,0,0,0,0];
+enc_value_2(addr, {inet6,{loopback,Port}}) ->
+ [?INET_AF_INET6,?int16(Port),0,0,0,0,0,0,0,1];
+enc_value_2(addr, {inet6,{IP,Port}}) ->
+ [?INET_AF_INET6,?int16(Port)|ip6_to_bytes(IP)];
+enc_value_2(addr, {local,Addr}) ->
+ %% A binary is passed as is, but anything else will be
+ %% regarded as a filename and therefore encoded according to
+ %% the current system filename encoding mode.
+ Bin =
+ if
+ is_binary(Addr) ->
+ Addr;
+ true ->
+ unicode:characters_to_binary(
+ Addr, file:native_name_encoding())
+ end,
+ [?INET_AF_LOCAL,byte_size(Bin),Bin];
+%%
enc_value_2(ether, [_,_,_,_,_,_]=Xs) -> Xs;
enc_value_2(sockaddr, any) ->
[?INET_AF_ANY];
@@ -1702,6 +1927,11 @@ dec_value(binary,[L0,L1,L2,L3|List]) ->
Len = ?i32(L0,L1,L2,L3),
{X,T}=split(Len,List),
{list_to_binary(X),T};
+dec_value(opts, [L0,L1,L2,L3|List]) ->
+ Len = ?u32(L0,L1,L2,L3),
+ {X,T} = split(Len, List),
+ Opts = dec_opt_val(X),
+ {Opts,T};
dec_value(Types, List) when is_tuple(Types) ->
{L,T} = dec_value_tuple(Types, List, 1, []),
{list_to_tuple(L),T};
@@ -2256,9 +2486,6 @@ utf8_to_characters(Bs, U, 0) ->
utf8_to_characters([B|Bs], U, N) when ((B band 16#3F) bor 16#80) =:= B ->
utf8_to_characters(Bs, (U bsl 6) bor (B band 16#3F), N-1).
-ip_to_bytes(IP) when tuple_size(IP) =:= 4 -> ip4_to_bytes(IP);
-ip_to_bytes(IP) when tuple_size(IP) =:= 8 -> ip6_to_bytes(IP).
-
ip4_to_bytes({A,B,C,D}) ->
[A band 16#ff, B band 16#ff, C band 16#ff, D band 16#ff].
@@ -2268,18 +2495,32 @@ ip6_to_bytes({A,B,C,D,E,F,G,H}) ->
get_addrs([]) ->
[];
-get_addrs([F,P1,P0|Addr]) ->
- {IP,Addrs} = get_ip(F, Addr),
- [{IP,?u16(P1, P0)}|get_addrs(Addrs)].
-
-get_ip(?INET_AF_INET, Addr) -> get_ip4(Addr);
-get_ip(?INET_AF_INET6, Addr) -> get_ip6(Addr).
+get_addrs([F|Addrs]) ->
+ {Addr,Rest} = get_addr(F, Addrs),
+ [Addr|get_addrs(Rest)].
+
+get_addr(?INET_AF_LOCAL, [N|Addr]) ->
+ {A,Rest} = lists:split(N, Addr),
+ {{local,iolist_to_binary(A)},Rest};
+get_addr(?INET_AF_UNSPEC, Rest) ->
+ {{unspec,<<>>},Rest};
+get_addr(?INET_AF_UNDEFINED, Rest) ->
+ {{undefined,<<>>},Rest};
+get_addr(Family, [P1,P0|Addr]) ->
+ {IP,Rest} = get_ip(Family, Addr),
+ {{IP,?u16(P1, P0)},Rest}.
+
+get_ip(?INET_AF_INET, Addr) ->
+ get_ip4(Addr);
+get_ip(?INET_AF_INET6, Addr) ->
+ get_ip6(Addr).
get_ip4([A,B,C,D | T]) -> {{A,B,C,D},T}.
get_ip6([X1,X2,X3,X4,X5,X6,X7,X8,X9,X10,X11,X12,X13,X14,X15,X16 | T]) ->
{ { ?u16(X1,X2),?u16(X3,X4),?u16(X5,X6),?u16(X7,X8),
- ?u16(X9,X10),?u16(X11,X12),?u16(X13,X14),?u16(X15,X16)}, T}.
+ ?u16(X9,X10),?u16(X11,X12),?u16(X13,X14),?u16(X15,X16)},
+ T }.
%% Control command
diff --git a/erts/preloaded/src/prim_zip.erl b/erts/preloaded/src/prim_zip.erl
index 1d5ab52a24..ca5cfec0e3 100644
--- a/erts/preloaded/src/prim_zip.erl
+++ b/erts/preloaded/src/prim_zip.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
@@ -73,8 +74,8 @@ open(FilterFun, FilterAcc, F) when is_function(FilterFun, 2) ->
throw(Reason);
throw:InternalReason ->
{error, InternalReason};
- Class:Reason ->
- erlang:error(erlang:raise(Class, Reason, erlang:get_stacktrace()))
+ Class:Reason:Stk ->
+ erlang:error(erlang:raise(Class, Reason, Stk))
end;
open(_, _, _) ->
{error, einval}.
@@ -88,9 +89,9 @@ do_open(FilterFun, FilterAcc, F) ->
{PrimZip2, FilterAcc2} = get_central_dir(PrimZip, FilterFun, FilterAcc),
{ok, PrimZip2, FilterAcc2}
catch
- Class:Reason ->
+ Class:Reason:Stk ->
_ = close(PrimZip),
- erlang:error(erlang:raise(Class, Reason, erlang:get_stacktrace()))
+ erlang:error(erlang:raise(Class, Reason, Stk))
end.
%% iterate over all files in a zip archive
@@ -105,8 +106,8 @@ foldl(FilterFun, FilterAcc, #primzip{files = Files} = PrimZip)
throw(Reason);
throw:InternalReason ->
{error, InternalReason};
- Class:Reason ->
- erlang:error(erlang:raise(Class, Reason, erlang:get_stacktrace()))
+ Class:Reason:Stk ->
+ erlang:error(erlang:raise(Class, Reason, Stk))
end;
foldl(_, _, _) ->
{error, einval}.
diff --git a/erts/preloaded/src/zip_internal.hrl b/erts/preloaded/src/zip_internal.hrl
index a8f7b1f1b7..2769ca152d 100644
--- a/erts/preloaded/src/zip_internal.hrl
+++ b/erts/preloaded/src/zip_internal.hrl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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%
%%
diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl
index df7b2e6198..6f53e67901 100644
--- a/erts/preloaded/src/zlib.erl
+++ b/erts/preloaded/src/zlib.erl
@@ -1,36 +1,50 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2018. All Rights Reserved.
%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% 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
%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
+%% 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(zlib).
--export([open/0,close/1,deflateInit/1,deflateInit/2,deflateInit/6,
- deflateSetDictionary/2,deflateReset/1,deflateParams/3,
- deflate/2,deflate/3,deflateEnd/1,
- inflateInit/1,inflateInit/2,inflateSetDictionary/2,
- inflateSync/1,inflateReset/1,inflate/2,inflateEnd/1,
- setBufSize/2,getBufSize/1,
- crc32/1,crc32/2,crc32/3,adler32/2,adler32/3,getQSize/1,
- crc32_combine/4,adler32_combine/4,
- compress/1,uncompress/1,zip/1,unzip/1,
- gzip/1,gunzip/1]).
-
--export_type([zstream/0]).
+-export([open/0,close/1,set_controlling_process/2,
+ deflateInit/1,deflateInit/2,deflateInit/6,
+ deflateSetDictionary/2,deflateReset/1,deflateParams/3,
+ deflate/2,deflate/3,deflateEnd/1,
+ inflateInit/1,inflateInit/2,inflateInit/3,
+ inflateSetDictionary/2,inflateGetDictionary/1, inflateReset/1,
+ inflate/2,inflate/3,inflateEnd/1,
+ inflateChunk/2,inflateChunk/1,
+ safeInflate/2,
+ setBufSize/2,getBufSize/1,
+ crc32/1,crc32/2,crc32/3,adler32/2,adler32/3,
+ crc32_combine/4,adler32_combine/4,
+ compress/1,uncompress/1,zip/1,unzip/1,
+ gzip/1,gunzip/1]).
+
+-export([on_load/0]).
+
+%% These are soft-deprecated until OTP 21.
+% -deprecated([inflateChunk/1, inflateChunk/2,
+% getBufSize/1, setBufSize/2,
+% crc32/1,crc32/2,crc32/3,adler32/2,adler32/3,
+% crc32_combine/4,adler32_combine/4]).
+
+-export_type([zstream/0, zflush/0, zlevel/0, zwindowbits/0, zmemlevel/0,
+ zstrategy/0]).
%% flush argument encoding
-define(Z_NO_FLUSH, 0).
@@ -53,114 +67,88 @@
%% deflate compression method
-define(Z_DEFLATED, 8).
--define(Z_NULL, 0).
-
-define(MAX_WBITS, 15).
-%% gzip defs (rfc 1952)
-
--define(ID1, 16#1f).
--define(ID2, 16#8b).
-
--define(FTEXT, 16#01).
--define(FHCRC, 16#02).
--define(FEXTRA, 16#04).
--define(FNAME, 16#08).
--define(FCOMMENT, 16#10).
--define(RESERVED, 16#E0).
-
--define(OS_MDDOS, 0).
--define(OS_AMIGA, 1).
--define(OS_OPENVMS, 2).
--define(OS_UNIX, 3).
--define(OS_VMCMS, 4).
--define(OS_ATARI, 5).
--define(OS_OS2, 6).
--define(OS_MAC, 7).
--define(OS_ZSYS, 8).
--define(OS_CPM, 9).
--define(OS_TOP20, 10).
--define(OS_NTFS, 11).
--define(OS_QDOS, 12).
--define(OS_ACORN, 13).
--define(OS_UNKNOWN,255).
-
--define(DEFLATE_INIT, 1).
--define(DEFLATE_INIT2, 2).
--define(DEFLATE_SETDICT, 3).
--define(DEFLATE_RESET, 4).
--define(DEFLATE_END, 5).
--define(DEFLATE_PARAMS, 6).
--define(DEFLATE, 7).
-
--define(INFLATE_INIT, 8).
--define(INFLATE_INIT2, 9).
--define(INFLATE_SETDICT, 10).
--define(INFLATE_SYNC, 11).
--define(INFLATE_RESET, 12).
--define(INFLATE_END, 13).
--define(INFLATE, 14).
-
--define(CRC32_0, 15).
--define(CRC32_1, 16).
--define(CRC32_2, 17).
-
--define(SET_BUFSZ, 18).
--define(GET_BUFSZ, 19).
--define(GET_QSIZE, 20).
-
--define(ADLER32_1, 21).
--define(ADLER32_2, 22).
-
--define(CRC32_COMBINE, 23).
--define(ADLER32_COMBINE, 24).
+-define(DEFAULT_MEMLEVEL, 8).
+-define(DEFAULT_WBITS, 15).
+
+-define(EOS_BEHAVIOR_ERROR, 0).
+-define(EOS_BEHAVIOR_RESET, 1).
+-define(EOS_BEHAVIOR_CUT, 2).
+
+%% Chunk sizes are hardcoded on account of them screwing with the
+%% predictability of the system. zlib is incapable of trapping so we need to
+%% ensure that it never operates on any significant amount of data.
+-define(DEFLATE_IN_CHUNKSIZE, 8 bsl 10).
+-define(DEFLATE_OUT_CHUNKSIZE, 8 bsl 10).
+-define(INFLATE_IN_CHUNKSIZE, 8 bsl 10).
+-define(INFLATE_OUT_CHUNKSIZE, 16 bsl 10).
%%------------------------------------------------------------------------
-%% Main data types of the file
--type zstream() :: port().
+%% Public data types.
+-type zstream() :: reference().
+-type zflush() :: 'none' | 'sync' | 'full' | 'finish'.
+
+-type zlevel() ::
+ 'none' | 'default' | 'best_compression' | 'best_speed' | 0..9.
+-type zstrategy() :: 'default' | 'filtered' | 'huffman_only' | 'rle'.
+
+-type zmemlevel() :: 1..9.
+-type zwindowbits() :: -15..-8 | 8..47.
+
+%% Private data types.
-%% Auxiliary data types of the file
--type zlevel() :: 'none' | 'default' | 'best_compression' | 'best_speed'
- | 0..9.
-type zmethod() :: 'deflated'.
--type zwindowbits() :: -15..-9 | 9..47.
--type zmemlevel() :: 1..9.
--type zstrategy() :: 'default' | 'filtered' | 'huffman_only' | 'rle'.
+
+-record(zlib_opts, {
+ stream :: zstream() | 'undefined',
+ method :: function(),
+ input_chunk_size :: pos_integer(),
+ output_chunk_size :: pos_integer(),
+ flush :: non_neg_integer()
+ }).
%%------------------------------------------------------------------------
-%% open a z_stream
+on_load() ->
+ case erlang:load_nif(atom_to_list(?MODULE), 0) of
+ ok -> ok
+ end.
+
-spec open() -> zstream().
open() ->
- open_port({spawn, "zlib_drv"}, [binary]).
+ open_nif().
+open_nif() ->
+ erlang:nif_error(undef).
-%% close and release z_stream
-spec close(Z) -> 'ok' when
Z :: zstream().
close(Z) ->
- try
- true = port_close(Z),
- receive %In case the caller is the owner and traps exits
- {'EXIT',Z,_} -> ok
- after 0 -> ok
- end
- catch _:_ -> erlang:error(badarg)
- end.
+ close_nif(Z).
+close_nif(_Z) ->
+ erlang:nif_error(undef).
+
+-spec set_controlling_process(Z, Pid) -> 'ok' when
+ Z :: zstream(),
+ Pid :: pid().
+set_controlling_process(Z, Pid) ->
+ set_controller_nif(Z, Pid).
+set_controller_nif(_Z, _Pid) ->
+ erlang:nif_error(undef).
-spec deflateInit(Z) -> 'ok' when
Z :: zstream().
deflateInit(Z) ->
- call(Z, ?DEFLATE_INIT, <<?Z_DEFAULT_COMPRESSION:32>>).
+ deflateInit(Z, default).
-spec deflateInit(Z, Level) -> 'ok' when
Z :: zstream(),
Level :: zlevel().
deflateInit(Z, Level) ->
- call(Z, ?DEFLATE_INIT, <<(arg_level(Level)):32>>).
+ deflateInit(Z, Level, deflated, ?DEFAULT_WBITS, ?DEFAULT_MEMLEVEL, default).
--spec deflateInit(Z, Level, Method,
- WindowBits, MemLevel, Strategy) -> 'ok' when
+-spec deflateInit(Z, Level, Method, WindowBits, MemLevel, Strategy) -> 'ok' when
Z :: zstream(),
Level :: zlevel(),
Method :: zmethod(),
@@ -168,31 +156,48 @@ deflateInit(Z, Level) ->
MemLevel :: zmemlevel(),
Strategy :: zstrategy().
deflateInit(Z, Level, Method, WindowBits, MemLevel, Strategy) ->
- call(Z, ?DEFLATE_INIT2, <<(arg_level(Level)):32,
- (arg_method(Method)):32,
- (arg_bitsz(WindowBits)):32,
- (arg_mem(MemLevel)):32,
- (arg_strategy(Strategy)):32>>).
+ deflateInit_nif(Z,
+ arg_level(Level),
+ arg_method(Method),
+ arg_bitsz(WindowBits),
+ arg_mem(MemLevel),
+ arg_strategy(Strategy)).
+deflateInit_nif(_Z, _Level, _Method, _WindowBits, _MemLevel, _Strategy) ->
+ erlang:nif_error(undef).
-spec deflateSetDictionary(Z, Dictionary) -> Adler32 when
Z :: zstream(),
Dictionary :: iodata(),
- Adler32 :: integer().
+ Adler32 :: non_neg_integer().
deflateSetDictionary(Z, Dictionary) ->
- call(Z, ?DEFLATE_SETDICT, Dictionary).
+ deflateSetDictionary_nif(Z, Dictionary).
+deflateSetDictionary_nif(_Z, _Dictionary) ->
+ erlang:nif_error(undef).
-spec deflateReset(Z) -> 'ok' when
Z :: zstream().
deflateReset(Z) ->
- call(Z, ?DEFLATE_RESET, []).
+ deflateReset_nif(Z).
+deflateReset_nif(_Z) ->
+ erlang:nif_error(undef).
-spec deflateParams(Z, Level, Strategy) -> ok when
Z :: zstream(),
Level :: zlevel(),
Strategy :: zstrategy().
-deflateParams(Z, Level, Strategy) ->
- call(Z, ?DEFLATE_PARAMS, <<(arg_level(Level)):32,
- (arg_strategy(Strategy)):32>>).
+deflateParams(Z, Level0, Strategy0) ->
+ Level = arg_level(Level0),
+ Strategy = arg_strategy(Strategy0),
+ Progress = deflate(Z, <<>>, sync),
+ case deflateParams_nif(Z, Level, Strategy) of
+ ok ->
+ save_progress(Z, deflate, Progress),
+ ok;
+ Other ->
+ Other
+ end.
+deflateParams_nif(_Z, _Level, _Strategy) ->
+ erlang:nif_error(undef).
-spec deflate(Z, Data) -> Compressed when
Z :: zstream(),
@@ -204,148 +209,252 @@ deflate(Z, Data) ->
-spec deflate(Z, Data, Flush) -> Compressed when
Z :: zstream(),
Data :: iodata(),
- Flush :: none | sync | full | finish,
+ Flush :: zflush(),
Compressed :: iolist().
deflate(Z, Data, Flush) ->
- try port_command(Z, Data) of
- true ->
- _ = call(Z, ?DEFLATE, <<(arg_flush(Flush)):32>>),
- collect(Z)
- catch
- error:_Err ->
- flush(Z),
- erlang:error(badarg)
- end.
+ Progress = restore_progress(Z, deflate),
+ enqueue_input(Z, Data),
+ append_iolist(Progress, dequeue_all_chunks(Z, deflate_opts(Flush))).
+
+deflate_opts(Flush) ->
+ #zlib_opts{
+ method = fun deflate_nif/4,
+ input_chunk_size = ?DEFLATE_IN_CHUNKSIZE,
+ output_chunk_size = ?DEFLATE_OUT_CHUNKSIZE,
+ flush = arg_flush(Flush)
+ }.
+
+deflate_nif(_Z, _InputChSize, _OutputChSize, _Flush) ->
+ erlang:nif_error(undef).
-spec deflateEnd(Z) -> 'ok' when
Z :: zstream().
deflateEnd(Z) ->
- call(Z, ?DEFLATE_END, []).
+ deflateEnd_nif(Z).
+deflateEnd_nif(_Z) ->
+ erlang:nif_error(undef).
-spec inflateInit(Z) -> 'ok' when
Z :: zstream().
inflateInit(Z) ->
- call(Z, ?INFLATE_INIT, []).
+ inflateInit(Z, ?DEFAULT_WBITS).
-spec inflateInit(Z, WindowBits) -> 'ok' when
Z :: zstream(),
WindowBits :: zwindowbits().
-inflateInit(Z, WindowBits) ->
- call(Z, ?INFLATE_INIT2, <<(arg_bitsz(WindowBits)):32>>).
+inflateInit(Z, WindowBits) ->
+ inflateInit(Z, WindowBits, cut).
+
+-spec inflateInit(Z, WindowBits, EoSBehavior) -> 'ok' when
+ Z :: zstream(),
+ WindowBits :: zwindowbits(),
+ EoSBehavior :: error | reset | cut.
+inflateInit(Z, WindowBits, EoSBehavior) ->
+ inflateInit_nif(Z, arg_bitsz(WindowBits), arg_eos_behavior(EoSBehavior)).
+inflateInit_nif(_Z, _WindowBits, _EoSBehavior) ->
+ erlang:nif_error(undef).
-spec inflateSetDictionary(Z, Dictionary) -> 'ok' when
Z :: zstream(),
Dictionary :: iodata().
-inflateSetDictionary(Z, Dictionary) ->
- call(Z, ?INFLATE_SETDICT, Dictionary).
+inflateSetDictionary(Z, Dictionary) ->
+ inflateSetDictionary_nif(Z, Dictionary).
+inflateSetDictionary_nif(_Z, _Dictionary) ->
+ erlang:nif_error(undef).
--spec inflateSync(zstream()) -> 'ok'.
-inflateSync(Z) ->
- call(Z, ?INFLATE_SYNC, []).
+-spec inflateGetDictionary(Z) -> Dictionary when
+ Z :: zstream(),
+ Dictionary :: binary().
+inflateGetDictionary(Z) ->
+ case inflateGetDictionary_nif(Z) of
+ Dictionary when is_binary(Dictionary) ->
+ Dictionary;
+ not_supported ->
+ erlang:error(enotsup)
+ end.
+inflateGetDictionary_nif(_Z) ->
+ erlang:nif_error(undef).
-spec inflateReset(Z) -> 'ok' when
Z :: zstream().
-inflateReset(Z) ->
- call(Z, ?INFLATE_RESET, []).
+inflateReset(Z) ->
+ inflateReset_nif(Z).
+inflateReset_nif(_Z) ->
+ erlang:nif_error(undef).
-spec inflate(Z, Data) -> Decompressed when
Z :: zstream(),
Data :: iodata(),
Decompressed :: iolist().
inflate(Z, Data) ->
- try port_command(Z, Data) of
- true ->
- _ = call(Z, ?INFLATE, <<?Z_NO_FLUSH:32>>),
- collect(Z)
- catch
- error:_Err ->
- flush(Z),
- erlang:error(badarg)
+ inflate(Z, Data, []).
+
+-spec inflate(Z, Data, Options) -> Decompressed when
+ Z :: zstream(),
+ Data :: iodata(),
+ Options :: list({exception_on_need_dict, boolean()}),
+ Decompressed :: iolist() |
+ {need_dictionary,
+ Adler32 :: non_neg_integer(),
+ Output :: iolist()}.
+inflate(Z, Data, Options) ->
+ enqueue_input(Z, Data),
+ Result = dequeue_all_chunks(Z, inflate_opts()),
+ case proplist_get_value(Options, exception_on_need_dict, true) of
+ true -> exception_on_need_dict(Z, Result);
+ false -> Result
end.
+inflate_nif(_Z, _InputChSize, _OutputChSize, _Flush) ->
+ erlang:nif_error(undef).
+
+inflate_opts() ->
+ #zlib_opts{
+ method = fun inflate_nif/4,
+ input_chunk_size = ?INFLATE_IN_CHUNKSIZE,
+ output_chunk_size = ?INFLATE_OUT_CHUNKSIZE,
+ flush = arg_flush(none)
+ }.
+
+-spec inflateChunk(Z, Data) -> Decompressed | {more, Decompressed} when
+ Z :: zstream(),
+ Data :: iodata(),
+ Decompressed :: iolist().
+inflateChunk(Z, Data) ->
+ enqueue_input(Z, Data),
+ inflateChunk(Z).
+
+-spec inflateChunk(Z) -> Decompressed | {more, Decompressed} when
+ Z :: zstream(),
+ Decompressed :: iolist().
+inflateChunk(Z) ->
+ Opts0 = inflate_opts(),
+ Opts = Opts0#zlib_opts { output_chunk_size = getBufSize(Z) },
+
+ Result0 = dequeue_next_chunk(Z, Opts),
+ Result1 = exception_on_need_dict(Z, Result0),
+ yield_inflateChunk(Z, Result1).
+
+yield_inflateChunk(_Z, {continue, Output}) ->
+ {more, lists:flatten(Output)};
+yield_inflateChunk(_Z, {finished, Output}) ->
+ lists:flatten(Output).
+
+exception_on_need_dict(Z, {need_dictionary, Adler, Output}) ->
+ Progress = restore_progress(Z, inflate),
+ save_progress(Z, inflate, append_iolist(Progress, Output)),
+ erlang:error({need_dictionary, Adler});
+exception_on_need_dict(Z, {Mark, Output}) ->
+ Progress = restore_progress(Z, inflate),
+ {Mark, append_iolist(Progress, Output)};
+exception_on_need_dict(Z, Output) when is_list(Output); is_binary(Output) ->
+ Progress = restore_progress(Z, inflate),
+ append_iolist(Progress, Output).
+
+-spec safeInflate(Z, Data) -> Result when
+ Z :: zstream(),
+ Data :: iodata(),
+ Result :: {continue, Output :: iolist()} |
+ {finished, Output :: iolist()} |
+ {need_dictionary,
+ Adler32 :: non_neg_integer(),
+ Output :: iolist()}.
+safeInflate(Z, Data) ->
+ enqueue_input(Z, Data),
+ dequeue_next_chunk(Z, inflate_opts()).
+
-spec inflateEnd(Z) -> 'ok' when
Z :: zstream().
inflateEnd(Z) ->
- call(Z, ?INFLATE_END, []).
+ inflateEnd_nif(Z).
+inflateEnd_nif(_Z) ->
+ erlang:nif_error(undef).
-spec setBufSize(Z, Size) -> 'ok' when
Z :: zstream(),
Size :: non_neg_integer().
-setBufSize(Z, Size) ->
- call(Z, ?SET_BUFSZ, <<Size:32>>).
+setBufSize(Z, Size) when is_integer(Size), Size > 16, Size < (1 bsl 24) ->
+ setBufSize_nif(Z, Size);
+setBufSize(_Z, _Size) ->
+ erlang:error(badarg).
+setBufSize_nif(_Z, _Size) ->
+ erlang:nif_error(undef).
--spec getBufSize(Z) -> Size when
- Z :: zstream(),
- Size :: non_neg_integer().
+-spec getBufSize(Z) -> non_neg_integer() when
+ Z :: zstream().
getBufSize(Z) ->
- call(Z, ?GET_BUFSZ, []).
+ getBufSize_nif(Z).
+getBufSize_nif(_Z) ->
+ erlang:nif_error(undef).
-spec crc32(Z) -> CRC when
Z :: zstream(),
- CRC :: integer().
+ CRC :: non_neg_integer().
crc32(Z) ->
- call(Z, ?CRC32_0, []).
+ crc32_nif(Z).
+crc32_nif(_Z) ->
+ erlang:nif_error(undef).
-spec crc32(Z, Data) -> CRC when
Z :: zstream(),
Data :: iodata(),
- CRC :: integer().
-crc32(Z, Data) ->
- call(Z, ?CRC32_1, Data).
+ CRC :: non_neg_integer().
+crc32(Z, Data) when is_reference(Z) ->
+ erlang:crc32(Data);
+crc32(_Z, _Data) ->
+ erlang:error(badarg).
-spec crc32(Z, PrevCRC, Data) -> CRC when
Z :: zstream(),
- PrevCRC :: integer(),
+ PrevCRC :: non_neg_integer(),
Data :: iodata(),
- CRC :: integer().
-crc32(Z, CRC, Data) ->
- call(Z, ?CRC32_2, [<<CRC:32>>, Data]).
+ CRC :: non_neg_integer().
+crc32(Z, CRC, Data) when is_reference(Z) ->
+ erlang:crc32(CRC, Data);
+crc32(_Z, _CRC, _Data) ->
+ erlang:error(badarg).
--spec adler32(Z, Data) -> CheckSum when
+-spec crc32_combine(Z, CRC1, CRC2, Size2) -> CRC when
Z :: zstream(),
- Data :: iodata(),
- CheckSum :: integer().
-adler32(Z, Data) ->
- call(Z, ?ADLER32_1, Data).
+ CRC :: non_neg_integer(),
+ CRC1 :: non_neg_integer(),
+ CRC2 :: non_neg_integer(),
+ Size2 :: non_neg_integer().
+crc32_combine(Z, CRC1, CRC2, Size2) when is_reference(Z) ->
+ erlang:crc32_combine(CRC1, CRC2, Size2);
+crc32_combine(_Z, _CRC1, _CRC2, _Size2) ->
+ erlang:error(badarg).
--spec adler32(Z, PrevAdler, Data) -> CheckSum when
+-spec adler32(Z, Data) -> CheckSum when
Z :: zstream(),
- PrevAdler :: integer(),
Data :: iodata(),
- CheckSum :: integer().
-adler32(Z, Adler, Data) when is_integer(Adler) ->
- call(Z, ?ADLER32_2, [<<Adler:32>>, Data]);
-adler32(_Z, _Adler, _Data) ->
+ CheckSum :: non_neg_integer().
+adler32(Z, Data) when is_reference(Z) ->
+ erlang:adler32(Data);
+adler32(_Z, _Data) ->
erlang:error(badarg).
--spec crc32_combine(Z, CRC1, CRC2, Size2) -> CRC when
+-spec adler32(Z, PrevAdler, Data) -> CheckSum when
Z :: zstream(),
- CRC :: integer(),
- CRC1 :: integer(),
- CRC2 :: integer(),
- Size2 :: integer().
-crc32_combine(Z, CRC1, CRC2, Len2)
- when is_integer(CRC1), is_integer(CRC2), is_integer(Len2) ->
- call(Z, ?CRC32_COMBINE, <<CRC1:32, CRC2:32, Len2:32>>);
-crc32_combine(_Z, _CRC1, _CRC2, _Len2) ->
+ PrevAdler :: non_neg_integer(),
+ Data :: iodata(),
+ CheckSum :: non_neg_integer().
+adler32(Z, Adler, Data) when is_reference(Z) ->
+ erlang:adler32(Adler, Data);
+adler32(_Z, _Adler, _Data) ->
erlang:error(badarg).
-spec adler32_combine(Z, Adler1, Adler2, Size2) -> Adler when
Z :: zstream(),
- Adler :: integer(),
- Adler1 :: integer(),
- Adler2 :: integer(),
- Size2 :: integer().
-adler32_combine(Z, Adler1, Adler2, Len2)
- when is_integer(Adler1), is_integer(Adler2), is_integer(Len2) ->
- call(Z, ?ADLER32_COMBINE, <<Adler1:32, Adler2:32, Len2:32>>);
-adler32_combine(_Z, _Adler1, _Adler2, _Len2) ->
+ Adler :: non_neg_integer(),
+ Adler1 :: non_neg_integer(),
+ Adler2 :: non_neg_integer(),
+ Size2 :: non_neg_integer().
+adler32_combine(Z, Adler1, Adler2, Size2) when is_reference(Z) ->
+ erlang:adler32_combine(Adler1, Adler2, Size2);
+adler32_combine(_Z, _Adler1, _Adler2, _Size2) ->
erlang:error(badarg).
--spec getQSize(zstream()) -> non_neg_integer().
-getQSize(Z) ->
- call(Z, ?GET_QSIZE, []).
-
%% compress/uncompress zlib with header
-spec compress(Data) -> Compressed when
Data :: iodata(),
@@ -353,13 +462,13 @@ getQSize(Z) ->
compress(Data) ->
Z = open(),
Bs = try
- deflateInit(Z, default),
- B = deflate(Z, Data, finish),
- deflateEnd(Z),
- B
- after
- close(Z)
- end,
+ deflateInit(Z, default),
+ B = deflate(Z, Data, finish),
+ deflateEnd(Z),
+ B
+ after
+ close(Z)
+ end,
iolist_to_binary(Bs).
-spec uncompress(Data) -> Decompressed when
@@ -371,14 +480,14 @@ uncompress(Data) ->
if
Size >= 8 ->
Z = open(),
- Bs = try
- inflateInit(Z),
- B = inflate(Z, Data),
- inflateEnd(Z),
- B
- after
- close(Z)
- end,
+ Bs = try
+ inflateInit(Z),
+ B = inflate(Z, Data),
+ inflateEnd(Z),
+ B
+ after
+ close(Z)
+ end,
iolist_to_binary(Bs);
true ->
erlang:error(data_error)
@@ -395,13 +504,13 @@ uncompress(Data) ->
zip(Data) ->
Z = open(),
Bs = try
- deflateInit(Z, default, deflated, -?MAX_WBITS, 8, default),
- B = deflate(Z, Data, finish),
- deflateEnd(Z),
- B
- after
- close(Z)
- end,
+ deflateInit(Z, default, deflated, -?MAX_WBITS, 8, default),
+ B = deflate(Z, Data, finish),
+ deflateEnd(Z),
+ B
+ after
+ close(Z)
+ end,
iolist_to_binary(Bs).
-spec unzip(Data) -> Decompressed when
@@ -410,28 +519,28 @@ zip(Data) ->
unzip(Data) ->
Z = open(),
Bs = try
- inflateInit(Z, -?MAX_WBITS),
- B = inflate(Z, Data),
- inflateEnd(Z),
- B
- after
- close(Z)
- end,
+ inflateInit(Z, -?MAX_WBITS),
+ B = inflate(Z, Data),
+ inflateEnd(Z),
+ B
+ after
+ close(Z)
+ end,
iolist_to_binary(Bs).
-
+
-spec gzip(Data) -> Compressed when
Data :: iodata(),
Compressed :: binary().
gzip(Data) ->
Z = open(),
Bs = try
- deflateInit(Z, default, deflated, 16+?MAX_WBITS, 8, default),
- B = deflate(Z, Data, finish),
- deflateEnd(Z),
- B
- after
- close(Z)
- end,
+ deflateInit(Z, default, deflated, 16+?MAX_WBITS, 8, default),
+ B = deflate(Z, Data, finish),
+ deflateEnd(Z),
+ B
+ after
+ close(Z)
+ end,
iolist_to_binary(Bs).
-spec gunzip(Data) -> Decompressed when
@@ -440,90 +549,155 @@ gzip(Data) ->
gunzip(Data) ->
Z = open(),
Bs = try
- inflateInit(Z, 16+?MAX_WBITS),
- B = inflate(Z, Data),
- inflateEnd(Z),
- B
- after
- close(Z)
- end,
+ inflateInit(Z, 16+?MAX_WBITS, reset),
+ B = inflate(Z, Data),
+ inflateEnd(Z),
+ B
+ after
+ close(Z)
+ end,
iolist_to_binary(Bs).
--spec collect(zstream()) -> iolist().
-collect(Z) ->
- collect(Z, []).
-
--spec collect(zstream(), iolist()) -> iolist().
-collect(Z, Acc) ->
- receive
- {Z, {data, Bin}} ->
- collect(Z, [Bin|Acc])
- after 0 ->
- reverse(Acc)
+-spec dequeue_all_chunks(Z, Opts) -> Result when
+ Z :: zstream(),
+ Opts :: #zlib_opts{},
+ Result :: {need_dictionary, integer(), iolist()} |
+ iolist().
+dequeue_all_chunks(Z, Opts) ->
+ dequeue_all_chunks_1(Z, Opts, []).
+dequeue_all_chunks_1(Z, Opts, Output) ->
+ case dequeue_next_chunk(Z, Opts) of
+ {need_dictionary, _, _} = NeedDict ->
+ NeedDict;
+ {continue, Chunk} ->
+ dequeue_all_chunks_1(Z, Opts, append_iolist(Output, Chunk));
+ {finished, Chunk} ->
+ append_iolist(Output, Chunk)
end.
--spec flush(zstream()) -> 'ok'.
-flush(Z) ->
- receive
- {Z, {data,_}} ->
- flush(Z)
- after 0 ->
- ok
+-spec dequeue_next_chunk(Z, Opts) -> Result when
+ Z :: zstream(),
+ Opts :: #zlib_opts{},
+ Result :: {need_dictionary, integer(), iolist()} |
+ {continue, iolist()} |
+ {finished, iolist()}.
+dequeue_next_chunk(Z, Opts) ->
+ Method = Opts#zlib_opts.method,
+ IChSz = Opts#zlib_opts.input_chunk_size,
+ OChSz = Opts#zlib_opts.output_chunk_size,
+ Flush = Opts#zlib_opts.flush,
+ Method(Z, IChSz, OChSz, Flush).
+
+-spec append_iolist(IO, D) -> iolist() when
+ IO :: iodata(),
+ D :: iodata().
+append_iolist([], D) when is_list(D) -> D;
+append_iolist([], D) -> [D];
+append_iolist(IO, []) -> IO;
+append_iolist(IO, [D]) -> [IO, D];
+append_iolist(IO, D) -> [IO, D].
+
+%% inflate/2 and friends are documented as throwing an error on Z_NEED_DICT
+%% rather than simply returning something to that effect, and deflateParams/3
+%% may flush behind the scenes. This requires us to stow away our current
+%% progress in the handle and resume from that point on our next call.
+%%
+%% Generally speaking this is either a refc binary or nothing at all, so it's
+%% pretty cheap.
+
+-spec save_progress(Z, Kind, Output) -> ok when
+ Z :: zstream(),
+ Kind :: inflate | deflate,
+ Output :: iolist().
+save_progress(Z, Kind, Output) ->
+ ok = setStash_nif(Z, {Kind, Output}).
+
+-spec restore_progress(Z, Kind) -> iolist() when
+ Z :: zstream(),
+ Kind :: inflate | deflate.
+restore_progress(Z, Kind) ->
+ case getStash_nif(Z) of
+ {ok, {Kind, Output}} ->
+ ok = clearStash_nif(Z),
+ Output;
+ empty ->
+ []
end.
-
-arg_flush(none) -> ?Z_NO_FLUSH;
+
+-spec clearStash_nif(Z) -> ok when
+ Z :: zstream().
+clearStash_nif(_Z) ->
+ erlang:nif_error(undef).
+
+-spec setStash_nif(Z, Term) -> ok when
+ Z :: zstream(),
+ Term :: term().
+setStash_nif(_Z, _Term) ->
+ erlang:nif_error(undef).
+
+-spec getStash_nif(Z) -> {ok, term()} | empty when
+ Z :: zstream().
+getStash_nif(_Z) ->
+ erlang:nif_error(undef).
+
+%% The 'proplists' module isn't preloaded so we can't rely on its existence.
+proplist_get_value([], _Name, DefVal) -> DefVal;
+proplist_get_value([{Name, Value} | _Opts], Name, _DefVal) -> Value;
+proplist_get_value([_Head | Opts], Name, DefVal) ->
+ proplist_get_value(Opts, Name, DefVal).
+
+arg_flush(none) -> ?Z_NO_FLUSH;
%% ?Z_PARTIAL_FLUSH is deprecated in zlib -- deliberately not included.
-arg_flush(sync) -> ?Z_SYNC_FLUSH;
-arg_flush(full) -> ?Z_FULL_FLUSH;
-arg_flush(finish) -> ?Z_FINISH;
-arg_flush(_) -> erlang:error(badarg).
+arg_flush(sync) -> ?Z_SYNC_FLUSH;
+arg_flush(full) -> ?Z_FULL_FLUSH;
+arg_flush(finish) -> ?Z_FINISH;
+arg_flush(_) -> erlang:error(bad_flush_mode).
arg_level(none) -> ?Z_NO_COMPRESSION;
arg_level(best_speed) -> ?Z_BEST_SPEED;
arg_level(best_compression) -> ?Z_BEST_COMPRESSION;
arg_level(default) -> ?Z_DEFAULT_COMPRESSION;
arg_level(Level) when is_integer(Level), Level >= 0, Level =< 9 -> Level;
-arg_level(_) -> erlang:error(badarg).
-
+arg_level(_) -> erlang:error(bad_compression_level).
+
arg_strategy(filtered) -> ?Z_FILTERED;
arg_strategy(huffman_only) -> ?Z_HUFFMAN_ONLY;
arg_strategy(rle) -> ?Z_RLE;
arg_strategy(default) -> ?Z_DEFAULT_STRATEGY;
-arg_strategy(_) -> erlang:error(badarg).
+arg_strategy(_) -> erlang:error(bad_compression_strategy).
arg_method(deflated) -> ?Z_DEFLATED;
-arg_method(_) -> erlang:error(badarg).
+arg_method(_) -> erlang:error(bad_compression_method).
+
+arg_eos_behavior(error) -> ?EOS_BEHAVIOR_ERROR;
+arg_eos_behavior(reset) -> ?EOS_BEHAVIOR_RESET;
+arg_eos_behavior(cut) -> ?EOS_BEHAVIOR_CUT;
+arg_eos_behavior(_) -> erlang:error(bad_eos_behavior).
-spec arg_bitsz(zwindowbits()) -> zwindowbits().
arg_bitsz(Bits) when is_integer(Bits) andalso
- ((8 < Bits andalso Bits < 48) orelse
- (-15 =< Bits andalso Bits < -8)) ->
+ ((8 =< Bits andalso Bits < 48) orelse
+ (-15 =< Bits andalso Bits =< -8)) ->
Bits;
-arg_bitsz(_) -> erlang:error(badarg).
+arg_bitsz(_) -> erlang:error(bad_windowbits).
-spec arg_mem(zmemlevel()) -> zmemlevel().
arg_mem(Level) when is_integer(Level), 1 =< Level, Level =< 9 -> Level;
-arg_mem(_) -> erlang:error(badarg).
-
-call(Z, Cmd, Arg) ->
- try port_control(Z, Cmd, Arg) of
- [0|Res] -> list_to_atom(Res);
- [1|Res] ->
- flush(Z),
- erlang:error(list_to_atom(Res));
- [2,A,B,C,D] ->
- (A bsl 24)+(B bsl 16)+(C bsl 8)+D;
- [3,A,B,C,D] ->
- erlang:error({need_dictionary,(A bsl 24)+(B bsl 16)+(C bsl 8)+D})
- catch
- error:badarg -> %% Rethrow loses port_control from stacktrace.
- erlang:error(badarg)
- end.
+arg_mem(_) -> erlang:error(bad_memlevel).
-reverse(X) ->
- reverse(X, []).
+-spec enqueue_input(Z, IOData) -> ok when
+ Z :: zstream(),
+ IOData :: iodata().
+enqueue_input(Z, IOData) ->
+ enqueue_input_1(Z, erlang:iolist_to_iovec(IOData)).
+
+enqueue_input_1(_Z, []) ->
+ ok;
+enqueue_input_1(Z, IOVec) ->
+ case enqueue_nif(Z, IOVec) of
+ {continue, Remainder} -> enqueue_input_1(Z, Remainder);
+ ok -> ok
+ end.
-reverse([H|T], Y) ->
- reverse(T, [H|Y]);
-reverse([], X) ->
- X.
+enqueue_nif(_Z, _IOVec) ->
+ erlang:nif_error(undef).