diff options
Diffstat (limited to 'lib/kernel')
-rw-r--r-- | lib/kernel/doc/src/notes.xml | 47 | ||||
-rw-r--r-- | lib/kernel/src/net_kernel.erl | 110 | ||||
-rw-r--r-- | lib/kernel/src/os.erl | 59 | ||||
-rw-r--r-- | lib/kernel/test/os_SUITE.erl | 46 | ||||
-rw-r--r-- | lib/kernel/vsn.mk | 16 |
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 |