aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel')
-rw-r--r--lib/kernel/doc/src/notes.xml47
-rw-r--r--lib/kernel/src/net_kernel.erl110
-rw-r--r--lib/kernel/src/os.erl59
-rw-r--r--lib/kernel/test/os_SUITE.erl46
-rw-r--r--lib/kernel/vsn.mk16
5 files changed, 209 insertions, 69 deletions
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index 7bb6aea40e..aa652020d9 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -30,6 +30,53 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 2.13.5.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A bug introduced in Kernel 2.13.5.2 has been fixed.</p>
+ <p>
+ Own Id: OTP-8686 Aux Id: OTP-8643</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 2.13.5.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Under certain circumstances the net kernel could hang.
+ (Thanks to Scott Lystig Fritchie.)</p>
+ <p>
+ Own Id: OTP-8643 Aux Id: seq11584</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 2.13.5.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A race condition in <c>os:cmd/1</c> could cause the
+ caller to get stuck in <c>os:cmd/1</c> forever.</p>
+ <p>
+ Own Id: OTP-8502</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 2.13.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl
index 3afaedf274..1353ac65c6 100644
--- a/lib/kernel/src/net_kernel.erl
+++ b/lib/kernel/src/net_kernel.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. 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%
%%
-module(net_kernel).
@@ -354,13 +354,13 @@ init({Name, LongOrShortNames, TickT}) ->
%% The response is delayed until the connection is up and
%% running.
%%
-handle_call({connect, _, Node}, _From, State) when Node =:= node() ->
- {reply, true, State};
+handle_call({connect, _, Node}, From, State) when Node =:= node() ->
+ async_reply({reply, true, State}, From);
handle_call({connect, Type, Node}, From, State) ->
verbose({connect, Type, Node}, 1, State),
case ets:lookup(sys_dist, Node) of
[Conn] when Conn#connection.state =:= up ->
- {reply, true, State};
+ async_reply({reply, true, State}, From);
[Conn] when Conn#connection.state =:= pending ->
Waiting = Conn#connection.waiting,
ets:insert(sys_dist, Conn#connection{waiting = [From|Waiting]}),
@@ -376,19 +376,19 @@ handle_call({connect, Type, Node}, From, State) ->
{noreply,State#state{conn_owners=Owners}};
_ ->
?connect_failure(Node, {setup_call, failed}),
- {reply, false, State}
+ async_reply({reply, false, State}, From)
end
end;
%%
%% Close the connection to Node.
%%
-handle_call({disconnect, Node}, _From, State) when Node =:= node() ->
- {reply, false, State};
-handle_call({disconnect, Node}, _From, State) ->
+handle_call({disconnect, Node}, From, State) when Node =:= node() ->
+ async_reply({reply, false, State}, From);
+handle_call({disconnect, Node}, From, State) ->
verbose({disconnect, Node}, 1, State),
{Reply, State1} = do_disconnect(Node, State),
- {reply, Reply, State1};
+ async_reply({reply, Reply, State1}, From);
%%
%% The spawn/4 BIF ends up here.
@@ -411,39 +411,40 @@ handle_call({spawn_opt,M,F,A,O,L,Gleader},{From,Tag},State) when is_pid(From) ->
%%
%% Only allow certain nodes.
%%
-handle_call({allow, Nodes}, _From, State) ->
+handle_call({allow, Nodes}, From, State) ->
case all_atoms(Nodes) of
true ->
Allowed = State#state.allowed,
- {reply,ok,State#state{allowed = Allowed ++ Nodes}};
+ async_reply({reply,ok,State#state{allowed = Allowed ++ Nodes}},
+ From);
false ->
- {reply,error,State}
+ async_reply({reply,error,State}, From)
end;
%%
%% authentication, used by auth. Simply works as this:
%% if the message comes through, the other node IS authorized.
%%
-handle_call({is_auth, _Node}, _From, State) ->
- {reply,yes,State};
+handle_call({is_auth, _Node}, From, State) ->
+ async_reply({reply,yes,State}, From);
%%
%% Not applicable any longer !?
%%
handle_call({apply,_Mod,_Fun,_Args}, {From,Tag}, State)
when is_pid(From), node(From) =:= node() ->
- gen_server:reply({From,Tag}, not_implemented),
+ async_gen_server_reply({From,Tag}, not_implemented),
% Port = State#state.port,
% catch apply(Mod,Fun,[Port|Args]),
{noreply,State};
-handle_call(longnames, _From, State) ->
- {reply, get(longnames), State};
+handle_call(longnames, From, State) ->
+ async_reply({reply, get(longnames), State}, From);
-handle_call({update_publish_nodes, Ns}, _From, State) ->
- {reply, ok, State#state{publish_on_nodes = Ns}};
+handle_call({update_publish_nodes, Ns}, From, State) ->
+ async_reply({reply, ok, State#state{publish_on_nodes = Ns}}, From);
-handle_call({publish_on_node, Node}, _From, State) ->
+handle_call({publish_on_node, Node}, From, State) ->
NewState = case State#state.publish_on_nodes of
undefined ->
State#state{publish_on_nodes =
@@ -457,11 +458,12 @@ handle_call({publish_on_node, Node}, _From, State) ->
Nodes ->
lists:member(Node, Nodes)
end,
- {reply, Publish, NewState};
+ async_reply({reply, Publish, NewState}, From);
-handle_call({verbose, Level}, _From, State) ->
- {reply, State#state.verbose, State#state{verbose = Level}};
+handle_call({verbose, Level}, From, State) ->
+ async_reply({reply, State#state.verbose, State#state{verbose = Level}},
+ From);
%%
%% Set new ticktime
@@ -471,16 +473,16 @@ handle_call({verbose, Level}, _From, State) ->
%% #tick_change{} record if the ticker process has been upgraded;
%% otherwise, an integer or an atom.
-handle_call(ticktime, _, #state{tick = #tick{time = T}} = State) ->
- {reply, T, State};
-handle_call(ticktime, _, #state{tick = #tick_change{time = T}} = State) ->
- {reply, {ongoing_change_to, T}, State};
+handle_call(ticktime, From, #state{tick = #tick{time = T}} = State) ->
+ async_reply({reply, T, State}, From);
+handle_call(ticktime, From, #state{tick = #tick_change{time = T}} = State) ->
+ async_reply({reply, {ongoing_change_to, T}, State}, From);
-handle_call({new_ticktime,T,_TP}, _, #state{tick = #tick{time = T}} = State) ->
+handle_call({new_ticktime,T,_TP}, From, #state{tick = #tick{time = T}} = State) ->
?tckr_dbg(no_tick_change),
- {reply, unchanged, State};
+ async_reply({reply, unchanged, State}, From);
-handle_call({new_ticktime,T,TP}, _, #state{tick = #tick{ticker = Tckr,
+handle_call({new_ticktime,T,TP}, From, #state{tick = #tick{ticker = Tckr,
time = OT}} = State) ->
?tckr_dbg(initiating_tick_change),
start_aux_ticker(T, OT, TP),
@@ -493,14 +495,15 @@ handle_call({new_ticktime,T,TP}, _, #state{tick = #tick{ticker = Tckr,
?tckr_dbg(shorter_ticktime),
shorter
end,
- {reply, change_initiated, State#state{tick = #tick_change{ticker = Tckr,
- time = T,
- how = How}}};
+ async_reply({reply, change_initiated,
+ State#state{tick = #tick_change{ticker = Tckr,
+ time = T,
+ how = How}}}, From);
-handle_call({new_ticktime,_,_},
+handle_call({new_ticktime,From,_},
_,
#state{tick = #tick_change{time = T}} = State) ->
- {reply, {ongoing_change_to, T}, State}.
+ async_reply({reply, {ongoing_change_to, T}, State}, From).
%% ------------------------------------------------------------
%% handle_cast.
@@ -1063,11 +1066,12 @@ safesend(Pid, Mess) -> Pid ! Mess.
-endif.
do_spawn(SpawnFuncArgs, SpawnOpts, State) ->
+ [_,From|_] = SpawnFuncArgs,
case catch spawn_opt(?MODULE, spawn_func, SpawnFuncArgs, SpawnOpts) of
- {'EXIT', {Reason,_}} ->
- {reply, {'EXIT', {Reason,[]}}, State};
- {'EXIT', Reason} ->
- {reply, {'EXIT', {Reason,[]}}, State};
+ {'EXIT', {Reason,_}} ->
+ async_reply({reply, {'EXIT', {Reason,[]}}, State}, From);
+ {'EXIT', Reason} ->
+ async_reply({reply, {'EXIT', {Reason,[]}}, State}, From);
_ ->
{noreply,State}
end.
@@ -1409,7 +1413,7 @@ reply_waiting(_Node, Waiting, Rep) ->
reply_waiting1(lists:reverse(Waiting), Rep).
reply_waiting1([From|W], Rep) ->
- gen_server:reply(From, Rep),
+ async_gen_server_reply(From, Rep),
reply_waiting1(W, Rep);
reply_waiting1([], _) ->
ok.
@@ -1511,3 +1515,21 @@ verbose(_, _, _) ->
getnode(P) when is_pid(P) -> node(P);
getnode(P) -> P.
+
+async_reply({reply, Msg, State}, From) ->
+ async_gen_server_reply(From, Msg),
+ {noreply, State}.
+
+async_gen_server_reply(From, Msg) ->
+ {Pid, Tag} = From,
+ M = {Tag, Msg},
+ case catch erlang:send(Pid, M, [nosuspend, noconnect]) of
+ ok ->
+ ok;
+ nosuspend ->
+ spawn(fun() -> catch erlang:send(Pid, M, [noconnect]) end);
+ noconnect ->
+ ok; % The gen module takes care of this case.
+ {'EXIT', _}=EXIT ->
+ EXIT
+ end.
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index 196e6cdeb2..d0b498edc9 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. 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%
%%
-module(os).
@@ -169,6 +169,7 @@ unix_cmd(Cmd) ->
%% $1 parameter for easy identification of the resident shell.
%%
-define(SHELL, "/bin/sh -s unix:cmd 2>&1").
+-define(PORT_CREATOR_NAME, os_cmd_port_creator).
%%
%% Serializing open_port through a process to avoid smp lock contention
@@ -176,18 +177,37 @@ unix_cmd(Cmd) ->
%%
-spec start_port() -> port().
start_port() ->
- {Ref,Client} = {make_ref(),self()},
- try (os_cmd_port_creator ! {Ref,Client})
- catch
- error:_ -> spawn(fun() -> start_port_srv({Ref,Client}) end)
- end,
+ Ref = make_ref(),
+ Request = {Ref,self()},
+ {Pid, Mon} = case whereis(?PORT_CREATOR_NAME) of
+ undefined ->
+ spawn_monitor(fun() ->
+ start_port_srv(Request)
+ end);
+ P ->
+ P ! Request,
+ M = erlang:monitor(process, P),
+ {P, M}
+ end,
receive
- {Ref,Port} when is_port(Port) -> Port;
- {Ref,Error} -> exit(Error)
+ {Ref, Port} when is_port(Port) ->
+ erlang:demonitor(Mon, [flush]),
+ Port;
+ {Ref, Error} ->
+ erlang:demonitor(Mon, [flush]),
+ exit(Error);
+ {'DOWN', Mon, process, Pid, _Reason} ->
+ start_port()
end.
start_port_srv(Request) ->
- StayAlive = try register(os_cmd_port_creator, self())
+ %% We don't want a group leader of some random application. Use
+ %% kernel_sup's group leader.
+ {group_leader, GL} = process_info(whereis(kernel_sup),
+ group_leader),
+ true = group_leader(GL, self()),
+ process_flag(trap_exit, true),
+ StayAlive = try register(?PORT_CREATOR_NAME, self())
catch
error:_ -> false
end,
@@ -196,7 +216,7 @@ start_port_srv(Request) ->
start_port_srv_loop({Ref,Client}, StayAlive) ->
Reply = try open_port({spawn, ?SHELL},[stream]) of
Port when is_port(Port) ->
- port_connect(Port, Client),
+ (catch port_connect(Port, Client)),
unlink(Port),
Port
catch
@@ -205,10 +225,19 @@ start_port_srv_loop({Ref,Client}, StayAlive) ->
end,
Client ! {Ref,Reply},
case StayAlive of
- true -> start_port_srv_loop(receive Msg -> Msg end, true);
+ true -> start_port_srv_loop(get_open_port_request(), true);
false -> exiting
end.
+get_open_port_request() ->
+ receive
+ {Ref, Client} = Request when is_reference(Ref),
+ is_pid(Client) ->
+ Request;
+ _Junk ->
+ get_open_port_request()
+ end.
+
%%
%% unix_get_data(Port) -> Result
%%
diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index 1673b33010..6a3534b094 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -20,13 +20,13 @@
-export([all/1]).
-export([space_in_cwd/1, quoting/1, space_in_name/1, bad_command/1,
- find_executable/1, unix_comment_in_command/1]).
+ find_executable/1, unix_comment_in_command/1, evil/1]).
-include("test_server.hrl").
all(suite) ->
[space_in_cwd, quoting, space_in_name, bad_command, find_executable,
- unix_comment_in_command].
+ unix_comment_in_command, evil].
space_in_cwd(doc) ->
"Test that executing a command in a current working directory "
@@ -186,6 +186,48 @@ unix_comment_in_command(Config) when is_list(Config) ->
?line test_server:timetrap_cancel(Dog),
ok.
+-define(EVIL_PROCS, 100).
+-define(EVIL_LOOPS, 100).
+-define(PORT_CREATOR, os_cmd_port_creator).
+evil(Config) when is_list(Config) ->
+ Dog = test_server:timetrap(test_server:minutes(5)),
+ Parent = self(),
+ Ps = lists:map(fun (N) ->
+ spawn_link(fun () ->
+ evil_loop(Parent, ?EVIL_LOOPS,N)
+ end)
+ end, lists:seq(1, ?EVIL_PROCS)),
+ Devil = spawn(fun () -> devil(hd(Ps), hd(lists:reverse(Ps))) end),
+ lists:foreach(fun (P) -> receive {P, done} -> ok end end, Ps),
+ exit(Devil, kill),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+devil(P1, P2) ->
+ erlang:display({?PORT_CREATOR, whereis(?PORT_CREATOR)}),
+ (catch ?PORT_CREATOR ! lists:seq(1,1000000)),
+ (catch ?PORT_CREATOR ! lists:seq(1,666)),
+ (catch ?PORT_CREATOR ! grrrrrrrrrrrrrrrr),
+ (catch ?PORT_CREATOR ! {'EXIT', P1, buhuuu}),
+ (catch ?PORT_CREATOR ! {'EXIT', hd(erlang:ports()), buhuuu}),
+ (catch ?PORT_CREATOR ! {'EXIT', P2, arggggggg}),
+ receive after 500 -> ok end,
+ (catch exit(whereis(?PORT_CREATOR), kill)),
+ (catch ?PORT_CREATOR ! ">8|"),
+ receive after 500 -> ok end,
+ (catch exit(whereis(?PORT_CREATOR), diiiiiiiiiiiiiiiiiiiie)),
+ receive after 100 -> ok end,
+ devil(P1, P2).
+
+evil_loop(Parent, Loops, N) ->
+ Res = integer_to_list(N),
+ evil_loop(Parent, Loops, Res, "echo " ++ Res).
+
+evil_loop(Parent, 0, _Res, _Cmd) ->
+ Parent ! {self(), done};
+evil_loop(Parent, Loops, Res, Cmd) ->
+ comp(Res, os:cmd(Cmd)),
+ evil_loop(Parent, Loops-1, Res, Cmd).
comp(Expected, Got) ->
case strip_nl(Got) of
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index 5b4369740d..fafd1d2c60 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1,20 +1,20 @@
-#
+#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1997-2010. 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%
-#
+#
-KERNEL_VSN = 2.13.5
+KERNEL_VSN = 2.13.5.3