%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2012-2013. 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.
%%
%% %CopyrightEnd%
%%
%%
%% As the module name imply, this module is here for ERTS internal
%% functionality. As an application programmer you should *never*
%% call anything in this module directly. Functions exported by
%% this module may change behaviour or be removed at any time
%% without any notice whatsoever. Everything in this module is
%% intentionally left undocumented, and should remain so.
%%
-module(erts_internal).
-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,
port_control/3, port_call/3, port_info/1, port_info/2]).
-export([request_system_task/3]).
-export([check_process_code/2]).
-export([flush_monitor_messages/3]).
-export([time_unit/0]).
-export([bif_timer_server/2]).
-export([get_bif_timer_servers/0, create_bif_timer/0, access_bif_timer/1]).
-export([monitor_process/2]).
-export([is_system_process/1]).
%%
%% Await result of send to port
%%
await_port_send_result(Ref, Busy, Ok) ->
receive
{Ref, false} -> Busy;
{Ref, _} -> Ok
end.
%%
%% Statically linked port NIFs
%%
-spec erts_internal:port_command(Port, Data, OptionList) -> Result when
Port :: port() | atom(),
Data :: iodata(),
OptionList :: [Option],
Option :: force | nosuspend,
Result :: boolean() | reference() | badarg | notsup.
port_command(_Port, _Data, _OptionList) ->
erlang:nif_error(undefined).
-spec erts_internal:port_connect(Port, Pid) -> Result when
Port :: port() | atom(),
Pid :: pid(),
Result :: true | reference() | badarg.
port_connect(_Port, _Pid) ->
erlang:nif_error(undefined).
-spec erts_internal:port_close(Port) -> Result when
Port :: port() | atom(),
Result :: true | reference() | badarg.
port_close(_Port) ->
erlang:nif_error(undefined).
-spec erts_internal:port_control(Port, Operation, Data) -> Result when
Port :: port() | atom(),
Operation :: integer(),
Data :: iodata(),
Result :: string() | binary() | reference() | badarg.
port_control(_Port, _Operation, _Data) ->
erlang:nif_error(undefined).
-spec erts_internal:port_call(Port, Operation, Data) -> Result when
Port :: port() | atom(),
Operation :: integer(),
Data :: term(),
Result :: {ok, term()} | reference() | badarg.
port_call(_Port, _Operation, _Data) ->
erlang:nif_error(undefined).
-type port_info_1_result_item() ::
{registered_name, RegName :: atom()} |
{id, Index :: non_neg_integer()} |
{connected, Pid :: pid()} |
{links, Pids :: [pid()]} |
{name, String :: string()} |
{input, Bytes :: non_neg_integer()} |
{output, Bytes :: non_neg_integer()} |
{os_pid, OsPid :: non_neg_integer() | 'undefined'}.
-spec erts_internal:port_info(Port) -> Result when
Port :: port() | atom(),
Result :: [port_info_1_result_item()] | undefined | reference() | badarg | [].
port_info(_Result) ->
erlang:nif_error(undefined).
-type port_info_2_item() ::
registered_name |
id |
connected |
links |
name |
input |
output |
os_pid |
monitors |
memory |
parallelism |
queue_size |
locking.
-type port_info_2_result_item() ::
{registered_name, RegName :: atom()} |
[] | % No registered name
{id, Index :: non_neg_integer()} |
{connected, Pid :: pid()} |
{links, Pids :: [pid()]} |
{name, String :: string()} |
{input, Bytes :: non_neg_integer()} |
{output, Bytes :: non_neg_integer()} |
{os_pid, OsPid :: non_neg_integer() | 'undefined'} |
{monitors, Monitors :: [{process, pid()}]} |
{memory, MemSz :: non_neg_integer()} |
{parallelism, Boolean :: boolean()} |
{queue_size, QSz :: non_neg_integer()} |
{locking, Locking :: 'false' | 'port_level' | 'driver_level'}.
-spec erts_internal:port_info(Port, Item) -> Result when
Port :: port() | atom(),
Item :: port_info_2_item(),
Result :: port_info_2_result_item() | undefined | reference() | badarg.
port_info(_Result, _Item) ->
erlang:nif_error(undefined).
-spec request_system_task(Pid, Prio, Request) -> 'ok' when
Prio :: 'max' | 'high' | 'normal' | 'low',
Request :: {'garbage_collect', term()}
| {'check_process_code', term(), module(), boolean()},
Pid :: pid().
request_system_task(_Pid, _Prio, _Request) ->
erlang:nif_error(undefined).
-spec check_process_code(Module, OptionList) -> boolean() when
Module :: module(),
Option :: {allow_gc, boolean()},
OptionList :: [Option].
check_process_code(_Module, _OptionList) ->
erlang:nif_error(undefined).
%% term compare where integer() < float() = true
-spec cmp_term(A,B) -> Result when
A :: term(),
B :: term(),
Result :: -1 | 0 | 1.
cmp_term(_A,_B) ->
erlang:nif_error(undefined).
%% return the internal key tuple for map keys
-spec map_to_tuple_keys(M) -> Keys when
M :: map(),
Keys :: tuple().
map_to_tuple_keys(_M) ->
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:get_bif_timer_servers() -> Pids when
Pid :: pid(),
Pids :: [Pid].
get_bif_timer_servers() ->
erlang:nif_error(undefined).
-spec erts_internal:create_bif_timer() -> Res when
Res :: {reference(), pid(), reference()}.
create_bif_timer() ->
erlang:nif_error(undefined).
-spec erts_internal:access_bif_timer(Ref) -> Res when
Ref :: reference(),
Res :: {reference(), pid()} | 'undefined'.
access_bif_timer(_Ref) ->
erlang:nif_error(undefined).
-spec erts_internal:monitor_process(Pid, Ref) -> boolean() when
Pid :: pid(),
Ref :: reference().
monitor_process(_Pid, _Ref) ->
erlang:nif_error(undefined).
-spec erts_internal:is_system_process(Pid) -> boolean() when
Pid :: pid().
is_system_process(_Pid) ->
erlang:nif_error(undefined).
%%
%% BIF timer servers
%%
-record(tsrv_state, {rtab,
ttab,
btr,
unit,
next}).
bif_timer_server(N, BTR) ->
try
tsrv_loop(tsrv_init_static_state(N, BTR), infinity)
catch
Type:Reason ->
erlang:display({'BIF_timer_server',
{Type, Reason},
erlang:get_stacktrace()}),
exit(Reason)
end.
tsrv_init_static_state(N, BTR) ->
process_flag(trap_exit, true),
NList = integer_to_list(N),
RTabName = list_to_atom("BIF_timer_reference_table_" ++ NList),
TTabName = list_to_atom("BIF_timer_time_table_" ++ NList),
#tsrv_state{rtab = ets:new(RTabName,
[set, private, {keypos, 2}]),
ttab = ets:new(TTabName,
[ordered_set, private, {keypos, 1}]),
btr = BTR,
unit = erts_internal:time_unit(),
next = infinity}.
tsrv_loop(#tsrv_state{unit = Unit} = StaticState, Nxt) ->
CallTime = erlang:monotonic_time(),
%% 'infinity' is greater than all integers...
NewNxt = case CallTime >= Nxt of
true ->
tsrv_handle_timeout(CallTime, StaticState);
false ->
TMO = try
(1000*(Nxt - CallTime - 1)) div Unit + 1
catch
error:badarith when Nxt == infinity -> infinity
end,
receive
Msg ->
tsrv_handle_msg(Msg, StaticState, Nxt)
after TMO ->
Nxt
end
end,
tsrv_loop(StaticState, NewNxt).
tsrv_handle_msg({set_timeout, BTR, Proc, Time, TRef, Msg},
#tsrv_state{rtab = RTab,
ttab = TTab,
btr = BTR},
Nxt) when erlang:is_integer(Time) ->
RcvTime = erlang:monotonic_time(),
case Time =< RcvTime of
true ->
try Proc ! Msg catch _:_ -> ok end,
Nxt;
false ->
Ins = case erlang:is_atom(Proc) of
true ->
true;
false ->
try
erts_internal:monitor_process(Proc, TRef)
catch
_:_ -> false
end
end,
case Ins of
false ->
Nxt;
true ->
TKey = {Time, TRef},
true = ets:insert(RTab, TKey),
true = ets:insert(TTab, {TKey, Proc, Msg}),
case Time < Nxt of
true -> Time;
false -> Nxt
end
end
end;
tsrv_handle_msg({cancel_timeout, BTR, From, Reply, Req, TRef},
#tsrv_state{rtab = RTab,
ttab = TTab,
unit = Unit,
btr = BTR},
Nxt) ->
case ets:lookup(RTab, TRef) of
[] ->
case Reply of
false ->
ok;
_ ->
_ = try From ! {cancel_timer, Req, false} catch _:_ -> ok end
end,
Nxt;
[{Time, TRef} = TKey] ->
ets:delete(RTab, TRef),
ets:delete(TTab, TKey),
erlang:demonitor(TRef),
case Reply of
false ->
ok;
_ ->
RcvTime = erlang:monotonic_time(),
RT = case Time =< RcvTime of
true ->
0;
false ->
((1000*(Time - RcvTime)) div Unit)
end,
_ = try From ! {cancel_timer, Req, RT} catch _:_ -> ok end
end,
case Time =:= Nxt of
false ->
Nxt;
true ->
case ets:first(TTab) of
'$end_of_table' -> infinity;
{NextTime, _TRef} -> NextTime
end
end
end;
tsrv_handle_msg({read_timeout, BTR, From, Req, TRef},
#tsrv_state{rtab = RTab,
unit = Unit,
btr = BTR},
Nxt) ->
case ets:lookup(RTab, TRef) of
[] ->
_ = try From ! {read_timer, Req, false} catch _:_ -> ok end;
[{Time, TRef}] ->
RcvTime = erlang:monotonic_time(),
RT = case Time =< RcvTime of
true -> 0;
false -> (1000*(Time - RcvTime)) div Unit
end,
_ = try From ! {read_timer, Req, RT} catch _:_ -> ok end
end,
Nxt;
tsrv_handle_msg({'DOWN', TRef, process, _, _},
#tsrv_state{rtab = RTab,
ttab = TTab},
Nxt) ->
case ets:lookup(RTab, TRef) of
[] ->
Nxt;
[{Time, TRef} = TKey] ->
ets:delete(RTab, TRef),
ets:delete(TTab, TKey),
case Time =:= Nxt of
false ->
Nxt;
true ->
case ets:first(TTab) of
'$end_of_table' -> infinity;
{NextTime, _} -> NextTime
end
end
end;
tsrv_handle_msg({cancel_all_timeouts, BTR, From, Ref},
#tsrv_state{rtab = RTab,
ttab = TTab,
btr = BTR},
_Nxt) ->
tsrv_delete_monitor_objects(RTab),
ets:delete_all_objects(TTab),
try From ! {canceled_all_timeouts, Ref} catch _:_ -> ok end,
infinity;
tsrv_handle_msg(_GarbageMsg, _StaticState, Nxt) ->
Nxt.
tsrv_delete_monitor_objects(RTab) ->
case ets:first(RTab) of
'$end_of_table' ->
ok;
TRef ->
erlang:demonitor(TRef),
ets:delete(RTab, TRef),
tsrv_delete_monitor_objects(RTab)
end.
tsrv_handle_timeout(CallTime, #tsrv_state{rtab = RTab,
ttab = TTab} = S) ->
case ets:first(TTab) of
'$end_of_table' ->
infinity;
{Time, _TRef} when Time > CallTime ->
Time;
{_Time, TRef} = TKey ->
[{TKey, Proc, Msg}] = ets:lookup(TTab, TKey),
case erlang:is_pid(Proc) of
false -> ok;
_ -> erlang:demonitor(TRef)
end,
ets:delete(TTab, TKey),
ets:delete(RTab, TRef),
_ = try Proc ! Msg catch _:_ -> ok end,
tsrv_handle_timeout(CallTime, S)
end.