diff options
Diffstat (limited to 'erts/preloaded')
28 files changed, 565 insertions, 86 deletions
diff --git a/erts/preloaded/ebin/atomics.beam b/erts/preloaded/ebin/atomics.beam Binary files differnew file mode 100644 index 0000000000..1de97fa668 --- /dev/null +++ b/erts/preloaded/ebin/atomics.beam diff --git a/erts/preloaded/ebin/counters.beam b/erts/preloaded/ebin/counters.beam Binary files differnew file mode 100644 index 0000000000..caaa6167e1 --- /dev/null +++ b/erts/preloaded/ebin/counters.beam diff --git a/erts/preloaded/ebin/erl_init.beam b/erts/preloaded/ebin/erl_init.beam Binary files differnew file mode 100644 index 0000000000..93f23377b5 --- /dev/null +++ b/erts/preloaded/ebin/erl_init.beam diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam Binary files differindex 0f5f5036f0..42629bbe8e 100644 --- a/erts/preloaded/ebin/erl_prim_loader.beam +++ b/erts/preloaded/ebin/erl_prim_loader.beam diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam Binary files differindex 6017112dac..a74e87d19c 100644 --- a/erts/preloaded/ebin/erl_tracer.beam +++ b/erts/preloaded/ebin/erl_tracer.beam diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam Binary files differindex 7a4167b0e9..54557d9d18 100644 --- a/erts/preloaded/ebin/erlang.beam +++ b/erts/preloaded/ebin/erlang.beam diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam Binary files differindex c899b69a2c..3cbfd94a43 100644 --- a/erts/preloaded/ebin/erts_code_purger.beam +++ b/erts/preloaded/ebin/erts_code_purger.beam diff --git a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam Binary files differindex 9490a56758..43604e0480 100644 --- a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam +++ b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam Binary files differindex 15c59de80a..2b98e87843 100644 --- a/erts/preloaded/ebin/erts_internal.beam +++ b/erts/preloaded/ebin/erts_internal.beam diff --git a/erts/preloaded/ebin/erts_literal_area_collector.beam b/erts/preloaded/ebin/erts_literal_area_collector.beam Binary files differindex e650a6b5af..1f7fbef85a 100644 --- a/erts/preloaded/ebin/erts_literal_area_collector.beam +++ b/erts/preloaded/ebin/erts_literal_area_collector.beam diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam Binary files differindex 858a9dc63e..bcbf4a1469 100644 --- a/erts/preloaded/ebin/init.beam +++ b/erts/preloaded/ebin/init.beam diff --git a/erts/preloaded/ebin/persistent_term.beam b/erts/preloaded/ebin/persistent_term.beam Binary files differnew file mode 100644 index 0000000000..79ef03b9a6 --- /dev/null +++ b/erts/preloaded/ebin/persistent_term.beam diff --git a/erts/preloaded/ebin/prim_buffer.beam b/erts/preloaded/ebin/prim_buffer.beam Binary files differindex 4ad1380d0b..70675f47ee 100644 --- a/erts/preloaded/ebin/prim_buffer.beam +++ b/erts/preloaded/ebin/prim_buffer.beam diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam Binary files differindex 2ae18846bf..5aa5a7f384 100644 --- a/erts/preloaded/ebin/prim_eval.beam +++ b/erts/preloaded/ebin/prim_eval.beam diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam Binary files differindex df611f2bb0..37a1a563a9 100644 --- a/erts/preloaded/ebin/prim_file.beam +++ b/erts/preloaded/ebin/prim_file.beam diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam Binary files differindex 4a345f8152..23a8e8f210 100644 --- a/erts/preloaded/ebin/prim_inet.beam +++ b/erts/preloaded/ebin/prim_inet.beam diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam Binary files differindex 4923cadbdc..8483d11aed 100644 --- a/erts/preloaded/ebin/prim_zip.beam +++ b/erts/preloaded/ebin/prim_zip.beam diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam Binary files differindex 07e7e97814..f3737a524e 100644 --- a/erts/preloaded/ebin/zlib.beam +++ b/erts/preloaded/ebin/zlib.beam diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index 4333f6643a..c655bff72e 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -41,13 +41,16 @@ PRE_LOADED_ERL_MODULES = \ prim_inet \ zlib \ prim_zip \ - otp_ring0 \ + erl_init \ erts_code_purger \ erlang \ erts_internal \ erl_tracer \ erts_literal_area_collector \ - erts_dirty_process_signal_handler + erts_dirty_process_signal_handler \ + atomics \ + counters \ + persistent_term PRE_LOADED_BEAM_MODULES = \ prim_eval diff --git a/erts/preloaded/src/atomics.erl b/erts/preloaded/src/atomics.erl new file mode 100644 index 0000000000..d1fe5e65cf --- /dev/null +++ b/erts/preloaded/src/atomics.erl @@ -0,0 +1,119 @@ +%% +%% %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% +%% + +%% Purpose : Main atomics API module. + +-module(atomics). + +-export([new/2, + put/3, get/2, + add/3, add_get/3, + sub/3, sub_get/3, + exchange/3, compare_exchange/4, + info/1]). + +-export_type([atomics_ref/0]). + +-opaque atomics_ref() :: reference(). + +-define(OPT_SIGNED, (1 bsl 0)). +-define(OPT_DEFAULT, ?OPT_SIGNED). + +-spec new(Arity, Opts) -> atomics_ref() when + Arity :: pos_integer(), + Opts :: [Opt], + Opt :: {signed, boolean()}. +new(Arity, Opts) -> + erts_internal:atomics_new(Arity, encode_opts(Opts, ?OPT_DEFAULT)). + +encode_opts([{signed, true}|T], Acc) -> + encode_opts(T, Acc bor ?OPT_SIGNED); +encode_opts([{signed, false}|T], Acc) -> + encode_opts(T, Acc band (bnot ?OPT_SIGNED)); +encode_opts([], Acc) -> + Acc; +encode_opts(_, _) -> + erlang:error(badarg). + +-spec put(Ref, Ix, Value) -> ok when + Ref :: atomics_ref(), + Ix :: integer(), + Value :: integer(). +put(_Ref, _Ix, _Value) -> + erlang:nif_error(undef). + +-spec get(Ref, Ix) -> integer() when + Ref :: atomics_ref(), + Ix :: integer(). +get(_Ref, _Ix) -> + erlang:nif_error(undef). + +-spec add(Ref, Ix, Incr) -> ok when + Ref :: atomics_ref(), + Ix :: integer(), + Incr :: integer(). +add(_Ref, _Ix, _Incr) -> + erlang:nif_error(undef). + +-spec add_get(Ref, Ix, Incr) -> integer() when + Ref :: atomics_ref(), + Ix :: integer(), + Incr :: integer(). +add_get(_Ref, _Ix, _Incr) -> + erlang:nif_error(undef). + +-spec sub(Ref, Ix, Decr) -> ok when + Ref :: atomics_ref(), + Ix :: integer(), + Decr :: integer(). +sub(Ref, Ix, Decr) -> + ?MODULE:add(Ref, Ix, -Decr). + +-spec sub_get(Ref, Ix, Decr) -> integer() when + Ref :: atomics_ref(), + Ix :: integer(), + Decr :: integer(). +sub_get(Ref, Ix, Decr) -> + ?MODULE:add_get(Ref, Ix, -Decr). + +-spec exchange(Ref, Ix, Desired) -> integer() when + Ref :: atomics_ref(), + Ix :: integer(), + Desired :: integer(). +exchange(_Ref, _Ix, _Desired) -> + erlang:nif_error(undef). + +-spec compare_exchange(Ref, Ix, Expected, Desired) -> ok | integer() when + Ref :: atomics_ref(), + Ix :: integer(), + Expected :: integer(), + Desired :: integer(). +compare_exchange(_Ref, _Ix, _Expected, _Desired) -> + erlang:nif_error(undef). + +-spec info(Ref) -> Info when + Ref :: atomics_ref(), + Info :: #{'size':=Size,'max':=Max,'min':=Min,'memory':=Memory}, + Size :: non_neg_integer(), + Max :: integer(), + Min :: integer(), + Memory :: non_neg_integer(). +info(_Ref) -> + erlang:nif_error(undef). diff --git a/erts/preloaded/src/counters.erl b/erts/preloaded/src/counters.erl new file mode 100644 index 0000000000..67354f648d --- /dev/null +++ b/erts/preloaded/src/counters.erl @@ -0,0 +1,90 @@ +%% +%% %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% +%% + +%% Purpose : Main atomics API module. + +-module(counters). + +-export([new/2, + get/2, + add/3, + sub/3, + info/1]). + +-export_type([counters_ref/0]). + +-opaque counters_ref() :: {atomics, reference()} | {write_concurrency, reference()}. + +-spec new(Size, Opts) -> counters_ref() when + Size :: pos_integer(), + Opts :: [Opt], + Opt :: atomics | write_concurrency. +new(Size, [atomics]) -> + {atomics, atomics:new(Size, [{signed, true}])}; +new(Size, [write_concurrency]) -> + {write_concurrency, erts_internal:counters_new(Size)}; +new(Size, []) -> + new(Size, [atomics]); +new(_, _) -> + erlang:error(badarg). + +-spec get(Ref, Ix) -> integer() when + Ref :: counters_ref(), + Ix :: integer(). +get({atomics,Ref}, Ix) -> + atomics:get(Ref, Ix); +get({write_concurrency, Ref}, Ix) -> + erts_internal:counters_get(Ref, Ix); +get(_, _) -> + erlang:error(badarg). + + + +-spec add(Ref, Ix, Incr) -> ok when + Ref :: counters_ref(), + Ix :: integer(), + Incr :: integer(). +add({atomics, Ref}, Ix, Incr) -> + atomics:add(Ref, Ix, Incr); +add({write_concurrency, Ref}, Ix, Incr) -> + erts_internal:counters_add(Ref, Ix, Incr); +add(_, _, _) -> + erlang:error(badarg). + + +-spec sub(Ref, Ix, Decr) -> ok when + Ref :: counters_ref(), + Ix :: integer(), + Decr :: integer(). +sub(Ref, Ix, Decr) -> + add(Ref, Ix, -Decr). + +-spec info(Ref) -> Info when + Ref :: counters_ref(), + Info :: #{'size':=Size, 'memory':=Memory}, + Size :: non_neg_integer(), + Memory :: non_neg_integer(). +info({atomics, Ref}) -> + atomics:info(Ref); +info({write_concurrency, Ref}) -> + erts_internal:counters_info(Ref); +info(_) -> + erlang:error(badarg). + diff --git a/erts/preloaded/src/otp_ring0.erl b/erts/preloaded/src/erl_init.erl index 62a60fffe2..d681f05398 100644 --- a/erts/preloaded/src/otp_ring0.erl +++ b/erts/preloaded/src/erl_init.erl @@ -17,15 +17,26 @@ %% %% %CopyrightEnd% %% --module(otp_ring0). +-module(erl_init). -%% Purpose : Start up of erlang system. +%% Initial process of an Erlang system. -export([start/2]). --spec start(_, term()) -> term(). -start(_Env, Argv) -> - run(init, boot, Argv). +%% This gets the module name given by the +i option (default 'init') +%% and the list of command line arguments + +-spec start(Mod, BootArgs) -> no_return() when + Mod :: module(), + BootArgs :: [binary()]. +start(Mod, BootArgs) -> + %% Load the static nifs + zlib:on_load(), + erl_tracer:on_load(), + prim_buffer:on_load(), + prim_file:on_load(), + %% Proceed to the specified boot module + run(Mod, boot, BootArgs). run(M, F, A) -> case erlang:function_exported(M, F, 1) of diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src index 8c34c99a98..13fbbc7ff7 100644 --- a/erts/preloaded/src/erts.app.src +++ b/erts/preloaded/src/erts.app.src @@ -26,19 +26,21 @@ erl_prim_loader, erts_internal, init, - otp_ring0, + erl_init, erts_code_purger, prim_buffer, prim_eval, prim_file, prim_inet, prim_zip, + atomics, + counters, zlib ]}, {registered, []}, {applications, []}, {env, []}, - {runtime_dependencies, ["stdlib-3.5", "kernel-6.1", "sasl-3.0.1"]} + {runtime_dependencies, ["stdlib-3.5", "kernel-6.1", "sasl-@OTP-13468@"]} ]}. %% vim: ft=erlang diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 88f47e917b..d491a505c6 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -90,6 +90,13 @@ -export([create_dist_channel/4]). +-export([erase_persistent_terms/0]). + +-export([atomics_new/2]). + +-export([counters_new/1, counters_get/2, counters_add/3, + counters_info/1]). + %% %% Await result of send to port %% @@ -691,3 +698,27 @@ process_flag(_Pid, _Flag, _Value) -> create_dist_channel(_Node, _DistCtrlr, _Flags, _Ver) -> erlang:nif_error(undefined). + +-spec erase_persistent_terms() -> 'ok'. +erase_persistent_terms() -> + erlang:nif_error(undefined). + +-spec atomics_new(pos_integer(), pos_integer()) -> reference(). +atomics_new(_Arity, _EncOpts) -> + erlang:nif_error(undef). + +-spec counters_new(pos_integer()) -> reference(). +counters_new(_Size) -> + erlang:nif_error(undef). + +-spec counters_get(reference(), pos_integer()) -> integer(). +counters_get(_Ref, _Ix) -> + erlang:nif_error(undef). + +-spec counters_add(reference(), pos_integer(), integer()) -> ok. +counters_add(_Ref, _Ix, _Incr) -> + erlang:nif_error(undef). + +-spec counters_info(reference()) -> #{}. +counters_info(_Ref) -> + erlang:nif_error(undef). diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 253fcf7a1f..d8f5c9a945 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -200,12 +200,6 @@ boot(BootArgs) -> register(init, self()), process_flag(trap_exit, true), - %% 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 @@ -552,6 +546,7 @@ stop(Reason,State) -> do_stop(restart,#state{start = Start, flags = Flags, args = Args}) -> %% Make sure we don't have any outstanding messages before doing the restart. flush(), + erts_internal:erase_persistent_terms(), boot(Start,Flags,Args); do_stop(reboot,_) -> halt(); diff --git a/erts/preloaded/src/persistent_term.erl b/erts/preloaded/src/persistent_term.erl new file mode 100644 index 0000000000..5d0c266127 --- /dev/null +++ b/erts/preloaded/src/persistent_term.erl @@ -0,0 +1,55 @@ +%% +%% %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(persistent_term). + +-export([erase/1,get/0,get/1,info/0,put/2]). + +-type key() :: term(). +-type value() :: term(). + +-spec erase(Key) -> Result when + Key :: key(), + Result :: boolean(). +erase(_Key) -> + erlang:nif_error(undef). + +-spec get() -> List when + List :: [{key(),value()}]. +get() -> + erlang:nif_error(undef). + +-spec get(Key) -> Value when + Key :: key(), + Value :: value(). +get(_Key) -> + erlang:nif_error(undef). + +-spec info() -> Info when + Info :: #{'count':=Count,'memory':=Memory}, + Count :: non_neg_integer(), + Memory :: non_neg_integer(). +info() -> + erlang:nif_error(undef). + +-spec put(Key, Value) -> 'ok' when + Key :: key(), + Value :: value(). +put(_Key, _Value) -> + erlang:nif_error(undef). diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 6d85868183..5fc22bc582 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -83,6 +83,15 @@ internal_normalize_utf8(_) -> is_translatable(_) -> erlang:nif_error(undefined). +%% This is a janitor process used to close files whose controlling process has +%% died. The emulator will be torn down if this is killed. +delayed_close_loop() -> + receive + {close, FRef} when is_reference(FRef) -> delayed_close_nif(FRef); + _ -> ok + end, + delayed_close_loop(). + %% %% Returns {error, Reason} | {ok, BytesCopied} @@ -95,7 +104,12 @@ copy(#file_descriptor{module = ?MODULE} = Source, file:copy_opened(Source, Dest, Length). on_load() -> - ok = erlang:load_nif(atom_to_list(?MODULE), 0). + Pid = spawn(fun() -> + process_flag(trap_exit, true), + delayed_close_loop() + end), + true = register(erts_prim_file, Pid), + ok = erlang:load_nif(atom_to_list(?MODULE), Pid). open(Name, Modes) -> %% The try/catch pattern seen here is used throughout the file to adhere to @@ -482,6 +496,8 @@ truncate_nif(_FileRef) -> erlang:nif_error(undef). get_handle_nif(_FileRef) -> erlang:nif_error(undef). +delayed_close_nif(_FileRef) -> + erlang:nif_error(undef). %% %% Quality-of-life helpers diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index b746aab049..cc2711b540 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -49,9 +49,15 @@ -include("inet_sctp.hrl"). -include("inet_int.hrl"). -%-define(DEBUG, 1). +%%%-define(DEBUG, 1). -ifdef(DEBUG). --define(DBG_FORMAT(Format, Args), (io:format((Format), (Args)))). +-define( + DBG_FORMAT(Format, Args), + begin + %% io:format((Format), (Args)), + erlang:display(lists:flatten(io_lib:format((Format), (Args)))), + ok + end). -else. -define(DBG_FORMAT(Format, Args), ok). -endif. @@ -150,39 +156,96 @@ shutdown_1(S, How) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% close(S) when is_port(S) -> + ?DBG_FORMAT("prim_inet:close(~p)~n", [S]), case getopt(S, linger) of {ok,{true,0}} -> 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 + {ok,{true,T}} -> + %% Wait for T seconds for pending output to be sent + %% + %% Note that this handling of Linger may look ok, + %% but sweeps some problems under the rug since + %% there are OS buffers that may have remaining data + %% after the inet driver has emptied its buffers. + %% But Linger for nonblocking sockets is broken + %% anyway on all OS:es, according to hearsay, + %% and is a contradiction in itself. + %% We have hereby done our best... + %% + Tref = erlang:start_timer(T * 1000, self(), close_port), + close_pend_loop(S, Tref, undefined); + _ -> % Regard this as {ok,{false,_}} + case subscribe(S, [subs_empty_out_q]) of + {ok, [{subs_empty_out_q,N}]} when N > 0 -> + %% Wait for pending output to be sent + DefaultT = 180000, % Arbitrary system timeout 3 min + Tref = erlang:start_timer(DefaultT, self(), close_port), + close_pend_loop(S, Tref, N); + _ -> + %% Subscribe failed or empty out q - give up or done + close_port(S) + end end. -close_pend_loop(S, N) -> +close_pend_loop(S, Tref, N) -> + ?DBG_FORMAT("prim_inet:close_pend_loop(~p, _, ~p)~n", [S,N]), receive - {empty_out_q,S} -> - close_port(S) + {timeout,Tref,_} -> % Linger timeout + ?DBG_FORMAT("prim_inet:close_pend_loop(~p, _, _) timeout~n", [S]), + close_port(S); + {empty_out_q,S} when N =/= undefined -> + ?DBG_FORMAT( + "prim_inet:close_pend_loop(~p, _, _) empty_out_q~n", [S]), + close_port(S, Tref) after ?INET_CLOSE_TIMEOUT -> case getstat(S, [send_pend]) of {ok, [{send_pend,N1}]} -> + ?DBG_FORMAT( + "prim_inet:close_pend_loop(~p, _, _) send_pend ~p~n", + [S,N1]), if - N1 =:= N -> - close_port(S); - true -> - close_pend_loop(S, N1) + N1 =:= 0 -> + %% Empty outq - done + close_port(S, Tref); + N =:= undefined -> + %% Within linger time - wait some more + close_pend_loop(S, Tref, N); + N1 =:= N -> + %% Inactivity - give up + close_port(S, Tref); + true -> + %% Still moving - wait some more + close_pend_loop(S, Tref, N) end; - _ -> - close_port(S) - end + _Stat -> + %% Failed getstat - give up + ?DBG_FORMAT( + "prim_inet:close_pend_loop(~p, _, _) getstat ~p~n", + [S,_Stat]), + close_port(S, Tref) + end end. + +close_port(S, Tref) -> + ?DBG_FORMAT("prim_inet:close_port(~p, _)~n", [S]), + case erlang:cancel_timer(Tref) of + false -> + receive + {timeout,Tref,_} -> + ok + end; + _N -> + ok + end, + close_port(S). +%% close_port(S) -> - catch erlang:port_close(S), - receive {'EXIT',S,_} -> ok after 0 -> ok end. + ?DBG_FORMAT("prim_inet:close_port(~p)~n", [S]), + _Closed = (catch erlang:port_close(S)), + receive {'EXIT',S,_} -> ok after 0 -> ok end, + ?DBG_FORMAT("prim_inet:close_port(~p) ~p~n", [S,_Closed]), + ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% @@ -424,23 +487,49 @@ peeloff(S, AssocId) -> %% be called directly -- use "sendmsg" instead: %% send(S, Data, OptList) when is_port(S), is_list(OptList) -> - ?DBG_FORMAT("prim_inet:send(~p, ~p)~n", [S,Data]), + ?DBG_FORMAT("prim_inet:send(~p, _, ~p)~n", [S,OptList]), try erlang:port_command(S, Data, OptList) of false -> % Port busy and nosuspend option passed ?DBG_FORMAT("prim_inet:send() -> {error,busy}~n", []), {error,busy}; true -> - receive - {inet_reply,S,Status} -> - ?DBG_FORMAT("prim_inet:send() -> ~p~n", [Status]), - Status - end + send_recv_reply(S, undefined) catch error:_Error -> ?DBG_FORMAT("prim_inet:send() -> {error,einval}~n", []), {error,einval} end. +send_recv_reply(S, Mref) -> + ReplyTimeout = + case Mref of + undefined -> + ?INET_CLOSE_TIMEOUT; + _ -> + infinity + end, + receive + {inet_reply,S,Status} -> + ?DBG_FORMAT( + "prim_inet:send_recv_reply(~p, _): inet_reply ~p~n", + [S,Status]), + case Mref of + undefined -> ok; + _ -> + demonitor(Mref, [flush]), + ok + end, + Status; + {'DOWN',Mref,_,_,_Reason} when Mref =/= undefined -> + ?DBG_FORMAT( + "prim_inet:send_recv_reply(~p, _) 'DOWN' ~p~n", + [S,_Reason]), + {error,closed} + after ReplyTimeout -> + send_recv_reply(S, monitor(port, S)) + end. + + send(S, Data) -> send(S, Data, []). @@ -520,13 +609,35 @@ sendfile(S, FileHandle, Offset, Length) sendfile(S, FileHandle, Offset, Length) -> case erlang:port_info(S, connected) of {connected, Pid} when Pid =:= self() -> - sendfile_1(S, FileHandle, Offset, Length); + Uncork = sendfile_maybe_cork(S), + Result = sendfile_1(S, FileHandle, Offset, Length), + sendfile_maybe_uncork(S, Uncork), + Result; {connected, Pid} when Pid =/= self() -> {error, not_owner}; _Other -> {error, einval} end. +sendfile_maybe_cork(S) -> + case getprotocol(S) of + tcp -> + case getopts(S, [nopush]) of + {ok, [{nopush,false}]} -> + _ = setopts(S, [{nopush,true}]), + true; + _ -> + false + end; + _ -> false + end. + +sendfile_maybe_uncork(S, true) -> + _ = setopts(S, [{nopush,false}]), + ok; +sendfile_maybe_uncork(_, false) -> + ok. + sendfile_1(S, FileHandle, Offset, 0) -> sendfile_1(S, FileHandle, Offset, (1 bsl 63) - 1); sendfile_1(_S, _FileHandle, Offset, Length) when @@ -870,9 +981,9 @@ chgopts(S, Opts) when is_port(S), is_list(Opts) -> getifaddrs(S) when is_port(S) -> case ctl_cmd(S, ?INET_REQ_GETIFADDRS, []) of - {ok, Data} -> - {ok, comp_ifaddrs(build_ifaddrs(Data), ktree_empty())}; - {error,enotsup} -> + {ok, Data} -> + {ok, comp_ifaddrs(build_ifaddrs(Data))}; + {error,enotsup} -> case getiflist(S) of {ok, IFs} -> {ok, getifaddrs_ifget(S, IFs)}; @@ -881,30 +992,75 @@ getifaddrs(S) when is_port(S) -> Err2 -> Err2 end. -%% Restructure interface properties per interface and remove duplicates - -comp_ifaddrs([{If,Opts}|IfOpts], T) -> - case ktree_is_defined(If, T) of - true -> - OptSet = comp_ifaddrs_add(ktree_get(If, T), Opts), - comp_ifaddrs(IfOpts, ktree_update(If, OptSet, T)); - false -> - OptSet = comp_ifaddrs_add(ktree_empty(), Opts), - comp_ifaddrs(IfOpts, ktree_insert(If, OptSet, T)) - end; -comp_ifaddrs([], T) -> - [{If,ktree_keys(ktree_get(If, T))} || If <- ktree_keys(T)]. - -comp_ifaddrs_add(OptSet, [Opt|Opts]) -> - case ktree_is_defined(Opt, OptSet) of - true - when element(1, Opt) =:= flags; - element(1, Opt) =:= hwaddr -> - comp_ifaddrs_add(OptSet, Opts); - _ -> - comp_ifaddrs_add(ktree_insert(Opt, undefined, OptSet), Opts) +%% Restructure interface properties per interface + +comp_ifaddrs(IfOpts) -> + comp_ifaddrs(IfOpts, ktree_empty()). +%% +comp_ifaddrs([{If,[{flags,Flags}|Opts]}|IfOpts], IfT) -> + case ktree_is_defined(If, IfT) of + true -> + comp_ifaddrs( + IfOpts, + ktree_update( + If, + comp_ifaddrs_flags(Flags, Opts, ktree_get(If, IfT)), + IfT)); + false -> + comp_ifaddrs( + IfOpts, + ktree_insert( + If, + comp_ifaddrs_flags(Flags, Opts, ktree_empty()), + IfT)) end; -comp_ifaddrs_add(OptSet, []) -> OptSet. +comp_ifaddrs([], IfT) -> + comp_ifaddrs_2(ktree_keys(IfT), IfT). + +comp_ifaddrs_flags(Flags, Opts, FlagsT) -> + case ktree_is_defined(Flags, FlagsT) of + true -> + ktree_update( + Flags, + rev(Opts, ktree_get(Flags, FlagsT)), + FlagsT); + false -> + ktree_insert(Flags, rev(Opts), FlagsT) + end. + +comp_ifaddrs_2([If|Ifs], IfT) -> + FlagsT = ktree_get(If, IfT), + [{If,comp_ifaddrs_3(ktree_keys(FlagsT), FlagsT)} + | comp_ifaddrs_2(Ifs, IfT)]; +comp_ifaddrs_2([], _IfT) -> + []. +%% +comp_ifaddrs_3([Flags|FlagsL], FlagsT) -> + [{flags,Flags}|hwaddr_last(rev(ktree_get(Flags, FlagsT)))] + ++ hwaddr_last(comp_ifaddrs_3(FlagsL, FlagsT)); +comp_ifaddrs_3([], _FlagsT) -> + []. + +%% Place hwaddr last to look more like legacy emulation +hwaddr_last(Opts) -> + hwaddr_last(Opts, Opts, []). +%% +hwaddr_last([{hwaddr,_} = Opt|Opts], L, R) -> + hwaddr_last(Opts, L, [Opt|R]); +hwaddr_last([_|Opts], L, R) -> + hwaddr_last(Opts, L, R); +hwaddr_last([], L, []) -> + L; +hwaddr_last([], L, R) -> + rev(hwaddr_last(L, []), rev(R)). +%% +hwaddr_last([{hwaddr,_}|Opts], R) -> + hwaddr_last(Opts, R); +hwaddr_last([Opt|Opts], R) -> + hwaddr_last(Opts, [Opt|R]); +hwaddr_last([], R) -> + R. + %% Legacy emulation of getifaddrs @@ -912,21 +1068,19 @@ getifaddrs_ifget(_, []) -> []; getifaddrs_ifget(S, [IF|IFs]) -> case ifget(S, IF, [flags]) of {ok,[{flags,Flags}]=FlagsVals} -> - BroadOpts = - case member(broadcast, Flags) of - true -> - [broadaddr,hwaddr]; - false -> - [hwaddr] - end, - P2POpts = - case member(pointtopoint, Flags) of - true -> - [dstaddr|BroadOpts]; - false -> - BroadOpts - end, - getifaddrs_ifget(S, IFs, IF, FlagsVals, [addr,netmask|P2POpts]); + GetOpts = + case member(pointtopoint, Flags) of + true -> + [dstaddr,hwaddr]; + false -> + case member(broadcast, Flags) of + true -> + [broadaddr,hwaddr]; + false -> + [hwaddr] + end + end, + getifaddrs_ifget(S, IFs, IF, FlagsVals, [addr,netmask|GetOpts]); _ -> getifaddrs_ifget(S, IFs, IF, [], [addr,netmask,hwaddr]) end. @@ -1275,6 +1429,7 @@ 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(nopush) -> ?TCP_OPT_NOPUSH; enc_opt(multicast_if) -> ?UDP_OPT_MULTICAST_IF; enc_opt(multicast_ttl) -> ?UDP_OPT_MULTICAST_TTL; enc_opt(multicast_loop) -> ?UDP_OPT_MULTICAST_LOOP; @@ -1336,6 +1491,7 @@ 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(?TCP_OPT_NOPUSH) -> nopush; dec_opt(?INET_OPT_RECVTOS) -> recvtos; dec_opt(?INET_OPT_RECVTCLASS) -> recvtclass; dec_opt(?INET_OPT_PKTOPTIONS) -> pktoptions; @@ -1422,6 +1578,7 @@ type_opt_1(pktoptions) -> opts; type_opt_1(ttl) -> int; type_opt_1(recvttl) -> bool; type_opt_1(nodelay) -> bool; +type_opt_1(nopush) -> bool; type_opt_1(ipv6_v6only) -> bool; %% multicast type_opt_1(multicast_ttl) -> int; @@ -2500,7 +2657,7 @@ get_addrs([F|Addrs]) -> [Addr|get_addrs(Rest)]. get_addr(?INET_AF_LOCAL, [N|Addr]) -> - {A,Rest} = lists:split(N, Addr), + {A,Rest} = split(N, Addr), {{local,iolist_to_binary(A)},Rest}; get_addr(?INET_AF_UNSPEC, Rest) -> {{unspec,<<>>},Rest}; |