aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/asn1/doc/src/notes.xml17
-rw-r--r--lib/asn1/src/asn1ct_check.erl8
-rw-r--r--lib/asn1/test/asn1_SUITE.erl5
-rw-r--r--lib/asn1/test/asn1_SUITE_data/Param2.asn148
-rw-r--r--lib/asn1/test/testParameterizedInfObj.erl57
-rw-r--r--lib/asn1/vsn.mk2
-rw-r--r--lib/common_test/priv/ct_default.css8
-rw-r--r--lib/common_test/src/ct_framework.erl11
-rw-r--r--lib/common_test/src/ct_logs.erl1095
-rw-r--r--lib/common_test/src/ct_run.erl14
-rw-r--r--lib/common_test/src/ct_util.erl2
-rw-r--r--lib/common_test/src/cth_log_redirect.erl8
-rw-r--r--lib/common_test/test/ct_cover_SUITE_data/cover_SUITE.erl50
-rw-r--r--lib/common_test/test/ct_repeat_testrun_SUITE.erl4
-rw-r--r--lib/common_test/test/ct_test_support.erl39
-rw-r--r--lib/compiler/src/beam_receive.erl92
-rw-r--r--lib/compiler/test/receive_SUITE.erl13
-rw-r--r--lib/compiler/test/receive_SUITE_data/ref_opt/yes_14.S71
-rw-r--r--lib/compiler/test/receive_SUITE_data/ref_opt/yes_5.erl6
-rwxr-xr-xlib/crypto/doc/src/crypto.xml2
-rw-r--r--lib/debugger/src/dbg_debugged.erl14
-rw-r--r--lib/debugger/src/dbg_istk.erl2
-rw-r--r--lib/dialyzer/src/dialyzer_gui.erl5
-rw-r--r--lib/dialyzer/src/dialyzer_gui_wx.erl5
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl2
-rw-r--r--lib/diameter/doc/src/diameter.xml11
-rw-r--r--lib/diameter/doc/src/diameter_tcp.xml20
-rw-r--r--lib/diameter/doc/src/diameter_transport.xml30
-rw-r--r--lib/diameter/examples/code/peer.erl28
-rw-r--r--lib/diameter/src/Makefile2
-rw-r--r--lib/diameter/src/base/diameter_peer.erl8
-rw-r--r--lib/diameter/src/base/diameter_peer_fsm.erl13
-rw-r--r--lib/diameter/src/base/diameter_service.erl24
-rw-r--r--lib/diameter/src/transport/diameter_tcp.erl79
-rw-r--r--lib/diameter/test/diameter_capx_SUITE.erl2
-rw-r--r--lib/diameter/test/diameter_event_SUITE.erl2
-rw-r--r--lib/diameter/test/diameter_examples_SUITE.erl2
-rw-r--r--lib/diameter/test/diameter_tls_SUITE.erl2
-rw-r--r--lib/diameter/test/diameter_transport_SUITE.erl122
-rw-r--r--lib/diameter/test/diameter_util.erl29
-rw-r--r--lib/diameter/test/diameter_watchdog_SUITE.erl2
-rw-r--r--lib/et/src/et_collector.erl9
-rw-r--r--lib/inets/src/http_server/httpd_manager.erl4
-rw-r--r--lib/kernel/src/application_master.erl9
-rw-r--r--lib/kernel/src/disk_log.erl18
-rw-r--r--lib/kernel/src/file.erl43
-rw-r--r--lib/kernel/src/file_io_server.erl3
-rw-r--r--lib/kernel/src/file_server.erl3
-rw-r--r--lib/kernel/src/inet_gethost_native.erl3
-rw-r--r--lib/kernel/src/rpc.erl24
-rw-r--r--lib/kernel/test/file_SUITE.erl29
-rw-r--r--lib/kernel/test/gen_sctp_SUITE.erl3
-rw-r--r--lib/kernel/test/global_SUITE_data/global_trace.erl9
-rw-r--r--lib/orber/src/orber_iiop_outproxy.erl35
-rw-r--r--lib/public_key/test/erl_make_certs.erl4
-rw-r--r--lib/reltool/test/reltool_server_SUITE.erl6
-rw-r--r--lib/runtime_tools/src/dbg.erl6
-rw-r--r--lib/runtime_tools/src/ttb_autostart.erl1
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl8
-rw-r--r--lib/snmp/src/agent/snmpa_target_cache.erl37
-rw-r--r--lib/ssh/src/ssh_cli.erl4
-rw-r--r--lib/ssh/src/ssh_xfer.erl26
-rw-r--r--lib/ssl/test/erl_make_certs.erl4
-rw-r--r--lib/stdlib/doc/src/unicode_usage.xml1
-rw-r--r--lib/stdlib/src/dets.erl9
-rw-r--r--lib/stdlib/src/epp.erl3
-rw-r--r--lib/stdlib/src/gen_server.erl18
-rw-r--r--lib/stdlib/src/io.erl6
-rw-r--r--lib/stdlib/src/supervisor.erl27
-rw-r--r--lib/stdlib/src/timer.erl4
-rw-r--r--lib/stdlib/test/Makefile1
-rw-r--r--lib/stdlib/test/erl_eval_SUITE.erl3
-rw-r--r--lib/stdlib/test/qlc_SUITE_data/join_info_compat.erl215
-rw-r--r--lib/stdlib/test/shell_SUITE.erl3
-rw-r--r--lib/stdlib/test/supervisor_3.erl45
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl106
-rw-r--r--lib/test_server/src/erl2html2.erl3
-rw-r--r--lib/test_server/src/test_server_ctrl.erl4
-rw-r--r--lib/test_server/src/test_server_io.erl17
-rw-r--r--lib/test_server/test/test_server_SUITE.erl13
-rw-r--r--lib/tools/src/fprof.erl8
81 files changed, 1861 insertions, 869 deletions
diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml
index e619408591..76d605569d 100644
--- a/lib/asn1/doc/src/notes.xml
+++ b/lib/asn1/doc/src/notes.xml
@@ -31,6 +31,23 @@
<p>This document describes the changes made to the asn1 application.</p>
+<section><title>Asn1 2.0.1.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ When an object set is an actual parameter, the extension
+ marker for the object set could get lost (which would
+ cause the decoding of unknown values to fail).</p>
+ <p>
+ Own Id: OTP-10995 Aux Id: seq12290 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Asn1 2.0.1.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl
index e1911d8170..0622998445 100644
--- a/lib/asn1/src/asn1ct_check.erl
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -1025,8 +1025,8 @@ prepare_objset({{'SingleValue',Set},Ext}) ->
%% {set,lists:append([Set,Ext]),true};
prepare_objset({Set,Ext}) when is_list(Set) ->
{set,merge_sets(Set,Ext),true};
-prepare_objset({ObjDef={object,definedsyntax,_ObjFields},_Ext}) ->
- {set,[ObjDef],true};
+prepare_objset({{object,definedsyntax,_ObjFields}=Set,Ext}) ->
+ {set,merge_sets(Set, Ext),true};
prepare_objset(ObjDef={object,definedsyntax,_ObjFields}) ->
{set,[ObjDef],false};
prepare_objset({ObjDef=#type{},Ext}) when is_list(Ext) ->
@@ -4034,8 +4034,8 @@ categorize(S,value,Type,Value) ->
[#valuedef{type=Type,value=Value,module=S#state.mname}].
-parse_objectset({valueset,T=#type{}}) ->
- [T];
+parse_objectset({valueset,#type{def=#'Externaltypereference'{}=Ref}}) ->
+ Ref;
parse_objectset({valueset,Set}) ->
Set;
parse_objectset(#type{def=Ref}) when is_record(Ref,'Externaltypereference') ->
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index 62418e554e..8deabece37 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -829,8 +829,9 @@ testInfObjectClass(Config, Rule, Opts) ->
testParameterizedInfObj(Config) ->
test(Config, fun testParameterizedInfObj/3).
testParameterizedInfObj(Config, Rule, Opts) ->
- asn1_test_lib:compile("Param", Config, [Rule|Opts]),
- testParameterizedInfObj:main(Rule).
+ Files = ["Param","Param2"],
+ asn1_test_lib:compile_all(Files, Config, [Rule|Opts]),
+ testParameterizedInfObj:main(Config, Rule).
testMergeCompile(Config) -> test(Config, fun testMergeCompile/3).
testMergeCompile(Config, Rule, Opts) ->
diff --git a/lib/asn1/test/asn1_SUITE_data/Param2.asn1 b/lib/asn1/test/asn1_SUITE_data/Param2.asn1
new file mode 100644
index 0000000000..09ccb367d8
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/Param2.asn1
@@ -0,0 +1,48 @@
+Param2 DEFINITIONS AUTOMATIC TAGS ::=
+BEGIN
+
+ S1AP-PROTOCOL-IES ::= CLASS {
+ &id INTEGER UNIQUE,
+ &Value
+ }
+ WITH SYNTAX {
+ ID &id
+ TYPE &Value
+ }
+
+ ProtocolIE-Field {S1AP-PROTOCOL-IES : IEsSetParam} ::= SEQUENCE {
+ id S1AP-PROTOCOL-IES.&id ({IEsSetParam}),
+ value S1AP-PROTOCOL-IES.&Value ({IEsSetParam}{@id})
+ }
+
+ ProtocolIE-Container {S1AP-PROTOCOL-IES : IEsSetParam} ::=
+ SEQUENCE (SIZE (0..10)) OF ProtocolIE-Field {{IEsSetParam}}
+
+ HandoverRequired ::= SEQUENCE {
+ protocolIEs ProtocolIE-Container { { HandoverRequiredIEs } },
+ ...
+ }
+
+ HandoverRequiredIEs S1AP-PROTOCOL-IES ::= {
+ { ID 1 TYPE OCTET STRING } |
+ { ID 2 TYPE INTEGER },
+--Delete-start
+ ...,
+ { ID 100 TYPE INTEGER (0..1023) } |
+ { ID 101 TYPE ENUMERATED {true,false} }
+--Delete-end
+ }
+
+ SingleRoot ::= SEQUENCE {
+ protocolIEs ProtocolIE-Container { { SingleRootIEs } },
+ ...
+ }
+
+ -- The extension was lost when there was a single root item.
+ SingleRootIEs S1AP-PROTOCOL-IES ::= {
+ { ID 1 TYPE OCTET STRING },
+ ...,
+ { ID 2 TYPE INTEGER }
+ }
+
+END
diff --git a/lib/asn1/test/testParameterizedInfObj.erl b/lib/asn1/test/testParameterizedInfObj.erl
index 17108e285b..212df79fd4 100644
--- a/lib/asn1/test/testParameterizedInfObj.erl
+++ b/lib/asn1/test/testParameterizedInfObj.erl
@@ -20,7 +20,7 @@
-module(testParameterizedInfObj).
--export([main/1,ranap/1]).
+-export([main/2,ranap/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -31,7 +31,11 @@
-record('Iu-ReleaseCommand',{protocolIEs,protocolExtensions}).
-main(Erule) ->
+main(Config, Erule) ->
+ param(Erule),
+ param2(Config, Erule).
+
+param(Erule) ->
PERVal = #'AllocationOrRetentionPriority'
{priorityLevel = true,
iE_Extensions =
@@ -84,7 +88,6 @@ main(Erule) ->
ok.
-
ranap(_Erule) ->
PIEVal2 = [{'ProtocolIE-Field',4,ignore,{radioNetwork,'rab-pre-empted'}}],
?line Val2 =
@@ -102,3 +105,51 @@ open_type(uper,Val) when is_list(Val) ->
list_to_binary(Val);
open_type(_,Val) ->
Val.
+
+param2(Config, Erule) ->
+ roundtrip2('HandoverRequired',
+ {'HandoverRequired',
+ [{'ProtocolIE-Field',1,"ABC"},
+ {'ProtocolIE-Field',2,577799}]}),
+ Enc = roundtrip2('HandoverRequired',
+ {'HandoverRequired',
+ [{'ProtocolIE-Field',1,"ABC"},
+ {'ProtocolIE-Field',2,-42},
+ {'ProtocolIE-Field',100,533},
+ {'ProtocolIE-Field',101,true}]}),
+
+ %% Now remove the data after the extension mark in the object set.
+ DataDir = ?config(data_dir, Config),
+ CaseDir = ?config(case_dir, Config),
+ Asn1SrcBase = "Param2.asn1",
+ Asn1SrcFile0 = filename:join(DataDir, Asn1SrcBase),
+ {ok,Src0} = file:read_file(Asn1SrcFile0),
+ Src = re:replace(Src0, "--Delete-start.*?--Delete-end", "...\n",
+ [dotall,global,{return,binary}]),
+ io:format("~s\n\n", [Src]),
+
+ Asn1SrcFile = filename:join(CaseDir, Asn1SrcBase),
+ ok = file:write_file(Asn1SrcFile, Src),
+ ok = asn1ct:compile(Asn1SrcFile,
+ [{i,DataDir},{outdir,CaseDir},Erule]),
+
+ %% Decompile extended data.
+ {ok,{'HandoverRequired',[{'ProtocolIE-Field',1,"ABC"},
+ {'ProtocolIE-Field',2,-42},
+ {'ProtocolIE-Field',100,Open100},
+ {'ProtocolIE-Field',101,Open101}]}} =
+ asn1_wrapper:decode('Param2', 'HandoverRequired', Enc),
+ true = is_binary(Open100),
+ true = is_binary(Open101),
+
+ %% Test single root.
+ roundtrip2('SingleRoot',
+ {'SingleRoot',[{'ProtocolIE-Field',1,"ABC"},
+ {'ProtocolIE-Field',2,9999}]}),
+ ok.
+
+
+roundtrip2(T, V) ->
+ {ok,Enc} = asn1_wrapper:encode('Param2', T, V),
+ {ok,V} = asn1_wrapper:decode('Param2', T, Enc),
+ Enc.
diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk
index 3c4f3ff122..9245f83280 100644
--- a/lib/asn1/vsn.mk
+++ b/lib/asn1/vsn.mk
@@ -1,2 +1,2 @@
#next version number to use is 2.0
-ASN1_VSN = 2.0.1.1
+ASN1_VSN = 2.0.1.2
diff --git a/lib/common_test/priv/ct_default.css b/lib/common_test/priv/ct_default.css
index 1188f8f676..ff48b4fdc0 100644
--- a/lib/common_test/priv/ct_default.css
+++ b/lib/common_test/priv/ct_default.css
@@ -96,6 +96,14 @@ div.ct_error_notify {
margin: .2em 0 0 0;
}
+div.ct_error_notify a:link {
+ color: #D0D0D0;
+}
+
+div.ct_error_notify a:visited {
+ color: #AAAAAA;
+}
+
div.default {
background: lightgreen; color: black;
font-family: "Monaco", "Andale Mono", "Consolas", monospace;
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index b92fe1555f..276f902b05 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -32,6 +32,7 @@
-export([error_in_suite/1, init_per_suite/1, end_per_suite/1,
init_per_group/2, end_per_group/2]).
+-include("ct.hrl").
-include("ct_event.hrl").
-include("ct_util.hrl").
@@ -806,8 +807,14 @@ error_notification(Mod,Func,_Args,{Error,Loc}) ->
"- - - - - - - - - -~n",
io:format(user, lists:concat([Div,ErrFormat,Div,"~n"]),
ErrArgs),
- ct_logs:tc_log(ct_error_notify, "CT Error Notification",
- ErrFormat, ErrArgs)
+ Link =
+ "\n\n<a href=\"#end\">"
+ "Full error description and stacktrace"
+ "</a>",
+ ct_logs:tc_log(ct_error_notify,
+ ?MAX_IMPORTANCE,
+ "CT Error Notification",
+ ErrFormat++Link, ErrArgs)
end,
case Loc of
[{?MODULE,error_in_suite}] ->
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 752033fdff..f5355bfefe 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -41,7 +41,8 @@
-export([uri/1]).
%% Logging stuff directly from testcase
--export([tc_log/3, tc_log/4, tc_log_async/3, tc_print/3, tc_print/4,
+-export([tc_log/3, tc_log/4, tc_log/5, tc_log_async/3, tc_log_async/5,
+ tc_print/3, tc_print/4,
tc_pal/3, tc_pal/4, ct_log/3, basic_html/0]).
%% Simulate logger process for use without ct environment running
@@ -59,6 +60,7 @@
-define(all_runs_name, "all_runs.html").
-define(index_name, "index.html").
-define(totals_name, "totals.info").
+-define(log_cache_name, "ct_log_cache").
-define(table_color1,"#ADD8E6").
-define(table_color2,"#E4F0FE").
@@ -68,6 +70,10 @@
-define(abs(Name), filename:absname(Name)).
+-record(log_cache, {version,
+ all_runs = [],
+ tests = []}).
+
%%%-----------------------------------------------------------------
%%% @spec init(Mode) -> Result
%%% Mode = normal | interactive
@@ -93,14 +99,25 @@ init(Mode, Verbosity) ->
exit({could_not_start_process,?MODULE,Reason})
end.
-make_dirname({{YY,MM,DD},{H,M,S}}) ->
- io_lib:format(logdir_node_prefix()++".~w-~2.2.0w-~2.2.0w_~2.2.0w.~2.2.0w.~2.2.0w",
- [YY,MM,DD,H,M,S]).
-
+date2str({{YY,MM,DD},{H,M,S}}) ->
+ lists:flatten(io_lib:format("~w-~2.2.0w-~2.2.0w_~2.2.0w.~2.2.0w.~2.2.0w",
+ [YY,MM,DD,H,M,S])).
logdir_prefix() ->
"ct_run".
logdir_node_prefix() ->
- logdir_prefix()++"."++atom_to_list(node()).
+ logdir_prefix() ++ "." ++ atom_to_list(node()).
+
+make_dirname(DateTime) ->
+ logdir_node_prefix() ++ "." ++ date2str(DateTime).
+
+datestr_from_dirname([Y1,Y2,Y3,Y4,$-,Mo1,Mo2,$-,D1,D2,$_,
+ H1,H2,$.,M1,M2,$.,S1,S2 | _]) ->
+ [Y1,Y2,Y3,Y4,$-,Mo1,Mo2,$-,D1,D2,$_,
+ H1,H2,$.,M1,M2,$.,S1,S2];
+datestr_from_dirname([_Ch | Rest]) ->
+ datestr_from_dirname(Rest);
+datestr_from_dirname([]) ->
+ "".
%%%-----------------------------------------------------------------
%%% @spec close(Info, StartDir) -> ok
@@ -108,8 +125,21 @@ logdir_node_prefix() ->
%%% @doc Create index pages with test results and close the CT Log
%%% (tool-internal use only).
close(Info, StartDir) ->
- make_last_run_index(),
-
+ %% close executes on the ct_util process, not on the logger process
+ %% so we need to use a local copy of the log cache data
+ LogCacheBin = make_last_run_index(),
+ put(ct_log_cache,LogCacheBin),
+ Cache2File = fun() ->
+ case get(ct_log_cache) of
+ undefined ->
+ ok;
+ CacheBin ->
+ %% save final version of the log cache to file
+ file:write_file(?log_cache_name,CacheBin),
+ put(ct_log_cache,undefined)
+ end
+ end,
+
ct_event:notify(#event{name=stop_logging,node=node(),data=[]}),
case whereis(?MODULE) of
@@ -132,11 +162,13 @@ close(Info, StartDir) ->
io:format("Warning! Cleanup failed: ~p~n", [Error])
end,
make_all_suites_index(stop),
- make_all_runs_index(stop);
+ make_all_runs_index(stop),
+ Cache2File();
true ->
file:set_cwd(".."),
make_all_suites_index(stop),
make_all_runs_index(stop),
+ Cache2File(),
case ct_util:get_profile_data(browser, StartDir) of
undefined ->
ok;
@@ -168,12 +200,19 @@ clear_stylesheet(TC) ->
%%%-----------------------------------------------------------------
%%% @spec get_log_dir() -> {ok,Dir} | {error,Reason}
get_log_dir() ->
- call({get_log_dir,false}).
+ get_log_dir(false).
%%%-----------------------------------------------------------------
%%% @spec get_log_dir(ReturnAbsName) -> {ok,Dir} | {error,Reason}
get_log_dir(ReturnAbsName) ->
- call({get_log_dir,ReturnAbsName}).
+ case call({get_log_dir,ReturnAbsName}) of
+ {error,does_not_exist} when ReturnAbsName == true ->
+ {ok,filename:absname(".")};
+ {error,does_not_exist} ->
+ {ok,"."};
+ Result ->
+ Result
+ end.
%%%-----------------------------------------------------------------
%%% make_last_run_index() -> ok
@@ -333,8 +372,15 @@ tc_log(Category,Format,Args) ->
%%%-----------------------------------------------------------------
%%% @spec tc_log(Category,Importance,Format,Args) -> ok
+%%% @equiv tc_log(Category,Importance,"User",Format,Args)
+tc_log(Category,Importance,Format,Args) ->
+ tc_log(Category,Importance,"User",Format,Args).
+
+%%%-----------------------------------------------------------------
+%%% @spec tc_log(Category,Importance,Printer,Format,Args) -> ok
%%% Category = atom()
%%% Importance = integer()
+%%% Printer = string()
%%% Format = string()
%%% Args = list()
%%%
@@ -343,9 +389,6 @@ tc_log(Category,Format,Args) ->
%%% <p>This function is called by <code>ct</code> when logging
%%% stuff directly from a testcase (i.e. not from within the CT
%%% framework).</p>
-tc_log(Category,Importance,Format,Args) ->
- tc_log(Category,Importance,"User",Format,Args).
-
tc_log(Category,Importance,Printer,Format,Args) ->
cast({log,sync,self(),group_leader(),Category,Importance,
[{div_header(Category,Printer),[]},
@@ -355,14 +398,15 @@ tc_log(Category,Importance,Printer,Format,Args) ->
%%%-----------------------------------------------------------------
%%% @spec tc_log_async(Category,Format,Args) -> ok
-%%% @equiv tc_log_async(Category,?STD_IMPORTANCE,Format,Args)
+%%% @equiv tc_log_async(Category,?STD_IMPORTANCE,"User",Format,Args)
tc_log_async(Category,Format,Args) ->
- tc_log_async(Category,?STD_IMPORTANCE,Format,Args).
+ tc_log_async(Category,?STD_IMPORTANCE,"User",Format,Args).
%%%-----------------------------------------------------------------
%%% @spec tc_log_async(Category,Importance,Format,Args) -> ok
%%% Category = atom()
%%% Importance = integer()
+%%% Printer = string()
%%% Format = string()
%%% Args = list()
%%%
@@ -373,9 +417,9 @@ tc_log_async(Category,Format,Args) ->
%%% to avoid deadlocks when e.g. the hook that handles SASL printouts
%%% prints to the test case log file at the same time test server
%%% asks ct_logs for an html wrapper.</p>
-tc_log_async(Category,Importance,Format,Args) ->
+tc_log_async(Category,Importance,Printer,Format,Args) ->
cast({log,async,self(),group_leader(),Category,Importance,
- [{div_header(Category),[]},
+ [{div_header(Category,Printer),[]},
{Format,Args},
{div_footer(),[]}]}),
ok.
@@ -515,7 +559,6 @@ log_timestamp({MS,S,US}) ->
logger(Parent, Mode, Verbosity) ->
register(?MODULE,self()),
-
%%! Below is a temporary workaround for the limitation of
%%! max one test run per second.
%%! --->
@@ -561,9 +604,10 @@ logger(Parent, Mode, Verbosity) ->
ok ->
case copy_priv_files(PrivFilesSrc, PrivFilesDestRun) of
{error,Src2,Dest2,Reason2} ->
- io:format(user, "ERROR! "++
- "Priv file ~p could not be copied to ~p. "++
- "Reason: ~p~n",
+ io:format(user,
+ "ERROR! "++
+ "Priv file ~p could not be copied to ~p. "
+ ++"Reason: ~p~n",
[Src2,Dest2,Reason2]),
exit({priv_file_error,Dest2});
ok ->
@@ -687,7 +731,7 @@ logger_loop(State) ->
logger_loop(State);
{make_last_run_index,From} ->
make_last_run_index(State#logger_state.start_time),
- return(From,filename:basename(State#logger_state.log_dir)),
+ return(From,get(ct_log_cache)),
logger_loop(State);
{set_stylesheet,_,SSFile} when State#logger_state.stylesheet ==
SSFile ->
@@ -946,40 +990,37 @@ print_style_error(Fd,StyleSheet,Reason) ->
print_style(Fd,undefined).
close_ctlog(Fd) ->
- io:format(Fd,"\n</pre>\n",[]),
- io:format(Fd,footer(),[]),
+ io:format(Fd, "\n</pre>\n", []),
+ io:format(Fd, [xhtml("<br><br>\n", "<br /><br />\n") | footer()], []),
file:close(Fd).
-
%%%-----------------------------------------------------------------
%%% Make an index page for the last run
make_last_run_index(StartTime) ->
IndexName = ?index_name,
AbsIndexName = ?abs(IndexName),
- case catch make_last_run_index1(StartTime,IndexName) of
- {'EXIT', Reason} ->
- io:put_chars("CRASHED while updating " ++ AbsIndexName ++ "!\n"),
- io:format("~p~n", [Reason]),
- {error, Reason};
- {error, Reason} ->
- io:put_chars("FAILED while updating " ++ AbsIndexName ++ "\n"),
- io:format("~p~n", [Reason]),
- {error, Reason};
- ok ->
-% io:put_chars("done\n"),
- ok;
- Err ->
- io:format("Unknown internal error while updating ~ts. "
- "Please report.\n(Err: ~p, ID: 1)",
- [AbsIndexName,Err]),
- {error, Err}
- end.
+ Result =
+ case catch make_last_run_index1(StartTime,IndexName) of
+ {'EXIT', Reason} ->
+ io:put_chars("CRASHED while updating " ++ AbsIndexName ++ "!\n"),
+ io:format("~p~n", [Reason]),
+ {error, Reason};
+ {error, Reason} ->
+ io:put_chars("FAILED while updating " ++ AbsIndexName ++ "\n"),
+ io:format("~p~n", [Reason]),
+ {error, Reason};
+ ok ->
+ ok;
+ Err ->
+ io:format("Unknown internal error while updating ~ts. "
+ "Please report.\n(Err: ~p, ID: 1)",
+ [AbsIndexName,Err]),
+ {error, Err}
+ end,
+ Result.
make_last_run_index1(StartTime,IndexName) ->
- %% this manoeuvre is to ensure the tests get logged
- %% in correct order of time (the 1 sec resolution
- %% of the dirnames may be too big)
Logs1 =
case filelib:wildcard([$*|?logdir_ext]) of
[Log] -> % first test
@@ -1007,7 +1048,8 @@ make_last_run_index1(StartTime,IndexName) ->
0, 0, 0, 0, 0, Missing),
%% write current Totals to file, later to be used in all_runs log
write_totals_file(?totals_name,Label,Logs1,Totals),
- Index = [Index0|index_footer()],
+ Index = [Index0|last_run_index_footer()],
+
case force_write_file(IndexName, unicode:characters_to_binary(Index)) of
ok ->
ok;
@@ -1046,22 +1088,26 @@ make_last_run_index([Name|Rest], Result, TotSucc, TotFail,
TotNotBuilt1, Missing)
end;
-make_last_run_index([], Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, _) ->
- {ok, [Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, false)],
+make_last_run_index([], Result, TotSucc, TotFail, UserSkip, AutoSkip,
+ TotNotBuilt, _) ->
+ {ok, [Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip,
+ TotNotBuilt, false)],
{TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}}.
make_last_run_index1(SuiteName, [LogDir | LogDirs], Result, TotSucc, TotFail,
UserSkip, AutoSkip, TotNotBuilt, Missing) ->
- case make_one_index_entry(SuiteName, LogDir, "-", false, Missing) of
- {Result1,Succ,Fail,USkip,ASkip,NotBuilt} ->
+ case make_one_index_entry(SuiteName, LogDir, "-", false,
+ Missing, undefined) of
+ {Result1,Succ,Fail,USkip,ASkip,NotBuilt,_URIs1} ->
%% for backwards compatibility
AutoSkip1 = case catch AutoSkip+ASkip of
{'EXIT',_} -> undefined;
Res -> Res
end,
- make_last_run_index1(SuiteName, LogDirs, [Result|Result1], TotSucc+Succ,
- TotFail+Fail, UserSkip+USkip, AutoSkip1,
- TotNotBuilt+NotBuilt, Missing);
+ make_last_run_index1(SuiteName, LogDirs, [Result|Result1],
+ TotSucc+Succ,
+ TotFail+Fail, UserSkip+USkip, AutoSkip1,
+ TotNotBuilt+NotBuilt, Missing);
error ->
make_last_run_index1(SuiteName, LogDirs, Result, TotSucc, TotFail,
UserSkip, AutoSkip, TotNotBuilt, Missing)
@@ -1070,35 +1116,49 @@ make_last_run_index1(_, [], Result, TotSucc, TotFail,
UserSkip, AutoSkip, TotNotBuilt, _) ->
{Result,TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}.
-make_one_index_entry(SuiteName, LogDir, Label, All, Missing) ->
+make_one_index_entry(SuiteName, LogDir, Label, All, Missing, URIs) ->
case count_cases(LogDir) of
{Succ,Fail,UserSkip,AutoSkip} ->
NotBuilt = not_built(SuiteName, LogDir, All, Missing),
- NewResult = make_one_index_entry1(SuiteName, LogDir, Label, Succ, Fail,
- UserSkip, AutoSkip, NotBuilt, All,
- normal),
- {NewResult,Succ,Fail,UserSkip,AutoSkip,NotBuilt};
+ {NewResult,URIs1} = make_one_index_entry1(SuiteName, LogDir, Label,
+ Succ, Fail,
+ UserSkip, AutoSkip,
+ NotBuilt, All,
+ normal, URIs),
+ {NewResult,Succ,Fail,UserSkip,AutoSkip,NotBuilt,URIs1};
error ->
error
end.
make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip,
- NotBuilt, All, Mode) ->
+ NotBuilt, All, Mode, URIs) ->
LogFile = filename:join(Link, ?suitelog_name ++ ".html"),
+ CtRunDir = filename:dirname(filename:dirname(Link)),
+ CrashDumpName = SuiteName ++ "_erl_crash.dump",
+
+ URIs1 = {CtRunLogURI,LogFileURI,CrashDumpURI} =
+ case URIs of
+ undefined ->
+ {uri(filename:join(CtRunDir,?ct_log_name)),
+ uri(LogFile),
+ uri(CrashDumpName)};
+ _ ->
+ URIs
+ end,
+
CrashDumpLink = case Mode of
- cached ->
+ temp ->
"";
normal ->
- CrashDumpName = SuiteName ++ "_erl_crash.dump",
case filelib:is_file(CrashDumpName) of
true ->
- ["&nbsp;<a href=\"", uri(CrashDumpName),
+ ["&nbsp;<a href=\"", CrashDumpURI,
"\">(CrashDump)</a>"];
false ->
""
end
end,
- CtRunDir = filename:dirname(filename:dirname(Link)),
+
{Lbl,Timestamp,Node,AllInfo} =
case All of
{true,OldRuns} ->
@@ -1107,7 +1167,9 @@ make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip,
0 -> "-";
_ -> NodeOrDate
end,
+
TS = timestamp(CtRunDir),
+
N = xhtml(["<td align=right><font size=\"-1\">",Node1,
"</font></td>\n"],
["<td align=right>",Node1,"</td>\n"]),
@@ -1116,28 +1178,31 @@ make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip,
["<td align=center><b>",Label,"</b></td>\n"]),
T = xhtml(["<td><font size=\"-1\">",TS,"</font></td>\n"],
["<td>",TS,"</td>\n"]),
- CtLogFile = filename:join(CtRunDir,?ct_log_name),
+
OldRunsLink =
case OldRuns of
[] -> "none";
_ -> "<a href=\""++?all_runs_name++"\">Old Runs</a>"
end,
- A = xhtml(["<td><font size=\"-1\"><a href=\"",uri(CtLogFile),
+
+ A = xhtml(["<td><font size=\"-1\"><a href=\"",CtRunLogURI,
"\">CT Log</a></font></td>\n",
- "<td><font size=\"-1\">",OldRunsLink,"</font></td>\n"],
- ["<td><a href=\"",uri(CtLogFile),"\">CT Log</a></td>\n",
+ "<td><font size=\"-1\">",OldRunsLink,
+ "</font></td>\n"],
+ ["<td><a href=\"",CtRunLogURI,
+ "\">CT Log</a></td>\n",
"<td>",OldRunsLink,"</td>\n"]),
{L,T,N,A};
false ->
{"","","",""}
end,
+
NotBuiltStr =
if NotBuilt == 0 ->
["<td align=right>",integer_to_list(NotBuilt),"</td>\n"];
true ->
- ["<td align=right><a href=\"",
- uri(filename:join(CtRunDir,?ct_log_name)),"\">",
- integer_to_list(NotBuilt),"</a></td>\n"]
+ ["<td align=right><a href=\"",CtRunLogURI,"\">",
+ integer_to_list(NotBuilt),"</a></td>\n"]
end,
FailStr =
if Fail > 0 ->
@@ -1156,17 +1221,17 @@ make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip,
end,
{UserSkip+AutoSkip,integer_to_list(UserSkip),ASStr}
end,
- [xhtml("<tr valign=top>\n",
- ["<tr class=\"",odd_or_even(),"\">\n"]),
- xhtml("<td><font size=\"-1\"><a href=\"", "<td><a href=\""),
- uri(LogFile),"\">",SuiteName,"</a>", CrashDumpLink,
- xhtml("</font></td>\n", "</td>\n"),
- Lbl, Timestamp,
- "<td align=right>",integer_to_list(Success),"</td>\n",
- "<td align=right>",FailStr,"</td>\n",
- "<td align=right>",integer_to_list(AllSkip),
- " (",UserSkipStr,"/",AutoSkipStr,")</td>\n",
- NotBuiltStr, Node, AllInfo, "</tr>\n"].
+ {[xhtml("<tr valign=top>\n",
+ ["<tr class=\"",odd_or_even(),"\">\n"]),
+ xhtml("<td><font size=\"-1\"><a href=\"", "<td><a href=\""),
+ LogFileURI,"\">",SuiteName,"</a>", CrashDumpLink,
+ xhtml("</font></td>\n", "</td>\n"),
+ Lbl, Timestamp,
+ "<td align=right>",integer_to_list(Success),"</td>\n",
+ "<td align=right>",FailStr,"</td>\n",
+ "<td align=right>",integer_to_list(AllSkip),
+ " (",UserSkipStr,"/",AutoSkipStr,")</td>\n",
+ NotBuiltStr, Node, AllInfo, "</tr>\n"], URIs1}.
total_row(Success, Fail, UserSkip, AutoSkip, NotBuilt, All) ->
{Label,TimestampCell,AllInfo} =
@@ -1392,17 +1457,30 @@ header1(Title, SubTitle, TableCols) ->
"</center>\n",
SubTitleHTML,"\n"].
-index_footer() ->
- ["</table>\n"
+last_run_index_footer() ->
+ AllRuns = filename:join("../",?all_runs_name),
+ TestIndex = filename:join("../",?index_name),
+ ["</table>\n",
+ xhtml("<br><hr><p>\n", "<br /><hr /><p>\n"),
+ "<a href=\"", uri(AllRuns),
+ "\">Test run history\n</a> | ",
+ "<a href=\"", uri(TestIndex),
+ "\">Top level test index\n</a>\n</p>\n",
"</center>\n" | footer()].
+all_suites_index_footer() ->
+ ["</table>\n",
+ "</center>\n",
+ xhtml("<br><br>\n", "<br /><br />\n") | footer()].
+
all_runs_index_footer() ->
- ["</tbody>\n</table>\n"
- "</center>\n" | footer()].
+ ["</tbody>\n</table>\n",
+ "</center>\n",
+ xhtml("<br><br>\n", "<br /><br />\n") | footer()].
footer() ->
["<center>\n",
- xhtml("<br><br>\n<hr>\n", "<br /><br />\n"),
+ xhtml("<hr>\n", ""),
xhtml("<p><font size=\"-1\">\n", "<div class=\"copyright\">"),
"Copyright &copy; ", year(),
" <a href=\"http://www.erlang.org\">Open Telecom Platform</a>",
@@ -1414,7 +1492,6 @@ footer() ->
"</body>\n"
"</html>\n"].
-
body_tag() ->
CTPath = code:lib_dir(common_test),
TileFile = filename:join(filename:join(CTPath,"priv"),"tile1.jpg"),
@@ -1580,35 +1657,169 @@ make_all_runs_index(When) ->
if When == start -> ok;
true -> io:put_chars("Updating " ++ AbsName ++ "... ")
end,
+
+ %% check if log cache should be used, and if it exists
+ UseCache =
+ if When == refresh ->
+ save_only;
+ true ->
+ case application:get_env(common_test, disable_log_cache) of
+ {ok,true} ->
+ disabled;
+ _ ->
+ case get(ct_log_cache) of
+ undefined ->
+ file:read_file(?log_cache_name);
+ LogCacheBin ->
+ {ok,LogCacheBin}
+ end
+ end
+ end,
+
Dirs = filelib:wildcard(logdir_prefix()++"*.*"),
DirsSorted = (catch sort_all_runs(Dirs)),
- Header = all_runs_header(),
- Index = [runentry(Dir) || Dir <- DirsSorted],
- Result = file:write_file(AbsName,
- unicode:characters_to_binary(
- Header++Index++all_runs_index_footer())),
+
+ LogCacheInfo = get_cache_data(UseCache),
+
+ Result =
+ case LogCacheInfo of
+ {ok,LogCache} ->
+ %% use the log cache file to generate the index
+ make_all_runs_from_cache(AbsName,DirsSorted,LogCache);
+
+ _WhyNot ->
+ %% no cache file exists (or feature has been disabled)
+ Header = all_runs_header(),
+ GetLogResult =
+ fun(Dir,{RunData,LogTxt}) ->
+ {Tot,XHTML,IxLink} = runentry(Dir,
+ undefined,
+ undefined),
+ {[{Dir,Tot,IxLink}|RunData],[XHTML|LogTxt]}
+ end,
+ {AllRunsData,Index} =
+ lists:foldr(GetLogResult,{[],[]},DirsSorted),
+
+ %% update cache with result unless the cache is disabled
+ if UseCache == disabled -> ok;
+ true -> update_all_runs_in_cache(AllRunsData)
+ end,
+ %% write all_runs log file
+ ok = file:write_file(AbsName,
+ unicode:characters_to_binary(
+ Header++Index++
+ all_runs_index_footer()))
+ end,
+ notify_and_unlock_file(AbsName),
if When == start -> ok;
true -> io:put_chars("done\n")
end,
- notify_and_unlock_file(AbsName),
Result.
+make_all_runs_from_cache(AbsName, Dirs, LogCache) ->
+ Header = all_runs_header(),
+
+ %% Note that both Dirs and the cache is sorted!
+ AllRunsDirs = dir_diff_all_runs(Dirs, LogCache),
+
+ GetLogResult =
+ fun({Dir,no_test_data,IxLink},{RunData,LogTxt}) ->
+ {Tot,XHTML,_} = runentry(Dir,undefined,IxLink),
+ {[{Dir,Tot,IxLink}|RunData],[XHTML|LogTxt]};
+ ({Dir,CachedTotals,IxLink},{RunData,LogTxt}) ->
+ %% create log entry using cached data
+ {Tot,XHTML,_} = runentry(Dir,CachedTotals,IxLink),
+ {[{Dir,Tot,IxLink}|RunData],[XHTML|LogTxt]};
+ (Dir,{RunData,LogTxt}) ->
+ %% create log entry from scratch
+ {Tot,XHTML,IxLink} = runentry(Dir,undefined,undefined),
+ {[{Dir,Tot,IxLink}|RunData],[XHTML|LogTxt]}
+ end,
+ {AllRunsData,Index} = lists:foldr(GetLogResult,{[],[]},AllRunsDirs),
+ %% update cache with result
+ update_all_runs_in_cache(AllRunsData,LogCache),
+ %% write all_runs log file
+ ok = file:write_file(AbsName,
+ unicode:characters_to_binary(
+ Header++Index++
+ all_runs_index_footer())).
+
+update_all_runs_in_cache(AllRunsData) ->
+ case get(ct_log_cache) of
+ undefined ->
+ LogCache = #log_cache{version = cache_vsn(),
+ all_runs = AllRunsData},
+ case {self(),whereis(?MODULE)} of
+ {_Pid,_Pid} ->
+ %% save the cache in RAM so it doesn't have to be
+ %% read from file as long as this logger process is alive
+ put(ct_log_cache,term_to_binary(LogCache));
+ _ ->
+ file:write_file(?log_cache_name,term_to_binary(LogCache))
+ end;
+ SavedLogCache ->
+ update_all_runs_in_cache(AllRunsData,binary_to_term(SavedLogCache))
+ end.
+
+update_all_runs_in_cache(AllRunsData, LogCache) ->
+ LogCache1 = LogCache#log_cache{all_runs = AllRunsData},
+ case {self(),whereis(?MODULE)} of
+ {_Pid,_Pid} ->
+ %% save the cache in RAM so it doesn't have to be
+ %% read from file as long as this logger process is alive
+ put(ct_log_cache,term_to_binary(LogCache1));
+ _ ->
+ file:write_file(?log_cache_name,term_to_binary(LogCache1))
+ end.
+
sort_all_runs(Dirs) ->
%% sort on time string, always last and on the format:
%% "YYYY-MM-DD_HH.MM.SS"
- KeyList =
- lists:map(fun(Dir) ->
- case lists:reverse(string:tokens(Dir,[$.,$_])) of
- [SS,MM,HH,Date|_] ->
- {{Date,HH,MM,SS},Dir};
- _Other ->
- throw(Dirs)
- end
- end,Dirs),
- lists:reverse(lists:map(fun({_,Dir}) ->
- Dir
- end,lists:keysort(1,KeyList))).
+ lists:sort(fun(Dir1,Dir2) ->
+ [SS1,MM1,HH1,Date1|_] =
+ lists:reverse(string:tokens(Dir1,[$.,$_])),
+ [SS2,MM2,HH2,Date2|_] =
+ lists:reverse(string:tokens(Dir2,[$.,$_])),
+ {Date1,HH1,MM1,SS1} > {Date2,HH2,MM2,SS2}
+ end, Dirs).
+
+dir_diff_all_runs(Dirs, LogCache) ->
+ case LogCache#log_cache.all_runs of
+ [] ->
+ Dirs;
+ Cached = [{CDir,_,_}|_] ->
+ AllRunsDirs =
+ dir_diff_all_runs(Dirs, Cached, datestr_from_dirname(CDir), []),
+ lists:reverse(AllRunsDirs)
+ end.
+
+dir_diff_all_runs(LogDirs=[Dir|Dirs], Cached=[CElem|CElems],
+ LatestInCache, AllRunsDirs) ->
+ DirDate = datestr_from_dirname(Dir),
+ if DirDate > LatestInCache ->
+ %% Dir is a new run entry
+ dir_diff_all_runs(Dirs, Cached, LatestInCache,
+ [Dir|AllRunsDirs]);
+ DirDate == LatestInCache, CElems /= [] ->
+ %% Dir is an existing run entry
+ dir_diff_all_runs(Dirs, CElems,
+ datestr_from_dirname(element(1,hd(CElems))),
+ [CElem|AllRunsDirs]);
+ DirDate == LatestInCache, CElems == [] ->
+ %% we're done, Dirs must all be new
+ lists:reverse(Dirs)++[CElem|AllRunsDirs];
+ CElems /= [] -> % DirDate < LatestInCache
+ %% current CDir not in Dirs, update timestamp and check next
+ dir_diff_all_runs(LogDirs, CElems,
+ datestr_from_dirname(element(1,hd(CElems))),
+ AllRunsDirs);
+ CElems == [] ->
+ %% we're done, LogDirs must all be new
+ lists:reverse(LogDirs)++AllRunsDirs
+ end;
+dir_diff_all_runs([], _Cached, _, AllRunsDirs) ->
+ AllRunsDirs.
interactive_link() ->
[Dir|_] = lists:reverse(filelib:wildcard(logdir_prefix()++"*.*")),
@@ -1619,12 +1830,14 @@ interactive_link() ->
"<html>\n"],
["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n",
"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n",
- "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"]),
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\" ",
+ "xml:lang=\"en\" lang=\"en\">\n"]),
"<!-- autogenerated by '"++atom_to_list(?MODULE)++"' -->\n",
"<head>\n",
"<title>Last interactive run</title>\n",
"<meta http-equiv=\"cache-control\" content=\"no-cache\">\n",
- "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n",
+ "<meta http-equiv=\"content-type\" content=\"text/html; "
+ "charset=utf-8\">\n",
"</head>\n",
"<body>\n",
"Log from last interactive run: <a href=\"",uri(CtLog),"\">",
@@ -1637,98 +1850,120 @@ interactive_link() ->
"Any CT activities will be logged here\n",
[?abs("last_interactive.html")]).
-runentry(Dir) ->
+%% use if cache disabled or non-existing
+runentry(Dir, undefined, _) ->
TotalsFile = filename:join(Dir,?totals_name),
- TotalsStr =
- case read_totals_file(TotalsFile) of
- {Node,Label,Logs,{TotSucc,TotFail,UserSkip,AutoSkip,NotBuilt}} ->
- TotFailStr =
- if TotFail > 0 ->
- ["<font color=\"red\">",
- integer_to_list(TotFail),"</font>"];
- true ->
- integer_to_list(TotFail)
- end,
- {AllSkip,UserSkipStr,AutoSkipStr} =
- if AutoSkip == undefined -> {UserSkip,"?","?"};
- true ->
- ASStr = if AutoSkip > 0 ->
- ["<font color=\"brown\">",
- integer_to_list(AutoSkip),"</font>"];
- true -> integer_to_list(AutoSkip)
- end,
- {UserSkip+AutoSkip,integer_to_list(UserSkip),ASStr}
- end,
- NoOfTests = case length(Logs) of
- 0 -> "-";
- N -> integer_to_list(N)
- end,
- StripExt =
- fun(File) ->
- string:sub_string(File,1,
- length(File)-
- length(?logdir_ext)) ++ ", "
- end,
- Polish = fun(S) -> case lists:reverse(S) of
- [32,$,|Rev] -> lists:reverse(Rev);
- [$,|Rev] -> lists:reverse(Rev);
- _ -> S
- end
- end,
- TestNames = Polish(lists:flatten(lists:map(StripExt,Logs))),
- TestNamesTrunc =
- if TestNames=="" ->
- "";
- length(TestNames) < ?testname_width ->
- TestNames;
- true ->
- Trunc = Polish(string:substr(TestNames,1,?testname_width-3)),
- lists:flatten(io_lib:format("~ts...",[Trunc]))
- end,
- Total = TotSucc+TotFail+AllSkip,
- A = xhtml(["<td align=center><font size=\"-1\">",Node,
- "</font></td>\n",
- "<td align=center><font size=\"-1\"><b>",Label,
- "</b></font></td>\n",
- "<td align=right>",NoOfTests,"</td>\n"],
- ["<td align=center>",Node,"</td>\n",
- "<td align=center><b>",Label,"</b></td>\n",
- "<td align=right>",NoOfTests,"</td>\n"]),
- B = xhtml(["<td align=center title='",TestNames,"'><font size=\"-1\"> ",
- TestNamesTrunc,"</font></td>\n"],
- ["<td align=center title='",TestNames,"'> ",
- TestNamesTrunc,"</td>\n"]),
- C = ["<td align=right>",integer_to_list(Total),"</td>\n",
- "<td align=right>",integer_to_list(TotSucc),"</td>\n",
- "<td align=right>",TotFailStr,"</td>\n",
- "<td align=right>",integer_to_list(AllSkip),
- " (",UserSkipStr,"/",AutoSkipStr,")</td>\n",
- "<td align=right>",integer_to_list(NotBuilt),"</td>\n"],
- A++B++C;
- _ ->
- A = xhtml(["<td align=center><font size=\"-1\" color=\"red\">"
- "Test data missing or corrupt</font></td>\n",
- "<td align=center><font size=\"-1\">?</font></td>\n",
- "<td align=right>?</td>\n"],
- ["<td align=center><font color=\"red\">"
- "Test data missing or corrupt</font></td>\n",
- "<td align=center>?</td>\n",
- "<td align=right>?</td>\n"]),
- B = xhtml(["<td align=center><font size=\"-1\">?</font></td>\n"],
- ["<td align=center>?</td>\n"]),
- C = ["<td align=right>?</td>\n",
- "<td align=right>?</td>\n",
- "<td align=right>?</td>\n",
- "<td align=right>?</td>\n",
- "<td align=right>?</td>\n"],
- A++B++C
- end,
Index = uri(filename:join(Dir,?index_name)),
- [xhtml("<tr>\n", ["<tr class=\"",odd_or_even(),"\">\n"]),
- xhtml(["<td><font size=\"-1\"><a href=\"",Index,"\">",timestamp(Dir),"</a>",
- TotalsStr,"</font></td>\n"],
- ["<td><a href=\"",Index,"\">",timestamp(Dir),"</a>",TotalsStr,"</td>\n"]),
- "</tr>\n"].
+ runentry(Dir, read_totals_file(TotalsFile), Index);
+
+%% use cached data
+runentry(Dir, Totals={Node,Label,Logs,
+ {TotSucc,TotFail,UserSkip,AutoSkip,NotBuilt}}, Index) ->
+ TotFailStr =
+ if TotFail > 0 ->
+ ["<font color=\"red\">",
+ integer_to_list(TotFail),"</font>"];
+ true ->
+ integer_to_list(TotFail)
+ end,
+ {AllSkip,UserSkipStr,AutoSkipStr} =
+ if AutoSkip == undefined -> {UserSkip,"?","?"};
+ true ->
+ ASStr = if AutoSkip > 0 ->
+ ["<font color=\"brown\">",
+ integer_to_list(AutoSkip),
+ "</font>"];
+ true -> integer_to_list(AutoSkip)
+ end,
+ {UserSkip+AutoSkip,integer_to_list(UserSkip),ASStr}
+ end,
+ NoOfTests = case length(Logs) of
+ 0 -> "-";
+ N -> integer_to_list(N)
+ end,
+ StripExt =
+ fun(File) ->
+ string:sub_string(File,1,
+ length(File)-
+ length(?logdir_ext)) ++ ", "
+ end,
+ Polish = fun(S) -> case lists:reverse(S) of
+ [32,$,|Rev] -> lists:reverse(Rev);
+ [$,|Rev] -> lists:reverse(Rev);
+ _ -> S
+ end
+ end,
+ TestNames = Polish(lists:flatten(lists:map(StripExt,Logs))),
+ TestNamesTrunc =
+ if TestNames=="" ->
+ "";
+ length(TestNames) < ?testname_width ->
+ TestNames;
+ true ->
+ Trunc = Polish(string:substr(TestNames,1,
+ ?testname_width-3)),
+ lists:flatten(io_lib:format("~ts...",[Trunc]))
+ end,
+ Total = TotSucc+TotFail+AllSkip,
+ A = xhtml(["<td align=center><font size=\"-1\">",Node,
+ "</font></td>\n",
+ "<td align=center><font size=\"-1\"><b>",Label,
+ "</b></font></td>\n",
+ "<td align=right>",NoOfTests,"</td>\n"],
+ ["<td align=center>",Node,"</td>\n",
+ "<td align=center><b>",Label,"</b></td>\n",
+ "<td align=right>",NoOfTests,"</td>\n"]),
+ B = xhtml(["<td align=center title='",TestNames,
+ "'><font size=\"-1\"> ",
+ TestNamesTrunc,"</font></td>\n"],
+ ["<td align=center title='",TestNames,"'> ",
+ TestNamesTrunc,"</td>\n"]),
+ C = ["<td align=right>",integer_to_list(Total),"</td>\n",
+ "<td align=right>",integer_to_list(TotSucc),"</td>\n",
+ "<td align=right>",TotFailStr,"</td>\n",
+ "<td align=right>",integer_to_list(AllSkip),
+ " (",UserSkipStr,"/",AutoSkipStr,")</td>\n",
+ "<td align=right>",integer_to_list(NotBuilt),"</td>\n"],
+ TotalsStr = A++B++C,
+
+ XHTML = [xhtml("<tr>\n", ["<tr class=\"",odd_or_even(),"\">\n"]),
+ xhtml(["<td><font size=\"-1\"><a href=\"",Index,"\">",
+ timestamp(Dir),"</a>",
+ TotalsStr,"</font></td>\n"],
+ ["<td><a href=\"",Index,"\">",timestamp(Dir),"</a>",TotalsStr,
+ "</td>\n"]),
+ "</tr>\n"],
+ {Totals,XHTML,Index};
+
+%% handle missing or corrupt data (missing e.g. if the test is in progress)
+runentry(Dir, _, _) ->
+ A = xhtml(["<td align=center><font size=\"-1\" color=\"red\">"
+ "Test data missing or corrupt</font></td>\n",
+ "<td align=center><font size=\"-1\">?</font></td>\n",
+ "<td align=right>?</td>\n"],
+ ["<td align=center><font color=\"red\">"
+ "Test data missing or corrupt</font></td>\n",
+ "<td align=center>?</td>\n",
+ "<td align=right>?</td>\n"]),
+ B = xhtml(["<td align=center><font size=\"-1\">?</font></td>\n"],
+ ["<td align=center>?</td>\n"]),
+ C = ["<td align=right>?</td>\n",
+ "<td align=right>?</td>\n",
+ "<td align=right>?</td>\n",
+ "<td align=right>?</td>\n",
+ "<td align=right>?</td>\n"],
+ TotalsStr = A++B++C,
+
+ Index = uri(filename:join(Dir,?index_name)),
+
+ XHTML = [xhtml("<tr>\n", ["<tr class=\"",odd_or_even(),"\">\n"]),
+ xhtml(["<td><font size=\"-1\"><a href=\"",Index,"\">",
+ timestamp(Dir),"</a>",
+ TotalsStr,"</font></td>\n"],
+ ["<td><a href=\"",Index,"\">",timestamp(Dir),"</a>",TotalsStr,
+ "</td>\n"]),
+ "</tr>\n"],
+ {no_test_data,XHTML,Index}.
write_totals_file(Name,Label,Logs,Totals) ->
AbsName = ?abs(Name),
@@ -1755,17 +1990,19 @@ read_totals_file(Name) ->
_ -> Label
end,
case Tot of
- {_Ok,_Fail,_USkip,_ASkip,_NoBuild} -> % latest format
+ {_Ok,_Fail,_USkip,_ASkip,_NoBuild} -> % latest format
{Node,Label1,Ls,Tot};
{TotSucc,TotFail,AllSkip,NotBuilt} ->
- {Node,Label1,Ls,{TotSucc,TotFail,AllSkip,undefined,NotBuilt}}
+ {Node,Label1,Ls,
+ {TotSucc,TotFail,AllSkip,undefined,NotBuilt}}
end;
{Node,Ls,Tot} -> % no label found
case Tot of
- {_Ok,_Fail,_USkip,_ASkip,_NoBuild} -> % latest format
+ {_Ok,_Fail,_USkip,_ASkip,_NoBuild} -> % latest format
{Node,"-",Ls,Tot};
{TotSucc,TotFail,AllSkip,NotBuilt} ->
- {Node,"-",Ls,{TotSucc,TotFail,AllSkip,undefined,NotBuilt}}
+ {Node,"-",Ls,
+ {TotSucc,TotFail,AllSkip,undefined,NotBuilt}}
end;
%% for backwards compatibility
{Ls,Tot} -> {"-",Ls,Tot};
@@ -1819,29 +2056,73 @@ timestamp(Dir) ->
%% run will not show until after the final refresh.
%% -------------------------------------------------------------------------
-%% Creates the top level index file. When == start | refresh.
-%% A copy of the dir tree under logdir is cached as a result.
+%% Creates the top level index file. When == start | stop | refresh.
+%% A copy of the dir tree under logdir is saved temporarily as a result.
make_all_suites_index(When) when is_atom(When) ->
put(basic_html, basic_html()),
AbsIndexName = ?abs(?index_name),
notify_and_lock_file(AbsIndexName),
+
+ %% check if log cache should be used, and if it exists
+ UseCache =
+ if When == refresh ->
+ save_only;
+ true ->
+ case application:get_env(common_test, disable_log_cache) of
+ {ok,true} ->
+ disabled;
+ _ ->
+ case get(ct_log_cache) of
+ undefined ->
+ file:read_file(?log_cache_name);
+ LogCacheBin ->
+ {ok,LogCacheBin}
+ end
+ end
+ end,
+
LogDirs = filelib:wildcard(logdir_prefix()++".*/*"++?logdir_ext),
- Sorted = sort_logdirs(LogDirs, []),
- Result = make_all_suites_index1(When, AbsIndexName, Sorted),
- notify_and_unlock_file(AbsIndexName),
- Result;
-%% This updates the top level index file using cached data from
-%% the initial index file creation.
-make_all_suites_index(NewTestData = {_TestName,DirName}) ->
+ LogCacheInfo = get_cache_data(UseCache),
+
+ Result =
+ case LogCacheInfo of
+ {ok,LogCache} ->
+ %% use the log cache file to generate the index
+ make_all_suites_index_from_cache(When,AbsIndexName,
+ LogDirs,LogCache);
+ _WhyNot ->
+ %% no cache file exists (or feature has been disabled)
+ Sorted = sort_and_filter_logdirs(LogDirs),
+ TempData = make_all_suites_index1(When,AbsIndexName,Sorted),
+ notify_and_unlock_file(AbsIndexName),
+
+ %% save new cache file unless the feature is disabled
+ if UseCache == disabled -> ok;
+ true -> update_tests_in_cache(TempData)
+ end,
+ TempData
+ end,
+
+ case Result of
+ Error = {error,_} -> Error;
+ _ -> ok
+ end;
+
+%% This updates the top level index file using data from the initial
+%% index file creation, saved temporarily in a table.
+make_all_suites_index(NewTestData = {_TestName,DirName}) ->
put(basic_html, basic_html()),
- %% AllLogDirs = [{TestName,Label,Missing,{LastLogDir,Summary},OldDirs}|...]
+
+ %% AllLogDirs = [{TestName,Label,Missing,
+ %% {LastLogDir,Summary,URIs},OldDirs}|...]
+
{AbsIndexName,LogDirData} = ct_util:get_testdata(test_index),
CtRunDirPos = length(filename:split(AbsIndexName)),
CtRunDir = filename:join(lists:sublist(filename:split(DirName),
CtRunDirPos)),
-
+
Label = case read_totals_file(filename:join(CtRunDir, ?totals_name)) of
{_,"-",_,_} -> "...";
{_,Lbl,_,_} -> Lbl;
@@ -1849,10 +2130,10 @@ make_all_suites_index(NewTestData = {_TestName,DirName}) ->
end,
notify_and_lock_file(AbsIndexName),
Result =
- case catch make_all_suites_ix_cached(AbsIndexName,
- NewTestData,
- Label,
- LogDirData) of
+ case catch make_all_suites_ix_temp(AbsIndexName,
+ NewTestData,
+ Label,
+ LogDirData) of
{'EXIT',Reason} ->
io:put_chars("CRASHED while updating " ++ AbsIndexName ++ "!\n"),
io:format("~p~n", [Reason]),
@@ -1869,46 +2150,219 @@ make_all_suites_index(NewTestData = {_TestName,DirName}) ->
[AbsIndexName,Err]),
{error, Err}
end,
- notify_and_unlock_file(AbsIndexName),
+ notify_and_unlock_file(AbsIndexName),
Result.
-sort_logdirs([Dir|Dirs],Groups) ->
+make_all_suites_index_from_cache(When, AbsIndexName, LogDirs, LogCache) ->
+
+ %% The structure of the cache:
+ %%
+ %% #log_cache{tests = {TestName,Label,Missing,
+ %% {LastLogDir,Summary,URIs},OldDirs}
+ %% }
+ %% Summary = {Succ,Fail,USkip,ASkip} | error
+ %%
+
+ {NewAdded,OldTests} = dir_diff_tests(LogDirs,LogCache),
+
+ LogCache1 = delete_tests_from_cache(OldTests,LogCache),
+ Sorted = sort_and_filter_logdirs(NewAdded,
+ LogCache1#log_cache.tests),
+ TempData =
+ if Sorted /= [] ->
+ make_all_suites_index1(When,AbsIndexName,
+ Sorted);
+ true ->
+ Data = LogCache1#log_cache.tests,
+ ct_util:set_testdata_async({test_index,{AbsIndexName,
+ Data}}),
+ Data
+ end,
+
+ notify_and_unlock_file(AbsIndexName),
+
+ update_tests_in_cache(TempData,LogCache1),
+ TempData.
+
+sort_and_filter_logdirs(NewDirs,CachedTests) when CachedTests /= [] ->
+ NewSorted = sort_and_filter_logdirs1(NewDirs,[]),
+ sort_and_filter_logdirs(NewSorted,CachedTests,[]);
+
+sort_and_filter_logdirs(NewDirs,_CachedTests) ->
+ sort_and_filter_logdirs(NewDirs).
+
+%% sort latest dirs found and combine them with cached entries
+sort_and_filter_logdirs([{TestName,IxDirs}|Tests],CachedTests,Combined) ->
+ case lists:keysearch(TestName,1,CachedTests) of
+ {value,{TestName,_,_,{IxDir0,_,_},IxDirs0}} ->
+ Groups = sort_and_filter_logdirs2(TestName,
+ IxDirs++[IxDir0|IxDirs0],
+ []),
+ sort_and_filter_logdirs(Tests,CachedTests,Groups++Combined);
+ _ ->
+ IxDirs1 = lists:map(fun(Elem = {_,_}) ->
+ Elem;
+ (RunDir) ->
+ {filename:basename(RunDir),RunDir}
+ end, IxDirs),
+ sort_and_filter_logdirs(Tests,CachedTests,
+ [{TestName,IxDirs1}|Combined])
+ end;
+sort_and_filter_logdirs([],CachedTests,Combined) ->
+ Cached1 = lists:foldl(fun({TestName,_},Cached) ->
+ lists:keydelete(TestName,1,Cached)
+ end, CachedTests, Combined),
+ lists:keysort(1,sort_each_group(Combined)++Cached1).
+
+sort_and_filter_logdirs(Dirs) ->
+ sort_and_filter_logdirs1(Dirs, []).
+
+%% sort and filter directories (no cache)
+sort_and_filter_logdirs1([Dir|Dirs],Groups) ->
TestName = filename:rootname(filename:basename(Dir)),
case filelib:wildcard(filename:join(Dir,"run.*")) of
RunDirs = [_|_] ->
- Groups1 = sort_logdirs1(TestName,RunDirs,Groups),
- sort_logdirs(Dirs,Groups1);
+ Groups1 = sort_and_filter_logdirs2(TestName,RunDirs,Groups),
+ sort_and_filter_logdirs1(Dirs,Groups1);
_ -> % ignore missing run directory
- sort_logdirs(Dirs,Groups)
+ sort_and_filter_logdirs1(Dirs,Groups)
end;
-sort_logdirs([],Groups) ->
+sort_and_filter_logdirs1([],Groups) ->
lists:keysort(1,sort_each_group(Groups)).
-sort_logdirs1(TestName,[RunDir|RunDirs],Groups) ->
+sort_and_filter_logdirs2(TestName,[RunDir|RunDirs],Groups) ->
Groups1 = insert_test(TestName,{filename:basename(RunDir),RunDir},Groups),
- sort_logdirs1(TestName,RunDirs,Groups1);
-sort_logdirs1(_,[],Groups) ->
+ sort_and_filter_logdirs2(TestName,RunDirs,Groups1);
+sort_and_filter_logdirs2(_,[],Groups) ->
Groups.
+%% new rundir for Test found, add to (not sorted) list of prev rundirs
insert_test(Test,IxDir,[{Test,IxDirs}|Groups]) ->
[{Test,[IxDir|IxDirs]}|Groups];
+%% first occurance of Test
insert_test(Test,IxDir,[]) ->
[{Test,[IxDir]}];
insert_test(Test,IxDir,[TestDir|Groups]) ->
[TestDir|insert_test(Test,IxDir,Groups)].
-
+
+%% sort the list of rundirs for each Test
sort_each_group([{Test,IxDirs}|Groups]) ->
Sorted = lists:reverse([Dir || {_,Dir} <- lists:keysort(1,IxDirs)]),
- [{Test,Sorted}| sort_each_group(Groups)];
+ [{Test,Sorted}|sort_each_group(Groups)];
sort_each_group([]) ->
[].
-make_all_suites_index1(When, AbsIndexName, AllLogDirs) ->
+dir_diff_tests(LogDirs, #log_cache{tests = CachedTests}) ->
+ AllTestNames =
+ [TestName || {TestName,_,_,_,_} <- CachedTests],
+ dir_diff_tests(LogDirs, CachedTests, [], AllTestNames, [], []).
+
+dir_diff_tests([LogDir|LogDirs], CachedTests, NewAdded, DeletedTests,
+ ValidLast, InvalidLast) ->
+ TestName = filename:rootname(filename:basename(LogDir)),
+ Time = datestr_from_dirname(LogDir),
+ %% check if the test already exists in the cache
+ {New,DeletedTests1,ValidLast1,InvalidLast1} =
+ case lists:keysearch(TestName,1,CachedTests) of
+ {value,{_,_,_,{LastLogDir,_,_},_PrevLogDirs}} ->
+ LastLogTime = datestr_from_dirname(LastLogDir),
+ if Time > LastLogTime ->
+ %% this is a new test run, not in cache
+ {[LogDir|NewAdded],
+ lists:delete(TestName,DeletedTests),
+ ValidLast,[{TestName,LastLogDir}|InvalidLast]};
+ Time == LastLogTime ->
+ %% this is the latest test run, already in cache
+ TDir = {TestName,LastLogDir},
+ {NewAdded,
+ lists:delete(TestName,DeletedTests),
+ [TDir|ValidLast],InvalidLast};
+ true ->
+ %% this is an old test run
+ {[],
+ lists:delete(TestName,DeletedTests),
+ ValidLast,[{TestName,LastLogDir}|InvalidLast]}
+ end;
+ _ ->
+ %% this is a test run for a new test, not in cache
+ {[LogDir|NewAdded],
+ DeletedTests,ValidLast,InvalidLast}
+ end,
+ dir_diff_tests(LogDirs, CachedTests, New, DeletedTests1,
+ ValidLast1,InvalidLast1);
+
+dir_diff_tests([], _CachedTests, NewAdded, DeletedTests,
+ ValidLast, InvalidLast) ->
+ %% We have to check if LastLogDir still exists or if it's been
+ %% deleted. InvalidLast contains all log dirs that should be deleted,
+ %% if not present in ValidLast.
+ InvalidLast1 =
+ lists:foldl(fun(TDir,IL) ->
+ case lists:member(TDir,ValidLast) of
+ true ->
+ [TD || TD <- IL, TD /= TDir];
+ false ->
+ [TDir | [TD || TD <- IL, TD /= TDir]]
+ end
+ end, InvalidLast, InvalidLast),
+
+ %% Collect all tests for which LastLogDir has been deleted.
+ DeletedTests1 = [T || {T,_} <- InvalidLast1] ++ DeletedTests,
+
+ %% Make sure that directories for tests that are to be deleted are
+ %% saved in NewAdded so that tests don't disappear from the log if
+ %% older run dirs for them exist.
+ NewAdded1 = lists:map(fun({_TestName,RunDir}) ->
+ [TopDir,TestDir|_] = filename:split(RunDir),
+ filename:join(TopDir,TestDir)
+ end, InvalidLast1) ++ NewAdded,
+
+ {NewAdded1,DeletedTests1}.
+
+delete_tests_from_cache(OldTests, LogCache=#log_cache{tests=Tests}) ->
+ Tests2 = lists:foldl(fun(T,Tests1) ->
+ lists:keydelete(T,1,Tests1)
+ end, Tests, OldTests),
+ LogCache#log_cache{tests = Tests2}.
+
+update_tests_in_cache(TempData) ->
+ case get(ct_log_cache) of
+ undefined ->
+ update_tests_in_cache(TempData,#log_cache{version = cache_vsn(),
+ tests=[]});
+ SavedLogCache ->
+ update_tests_in_cache(TempData,binary_to_term(SavedLogCache))
+ end.
+
+update_tests_in_cache(TempData,LogCache=#log_cache{tests=Tests}) ->
+ Cached1 =
+ if Tests == [] ->
+ [];
+ true ->
+ lists:foldl(fun({TestName,_,_,_,_},Cached) ->
+ lists:keydelete(TestName,1,Cached)
+ end, Tests, TempData)
+ end,
+ Tests1 = lists:keysort(1,TempData++Cached1),
+ CacheBin = term_to_binary(LogCache#log_cache{tests = Tests1}),
+ case {self(),whereis(?MODULE)} of
+ {_Pid,_Pid} ->
+ put(ct_log_cache,CacheBin);
+ _ ->
+ file:write_file(?log_cache_name,CacheBin)
+ end.
+
+%%
+%% AllTestLogDirs =
+%% [{TestName,[IxDir|IxDirs]} | ...] (non-cached), or
+%% [{TestName,Label,Missing,{IxDir,Summary,URIs},IxDirs} | ...] (cached)
+%%
+make_all_suites_index1(When, AbsIndexName, AllTestLogDirs) ->
IndexName = ?index_name,
if When == start -> ok;
true -> io:put_chars("Updating " ++ AbsIndexName ++ "... ")
end,
- case catch make_all_suites_index2(IndexName, AllLogDirs) of
+ case catch make_all_suites_index2(IndexName, AllTestLogDirs) of
{'EXIT', Reason} ->
io:put_chars("CRASHED while updating " ++ AbsIndexName ++ "!\n"),
io:format("~p~n", [Reason]),
@@ -1917,15 +2371,15 @@ make_all_suites_index1(When, AbsIndexName, AllLogDirs) ->
io:put_chars("FAILED while updating " ++ AbsIndexName ++ "\n"),
io:format("~p~n", [Reason]),
{error, Reason};
- {ok,CacheData} ->
+ {ok,TempData} ->
case When of
start ->
ct_util:set_testdata_async({test_index,{AbsIndexName,
- CacheData}}),
- ok;
+ TempData}}),
+ TempData;
_ ->
io:put_chars("done\n"),
- ok
+ TempData
end;
Err ->
io:format("Unknown internal error while updating ~ts. "
@@ -1935,21 +2389,57 @@ make_all_suites_index1(When, AbsIndexName, AllLogDirs) ->
end.
make_all_suites_index2(IndexName, AllTestLogDirs) ->
- {ok,Index0,_Totals,CacheData} =
+ {ok,Index0,_Totals,TempData} =
make_all_suites_index3(AllTestLogDirs,
all_suites_index_header(),
0, 0, 0, 0, 0, [], []),
- Index = [Index0|index_footer()],
+ Index = [Index0|all_suites_index_footer()],
case force_write_file(IndexName, unicode:characters_to_binary(Index)) of
ok ->
- {ok,CacheData};
+ {ok,TempData};
{error, Reason} ->
{error,{index_write_error, Reason}}
end.
+%%
+%% AllTestLogDirs = [{TestName,Label,Missing,{LogDir,Summary,URIs},OldDirs}]
+%% Summary = {Succ,Fail,UserSkip,AutoSkip} | error
+%% URIs = {CtRunLogURI,LogFileURI,CrashDumpURI} | undefined
+%%
+%% this clause is for handling entries in the log cache
+make_all_suites_index3([IxEntry = {TestName,Label,Missing,
+ {LastLogDir,Summary,URIs},OldDirs} | Rest],
+ Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt,
+ Labels, TempData) ->
+ [EntryDir|_] = filename:split(LastLogDir),
+ Labels1 = [{EntryDir,Label}|Labels],
+ case Summary of
+ {Succ,Fail,USkip,ASkip} ->
+ All = {true,OldDirs},
+ NotBuilt = not_built(TestName, LastLogDir, All, Missing),
+
+ {Result1,_} = make_one_index_entry1(TestName, LastLogDir, Label,
+ Succ, Fail, USkip, ASkip,
+ NotBuilt, All, temp, URIs),
+
+ AutoSkip1 = case catch AutoSkip+ASkip of
+ {'EXIT',_} -> undefined;
+ Res -> Res
+ end,
+ make_all_suites_index3(Rest, [Result|Result1], TotSucc+Succ,
+ TotFail+Fail, UserSkip+USkip, AutoSkip1,
+ TotNotBuilt+NotBuilt, Labels1,
+ [IxEntry|TempData]);
+ error ->
+ make_all_suites_index3(Rest, Result, TotSucc, TotFail,
+ UserSkip, AutoSkip, TotNotBuilt, Labels1,
+ [IxEntry|TempData])
+ end;
+
+%% this clause is for handling non-cached directories
make_all_suites_index3([{TestName,[LastLogDir|OldDirs]}|Rest],
Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt,
- Labels, CacheData) ->
+ Labels, TempData) ->
[EntryDir|_] = filename:split(LastLogDir),
Missing =
case file:read_file(filename:join(EntryDir, ?missing_suites_info)) of
@@ -1966,38 +2456,50 @@ make_all_suites_index3([{TestName,[LastLogDir|OldDirs]}|Rest],
Lbl ->
{Lbl,Labels}
end,
- case make_one_index_entry(TestName, LastLogDir, Label, {true,OldDirs}, Missing) of
- {Result1,Succ,Fail,USkip,ASkip,NotBuilt} ->
+ case make_one_index_entry(TestName, LastLogDir, Label,
+ {true,OldDirs}, Missing, undefined) of
+ {Result1,Succ,Fail,USkip,ASkip,NotBuilt,URIs} ->
%% for backwards compatibility
AutoSkip1 = case catch AutoSkip+ASkip of
{'EXIT',_} -> undefined;
Res -> Res
end,
IxEntry = {TestName,Label,Missing,
- {LastLogDir,{Succ,Fail,USkip,ASkip}},OldDirs},
+ {LastLogDir,{Succ,Fail,USkip,ASkip},URIs},OldDirs},
+
make_all_suites_index3(Rest, [Result|Result1], TotSucc+Succ,
TotFail+Fail, UserSkip+USkip, AutoSkip1,
TotNotBuilt+NotBuilt, Labels1,
- [IxEntry|CacheData]);
+ [IxEntry|TempData]);
error ->
- IxEntry = {TestName,Label,Missing,{LastLogDir,error},OldDirs},
+ IxEntry = {TestName,Label,Missing,
+ {LastLogDir,error,undefined},OldDirs},
make_all_suites_index3(Rest, Result, TotSucc, TotFail,
UserSkip, AutoSkip, TotNotBuilt, Labels1,
- [IxEntry|CacheData])
+ [IxEntry|TempData])
end;
+
+%% something wrong with this test dir, ignore
+make_all_suites_index3([_|Rest], Result, TotSucc, TotFail, UserSkip, AutoSkip,
+ TotNotBuilt, Labels, TempData) ->
+ make_all_suites_index3(Rest, Result, TotSucc, TotFail,
+ UserSkip, AutoSkip, TotNotBuilt, Labels,
+ TempData);
+
make_all_suites_index3([], Result, TotSucc, TotFail, UserSkip, AutoSkip,
- TotNotBuilt, _, CacheData) ->
- {ok, [Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt,true)],
- {TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}, lists:reverse(CacheData)}.
+ TotNotBuilt, _, TempData) ->
+ {ok, [Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip,
+ TotNotBuilt,true)],
+ {TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}, lists:reverse(TempData)}.
-make_all_suites_ix_cached(AbsIndexName, NewTestData, Label, AllTestLogDirs) ->
+make_all_suites_ix_temp(AbsIndexName, NewTestData, Label, AllTestLogDirs) ->
AllTestLogDirs1 = insert_new_test_data(NewTestData, Label, AllTestLogDirs),
IndexDir = filename:dirname(AbsIndexName),
- Index0 = make_all_suites_ix_cached1(AllTestLogDirs1,
- all_suites_index_header(IndexDir),
- 0, 0, 0, 0, 0),
- Index = [Index0|index_footer()],
+ Index0 = make_all_suites_ix_temp1(AllTestLogDirs1,
+ all_suites_index_header(IndexDir),
+ 0, 0, 0, 0, 0),
+ Index = [Index0|all_suites_index_footer()],
case force_write_file(AbsIndexName, unicode:characters_to_binary(Index)) of
ok ->
ok;
@@ -2008,51 +2510,94 @@ make_all_suites_ix_cached(AbsIndexName, NewTestData, Label, AllTestLogDirs) ->
insert_new_test_data({NewTestName,NewTestDir}, NewLabel, AllTestLogDirs) ->
AllTestLogDirs1 =
case lists:keysearch(NewTestName, 1, AllTestLogDirs) of
- {value,{_,_,_,{LastLogDir,_},OldDirs}} ->
- [{NewTestName,NewLabel,[],{NewTestDir,{0,0,0,0}},
+ {value,{_,_,_,{LastLogDir,_,_},OldDirs}} ->
+ [{NewTestName,NewLabel,[],{NewTestDir,{0,0,0,0},undefined},
[LastLogDir|OldDirs]} |
lists:keydelete(NewTestName, 1, AllTestLogDirs)];
false ->
- [{NewTestName,NewLabel,[],{NewTestDir,{0,0,0,0}},[]} |
+ [{NewTestName,NewLabel,[],{NewTestDir,{0,0,0,0},undefined},[]} |
AllTestLogDirs]
end,
lists:keysort(1, AllTestLogDirs1).
-make_all_suites_ix_cached1([{TestName,Label,Missing,LastLogDirData,OldDirs}|Rest],
- Result, TotSucc, TotFail, UserSkip, AutoSkip,
- TotNotBuilt) ->
-
- case make_one_ix_entry_cached(TestName, LastLogDirData,
- Label, {true,OldDirs}, Missing) of
- {Result1,Succ,Fail,USkip,ASkip,NotBuilt} ->
+make_all_suites_ix_temp1([{TestName,Label,Missing,LastLogDirData,OldDirs}|Rest],
+ Result, TotSucc, TotFail, UserSkip, AutoSkip,
+ TotNotBuilt) ->
+ case make_one_ix_entry_temp(TestName, LastLogDirData,
+ Label, {true,OldDirs}, Missing) of
+ {Result1,Succ,Fail,USkip,ASkip,NotBuilt,_URIs} ->
%% for backwards compatibility
AutoSkip1 = case catch AutoSkip+ASkip of
{'EXIT',_} -> undefined;
Res -> Res
end,
- make_all_suites_ix_cached1(Rest, [Result|Result1], TotSucc+Succ,
- TotFail+Fail, UserSkip+USkip, AutoSkip1,
- TotNotBuilt+NotBuilt);
+ make_all_suites_ix_temp1(Rest, [Result|Result1], TotSucc+Succ,
+ TotFail+Fail, UserSkip+USkip, AutoSkip1,
+ TotNotBuilt+NotBuilt);
error ->
- make_all_suites_ix_cached1(Rest, Result, TotSucc, TotFail,
- UserSkip, AutoSkip, TotNotBuilt)
+ make_all_suites_ix_temp1(Rest, Result, TotSucc, TotFail,
+ UserSkip, AutoSkip, TotNotBuilt)
end;
-make_all_suites_ix_cached1([], Result, TotSucc, TotFail, UserSkip, AutoSkip,
- TotNotBuilt) ->
+make_all_suites_ix_temp1([], Result, TotSucc, TotFail, UserSkip, AutoSkip,
+ TotNotBuilt) ->
[Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, true)].
-make_one_ix_entry_cached(TestName, {LogDir,Summary}, Label, All, Missing) ->
+make_one_ix_entry_temp(TestName, {LogDir,Summary,URIs}, Label, All, Missing) ->
case Summary of
{Succ,Fail,UserSkip,AutoSkip} ->
NotBuilt = not_built(TestName, LogDir, All, Missing),
- NewResult = make_one_index_entry1(TestName, LogDir, Label,
- Succ, Fail, UserSkip, AutoSkip,
- NotBuilt, All, cached),
- {NewResult,Succ,Fail,UserSkip,AutoSkip,NotBuilt};
+ {NewResult,URIs1} = make_one_index_entry1(TestName, LogDir, Label,
+ Succ, Fail,
+ UserSkip, AutoSkip,
+ NotBuilt, All, temp, URIs),
+ {NewResult,Succ,Fail,UserSkip,AutoSkip,NotBuilt,URIs1};
error ->
error
end.
+%%%-----------------------------------------------------------------
+%%%
+get_cache_data({ok,CacheBin}) ->
+ case binary_to_term(CacheBin) of
+ CacheRec when is_record(CacheRec,log_cache) ->
+ %% make sure we don't use a cache on old format
+ case is_correct_cache_vsn(CacheRec) of
+ true ->
+ {ok,CacheRec};
+ false ->
+ file:delete(?log_cache_name),
+ {error,old_cache_file}
+ end;
+ _ ->
+ file:delete(?log_cache_name),
+ {error,invalid_cache_file}
+ end;
+get_cache_data(NoCache) ->
+ NoCache.
+
+cache_vsn() ->
+ application:load(common_test),
+ case application:get_key(common_test,vsn) of
+ {ok,VSN} ->
+ VSN;
+ _ ->
+ EbinDir = filename:dirname(code:which(ct)),
+ VSNfile = filename:join([EbinDir,"..","vsn.mk"]),
+ case file:read_file(VSNfile) of
+ {ok,Bin} ->
+ [_,VSN] = string:tokens(binary_to_list(Bin),[$=,$\n,$ ]),
+ VSN;
+ _ ->
+ undefined
+ end
+ end.
+
+is_correct_cache_vsn(#log_cache{version = CVSN}) ->
+ case cache_vsn() of
+ CVSN -> true;
+ _ -> false
+ end.
+
%%-----------------------------------------------------------------
%% Remove log files.
%% Cwd should always be set to the root logdir when finished.
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index 57cfab532e..41d53c7b43 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -329,6 +329,13 @@ script_start1(Parent, Args) ->
application:set_env(common_test, basic_html, true),
true
end,
+ %% disable_log_cache - used by ct_logs
+ case proplists:get_value(disable_log_cache, Args) of
+ undefined ->
+ application:set_env(common_test, disable_log_cache, false);
+ _ ->
+ application:set_env(common_test, disable_log_cache, true)
+ end,
Opts = #opts{label = Label, profile = Profile,
vts = Vts, shell = Shell,
@@ -1039,6 +1046,13 @@ run_test2(StartOpts) ->
BasicHtmlBool
end,
+ case proplists:get_value(disable_log_cache, StartOpts) of
+ undefined ->
+ application:set_env(common_test, disable_log_cache, false);
+ DisableCacheBool ->
+ application:set_env(common_test, disable_log_cache, DisableCacheBool)
+ end,
+
%% stepped execution
Step = get_start_opt(step, value, StartOpts),
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index 02e58d0786..6a8b37bf3b 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -414,6 +414,8 @@ loop(Mode,TestData,StartDir) ->
[#conn{address=A,callback=CB}] ->
%% A connection crashed - remove the connection but don't die
ct_logs:tc_log_async(ct_error_notify,
+ ?MAX_IMPORTANCE,
+ "CT Error Notification",
"Connection process died: "
"Pid: ~w, Address: ~p, Callback: ~w\n"
"Reason: ~p\n\n",
diff --git a/lib/common_test/src/cth_log_redirect.erl b/lib/common_test/src/cth_log_redirect.erl
index 78ae70f37e..958b7a94c7 100644
--- a/lib/common_test/src/cth_log_redirect.erl
+++ b/lib/common_test/src/cth_log_redirect.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2011-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
@@ -33,6 +33,8 @@
handle_event/2, handle_call/2, handle_info/2,
terminate/1]).
+-include("ct.hrl").
+
id(_Opts) ->
?MODULE.
@@ -78,7 +80,7 @@ handle_event(Event, LogFunc) ->
SReport = sasl_report:format_report(group_leader(), ErrLogType,
tag_event(Event)),
if is_list(SReport) ->
- ct_logs:LogFunc(sasl, SReport, []);
+ ct_logs:LogFunc(sasl, ?STD_IMPORTANCE, "System", SReport, []);
true -> %% Report is an atom if no logging is to be done
ignore
end
@@ -86,7 +88,7 @@ handle_event(Event, LogFunc) ->
EReport = error_logger_tty_h:write_event(
tag_event(Event),io_lib),
if is_list(EReport) ->
- ct_logs:LogFunc(error_logger, EReport, []);
+ ct_logs:LogFunc(error_logger, ?STD_IMPORTANCE, "System", EReport, []);
true -> %% Report is an atom if no logging is to be done
ignore
end,
diff --git a/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE.erl b/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE.erl
index d967590c72..83d368c53d 100644
--- a/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE.erl
+++ b/lib/common_test/test/ct_cover_SUITE_data/cover_SUITE.erl
@@ -52,11 +52,10 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
end_per_testcase(Case, Config) ->
- %% try apply(?MODULE,Case,[cleanup,Config])
- %% catch error:undef -> ok
- %% end,
+ try apply(?MODULE,Case,[cleanup,Config])
+ catch error:undef -> ok
+ end,
- kill_slaves(Case,nodes()),
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
@@ -67,12 +66,12 @@ break(_Config) ->
test_server:break(""),
ok.
-default(Config) ->
+default(_Config) ->
cover_compiled = code:which(cover_test_mod),
cover_test_mod:foo(),
ok.
-slave(Config) ->
+slave(_Config) ->
cover_compiled = code:which(cover_test_mod),
cover_test_mod:foo(),
N1 = nodename(slave,1),
@@ -81,8 +80,10 @@ slave(Config) ->
rpc:call(Node,cover_test_mod,foo,[]),
{ok,Node} = ct_slave:stop(N1),
ok.
+slave(cleanup,_Config) ->
+ kill_slaves([nodename(slave,1)]).
-slave_start_slave(Config) ->
+slave_start_slave(_Config) ->
cover_compiled = code:which(cover_test_mod),
cover_test_mod:foo(),
N1 = nodename(slave_start_slave,1),
@@ -90,13 +91,16 @@ slave_start_slave(Config) ->
{ok,Node} = start_slave(N1),
cover_compiled = rpc:call(Node,code,which,[cover_test_mod]),
rpc:call(Node,cover_test_mod,foo,[]),
- {ok,Node2} = rpc:call(Node,ct_slave,start,[N2]),
+ {ok,Node2} = start_slave(Node,N2), % start slave N2 from node Node
rpc:call(Node2,cover_test_mod,foo,[]),
{ok,Node2} = rpc:call(Node,ct_slave,stop,[N2]),
{ok,Node} = ct_slave:stop(N1),
ok.
+slave_start_slave(cleanup,_Config) ->
+ kill_slaves([nodename(slave_start_slave,1),
+ nodename(slave_start_slave,2)]).
-cover_node_option(Config) ->
+cover_node_option(_Config) ->
cover_compiled = code:which(cover_test_mod),
cover_test_mod:foo(),
Node = fullname(existing_node_1),
@@ -104,7 +108,7 @@ cover_node_option(Config) ->
rpc:call(Node,cover_test_mod,foo,[]),
ok.
-ct_cover_add_remove_nodes(Config) ->
+ct_cover_add_remove_nodes(_Config) ->
cover_compiled = code:which(cover_test_mod),
cover_test_mod:foo(),
Node = fullname(existing_node_2),
@@ -143,22 +147,20 @@ fullname(Name) ->
{ok,Host} = inet:gethostname(),
list_to_atom(atom_to_list(Name) ++ "@" ++ Host).
-kill_slaves(Case, [Node|Nodes]) ->
- Prefix = nodeprefix(Case),
- case lists:prefix(Prefix,atom_to_list(Node)) of
- true ->
- rpc:call(Node,erlang,halt,[]);
- _ ->
- ok
- end,
- kill_slaves(Case,Nodes);
-kill_slaves(_,[]) ->
+kill_slaves([Name|Names]) ->
+ _ = rpc:call(fullname(Name),erlang,halt,[]),
+ kill_slaves(Names);
+kill_slaves([]) ->
ok.
start_slave(Name) ->
+ start_slave(node(),Name).
+
+start_slave(FromNode,Name) ->
{ok, HostStr}=inet:gethostname(),
Host = list_to_atom(HostStr),
- ct_slave:start(Host,Name,
- [{boot_timeout,10}, % extending some timers for slow test hosts
- {init_timeout,10},
- {startup_timeout,10}]).
+ rpc:call(FromNode,ct_slave,start,
+ [Host,Name,
+ [{boot_timeout,15}, % extending some timers for slow test hosts
+ {init_timeout,15},
+ {startup_timeout,15}]]).
diff --git a/lib/common_test/test/ct_repeat_testrun_SUITE.erl b/lib/common_test/test/ct_repeat_testrun_SUITE.erl
index 7ec384c932..35d67a10f2 100644
--- a/lib/common_test/test/ct_repeat_testrun_SUITE.erl
+++ b/lib/common_test/test/ct_repeat_testrun_SUITE.erl
@@ -51,8 +51,8 @@
%% least 20 seconds (10 sec for each r1_SUITE:tc1)
%%
-define(t1,30). % time shall expire during second run of r1_SUITE
--define(t2,6). % time shall expire during first run of tc1
--define(t3,16). % time shall expire during second run of tc1
+-define(t2,9). % time shall expire during first run of tc1
+-define(t3,19). % time shall expire during second run of tc1
%%--------------------------------------------------------------------
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 70dd087358..6bcac12326 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -224,11 +224,38 @@ get_opts(Config) ->
%%%-----------------------------------------------------------------
%%%
-run(Opts, Config) when is_list(Opts) ->
+run(Opts0, Config) when is_list(Opts0) ->
+ Opts =
+ %% read (and override) opts from env variable, the form expected:
+ %% "[{some_key1,SomeVal2}, {some_key2,SomeVal2}]"
+ case os:getenv("CT_TEST_OPTS") of
+ false -> Opts0;
+ "" -> Opts0;
+ Terms ->
+ case erl_scan:string(Terms++".", 0) of
+ {ok,Tokens,_} ->
+ case erl_parse:parse_term(Tokens) of
+ {ok,OROpts} ->
+ Override =
+ fun(O={Key,_}, Os) ->
+ io:format(user, "ADDING START "
+ "OPTION: ~p~n", [O]),
+ [O | lists:keydelete(Key, 1, Os)]
+ end,
+ lists:foldl(Override, Opts0, OROpts);
+ _ ->
+ Opts0
+ end;
+ _ ->
+ Opts0
+ end
+ end,
+
%% use ct interface
CtRunTestResult=run_ct_run_test(Opts,Config),
%% use run_test interface (simulated)
ExitStatus=run_ct_script_start(Opts,Config),
+
check_result(CtRunTestResult,ExitStatus,Opts).
run_ct_run_test(Opts,Config) ->
@@ -236,9 +263,10 @@ run_ct_run_test(Opts,Config) ->
Level = proplists:get_value(trace_level, Config),
test_server:format(Level, "~n[RUN #1] Calling ct:run_test(~p) on ~p~n",
[Opts, CTNode]),
+ T0 = now(),
CtRunTestResult = rpc:call(CTNode, ct, run_test, [Opts]),
- test_server:format(Level, "~n[RUN #1] Got return value ~p~n",
- [CtRunTestResult]),
+ test_server:format(Level, "~n[RUN #1] Got return value ~p after ~p ms~n",
+ [CtRunTestResult,trunc(timer:now_diff(now(), T0)/1000)]),
case rpc:call(CTNode, erlang, whereis, [ct_util_server]) of
undefined ->
ok;
@@ -261,9 +289,10 @@ run_ct_script_start(Opts, Config) ->
[common_test, run_test_start_opts, Opts1]),
test_server:format(Level, "[RUN #2] Calling ct_run:script_start() on ~p~n",
[CTNode]),
+ T0 = now(),
ExitStatus = rpc:call(CTNode, ct_run, script_start, []),
- test_server:format(Level, "[RUN #2] Got exit status value ~p~n",
- [ExitStatus]),
+ test_server:format(Level, "[RUN #2] Got exit status value ~p after ~p ms~n",
+ [ExitStatus,trunc(timer:now_diff(now(), T0)/1000)]),
ExitStatus.
check_result({_Ok,Failed,{_UserSkipped,_AutoSkipped}},1,_Opts)
diff --git a/lib/compiler/src/beam_receive.erl b/lib/compiler/src/beam_receive.erl
index 3dd5ed182e..97a9188ee7 100644
--- a/lib/compiler/src/beam_receive.erl
+++ b/lib/compiler/src/beam_receive.erl
@@ -151,20 +151,20 @@ opt_recv(Is, Regs, D) ->
opt_recv([{label,L}=Lbl,{loop_rec,{f,Fail},_}=Loop|Is], D, R0, _, Acc) ->
R = regs_kill_not_live(0, R0),
- case regs_to_list(R) of
- [{y,_}=RefReg] ->
- %% We now have the new reference in the Y register RefReg
+ case regs_empty(R) of
+ false ->
+ %% We now have the new reference in Y registers
%% and the current instruction is the beginning of a
%% receive statement. We must now verify that only messages
%% that contain the reference will be matched.
- case opt_ref_used(Is, RefReg, Fail, D) of
+ case opt_ref_used(Is, R, Fail, D) of
false ->
no;
true ->
RecvSet = {recv_set,{f,L}},
{yes,reverse(Acc, [RecvSet,Lbl,Loop|Is]),L}
end;
- [] ->
+ true ->
no
end;
opt_recv([I|Is], D, R0, L0, Acc) ->
@@ -226,9 +226,9 @@ opt_update_regs_bl([{set,Ds,_,_}|Is], Regs0) ->
opt_update_regs_bl(Is, Regs);
opt_update_regs_bl([], Regs) -> Regs.
-%% opt_ref_used([Instruction], RefRegister, FailLabel, LabelIndex) -> true|false
+%% opt_ref_used([Instruction], RefRegs, FailLabel, LabelIndex) -> true|false
%% Return 'true' if it is certain that only messages that contain the same
-%% reference as in RefRegister can be matched out. Otherwise return 'false'.
+%% reference as in RefRegs can be matched out. Otherwise return 'false'.
%%
%% Basically, we follow all possible paths through the receive statement.
%% If all paths are safe, we return 'true'.
@@ -236,7 +236,7 @@ opt_update_regs_bl([], Regs) -> Regs.
%% A branch to FailLabel is safe, because it exits the receive statement
%% and no further message may be matched out.
%%
-%% If a path hits an comparision between RefRegister and part of the message,
+%% If a path hits an comparision between RefRegs and part of the message,
%% that path is safe (any messages that may be matched further down the
%% path is guaranteed to contain the reference).
%%
@@ -245,11 +245,11 @@ opt_update_regs_bl([], Regs) -> Regs.
%% we hit an unrecognized instruction, we also give up and return
%% 'false' (the optimization may be unsafe).
-opt_ref_used(Is, RefReg, Fail, D) ->
+opt_ref_used(Is, RefRegs, Fail, D) ->
Done = gb_sets:singleton(Fail),
Regs = regs_init_x0(),
try
- _ = opt_ref_used_1(Is, RefReg, D, Done, Regs),
+ _ = opt_ref_used_1(Is, RefRegs, D, Done, Regs),
true
catch
throw:not_used ->
@@ -258,37 +258,39 @@ opt_ref_used(Is, RefReg, Fail, D) ->
%% This functions only returns if all paths through the receive
%% statement are safe, and throws an 'not_used' term otherwise.
-opt_ref_used_1([{block,Bl}|Is], RefReg, D, Done, Regs0) ->
+opt_ref_used_1([{block,Bl}|Is], RefRegs, D, Done, Regs0) ->
Regs = opt_ref_used_bl(Bl, Regs0),
- opt_ref_used_1(Is, RefReg, D, Done, Regs);
-opt_ref_used_1([{test,is_eq_exact,{f,Fail},Args}|Is], RefReg, D, Done0, Regs) ->
- Done = opt_ref_used_at(Fail, RefReg, D, Done0, Regs),
- case is_ref_msg_comparison(Args, RefReg, Regs) of
+ opt_ref_used_1(Is, RefRegs, D, Done, Regs);
+opt_ref_used_1([{test,is_eq_exact,{f,Fail},Args}|Is],
+ RefRegs, D, Done0, Regs) ->
+ Done = opt_ref_used_at(Fail, RefRegs, D, Done0, Regs),
+ case is_ref_msg_comparison(Args, RefRegs, Regs) of
false ->
- opt_ref_used_1(Is, RefReg, D, Done, Regs);
+ opt_ref_used_1(Is, RefRegs, D, Done, Regs);
true ->
%% The instructions that follow (Is) can only be executed
- %% if the message contains the same reference as in RefReg.
+ %% if the message contains the same reference as in RefRegs.
Done
end;
-opt_ref_used_1([{test,is_ne_exact,{f,Fail},Args}|Is], RefReg, D, Done0, Regs) ->
- Done = opt_ref_used_1(Is, RefReg, D, Done0, Regs),
- case is_ref_msg_comparison(Args, RefReg, Regs) of
+opt_ref_used_1([{test,is_ne_exact,{f,Fail},Args}|Is],
+ RefRegs, D, Done0, Regs) ->
+ Done = opt_ref_used_1(Is, RefRegs, D, Done0, Regs),
+ case is_ref_msg_comparison(Args, RefRegs, Regs) of
false ->
- opt_ref_used_at(Fail, RefReg, D, Done, Regs);
+ opt_ref_used_at(Fail, RefRegs, D, Done, Regs);
true ->
Done
end;
-opt_ref_used_1([{test,_,{f,Fail},_}|Is], RefReg, D, Done0, Regs) ->
- Done = opt_ref_used_at(Fail, RefReg, D, Done0, Regs),
- opt_ref_used_1(Is, RefReg, D, Done, Regs);
-opt_ref_used_1([{select,_,_,{f,Fail},List}|_], RefReg, D, Done, Regs) ->
+opt_ref_used_1([{test,_,{f,Fail},_}|Is], RefRegs, D, Done0, Regs) ->
+ Done = opt_ref_used_at(Fail, RefRegs, D, Done0, Regs),
+ opt_ref_used_1(Is, RefRegs, D, Done, Regs);
+opt_ref_used_1([{select,_,_,{f,Fail},List}|_], RefRegs, D, Done, Regs) ->
Lbls = [F || {f,F} <- List] ++ [Fail],
- opt_ref_used_in_all(Lbls, RefReg, D, Done, Regs);
-opt_ref_used_1([{label,Lbl}|Is], RefReg, D, Done, Regs) ->
+ opt_ref_used_in_all(Lbls, RefRegs, D, Done, Regs);
+opt_ref_used_1([{label,Lbl}|Is], RefRegs, D, Done, Regs) ->
case gb_sets:is_member(Lbl, Done) of
true -> Done;
- false -> opt_ref_used_1(Is, RefReg, D, Done, Regs)
+ false -> opt_ref_used_1(Is, RefRegs, D, Done, Regs)
end;
opt_ref_used_1([{loop_rec_end,_}|_], _, _, Done, _) ->
Done;
@@ -296,27 +298,25 @@ opt_ref_used_1([_I|_], _RefReg, _D, _Done, _Regs) ->
%% The optimization may be unsafe.
throw(not_used).
-%% is_ref_msg_comparison(Args, RefReg, RegisterSet) -> true|false.
+%% is_ref_msg_comparison(Args, RefRegs, RegisterSet) -> true|false.
%% Return 'true' if Args denotes a comparison between the
%% reference and message or part of the message.
-is_ref_msg_comparison([R,RefReg], RefReg, Regs) ->
- regs_is_member(R, Regs);
-is_ref_msg_comparison([RefReg,R], RefReg, Regs) ->
- regs_is_member(R, Regs);
-is_ref_msg_comparison([_,_], _, _) -> false.
-
-opt_ref_used_in_all([L|Ls], RefReg, D, Done0, Regs) ->
- Done = opt_ref_used_at(L, RefReg, D, Done0, Regs),
- opt_ref_used_in_all(Ls, RefReg, D, Done, Regs);
+is_ref_msg_comparison([R1,R2], RefRegs, Regs) ->
+ (regs_is_member(R2, RefRegs) andalso regs_is_member(R1, Regs)) orelse
+ (regs_is_member(R1, RefRegs) andalso regs_is_member(R2, Regs)).
+
+opt_ref_used_in_all([L|Ls], RefRegs, D, Done0, Regs) ->
+ Done = opt_ref_used_at(L, RefRegs, D, Done0, Regs),
+ opt_ref_used_in_all(Ls, RefRegs, D, Done, Regs);
opt_ref_used_in_all([], _, _, Done, _) -> Done.
-opt_ref_used_at(Fail, RefReg, D, Done0, Regs) ->
+opt_ref_used_at(Fail, RefRegs, D, Done0, Regs) ->
case gb_sets:is_member(Fail, Done0) of
true ->
Done0;
false ->
Is = beam_utils:code_at(Fail, D),
- Done = opt_ref_used_1(Is, RefReg, D, Done0, Regs),
+ Done = opt_ref_used_1(Is, RefRegs, D, Done0, Regs),
gb_sets:add(Fail, Done)
end.
@@ -408,15 +408,3 @@ regs_all_members([], _) -> true.
regs_is_member({x,N}, {Regs,_}) -> Regs band (1 bsl N) =/= 0;
regs_is_member({y,N}, {_,Regs}) -> Regs band (1 bsl N) =/= 0;
regs_is_member(_, _) -> false.
-
-%% regs_to_list(RegisterSet) -> [Register]
-%% Convert the register set to an explicit list of registers.
-regs_to_list({Xregs,Yregs}) ->
- regs_to_list_1(Xregs, 0, x, regs_to_list_1(Yregs, 0, y, [])).
-
-regs_to_list_1(0, _, _, Acc) ->
- Acc;
-regs_to_list_1(Regs, N, Tag, Acc) when (Regs band 1) =:= 1 ->
- regs_to_list_1(Regs bsr 1, N+1, Tag, [{Tag,N}|Acc]);
-regs_to_list_1(Regs, N, Tag, Acc) ->
- regs_to_list_1(Regs bsr 1, N+1, Tag, Acc).
diff --git a/lib/compiler/test/receive_SUITE.erl b/lib/compiler/test/receive_SUITE.erl
index b91f2922fb..e60584d4ab 100644
--- a/lib/compiler/test/receive_SUITE.erl
+++ b/lib/compiler/test/receive_SUITE.erl
@@ -188,7 +188,7 @@ ref_opt(Config) when is_list(Config) ->
ref_opt_1(Config) ->
?line DataDir = ?config(data_dir, Config),
?line PrivDir = ?config(priv_dir, Config),
- ?line Sources = filelib:wildcard(filename:join([DataDir,"ref_opt","*.erl"])),
+ Sources = filelib:wildcard(filename:join([DataDir,"ref_opt","*.{erl,S}"])),
?line test_lib:p_run(fun(Src) ->
do_ref_opt(Src, PrivDir)
end, Sources),
@@ -196,10 +196,15 @@ ref_opt_1(Config) ->
do_ref_opt(Source, PrivDir) ->
try
- {ok,Mod} = c:c(Source, [{outdir,PrivDir}]),
+ Ext = filename:extension(Source),
+ {ok,Mod} = compile:file(Source, [report_errors,report_warnings,
+ {outdir,PrivDir}] ++
+ [from_asm || Ext =:= ".S" ]),
+ Base = filename:rootname(filename:basename(Source), Ext),
+ code:purge(list_to_atom(Base)),
+ BeamFile = filename:join(PrivDir, Base),
+ code:load_abs(BeamFile),
ok = Mod:Mod(),
- Base = filename:rootname(filename:basename(Source), ".erl"),
- BeamFile = filename:join(PrivDir, Base),
{beam_file,Mod,_,_,_,Code} = beam_disasm:file(BeamFile),
case Base of
"no_"++_ ->
diff --git a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_14.S b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_14.S
new file mode 100644
index 0000000000..fd14228135
--- /dev/null
+++ b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_14.S
@@ -0,0 +1,71 @@
+{module, yes_14}. %% version = 0
+
+{exports, [{f,2},{module_info,0},{module_info,1},{yes_14,0}]}.
+
+{attributes, []}.
+
+{labels, 12}.
+
+
+{function, yes_14, 0, 2}.
+ {label,1}.
+ {func_info,{atom,yes_14},{atom,yes_14},0}.
+ {label,2}.
+ {move,{atom,ok},{x,0}}.
+ return.
+
+
+{function, f, 2, 4}.
+ {label,3}.
+ {func_info,{atom,yes_14},{atom,f},2}.
+ {label,4}.
+ {allocate_heap,2,3,2}.
+ {move,{x,0},{y,1}}.
+ {put_tuple,2,{y,0}}.
+ {put,{atom,data}}.
+ {put,{x,1}}.
+ {call_ext,0,{extfunc,erlang,make_ref,0}}. % Ref in [x0]
+ {test_heap,4,1}.
+ {put_tuple,3,{x,1}}.
+ {put,{atom,request}}.
+ {put,{x,0}}.
+ {put,{y,0}}.
+ {move,{x,0},{y,0}}. % Ref in [x0,y0]
+ {move,{y,1},{x,0}}. % Ref in [y0]
+ {kill,{y,1}}.
+ send.
+ {move,{y,0},{x,0}}. % Ref in [x0,y0]
+ {move,{x,0},{y,1}}. % Ref in [x0,y0,y1]
+ {label,5}.
+ {loop_rec,{f,7},{x,0}}. % Ref in [y0,y1]
+ {test,is_tuple,{f,6},[{x,0}]}.
+ {test,test_arity,{f,6},[{x,0},2]}.
+ {get_tuple_element,{x,0},0,{x,1}}.
+ {get_tuple_element,{x,0},1,{x,2}}.
+ {test,is_eq_exact,{f,6},[{x,1},{atom,reply}]}.
+ {test,is_eq_exact,{f,6},[{x,2},{y,1}]}.
+ remove_message.
+ {move,{atom,ok},{x,0}}.
+ {deallocate,2}.
+ return.
+ {label,6}.
+ {loop_rec_end,{f,5}}.
+ {label,7}.
+ {wait,{f,5}}.
+
+
+{function, module_info, 0, 9}.
+ {label,8}.
+ {func_info,{atom,yes_14},{atom,module_info},0}.
+ {label,9}.
+ {move,{atom,yes_14},{x,0}}.
+ {call_ext_only,1,{extfunc,erlang,get_module_info,1}}.
+
+
+{function, module_info, 1, 11}.
+ {label,10}.
+ {func_info,{atom,yes_14},{atom,module_info},1}.
+ {label,11}.
+ {move,{x,0},{x,1}}.
+ {move,{atom,yes_14},{x,0}}.
+ {call_ext_only,2,{extfunc,erlang,get_module_info,2}}.
diff --git a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_5.erl b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_5.erl
index 3f02fba6a6..5070e3e546 100644
--- a/lib/compiler/test/receive_SUITE_data/ref_opt/yes_5.erl
+++ b/lib/compiler/test/receive_SUITE_data/ref_opt/yes_5.erl
@@ -24,11 +24,7 @@ do_call(Process, Label, Request, Timeout) ->
{'DOWN', Mref, _, _, Reason} ->
exit(Reason)
after Timeout ->
- erlang:demonitor(Mref),
- receive
- {'DOWN', Mref, _, _, _} -> true
- after 0 -> true
- end,
+ erlang:demonitor(Mref, [flush]),
exit(timeout)
end
catch
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 29fc885152..7eca4557d9 100755
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -1354,7 +1354,7 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
<p>
Computes the SRP session key (shared secret) for the client side (first argument is DerivedKey)
or for the server side (first argument is Verifier). Also used
- as premaster secret by TLS-SRP ciher suites.
+ as premaster secret by TLS-SRP cipher suites.
</p>
</desc>
</func>
diff --git a/lib/debugger/src/dbg_debugged.erl b/lib/debugger/src/dbg_debugged.erl
index c21ad486e8..d8285d7288 100644
--- a/lib/debugger/src/dbg_debugged.erl
+++ b/lib/debugger/src/dbg_debugged.erl
@@ -19,8 +19,6 @@
-module(dbg_debugged).
%% External exports
-%% Avoid warning for local function demonitor/1 clashing with autoimported BIF.
--compile({no_auto_import,[demonitor/1]}).
-export([eval/3]).
%%====================================================================
@@ -47,7 +45,7 @@ msg_loop(Meta, Mref, SaveStacktrace) ->
%% Evaluated function has returned a value
{sys, Meta, {ready, Val}} ->
- demonitor(Mref),
+ erlang:demonitor(Mref, [flush]),
%% Restore original stacktrace and return the value
try erlang:raise(throw, stack, SaveStacktrace)
@@ -63,7 +61,7 @@ msg_loop(Meta, Mref, SaveStacktrace) ->
%% Evaluated function raised an (uncaught) exception
{sys, Meta, {exception,{Class,Reason,Stacktrace}}} ->
- demonitor(Mref),
+ erlang:demonitor(Mref, [flush]),
%% ...raise the same exception
erlang:error(erlang:raise(Class, Reason, Stacktrace),
@@ -107,14 +105,6 @@ reply({eval,Expr,Bs}) ->
%% Bindings is an orddict (sort them)
erl_eval:expr(Expr, lists:sort(Bs)). % {value, Value, Bs2}
-%% Demonitor and delete message from inbox
-%%
-demonitor(Mref) ->
- erlang:demonitor(Mref),
- receive {'DOWN',Mref,_,_,_} -> ok
- after 0 -> ok
- end.
-
%% Fix stacktrace - keep all above call to this module.
%%
stacktrace_f([]) -> [];
diff --git a/lib/debugger/src/dbg_istk.erl b/lib/debugger/src/dbg_istk.erl
index c6922a80e4..ced42a5f9f 100644
--- a/lib/debugger/src/dbg_istk.erl
+++ b/lib/debugger/src/dbg_istk.erl
@@ -78,7 +78,7 @@ push(Bs, #ieval{level=Le,module=Mod,function=Name,
pop() ->
case get(trace_stack) of
false -> ignore;
- _ -> % all ¦ no_tail
+ _ -> % all | no_tail
case get(?STACK) of
[_Entry|Entries] ->
put(?STACK, Entries);
diff --git a/lib/dialyzer/src/dialyzer_gui.erl b/lib/dialyzer/src/dialyzer_gui.erl
index ac9844c22c..97e5752577 100644
--- a/lib/dialyzer/src/dialyzer_gui.erl
+++ b/lib/dialyzer/src/dialyzer_gui.erl
@@ -2,7 +2,7 @@
%%------------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2006-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
@@ -1331,7 +1331,8 @@ build_analysis_record(#gui_state{mode = Mode, menu = Menu, options = Options,
#analysis{defines = Options#options.defines,
include_dirs = Options#options.include_dirs,
plt = InitPlt,
- start_from = StartFrom}.
+ start_from = StartFrom,
+ solvers = Options#options.solvers}.
get_anal_files(#gui_state{chosen_box = ChosenBox}, StartFrom) ->
Files = gs:read(ChosenBox, items),
diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl
index c6f7c56227..08f31c1e13 100644
--- a/lib/dialyzer/src/dialyzer_gui_wx.erl
+++ b/lib/dialyzer/src/dialyzer_gui_wx.erl
@@ -2,7 +2,7 @@
%%------------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2009-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
@@ -810,7 +810,8 @@ build_analysis_record(#gui_state{mode = Mode, menu = Menu, options = Options,
#analysis{defines = Options#options.defines,
include_dirs = Options#options.include_dirs,
plt = InitPlt,
- start_from = StartFrom}.
+ start_from = StartFrom,
+ solvers = Options#options.solvers}.
get_anal_files(#gui_state{files_to_analyze = Files}, StartFrom) ->
FilteredMods =
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl
index 8ec84d798f..7eb4c6ec97 100644
--- a/lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl
@@ -1,3 +1,5 @@
+%%% -*- coding: utf-8 -*-
+%%%
%%% Dialyzer was giving a warning with this input because of a bug in the
%%% substitution of remote types in specs. Remote types in the first element of
%%% a tuple would not update the tuple's tag set and we could end up with a
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index 7ea93d480b..318c98f786 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -343,8 +343,9 @@ Has one of the following types.</p>
An address list is available to the start function of a
&transport_module;, which
can return a new list for use in the subsequent CER or CEA.
-Host-IP-Address need not be specified if the transport start function
-returns an address list.</p>
+Host-IP-Address need not be specified if the transport module in
+question communicates an address list as described in
+&man_transport;</p>
</item>
<tag><c>{'Vendor-Id', &dict_Unsigned32;}</c></tag>
@@ -780,10 +781,10 @@ connections to the same peer.</p>
<p>
If type <c>[node()]</c> then a connection is rejected if another already
exists on any of the specified nodes.
-Values of type <c>false</c>, <c>node</c>, <c>nodes</c> or
+Types <c>false</c>, <c>node</c>, <c>nodes</c> and
&evaluable; are equivalent to
-values <c>[]</c>, <c>[node()]</c>, <c>[node()|nodes()]</c> and the
-evaluated value, respectively, evaluation of each expression taking
+<c>[]</c>, <c>[node()]</c>, <c>[node()|nodes()]</c> and the
+evaluated value respectively, evaluation of each expression taking
place whenever a new connection is to be established.
Note that <c>false</c> allows an unlimited number of connections to be
established with the same peer.</p>
diff --git a/lib/diameter/doc/src/diameter_tcp.xml b/lib/diameter/doc/src/diameter_tcp.xml
index 01c781d553..8e509aa829 100644
--- a/lib/diameter/doc/src/diameter_tcp.xml
+++ b/lib/diameter/doc/src/diameter_tcp.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="latin1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd" [
+ <!ENTITY start '<seealso marker="#start-3">start/3</seealso>'>
<!ENTITY gen_tcp_connect3
'<seealso marker="kernel:gen_tcp#connect-3">gen_tcp:connect/3</seealso>'>
<!ENTITY gen_tcp_listen2
@@ -81,7 +82,9 @@ before configuring TLS capability on diameter transports.</p>
<func>
<name>start({Type, Ref}, Svc, [Opt])
- -> {ok, Pid, [LAddr]} | {error, Reason}</name>
+ -> {ok, Pid}
+ | {ok, Pid, [LAddr]}
+ | {error, Reason}</name>
<fsummary>Start a transport process.</fsummary>
<type>
<v>Type = connect | accept</v>
@@ -153,13 +156,14 @@ that will not be forthcoming, which will eventually cause the RFC 3539
watchdog to take down the connection.</p>
<p>
-If the <c>#diameter_service{}</c> record has more than one
-<c>Host-IP-Address</c> and option <c>ip</c> is unspecified then the
-first of the these addresses is used as the local address.</p>
-
-<p>
-The returned local address list has length one.</p>
-
+If an <c>ip</c> option is not specified then the first element of a
+non-empty <c>Host-IP-Address</c> list in <c>Svc</c> provides the local
+IP address.
+If neither is specified then the default address selected by &gen_tcp;
+is used.
+In all cases, the selected address is either returned from
+&start; or passed in a <c>connected</c> message over the transport
+interface.</p>
</desc>
</func>
diff --git a/lib/diameter/doc/src/diameter_transport.xml b/lib/diameter/doc/src/diameter_transport.xml
index 55b531155f..8bccf6521e 100644
--- a/lib/diameter/doc/src/diameter_transport.xml
+++ b/lib/diameter/doc/src/diameter_transport.xml
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="latin1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd" [
<!ENTITY message '<seealso marker="#message">message()</seealso>'>
+ <!ENTITY MESSAGES '<seealso marker="#MESSAGES">MESSAGES</seealso>'>
+ <!ENTITY start '<seealso marker="#Mod:start-3">start/3</seealso>'>
<!ENTITY ip_address
'<seealso marker="kernel:inet#type-ip_address">inet:ip_address()</seealso>'>
<!ENTITY % also SYSTEM "seealso.ent" >
@@ -12,7 +14,7 @@
<erlref>
<header>
<copyright>
-<year>2011</year><year>2012</year>
+<year>2011</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -125,7 +127,7 @@ Ref is the value that was returned from the call to &mod_add_transport;
that has lead to starting of a transport process.</p>
<p>
-<c>Svc</c> contains the capabilities passed to &mod_start_service; and
+<c>Svc</c> contains capabilities passed to &mod_start_service; and
&mod_add_transport;, values passed to the latter overriding those
passed to the former.</p>
@@ -134,13 +136,16 @@ passed to the former.</p>
&mod_transport_opt; list passed to &mod_add_transport;.</p>
<p>
-The start function should use the <c>Host-IP-Address</c> list and/or
-<c>Config</c> to select an appropriate list of local IP addresses,
-and should return this list if different from the
-<c>#diameter_service{}</c> addresses.
-The returned list is used to populate <c>Host-IP-Address</c> AVPs in
-outgoing capabilities exchange messages, the
-<c>#diameter_service{}</c> addresses being used otherwise.</p>
+The start function should use the <c>Host-IP-Address</c> list in
+<c>Svc</c> and/or <c>Config</c> to select an appropriate list of local
+IP addresses, and should return this list if different from the
+<c>Svc</c> addresses.
+In the connecting case, the local address list can instead be
+communicated in a <c>connected</c> message (see &MESSAGES; below)
+following connection establishment.
+In either case, the local address list is used to populate
+<c>Host-IP-Address</c> AVPs in outgoing capabilities exchange
+messages.</p>
<p>
A transport process must implement the message interface documented below.
@@ -230,13 +235,16 @@ Not sent if the transport process has <c>Type=connect</c>.</p>
</item>
<tag><c>{diameter, {self(), connected, Remote}}</c></tag>
+<tag><c>{diameter, {self(), connected, Remote, [LocalAddr]}}</c></tag>
<item>
<p>
Inform the parent that the transport process with <c>Type=connect</c>
has established a connection with a peer.
-Not sent if the transport process has <c>Type=accept</c>.
+Not sent if the transport process has <c>Type=accept</c>.&nbsp;
<c>Remote</c> is an arbitrary term that uniquely identifies the remote
-endpoint to which the transport has connected.</p>
+endpoint to which the transport has connected.
+A <c>LocalAddr</c> list has the same semantics as one returned from
+&start;.</p>
</item>
<tag><c>{diameter, {recv, &message;}}</c></tag>
diff --git a/lib/diameter/examples/code/peer.erl b/lib/diameter/examples/code/peer.erl
index 8fdeba57bf..b4ee17e4b7 100644
--- a/lib/diameter/examples/code/peer.erl
+++ b/lib/diameter/examples/code/peer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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
@@ -51,7 +51,6 @@
| {protocol(), ip_address(), non_neg_integer()}
| {protocol(), ip_address(), ip_address(), non_neg_integer()}.
--define(DEFAULT_ADDR, {127,0,0,1}).
-define(DEFAULT_PORT, 3868).
%% ---------------------------------------------------------------------------
@@ -111,7 +110,7 @@ server({T, Addr, Port}) ->
{port, Port}]}];
server(T) ->
- server({T, ?DEFAULT_ADDR, ?DEFAULT_PORT}).
+ server({T, loopback, ?DEFAULT_PORT}).
%% client/1
%%
@@ -124,21 +123,28 @@ client({all, LA, RA, RP}) ->
client({T, LA, RA, RP}) ->
[{transport_module, tmod(T)},
- {transport_config, [{ip, addr(LA)},
- {raddr, addr(RA)},
+ {transport_config, [{raddr, addr(RA)},
{rport, RP},
- {reuseaddr, true}]}];
+ {reuseaddr, true}
+ | ip(LA)]}];
-client({T, LA, RP}) ->
- client({T, LA, LA, RP});
+client({T, RA, RP}) ->
+ client({T, default, RA, RP});
client(T) ->
- client({T, ?DEFAULT_ADDR, ?DEFAULT_ADDR, ?DEFAULT_PORT}).
+ client({T, loopback, loopback, ?DEFAULT_PORT}).
tmod(tcp) -> diameter_tcp;
tmod(sctp) -> diameter_sctp.
-addr(default) ->
- ?DEFAULT_ADDR;
+ip(default) ->
+ [];
+ip(loopback) ->
+ [{ip, {127,0,0,1}}];
+ip(Addr) ->
+ [{ip, Addr}].
+
+addr(loopback) ->
+ {127,0,0,1};
addr(A) ->
A.
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index df10c33268..c0cf418f06 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -230,7 +230,7 @@ include $(ERL_TOP)/make/otp_release_targets.mk
# Can't $(INSTALL_DIR) more than one directory at a time on Solaris.
release_spec: opt
- for d in bin ebin include src/dict; do \
+ for d in bin ebin examples include src/dict; do \
$(INSTALL_DIR) "$(RELSYSDIR)/$$d"; \
done
$(INSTALL_SCRIPT) $(BINS:%=../bin/%) "$(RELSYSDIR)/bin"
diff --git a/lib/diameter/src/base/diameter_peer.erl b/lib/diameter/src/base/diameter_peer.erl
index dfc76eb76e..0d2efd4d1f 100644
--- a/lib/diameter/src/base/diameter_peer.erl
+++ b/lib/diameter/src/base/diameter_peer.erl
@@ -24,7 +24,8 @@
%% Interface towards transport modules ...
-export([recv/2,
up/1,
- up/2]).
+ up/2,
+ up/3]).
%% ... and the stack.
-export([start/1,
@@ -180,7 +181,7 @@ start(Mod, Args) ->
apply(Mod, start, Args).
%%% ---------------------------------------------------------------------------
-%%% # up/[12]
+%%% # up/1-3
%%% ---------------------------------------------------------------------------
up(Pid) -> %% accepting transport
@@ -189,6 +190,9 @@ up(Pid) -> %% accepting transport
up(Pid, Remote) -> %% connecting transport
ifc_send(Pid, {self(), connected, Remote}).
+up(Pid, Remote, LAddrs) -> %% connecting transport
+ ifc_send(Pid, {self(), connected, Remote, LAddrs}).
+
%%% ---------------------------------------------------------------------------
%%% # recv/2
%%% ---------------------------------------------------------------------------
diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl
index bee3e507fd..6be4259510 100644
--- a/lib/diameter/src/base/diameter_peer_fsm.erl
+++ b/lib/diameter/src/base/diameter_peer_fsm.erl
@@ -351,10 +351,17 @@ transition({diameter, {TPid, connected, Remote}},
mode = M}
= S) ->
{'Wait-Conn-Ack', _} = PS, %% assert
- connect = M, %%
+ connect = M, %%
keep_transport(TPid),
send_CER(S#state{mode = {M, Remote}});
+transition({diameter, {TPid, connected, Remote, LAddrs}},
+ #state{transport = TPid,
+ service = Svc}
+ = S) ->
+ transition({diameter, {TPid, connected, Remote}},
+ S#state{service = readdr(Svc, LAddrs)});
+
%% Connection from peer.
transition({diameter, {TPid, connected}},
#state{transport = TPid,
@@ -363,7 +370,7 @@ transition({diameter, {TPid, connected}},
parent = Pid}
= S) ->
{'Wait-Conn-Ack', Tmo} = PS, %% assert
- accept = M, %%
+ accept = M, %%
keep_transport(TPid),
Pid ! {accepted, self()},
start_timer(Tmo, S#state{state = recv_CER});
@@ -376,6 +383,8 @@ transition({diameter, {_, connected}}, _) ->
{stop, connection_timeout};
transition({diameter, {_, connected, _}}, _) ->
{stop, connection_timeout};
+transition({diameter, {_, connected, _, _}}, _) ->
+ {stop, connection_timeout};
%% Connection has timed out: start an alternate.
transition({connection_timeout = T, TPid},
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index e4d1c60727..112e83476d 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -861,17 +861,21 @@ watchdog(TPid, [], ?WD_SUSPECT, ?WD_OKAY, Wd, State) ->
%% Watchdog has an unresponsive connection.
watchdog(TPid, [], ?WD_OKAY, ?WD_SUSPECT = To, Wd, State) ->
#watchdog{peer = TPid} = Wd, %% assert
- connection_down(Wd, To, State);
+ watchdog_down(Wd, To, State);
%% Watchdog has lost its connection.
watchdog(TPid, [], _, ?WD_DOWN = To, Wd, #state{peerT = PeerT} = S) ->
close(Wd, S),
- connection_down(Wd, To, S),
+ watchdog_down(Wd, To, S),
ets:delete(PeerT, TPid);
watchdog(_, [], _, _, _, _) ->
ok.
+watchdog_down(Wd, To, #state{watchdogT = WatchdogT} = S) ->
+ insert(WatchdogT, Wd#watchdog{state = To}),
+ connection_down(Wd, To, S).
+
%% ---------------------------------------------------------------------------
%% # connection_up/3
%% ---------------------------------------------------------------------------
@@ -1029,21 +1033,17 @@ connection_down(#watchdog{state = ?WD_OKAY,
remove_local_peer(SApps, {{TPid, Caps}, {SvcName, Apps}}, LDict),
diameter_traffic:peer_down(TPid);
-connection_down(#watchdog{}, #peer{}, _) ->
- ok;
-
-connection_down(#watchdog{state = WS,
+connection_down(#watchdog{state = ?WD_OKAY,
peer = TPid}
= Wd,
To,
- #state{watchdogT = WatchdogT,
- peerT = PeerT}
+ #state{peerT = PeerT}
= S)
when is_atom(To) ->
- insert(WatchdogT, Wd#watchdog{state = To}),
- ?WD_OKAY == WS
- andalso
- connection_down(Wd, fetch(PeerT, TPid), S).
+ connection_down(Wd, #peer{} = fetch(PeerT, TPid), S);
+
+connection_down(#watchdog{}, _, _) ->
+ ok.
remove_local_peer(SApps, T, LDict) ->
lists:foldl(fun(A,D) -> rlp(A, T, D) end, LDict, SApps).
diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl
index 132088b514..cbbba714ac 100644
--- a/lib/diameter/src/transport/diameter_tcp.erl
+++ b/lib/diameter/src/transport/diameter_tcp.erl
@@ -100,6 +100,18 @@
%% # start/3
%% ---------------------------------------------------------------------------
+-spec start({accept, Ref}, Svc, [Opt])
+ -> {ok, pid(), [inet:ip_address()]}
+ when Ref :: diameter:transport_ref(),
+ Svc :: #diameter_service{},
+ Opt :: diameter:transport_opt();
+ ({connect, Ref}, Svc, [Opt])
+ -> {ok, pid(), [inet:ip_address()]}
+ | {ok, pid()}
+ when Ref :: diameter:transport_ref(),
+ Svc :: #diameter_service{},
+ Opt :: diameter:transport_opt().
+
start({T, Ref}, #diameter_service{capabilities = Caps}, Opts) ->
diameter_tcp_sup:start(), %% start tcp supervisors on demand
{Mod, Rest} = split(Opts),
@@ -172,7 +184,7 @@ i({T, Ref, Mod, Pid, Opts, Addrs})
OwnOpts,
?DEFAULT_FRAGMENT_TIMEOUT),
?IS_TIMEOUT(Tmo) orelse ?ERROR({fragment_timer, Tmo}),
- Sock = i(T, Ref, Mod, Pid, SslOpts, Rest, Addrs),
+ Sock = init(T, Ref, Mod, Pid, SslOpts, Rest, Addrs),
MPid ! {stop, self()}, %% tell the monitor to die
M = if SslOpts -> ssl; true -> Mod end,
setopts(M, Sock),
@@ -199,14 +211,21 @@ i(#monitor{parent = Pid, transport = TPid} = S) ->
i({listen, LRef, APid, {Mod, Opts, Addrs}}) ->
{[LA, LP], Rest} = proplists:split(Opts, [ip, port]),
- LAddr = get_addr(LA, Addrs),
+ LAddrOpt = get_addr(LA, Addrs),
LPort = get_port(LP),
- {ok, LSock} = Mod:listen(LPort, gen_opts(LAddr, Rest)),
+ {ok, LSock} = Mod:listen(LPort, gen_opts(LAddrOpt, Rest)),
+ LAddr = laddr(LAddrOpt, Mod, LSock),
true = diameter_reg:add_new({?MODULE, listener, {LRef, {LAddr, LSock}}}),
proc_lib:init_ack({ok, self(), {LAddr, LSock}}),
erlang:monitor(process, APid),
start_timer(#listener{socket = LSock}).
+laddr([], Mod, Sock) ->
+ {ok, {Addr, _Port}} = sockname(Mod, Sock),
+ Addr;
+laddr([{ip, Addr}], _, _) ->
+ Addr.
+
own(Opts) ->
{Own, Rest} = proplists:split(Opts, [fragment_timer]),
{lists:append(Own), Rest}.
@@ -225,17 +244,19 @@ ssl_opts([{ssl_options, Opts}])
ssl_opts(L) ->
?ERROR({ssl_options, L}).
-%% i/7
+%% init/7
%% Establish a TLS connection before capabilities exchange ...
-i(Type, Ref, Mod, Pid, true, Opts, Addrs) ->
- i(Type, Ref, ssl, Pid, [{cb_info, ?TCP_CB(Mod)} | Opts], Addrs);
+init(Type, Ref, Mod, Pid, true, Opts, Addrs) ->
+ init(Type, Ref, ssl, Pid, [{cb_info, ?TCP_CB(Mod)} | Opts], Addrs);
%% ... or not.
-i(Type, Ref, Mod, Pid, _, Opts, Addrs) ->
- i(Type, Ref, Mod, Pid, Opts, Addrs).
+init(Type, Ref, Mod, Pid, _, Opts, Addrs) ->
+ init(Type, Ref, Mod, Pid, Opts, Addrs).
+
+%% init/6
-i(accept = T, Ref, Mod, Pid, Opts, Addrs) ->
+init(accept = T, Ref, Mod, Pid, Opts, Addrs) ->
{LAddr, LSock} = listener(Ref, {Mod, Opts, Addrs}),
proc_lib:init_ack({ok, self(), [LAddr]}),
Sock = ok(accept(Mod, LSock)),
@@ -243,17 +264,28 @@ i(accept = T, Ref, Mod, Pid, Opts, Addrs) ->
diameter_peer:up(Pid),
Sock;
-i(connect = T, Ref, Mod, Pid, Opts, Addrs) ->
+init(connect = T, Ref, Mod, Pid, Opts, Addrs) ->
{[LA, RA, RP], Rest} = proplists:split(Opts, [ip, raddr, rport]),
- LAddr = get_addr(LA, Addrs),
- RAddr = get_addr(RA, []),
+ LAddrOpt = get_addr(LA, Addrs),
+ RAddr = get_addr(RA),
RPort = get_port(RP),
- proc_lib:init_ack({ok, self(), [LAddr]}),
- Sock = ok(connect(Mod, RAddr, RPort, gen_opts(LAddr, Rest))),
+ proc_lib:init_ack(init_rc(LAddrOpt)),
+ Sock = ok(connect(Mod, RAddr, RPort, gen_opts(LAddrOpt, Rest))),
publish(Mod, T, Ref, Sock),
- diameter_peer:up(Pid, {RAddr, RPort}),
+ up(Pid, {RAddr, RPort}, LAddrOpt, Mod, Sock),
Sock.
+init_rc([{ip, Addr}]) ->
+ {ok, self(), [Addr]};
+init_rc([]) ->
+ {ok, self()}.
+
+up(Pid, Remote, [{ip, _Addr}], _, _) ->
+ diameter_peer:up(Pid, Remote);
+up(Pid, Remote, [], Mod, Sock) ->
+ {Addr, _Port} = ok(sockname(Mod, Sock)),
+ diameter_peer:up(Pid, Remote, [Addr]).
+
publish(Mod, T, Ref, Sock) ->
true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}),
putr(?INFO_KEY, {Mod, Sock}). %% for info/1
@@ -281,10 +313,17 @@ l([], LRef, T) ->
{ok, _, AS} = diameter_tcp_sup:start_child({listen, LRef, self(), T}),
AS.
+%% get_addr/1
+
+get_addr(As) ->
+ diameter_lib:ipaddr(addr(As, [])).
+
%% get_addr/2
+get_addr([], []) ->
+ [];
get_addr(As, Def) ->
- diameter_lib:ipaddr(addr(As, Def)).
+ [{ip, diameter_lib:ipaddr(addr(As, Def))}].
%% Take the first address from the service if several are unspecified.
addr([], [Addr | _]) ->
@@ -305,14 +344,10 @@ get_port(Ps) ->
%% gen_opts/2
-gen_opts(LAddr, Opts) ->
+gen_opts(LAddrOpt, Opts) ->
{L,_} = proplists:split(Opts, [binary, packet, active]),
[[],[],[]] == L orelse ?ERROR({reserved_options, Opts}),
- [binary,
- {packet, 0},
- {active, once},
- {ip, LAddr}
- | Opts].
+ [binary, {packet, 0}, {active, once}] ++ LAddrOpt ++ Opts.
%% ---------------------------------------------------------------------------
%% # ports/1
diff --git a/lib/diameter/test/diameter_capx_SUITE.erl b/lib/diameter/test/diameter_capx_SUITE.erl
index 9e6619ecdd..8c9bb67e61 100644
--- a/lib/diameter/test/diameter_capx_SUITE.erl
+++ b/lib/diameter/test/diameter_capx_SUITE.erl
@@ -444,7 +444,7 @@ connect(Config, T, Opts) ->
{CRef, LRef}.
connect(LRef, Opts) ->
- [PortNr] = ?util:lport(tcp, LRef, 20),
+ [PortNr] = ?util:lport(tcp, LRef),
{ok, CRef} = diameter:add_transport(?CLIENT,
{connect, opts(PortNr, Opts)}),
CRef.
diff --git a/lib/diameter/test/diameter_event_SUITE.erl b/lib/diameter/test/diameter_event_SUITE.erl
index 18bdcb1f54..94b4967921 100644
--- a/lib/diameter/test/diameter_event_SUITE.erl
+++ b/lib/diameter/test/diameter_event_SUITE.erl
@@ -100,7 +100,7 @@ start_server(Config) ->
ok = diameter:start_service(?SERVER, ?SERVICE(?SERVER, [?DICT_COMMON])),
LRef = ?util:listen(?SERVER, tcp, [{capabilities_cb, fun capx_cb/2},
{capx_timeout, ?SERVER_CAPX_TMO}]),
- [PortNr] = ?util:lport(tcp, LRef, 20),
+ [PortNr] = ?util:lport(tcp, LRef),
?util:write_priv(Config, portnr, PortNr),
start = event(?SERVER).
diff --git a/lib/diameter/test/diameter_examples_SUITE.erl b/lib/diameter/test/diameter_examples_SUITE.erl
index 6d797f6911..585fc9d3b8 100644
--- a/lib/diameter/test/diameter_examples_SUITE.erl
+++ b/lib/diameter/test/diameter_examples_SUITE.erl
@@ -283,7 +283,7 @@ start(server) ->
ok = diameter:start(),
ok = server:start(),
{ok, Ref} = server:listen(tcp),
- [_] = ?util:lport(tcp, Ref, 20),
+ [_] = ?util:lport(tcp, Ref),
ok;
start(client) ->
diff --git a/lib/diameter/test/diameter_tls_SUITE.erl b/lib/diameter/test/diameter_tls_SUITE.erl
index 77194a0f56..5a79c63d36 100644
--- a/lib/diameter/test/diameter_tls_SUITE.erl
+++ b/lib/diameter/test/diameter_tls_SUITE.erl
@@ -343,7 +343,7 @@ join(Strs) ->
server(Host, {Caps, Opts}) ->
ok = diameter:start_service(Host, ?SERVICE(Host, ?DICT_COMMON)),
{ok, LRef} = diameter:add_transport(Host, ?LISTEN(Caps, Opts)),
- {LRef, hd([_] = ?util:lport(tcp, LRef, 20))}.
+ {LRef, hd([_] = ?util:lport(tcp, LRef))}.
sopts(?SERVER1, Dir) ->
{inband_security([?TLS]),
diff --git a/lib/diameter/test/diameter_transport_SUITE.erl b/lib/diameter/test/diameter_transport_SUITE.erl
index 893b7ba2f9..97f4cec11f 100644
--- a/lib/diameter/test/diameter_transport_SUITE.erl
+++ b/lib/diameter/test/diameter_transport_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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
@@ -36,6 +36,7 @@
tcp_connect/1,
sctp_accept/1,
sctp_connect/1,
+ reconnect/1, reconnect/0,
stop/1]).
-export([accept/1,
@@ -54,9 +55,6 @@
-define(RECV(Pat, Ret), receive Pat -> Ret end).
-define(RECV(Pat), ?RECV(Pat, now())).
-%% Or not.
--define(WAIT(Ms), receive after Ms -> now() end).
-
%% Sockets are opened on the loopback address.
-define(ADDR, {127,0,0,1}).
@@ -102,7 +100,8 @@ tc() ->
[tcp_accept,
tcp_connect,
sctp_accept,
- sctp_connect].
+ sctp_connect,
+ reconnect].
init_per_suite(Config) ->
[{sctp, have_sctp()} | Config].
@@ -165,6 +164,90 @@ connect(Prot) ->
[] = ?util:run([{?MODULE, [init, X, T]} || X <- [gen_accept, connect]]).
%% ===========================================================================
+%% reconnect/1
+%%
+%% Exercise reconnection behaviour: that a connecting transport
+%% doesn't try to establish a new connection until the old one is
+%% broken.
+
+reconnect() ->
+ [{timetrap, {minutes, 4}}].
+
+reconnect({listen, Ref}) ->
+ SvcName = make_ref(),
+ ok = start_service(SvcName),
+ LRef = ?util:listen(SvcName, tcp, [{watchdog_timer, 6000}]),
+ [_] = diameter_reg:wait({diameter_tcp, listener, {LRef, '_'}}),
+ true = diameter_reg:add_new({?MODULE, Ref, LRef}),
+
+ %% Wait for partner to request transport death: kill to force the
+ %% peer to reconnect.
+ TPid = abort(SvcName, LRef, Ref),
+
+ exit(TPid, kill),
+
+ abort(SvcName, LRef, Ref);
+
+reconnect({connect, Ref}) ->
+ SvcName = make_ref(),
+ true = diameter:subscribe(SvcName),
+ ok = start_service(SvcName),
+ [{{_, _, LRef}, Pid}] = diameter_reg:wait({?MODULE, Ref, '_'}),
+ CRef = ?util:connect(SvcName, tcp, LRef, [{reconnect_timer, 2000},
+ {watchdog_timer, 6000}]),
+
+ %% Tell partner to kill transport after seeing that there are no
+ %% reconnection attempts.
+ abort(SvcName, Pid, Ref),
+
+ %% Transport does down and is reestablished.
+ ?RECV(#diameter_event{service = SvcName, info = {down, CRef, _, _}}),
+ ?RECV(#diameter_event{service = SvcName, info = {reconnect, CRef, _}}),
+ ?RECV(#diameter_event{service = SvcName, info = {up, CRef, _, _, _}}),
+
+ %% Kill again.
+ abort(SvcName, Pid, Ref),
+
+ %% Wait for partner to die.
+ MRef = erlang:monitor(process, Pid),
+ ?RECV({'DOWN', MRef, process, _, _});
+
+reconnect(_) ->
+ Ref = make_ref(),
+ [] = ?util:run([{?MODULE, [reconnect, {T, Ref}]}
+ || T <- [listen, connect]]).
+
+start_service(SvcName) ->
+ OH = io_lib:format("~p-~p-~p", tuple_to_list(now())),
+ Opts = [{application, [{dictionary, diameter_gen_base_rfc6733},
+ {module, diameter_callback}]},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OH ++ ".org"},
+ {'Vendor-Id', 0},
+ {'Product-Name', "x"},
+ {'Auth-Application-Id', [0]}],
+ diameter:start_service(SvcName, Opts).
+
+abort(SvcName, Pid, Ref)
+ when is_pid(Pid) ->
+ receive
+ #diameter_event{service = SvcName, info = {reconnect, _, _}} = E ->
+ erlang:error(E)
+ after 45000 ->
+ ok
+ end,
+ Pid ! {abort, Ref};
+
+abort(SvcName, LRef, Ref)
+ when is_reference(LRef) ->
+ ?RECV({abort, Ref}),
+ [[{ref, LRef}, {type, listen}, {options, _}, {accept, [_,_] = Ts} | _]]
+ %% assert on two accepting
+ = diameter:service_info(SvcName, transport),
+ [TPid] = [P || [{watchdog, {_,_,okay}}, {peer, {P,_}} | _] <- Ts],
+ TPid.
+
+%% ===========================================================================
%% ===========================================================================
%% have_sctp/0
@@ -209,7 +292,7 @@ init(accept, {Prot, Ref}) ->
init(gen_connect, {Prot, Ref}) ->
%% Lookup the peer's listening socket.
- [PortNr] = ?util:lport(Prot, Ref, 20),
+ [PortNr] = ?util:lport(Prot, Ref),
%% Connect, send a message and receive it back.
{ok, Sock} = gen_connect(Prot, PortNr),
@@ -230,7 +313,8 @@ init(gen_accept, {Prot, Ref}) ->
init(connect, {Prot, Ref}) ->
%% Lookup the peer's listening socket.
- [{?TEST_LISTENER(_, PortNr), _}] = match(?TEST_LISTENER(Ref, '_')),
+ [{?TEST_LISTENER(_, PortNr), _}]
+ = diameter_reg:wait(?TEST_LISTENER(Ref, '_')),
%% Start a connecting transport and receive notification of
%% the connection.
@@ -246,18 +330,6 @@ init(connect, {Prot, Ref}) ->
MRef = erlang:monitor(process, TPid),
?RECV({'DOWN', MRef, process, _, _}).
-match(Pat) ->
- match(Pat, 20).
-
-match(Pat, T) ->
- L = diameter_reg:match(Pat),
- if [] /= L orelse 1 == T ->
- L;
- true ->
- ?WAIT(50),
- match(Pat, T-1)
- end.
-
bin(sctp, #diameter_packet{bin = Bin}) ->
Bin;
bin(tcp, Bin) ->
@@ -310,22 +382,18 @@ start_connect(tcp, T, Svc, Opts) ->
%% start_accept/2
%%
%% Start transports sequentially by having each wait for a message
-%% from a job in a queue before commencing. Only one transport with
-%% a pending accept is started at a time since diameter_sctp currently
-%% assumes (and diameter currently implements) this.
+%% from a job in a queue before commencing. Only one transport with a
+%% pending accept is started at a time since diameter_{tcp,sctp}
+%% currently assume (and diameter currently implements) this.
start_accept(Prot, Ref) ->
Pid = sync(accept, Ref),
-
- %% Configure the same port number for transports on the same
- %% reference.
- [PortNr | _] = ?util:lport(Prot, Ref) ++ [0],
{Mod, Opts} = tmod(Prot),
try
{ok, TPid, [?ADDR]} = Mod:start({accept, Ref},
?SVC([?ADDR]),
- [{port, PortNr} | Opts]),
+ [{port, 0} | Opts]),
?RECV(?TMSG({TPid, connected})),
TPid
after
diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl
index a9872f32e1..aa489fef5f 100644
--- a/lib/diameter/test/diameter_util.erl
+++ b/lib/diameter/test/diameter_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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
@@ -33,7 +33,6 @@
%% diameter-specific
-export([lport/2,
- lport/3,
listen/2, listen/3,
connect/3, connect/4,
disconnect/4,
@@ -251,27 +250,17 @@ path(Config, Name) ->
filename:join([Dir, Name]).
%% ---------------------------------------------------------------------------
-%% lport/2-3
+%% lport/2
%%
%% Lookup the port number of a tcp/sctp listening transport.
-lport(M, Ref) ->
- lport(M, Ref, 1).
+lport(M, {Node, Ref}) ->
+ rpc:call(Node, ?MODULE, lport, [M, Ref]);
-lport(M, {Node, Ref}, Tries) ->
- rpc:call(Node, ?MODULE, lport, [M, Ref, Tries]);
-
-lport(M, Ref, Tries) ->
- lp(tmod(M), Ref, Tries).
-
-lp(M, Ref, T) ->
- L = [N || {listen, N, _} <- M:ports(Ref)],
- if [] /= L orelse T =< 1 ->
- L;
- true ->
- receive after 50 -> ok end,
- lp(M, Ref, T-1)
- end.
+lport(Prot, Ref) ->
+ Mod = tmod(Prot),
+ [_] = diameter_reg:wait({'_', listener, {Ref, '_'}}),
+ [N || {listen, N, _} <- Mod:ports(Ref)].
%% ---------------------------------------------------------------------------
%% listen/2-3
@@ -297,7 +286,7 @@ connect(Client, Prot, LRef) ->
connect(Client, Prot, LRef, []).
connect(Client, Prot, LRef, Opts) ->
- [PortNr] = lport(Prot, LRef, 20),
+ [PortNr] = lport(Prot, LRef),
Client = diameter:service_info(Client, name), %% assert
true = diameter:subscribe(Client),
Ref = add_transport(Client, {connect, opts(Prot, PortNr) ++ Opts}),
diff --git a/lib/diameter/test/diameter_watchdog_SUITE.erl b/lib/diameter/test/diameter_watchdog_SUITE.erl
index 704bf110c7..b6e8730ec2 100644
--- a/lib/diameter/test/diameter_watchdog_SUITE.erl
+++ b/lib/diameter/test/diameter_watchdog_SUITE.erl
@@ -524,7 +524,7 @@ cfg(listen, _) ->
cfg(connect, Ref) ->
[{{_, _, SvcName}, _Pid}] = diameter_reg:wait({listen, Ref, '_'}),
[[{ref, LRef} | _]] = diameter:service_info(SvcName, transport),
- [LP] = ?util:lport(tcp, LRef, 20),
+ [LP] = ?util:lport(tcp, LRef),
[{raddr, ?ADDR}, {rport, LP}].
%% ===========================================================================
diff --git a/lib/et/src/et_collector.erl b/lib/et/src/et_collector.erl
index a63d15fb4c..ce8cf6d4e0 100644
--- a/lib/et/src/et_collector.erl
+++ b/lib/et/src/et_collector.erl
@@ -654,13 +654,8 @@ start_trace_client(CollectorPid, Type, FileName) when Type =:= file ->
Ref = erlang:monitor(process, Pid),
receive
WaitFor ->
- erlang:demonitor(Ref),
- receive
- {'DOWN', Ref, _, _, _} ->
- file_loaded
- after 0 ->
- file_loaded
- end;
+ erlang:demonitor(Ref, [flush]),
+ file_loaded;
{'DOWN', Ref, _, _, Reason} ->
exit(Reason)
end;
diff --git a/lib/inets/src/http_server/httpd_manager.erl b/lib/inets/src/http_server/httpd_manager.erl
index 672a70a394..c83d06a158 100644
--- a/lib/inets/src/http_server/httpd_manager.erl
+++ b/lib/inets/src/http_server/httpd_manager.erl
@@ -691,11 +691,11 @@ handle_unblock(S, FromA) ->
handle_unblock(S, _FromA, unblocked) ->
{ok,S};
handle_unblock(S, FromA, _AdminState) ->
- stop_block_tmr(S#state.blocking_tmr),
case S#state.blocking_tmr of
- {_Tmr,FromB,Ref} ->
+ {Tmr,FromB,Ref} ->
%% Another process is trying to unblock
%% Inform the blocker
+ stop_block_tmr(Tmr),
FromB ! {block_reply, {error,{unblocked,FromA}},Ref};
_ ->
ok
diff --git a/lib/kernel/src/application_master.erl b/lib/kernel/src/application_master.erl
index 679fefaed9..34a3efcaf2 100644
--- a/lib/kernel/src/application_master.erl
+++ b/lib/kernel/src/application_master.erl
@@ -76,13 +76,8 @@ call(AppMaster, Req) ->
{'DOWN', Ref, process, _, _Info} ->
ok;
{Tag, Res} ->
- erlang:demonitor(Ref),
- receive
- {'DOWN', Ref, process, _, _Info} ->
- Res
- after 0 ->
- Res
- end
+ erlang:demonitor(Ref, [flush]),
+ Res
end.
%%%-----------------------------------------------------------------
diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl
index 0c5af2857e..c238eff12f 100644
--- a/lib/kernel/src/disk_log.erl
+++ b/lib/kernel/src/disk_log.erl
@@ -1914,13 +1914,8 @@ multi_req(Msg, Pids) ->
{'DOWN', Ref, process, Pid, _Info} ->
Reply;
{disk_log, Pid, _Reply} ->
- erlang:demonitor(Ref),
- receive
- {'DOWN', Ref, process, Pid, _Reason} ->
- ok
- after 0 ->
- ok
- end
+ erlang:demonitor(Ref, [flush]),
+ ok
end
end, {error, nonode}, Refs).
@@ -1965,13 +1960,8 @@ monitor_request(Pid, Req) ->
{error, no_such_log};
{disk_log, Pid, Reply} when not is_tuple(Reply) orelse
element(2, Reply) =/= disk_log_stopped ->
- erlang:demonitor(Ref),
- receive
- {'DOWN', Ref, process, Pid, _Reason} ->
- Reply
- after 0 ->
- Reply
- end
+ erlang:demonitor(Ref, [flush]),
+ Reply
end.
req2(Pid, R) ->
diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl
index a4c56b346f..36289053eb 100644
--- a/lib/kernel/src/file.erl
+++ b/lib/kernel/src/file.erl
@@ -480,8 +480,7 @@ open(Item, Mode) ->
Reason :: posix() | badarg | terminated.
close(File) when is_pid(File) ->
- R = file_request(File, close),
- case wait_file_reply(File, R) of
+ case file_request(File, close) of
{error, terminated} ->
ok;
Other ->
@@ -503,8 +502,7 @@ close(_) ->
Reason :: posix() | badarg.
advise(File, Offset, Length, Advise) when is_pid(File) ->
- R = file_request(File, {advise, Offset, Length, Advise}),
- wait_file_reply(File, R);
+ file_request(File, {advise, Offset, Length, Advise});
advise(#file_descriptor{module = Module} = Handle, Offset, Length, Advise) ->
Module:advise(Handle, Offset, Length, Advise);
advise(_, _, _, _) ->
@@ -517,8 +515,7 @@ advise(_, _, _, _) ->
Length :: non_neg_integer().
allocate(File, Offset, Length) when is_pid(File) ->
- R = file_request(File, {allocate, Offset, Length}),
- wait_file_reply(File, R);
+ file_request(File, {allocate, Offset, Length});
allocate(#file_descriptor{module = Module} = Handle, Offset, Length) ->
Module:allocate(Handle, Offset, Length).
@@ -601,8 +598,7 @@ pread_int(_, _, _) ->
Reason :: posix() | badarg | terminated.
pread(File, At, Sz) when is_pid(File), is_integer(Sz), Sz >= 0 ->
- R = file_request(File, {pread, At, Sz}),
- wait_file_reply(File, R);
+ file_request(File, {pread, At, Sz});
pread(#file_descriptor{module = Module} = Handle, Offs, Sz)
when is_integer(Sz), Sz >= 0 ->
Module:pread(Handle, Offs, Sz);
@@ -658,8 +654,7 @@ pwrite_int(_, _, _) ->
Reason :: posix() | badarg | terminated.
pwrite(File, At, Bytes) when is_pid(File) ->
- R = file_request(File, {pwrite, At, Bytes}),
- wait_file_reply(File, R);
+ file_request(File, {pwrite, At, Bytes});
pwrite(#file_descriptor{module = Module} = Handle, Offs, Bytes) ->
Module:pwrite(Handle, Offs, Bytes);
pwrite(_, _, _) ->
@@ -670,8 +665,7 @@ pwrite(_, _, _) ->
Reason :: posix() | badarg | terminated.
datasync(File) when is_pid(File) ->
- R = file_request(File, datasync),
- wait_file_reply(File, R);
+ file_request(File, datasync);
datasync(#file_descriptor{module = Module} = Handle) ->
Module:datasync(Handle);
datasync(_) ->
@@ -682,8 +676,7 @@ datasync(_) ->
Reason :: posix() | badarg | terminated.
sync(File) when is_pid(File) ->
- R = file_request(File, sync),
- wait_file_reply(File, R);
+ file_request(File, sync);
sync(#file_descriptor{module = Module} = Handle) ->
Module:sync(Handle);
sync(_) ->
@@ -696,8 +689,7 @@ sync(_) ->
Reason :: posix() | badarg | terminated.
position(File, At) when is_pid(File) ->
- R = file_request(File, {position,At}),
- wait_file_reply(File, R);
+ file_request(File, {position,At});
position(#file_descriptor{module = Module} = Handle, At) ->
Module:position(Handle, At);
position(_, _) ->
@@ -708,8 +700,7 @@ position(_, _) ->
Reason :: posix() | badarg | terminated.
truncate(File) when is_pid(File) ->
- R = file_request(File, truncate),
- wait_file_reply(File, R);
+ file_request(File, truncate);
truncate(#file_descriptor{module = Module} = Handle) ->
Module:truncate(Handle);
truncate(_) ->
@@ -1497,25 +1488,19 @@ check_args([]) ->
ok.
%%-----------------------------------------------------------------
-%% Functions for communicating with a file io server.
+%% Function for communicating with a file io server.
%% The messages sent have the following formats:
%%
%% {file_request,From,ReplyAs,Request}
%% {file_reply,ReplyAs,Reply}
file_request(Io, Request) ->
- R = erlang:monitor(process, Io),
- Io ! {file_request,self(),Io,Request},
- R.
-
-wait_file_reply(From, Ref) ->
+ Ref = erlang:monitor(process, Io),
+ Io ! {file_request,self(),Ref,Request},
receive
- {file_reply,From,Reply} ->
- erlang:demonitor(Ref),
- receive {'DOWN', Ref, _, _, _} -> ok after 0 -> ok end,
- %% receive {'EXIT', From, _} -> ok after 0 -> ok end,
+ {file_reply,Ref,Reply} ->
+ erlang:demonitor(Ref, [flush]),
Reply;
{'DOWN', Ref, _, _, _} ->
- %% receive {'EXIT', From, _} -> ok after 0 -> ok end,
{error, terminated}
end.
diff --git a/lib/kernel/src/file_io_server.erl b/lib/kernel/src/file_io_server.erl
index fad2ed7fb3..07fb55f390 100644
--- a/lib/kernel/src/file_io_server.erl
+++ b/lib/kernel/src/file_io_server.erl
@@ -92,8 +92,7 @@ do_start(Spawn, Owner, FileName, ModeList) ->
Mref = erlang:monitor(process, Pid),
receive
{Ref, {error, _Reason} = Error} ->
- erlang:demonitor(Mref),
- receive {'DOWN', Mref, _, _, _} -> ok after 0 -> ok end,
+ erlang:demonitor(Mref, [flush]),
Error;
{Ref, ok} ->
erlang:demonitor(Mref),
diff --git a/lib/kernel/src/file_server.erl b/lib/kernel/src/file_server.erl
index 49ec6f96cc..d036dbb516 100644
--- a/lib/kernel/src/file_server.erl
+++ b/lib/kernel/src/file_server.erl
@@ -317,8 +317,7 @@ do_start_slave(start, Filer, Name) ->
SlaveMonitor = erlang:monitor(process, Slave),
receive
{started, Token} ->
- erlang:demonitor(SlaveMonitor),
- receive {'DOWN', SlaveMonitor, _, _, _} -> ok after 0 -> ok end,
+ erlang:demonitor(SlaveMonitor, [flush]),
{ok, Slave};
{'DOWN', SlaveMonitor, _, _, Reason} ->
exit(Reason)
diff --git a/lib/kernel/src/inet_gethost_native.erl b/lib/kernel/src/inet_gethost_native.erl
index db3e44ce6f..df866660b4 100644
--- a/lib/kernel/src/inet_gethost_native.erl
+++ b/lib/kernel/src/inet_gethost_native.erl
@@ -503,8 +503,7 @@ getit(Req, DefaultName) ->
Pid, Reason} ->
{error, Reason}
end,
- catch erlang:demonitor(Ref2),
- receive {'DOWN',Ref2,_,_,_} -> ok after 0 -> ok end,
+ catch erlang:demonitor(Ref2, [flush]),
Res2
end.
diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl
index 7c965ca384..83e0b59cc2 100644
--- a/lib/kernel/src/rpc.erl
+++ b/lib/kernel/src/rpc.erl
@@ -388,13 +388,8 @@ server_call(Node, Name, ReplyWrapper, Msg)
{'DOWN', Ref, _, _, _} ->
{error, nodedown};
{ReplyWrapper, Node, Reply} ->
- erlang:demonitor(Ref),
- receive
- {'DOWN', Ref, _, _, _} ->
- Reply
- after 0 ->
- Reply
- end
+ erlang:demonitor(Ref, [flush]),
+ Reply
end
end.
@@ -501,17 +496,6 @@ start_monitor(Node, Name) ->
{Node,erlang:monitor(process, {Name, Node})}
end.
-%% Cancels a monitor started with Ref=erlang:monitor(_, _),
-%% i.e return value {Node, Ref} from start_monitor/2 above.
-unmonitor(Ref) when is_reference(Ref) ->
- erlang:demonitor(Ref),
- receive
- {'DOWN', Ref, _, _, _} ->
- true
- after 0 ->
- true
- end.
-
%% Call apply(M,F,A) on all nodes in parallel
-spec multicall(Module, Function, Args) -> {ResL, BadNodes} when
@@ -635,10 +619,10 @@ rec_nodes(Name, [{N,R} | Tail], Badnodes, Replies) ->
rec_nodes(Name, Tail, [N|Badnodes], Replies);
{?NAME, N, {nonexisting_name, _}} ->
%% used by sbcast()
- unmonitor(R),
+ erlang:demonitor(R, [flush]),
rec_nodes(Name, Tail, [N|Badnodes], Replies);
{Name, N, Reply} -> %% Name is bound !!!
- unmonitor(R),
+ erlang:demonitor(R, [flush]),
rec_nodes(Name, Tail, Badnodes, [Reply|Replies])
end.
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index c604e7073f..4218cfa646 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -91,6 +91,8 @@
-export([standard_io/1,mini_server/1]).
+-export([old_io_protocol/1]).
+
%% Debug exports
-export([create_file_slow/2, create_file/2, create_bin/2]).
-export([verify_file/2, verify_bin/3]).
@@ -114,7 +116,7 @@ all() ->
delayed_write, read_ahead, segment_read, segment_write,
ipread, pid2name, interleaved_read_write, otp_5814, otp_10852,
large_file, large_write, read_line_1, read_line_2, read_line_3,
- read_line_4, standard_io].
+ read_line_4, standard_io, old_io_protocol].
groups() ->
[{dirs, [], [make_del_dir, cur_dir_0, cur_dir_1,
@@ -310,6 +312,31 @@ standard_io(Config) when is_list(Config) ->
Pid ! die,
receive after 1000 -> ok end.
+old_io_protocol(suite) ->
+ [];
+old_io_protocol(doc) ->
+ ["Test that the old file IO protocol =< R16B still works"];
+old_io_protocol(Config) when is_list(Config) ->
+ Dog = test_server:timetrap(test_server:seconds(5)),
+ RootDir = ?config(priv_dir,Config),
+ Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"old_io_protocol.fil"),
+ MyData = "0123456789abcdefghijklmnopqrstuvxyz",
+ ok = ?FILE_MODULE:write_file(Name, MyData),
+ {ok, Fd} = ?FILE_MODULE:open(Name, write),
+ Fd ! {file_request,self(),Fd,truncate},
+ receive
+ {file_reply,Fd,ok} -> ok
+ end,
+ ok = ?FILE_MODULE:close(Fd),
+ {ok, <<>>} = ?FILE_MODULE:read_file(Name),
+ test_server:timetrap_cancel(Dog),
+ [] = flush(),
+ ok.
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
read_write_file(suite) -> [];
diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index 2a886b2efc..c5d8becfd3 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -1399,8 +1399,7 @@ s_req(S, Req) ->
{'DOWN',Mref,_,_,Error} ->
exit(Error);
{S,Mref,Reply} ->
- erlang:demonitor(Mref),
- receive {'DOWN',Mref,_,_,_} -> ok after 0 -> ok end,
+ erlang:demonitor(Mref, [flush]),
Reply
end.
diff --git a/lib/kernel/test/global_SUITE_data/global_trace.erl b/lib/kernel/test/global_SUITE_data/global_trace.erl
index 4f253baac4..ddbe608c0a 100644
--- a/lib/kernel/test/global_SUITE_data/global_trace.erl
+++ b/lib/kernel/test/global_SUITE_data/global_trace.erl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
@@ -122,12 +123,12 @@ state(Else) ->
%%% {ops,Ops}]
%%% NewKnown = Known ++ AddedNodes
%%% AddedNodes = NewNodes -- Known
-%%% NewNodes �r h�r den man f�rhandlat med plus de noder den k�nner till.
+%%% NewNodes är här den man förhandlat med plus de noder den känner till.
%%% {added, AddedNodes}, Extra = [{ops,Ops}]
%%% NewKnown = Known ++ AddedNodes
-%%% Den (passiva) noden f�r Nodes som �r NewNodes
-%%% hos den f�rhandlande. Sedan: AddedNodes = (Nodes -- Known) -- [node()].
-%%% Det �r som hos f�rhandlaren.
+%%% Den (passiva) noden får Nodes som är NewNodes
+%%% hos den förhandlande. Sedan: AddedNodes = (Nodes -- Known) -- [node()].
+%%% Det är som hos förhandlaren.
%%% {nodes_changed, {New,Old}}
%%% Every now and then the list [node() | nodes()] is checked for updates.
%%% New are the nodes that global does not know of (yet).
diff --git a/lib/orber/src/orber_iiop_outproxy.erl b/lib/orber/src/orber_iiop_outproxy.erl
index 879af8222d..9c4e603753 100644
--- a/lib/orber/src/orber_iiop_outproxy.erl
+++ b/lib/orber/src/orber_iiop_outproxy.erl
@@ -69,13 +69,8 @@ request(Pid, true, Timeout, Msg, RequestId) ->
gen_server:cast(Pid, {request, Timeout, Msg, RequestId, self(), MRef}),
receive
{MRef, Reply} ->
- erlang:demonitor(MRef),
- receive
- {'DOWN', MRef, _, _, _} ->
- Reply
- after 0 ->
- Reply
- end;
+ erlang:demonitor(MRef, [flush]),
+ Reply;
{'DOWN', MRef, _, Pid, _Reason} when is_pid(Pid) ->
receive
%% Clear EXIT message from queue
@@ -444,13 +439,7 @@ collect_fragments(GIOPHdr1, InBuffer, Bytes, Proxy, RequestId, MRef) ->
%% the reply and send it to the client.
{fragment, #giop_message{byte_order = ByteOrder,
message = Message} = GIOPHdr2, RequestId, MRef} ->
- erlang:demonitor(MRef),
- receive
- {'DOWN', MRef, _, _, _} ->
- ok
- after 0 ->
- ok
- end,
+ erlang:demonitor(MRef, [flush]),
case catch cdr_decode:dec_message_header(null, GIOPHdr2, Message) of
{_, #fragment_header{}, FragBody, _, _} ->
%% This buffer is all the fragments concatenated.
@@ -484,13 +473,8 @@ Unable to decode Reply or LocateReply header",[?LINE, NewGIOP, Error], ?DEBUG_LE
{MRef, {'EXCEPTION', E}} ->
orber:dbg("[~p] orber_iiop:collect_fragments(~p);",
[?LINE, E], ?DEBUG_LEVEL),
- erlang:demonitor(MRef),
- receive
- {'DOWN', MRef, _, _, _} ->
- corba:raise(E)
- after 0 ->
- corba:raise(E)
- end;
+ erlang:demonitor(MRef, [flush]),
+ corba:raise(E);
{'DOWN', MRef, _, Proxy, Reason} when is_pid(Proxy) ->
orber:dbg("[~p] orber_iiop:collect_fragments(~p);~n"
"Monitor generated a DOWN message.",
@@ -511,13 +495,8 @@ clear_queue(Proxy, RequestId, MRef) ->
{MRef, RequestId, cancelled} ->
%% This is the last message that the proxy will send
%% after we've cancelled the request.
- erlang:demonitor(MRef),
- receive
- {'DOWN', MRef, _, _, _} ->
- ok
- after 0 ->
- ok
- end;
+ erlang:demonitor(MRef, [flush]),
+ ok;
{'DOWN', MRef, _, Proxy, _Reason} ->
%% The proxy terminated. Clear EXIT message from queue
receive
diff --git a/lib/public_key/test/erl_make_certs.erl b/lib/public_key/test/erl_make_certs.erl
index 95e288cd71..897cf2f350 100644
--- a/lib/public_key/test/erl_make_certs.erl
+++ b/lib/public_key/test/erl_make_certs.erl
@@ -354,13 +354,13 @@ gen_dsa2(LSize, NSize) ->
X0 = prime(LSize),
P0 = prime((LSize div 2) +1),
- %% Choose L-bit prime modulus P such that p–1 is a multiple of q.
+ %% Choose L-bit prime modulus P such that p-1 is a multiple of q.
case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of
error ->
gen_dsa2(LSize, NSize);
P ->
G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q.
- %% such that This may be done by setting g = h^(p–1)/q mod p, commonly h=2 is used.
+ %% such that This may be done by setting g = h^(p-1)/q mod p, commonly h=2 is used.
X = prime(20), %% Choose x by some random method, where 0 < x < q.
Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p.
diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl
index b1e1787f18..752037042d 100644
--- a/lib/reltool/test/reltool_server_SUITE.erl
+++ b/lib/reltool/test/reltool_server_SUITE.erl
@@ -2595,8 +2595,8 @@ start_node(Name, ErlPath, Args0) ->
%io:format("open_port({spawn_executable, ~p}, [{args,~p}])~n",[ErlPath,Args]),
case open_port({spawn_executable, ErlPath}, [{args,Args}]) of
Port when is_port(Port) ->
- unlink(Port),
- erlang:port_close(Port),
+ %% no need to close port since node is detached (see
+ %% mk_node_args) so port will be closed anyway.
case ping_node(FullName, 50) of
ok -> {ok, FullName};
Other -> exit({failed_to_start_node, FullName, Other})
@@ -2629,7 +2629,7 @@ mk_node_args(Name, Args) ->
end,
{ok, Pwd} = file:get_cwd(),
NameStr = atom_to_list(Name),
- ["-detached", "-noinput",
+ ["-detached",
NameSw, NameStr,
"-pa", Pa,
"-env", "ERL_CRASH_DUMP", Pwd ++ "/erl_crash_dump." ++ NameStr,
diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl
index 6e3bfe31c6..6b2fb0460f 100644
--- a/lib/runtime_tools/src/dbg.erl
+++ b/lib/runtime_tools/src/dbg.erl
@@ -551,8 +551,7 @@ c(M, F, A, Flags) ->
stop_clear(),
{error, Reason};
{Pid, Res} ->
- erlang:demonitor(Mref),
- receive {'DOWN', Mref, _, _, _} -> ok after 0 -> ok end,
+ erlang:demonitor(Mref, [flush]),
%% 'sleep' prevents the tracer (recv_all_traces) from
%% receiving garbage {'EXIT',...} when dbg i stopped.
timer:sleep(1),
@@ -592,8 +591,7 @@ req(R) ->
{'DOWN', Mref, _, _, _} -> % If server died
exit(dbg_server_crash);
{dbg, Reply} ->
- erlang:demonitor(Mref),
- receive {'DOWN', Mref, _, _, _} -> ok after 0 -> ok end,
+ erlang:demonitor(Mref, [flush]),
Reply
end.
diff --git a/lib/runtime_tools/src/ttb_autostart.erl b/lib/runtime_tools/src/ttb_autostart.erl
index 4c6971c119..5339507cec 100644
--- a/lib/runtime_tools/src/ttb_autostart.erl
+++ b/lib/runtime_tools/src/ttb_autostart.erl
@@ -1,3 +1,4 @@
+%%%-*- coding: utf-8 -*-
%%%-------------------------------------------------------------------
%%% File : ttb_autostart.erl
%%% Author : Bartłomiej Puzoń <[email protected]>
diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl
index 97ba70c9bd..a56924d5ca 100644
--- a/lib/sasl/test/release_handler_SUITE.erl
+++ b/lib/sasl/test/release_handler_SUITE.erl
@@ -1112,10 +1112,10 @@ otp_9395_update_many_mods(Conf) when is_list(Conf) ->
true = rpc:call(Node,erlang,check_old_code,[m10]),
%% Run check_install_release with purge before install this time
- {TCheck,{ok, _RelVsn1, []}} =
+ {_TCheck,{ok, _RelVsn1, []}} =
timer:tc(rpc,call,[Node, release_handler, check_install_release,
[RelVsn2,[purge]]]),
-% ct:log("check_install_release with purge: ~.2f",[TCheck/1000000]),
+% ct:log("check_install_release with purge: ~.2f",[_TCheck/1000000]),
%% Finally install release after check and purge, and check that
%% this install was faster than the first.
@@ -1209,10 +1209,10 @@ otp_9395_rm_many_mods(Conf) when is_list(Conf) ->
true = rpc:call(Node,erlang,check_old_code,[m10]),
%% Run check_install_release with purge before install this time
- {TCheck,{ok, _RelVsn1, []}} =
+ {_TCheck,{ok, _RelVsn1, []}} =
timer:tc(rpc,call,[Node, release_handler, check_install_release,
[RelVsn2,[purge]]]),
-% ct:log("check_install_release with purge: ~.2f",[TCheck/1000000]),
+% ct:log("check_install_release with purge: ~.2f",[_TCheck/1000000]),
%% Finally install release after check and purge, and check that
%% this install was faster than the first.
diff --git a/lib/snmp/src/agent/snmpa_target_cache.erl b/lib/snmp/src/agent/snmpa_target_cache.erl
index 2aa35aa46a..1fcaf82373 100644
--- a/lib/snmp/src/agent/snmpa_target_cache.erl
+++ b/lib/snmp/src/agent/snmpa_target_cache.erl
@@ -21,8 +21,6 @@
-behaviour(gen_server).
%% External exports
-%% Avoid warning for local function demonitor/1 clashing with autoimported BIF.
--compile({no_auto_import,[demonitor/1]}).
-export([start_link/2, stop/0, verbosity/1]).
-export([
@@ -213,21 +211,6 @@ do_init(Prio, Opts) ->
%% requests will have to wait.
%%
-monitor(Pid) -> erlang:monitor(process, Pid).
--ifdef(SNMP_R10).
-demonitor(Ref) ->
- erlang:demonitor(Ref),
- receive
- {_, Ref, _, _, _} ->
- true
- after 0 ->
- true
- end.
--else.
-demonitor(Ref) ->
- erlang:demonitor(Ref, [flush]).
--endif.
-
%% (1) No write_lock active or waiting
handle_call({lock, read = Type, infinity}, {Pid, _} = From,
@@ -236,7 +219,7 @@ handle_call({lock, read = Type, infinity}, {Pid, _} = From,
"entry when no waiting or active writer with"
"~n Pid: ~p"
"~n Cnt: ~p", [Pid, Cnt]),
- MonRef = monitor(Pid),
+ MonRef = erlang:monitor(process, Pid),
Locker = #locker{pid = Pid,
from = From,
mon_ref = MonRef,
@@ -252,7 +235,7 @@ handle_call({lock, read = Type, infinity}, {Pid, _} = From, State) ->
?vlog("lock(read, infinity) -> "
"entry when active or waiting write locks with"
"~n Pid: ~p", [Pid]),
- MonRef = monitor(Pid),
+ MonRef = erlang:monitor(process, Pid),
Locker = #locker{pid = Pid,
from = From,
mon_ref = MonRef,
@@ -273,7 +256,7 @@ handle_call({lock, write = Type, infinity}, {Pid, _} = From,
?vlog("lock(write, infinity) -> "
"entry when no active lockers with"
"~n Pid: ~p", [Pid]),
- MonRef = monitor(Pid),
+ MonRef = erlang:monitor(process, Pid),
Locker = #locker{pid = Pid,
from = From,
mon_ref = MonRef,
@@ -290,7 +273,7 @@ handle_call({lock, write = Type, infinity}, {Pid, _} = From,
?vlog("lock(write, infinity) -> "
"entry when active lockers with"
"~n Pid: ~p", [Pid]),
- MonRef = monitor(Pid),
+ MonRef = erlang:monitor(process, Pid),
Locker = #locker{pid = Pid,
from = From,
mon_ref = MonRef,
@@ -307,7 +290,7 @@ handle_call({lock, write = Type, infinity}, {Pid, _} = From,
#state{writer = true} = State) ->
?vlog("lock(write, infinity) -> entry with"
"~n Pid: ~p", [Pid]),
- MonRef = monitor(Pid),
+ MonRef = erlang:monitor(process, Pid),
Locker = #locker{pid = Pid,
from = From,
mon_ref = MonRef,
@@ -429,7 +412,7 @@ handle_cast({unlock, Pid},
[#locker{mon_ref = MonRef, type = read}] ->
?vdebug("unlock -> found read locker"
"~n MonRef: ~p", [MonRef]),
- demonitor(MonRef),
+ erlang:demonitor(MonRef, [flush]),
ets:delete(?LOCKER_TAB, Pid),
%% ?vtrace("unlock -> done when"
%% "~n Lockers: ~p", [ets:tab2list(?LOCKER_TAB)]),
@@ -437,7 +420,7 @@ handle_cast({unlock, Pid},
[#locker{mon_ref = MonRef, type = write}] ->
?vdebug("unlock -> found write locker"
"~n MonRef: ~p", [MonRef]),
- demonitor(MonRef),
+ erlang:demonitor(MonRef, [flush]),
ets:delete(?LOCKER_TAB, Pid),
%% ?vtrace("unlock -> done when"
%% "~n Lockers: ~p", [ets:tab2list(?LOCKER_TAB)]),
@@ -459,7 +442,7 @@ handle_cast({unlock, Pid},
[#locker{mon_ref = MonRef, type = read}] when (Cnt == 1) ->
?vdebug("unlock -> found read locker"
"~n MonRef: ~p", [MonRef]),
- demonitor(MonRef),
+ erlang:demonitor(MonRef, [flush]),
ets:delete(?LOCKER_TAB, Pid),
case active_waiting_writer(Waiting) of
{true, StillWaiting} ->
@@ -482,7 +465,7 @@ handle_cast({unlock, Pid},
[#locker{mon_ref = MonRef, type = read}] ->
?vdebug("unlock -> found read locker"
"~n MonRef: ~p", [MonRef]),
- demonitor(MonRef),
+ erlang:demonitor(MonRef, [flush]),
ets:delete(?LOCKER_TAB, Pid),
%% ?vtrace("unlock -> done when"
%% "~n Lockers: ~p", [ets:tab2list(?LOCKER_TAB)]),
@@ -492,7 +475,7 @@ handle_cast({unlock, Pid},
%% Release the hord (maybe)
?vdebug("unlock -> found write locker"
"~n MonRef: ~p", [MonRef]),
- demonitor(MonRef),
+ erlang:demonitor(MonRef, [flush]),
ets:delete(?LOCKER_TAB, Pid),
{Active, StillWaiting, Writer} =
activate_waiting_readers_or_maybe_writer(Waiting),
diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl
index 0531ad7830..69b1ab186f 100644
--- a/lib/ssh/src/ssh_cli.erl
+++ b/lib/ssh/src/ssh_cli.erl
@@ -230,11 +230,11 @@ io_request({window_change, OldTty}, Buf, Tty) ->
io_request({put_chars, Cs}, Buf, Tty) ->
put_chars(bin_to_list(Cs), Buf, Tty);
io_request({put_chars, unicode, Cs}, Buf, Tty) ->
- put_chars([Ch || Ch <- unicode:characters_to_list(Cs,unicode), Ch =< 255], Buf, Tty);
+ put_chars(unicode:characters_to_list(Cs,unicode), Buf, Tty);
io_request({insert_chars, Cs}, Buf, Tty) ->
insert_chars(bin_to_list(Cs), Buf, Tty);
io_request({insert_chars, unicode, Cs}, Buf, Tty) ->
- insert_chars([Ch || Ch <- unicode:characters_to_list(Cs,unicode), Ch =< 255], Buf, Tty);
+ insert_chars(unicode:characters_to_list(Cs,unicode), Buf, Tty);
io_request({move_rel, N}, Buf, Tty) ->
move_rel(N, Buf, Tty);
io_request({delete_chars,N}, Buf, Tty) ->
diff --git a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl
index 4dfd9ed8b0..93f9e20663 100644
--- a/lib/ssh/src/ssh_xfer.erl
+++ b/lib/ssh/src/ssh_xfer.erl
@@ -72,7 +72,7 @@ protocol_version_request(XF) ->
open(XF, ReqID, FileName, Access, Flags, Attrs) ->
Vsn = XF#ssh_xfer.vsn,
- FileName1 = list_to_binary(FileName),
+ FileName1 = unicode:characters_to_binary(FileName),
MBits = if Vsn >= 5 ->
M = encode_ace_mask(Access),
?uint32(M);
@@ -115,7 +115,7 @@ write(XF,ReqID, Handle, Offset, Data) ->
is_binary(Data) ->
Data;
is_list(Data) ->
- list_to_binary(Data)
+ unicode:characters_to_binary(Data)
end,
xf_request(XF,?SSH_FXP_WRITE,
[?uint32(ReqID),
@@ -132,8 +132,8 @@ remove(XF, ReqID, File) ->
%% Rename a file/directory
rename(XF, ReqID, Old, New, Flags) ->
Vsn = XF#ssh_xfer.vsn,
- OldPath = list_to_binary(Old),
- NewPath = list_to_binary(New),
+ OldPath = unicode:characters_to_binary(Old),
+ NewPath = unicode:characters_to_binary(New),
FlagBits
= if Vsn >= 5 ->
F0 = encode_rename_flags(Flags),
@@ -151,7 +151,7 @@ rename(XF, ReqID, Old, New, Flags) ->
%% Create directory
mkdir(XF, ReqID, Path, Attrs) ->
- Path1 = list_to_binary(Path),
+ Path1 = unicode:characters_to_binary(Path),
xf_request(XF, ?SSH_FXP_MKDIR,
[?uint32(ReqID),
?binary(Path1),
@@ -159,14 +159,14 @@ mkdir(XF, ReqID, Path, Attrs) ->
%% Remove a directory
rmdir(XF, ReqID, Dir) ->
- Dir1 = list_to_binary(Dir),
+ Dir1 = unicode:characters_to_binary(Dir),
xf_request(XF, ?SSH_FXP_RMDIR,
[?uint32(ReqID),
?binary(Dir1)]).
%% Stat file
stat(XF, ReqID, Path, Flags) ->
- Path1 = list_to_binary(Path),
+ Path1 = unicode:characters_to_binary(Path),
Vsn = XF#ssh_xfer.vsn,
AttrFlags = if Vsn >= 5 ->
F = encode_attr_flags(Vsn, Flags),
@@ -182,7 +182,7 @@ stat(XF, ReqID, Path, Flags) ->
%% Stat file - follow symbolic links
lstat(XF, ReqID, Path, Flags) ->
- Path1 = list_to_binary(Path),
+ Path1 = unicode:characters_to_binary(Path),
Vsn = XF#ssh_xfer.vsn,
AttrFlags = if Vsn >= 5 ->
F = encode_attr_flags(Vsn, Flags),
@@ -211,7 +211,7 @@ fstat(XF, ReqID, Handle, Flags) ->
%% Modify file attributes
setstat(XF, ReqID, Path, Attrs) ->
- Path1 = list_to_binary(Path),
+ Path1 = unicode:characters_to_binary(Path),
xf_request(XF, ?SSH_FXP_SETSTAT,
[?uint32(ReqID),
?binary(Path1),
@@ -227,7 +227,7 @@ fsetstat(XF, ReqID, Handle, Attrs) ->
%% Read a symbolic link
readlink(XF, ReqID, Path) ->
- Path1 = list_to_binary(Path),
+ Path1 = unicode:characters_to_binary(Path),
xf_request(XF, ?SSH_FXP_READLINK,
[?uint32(ReqID),
?binary(Path1)]).
@@ -235,8 +235,8 @@ readlink(XF, ReqID, Path) ->
%% Create a symbolic link
symlink(XF, ReqID, LinkPath, TargetPath) ->
- LinkPath1 = list_to_binary(LinkPath),
- TargetPath1 = list_to_binary(TargetPath),
+ LinkPath1 = unicode:characters_to_binary(LinkPath),
+ TargetPath1 = unicode:characters_to_binary(TargetPath),
xf_request(XF, ?SSH_FXP_SYMLINK,
[?uint32(ReqID),
?binary(LinkPath1),
@@ -244,7 +244,7 @@ symlink(XF, ReqID, LinkPath, TargetPath) ->
%% Convert a path into a 'canonical' form
realpath(XF, ReqID, Path) ->
- Path1 = list_to_binary(Path),
+ Path1 = unicode:characters_to_binary(Path),
xf_request(XF, ?SSH_FXP_REALPATH,
[?uint32(ReqID),
?binary(Path1)]).
diff --git a/lib/ssl/test/erl_make_certs.erl b/lib/ssl/test/erl_make_certs.erl
index 5b92e551a5..71aa985c4f 100644
--- a/lib/ssl/test/erl_make_certs.erl
+++ b/lib/ssl/test/erl_make_certs.erl
@@ -349,13 +349,13 @@ gen_dsa2(LSize, NSize) ->
X0 = prime(LSize),
P0 = prime((LSize div 2) +1),
- %% Choose L-bit prime modulus P such that p–1 is a multiple of q.
+ %% Choose L-bit prime modulus P such that p-1 is a multiple of q.
case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of
error ->
gen_dsa2(LSize, NSize);
P ->
G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q.
- %% such that This may be done by setting g = h^(p–1)/q mod p, commonly h=2 is used.
+ %% such that This may be done by setting g = h^(p-1)/q mod p, commonly h=2 is used.
X = prime(20), %% Choose x by some random method, where 0 < x < q.
Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p.
diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml
index c5d476e54b..1f64b38554 100644
--- a/lib/stdlib/doc/src/unicode_usage.xml
+++ b/lib/stdlib/doc/src/unicode_usage.xml
@@ -625,6 +625,7 @@ Eshell V5.10.1 (abort with ^G)
</section>
<section>
<title>Unicode File Names</title>
+ <marker id="unicode_file_names"/>
<p>Most modern operating systems support Unicode file names in some
way or another. There are several different ways to do this and
Erlang by default treats the different approaches differently:</p>
diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl
index 45aef0ea80..50812cc532 100644
--- a/lib/stdlib/src/dets.erl
+++ b/lib/stdlib/src/dets.erl
@@ -1246,13 +1246,8 @@ req(Proc, R) ->
{'DOWN', Ref, process, Proc, _Info} ->
badarg;
{Proc, Reply} ->
- erlang:demonitor(Ref),
- receive
- {'DOWN', Ref, process, Proc, _Reason} ->
- Reply
- after 0 ->
- Reply
- end
+ erlang:demonitor(Ref, [flush]),
+ Reply
end.
%% Inlined.
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index 0a1caa7178..e31cd63f69 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -1339,8 +1339,7 @@ epp_reply(From, Rep) ->
wait_epp_reply(Epp, Mref) ->
receive
{epp_reply,Epp,Rep} ->
- erlang:demonitor(Mref),
- receive {'DOWN',Mref,_,_,_} -> ok after 0 -> ok end,
+ erlang:demonitor(Mref, [flush]),
Rep;
{'DOWN',Mref,_,_,E} ->
receive {epp_reply,Epp,Rep} -> Rep
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index 9c4b95acf6..30a81ade49 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -472,11 +472,11 @@ rec_nodes(Tag, [{N,R}|Tail], Name, Badnodes, Replies, Time, TimerId ) ->
{'DOWN', R, _, _, _} ->
rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, Time, TimerId);
{{Tag, N}, Reply} -> %% Tag is bound !!!
- unmonitor(R),
+ erlang:demonitor(R, [flush]),
rec_nodes(Tag, Tail, Name, Badnodes,
[{N,Reply}|Replies], Time, TimerId);
{timeout, TimerId, _} ->
- unmonitor(R),
+ erlang:demonitor(R, [flush]),
%% Collect all replies that already have arrived
rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies)
end;
@@ -527,10 +527,10 @@ rec_nodes_rest(Tag, [{N,R}|Tail], Name, Badnodes, Replies) ->
{'DOWN', R, _, _, _} ->
rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies);
{{Tag, N}, Reply} -> %% Tag is bound !!!
- unmonitor(R),
+ erlang:demonitor(R, [flush]),
rec_nodes_rest(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies])
after 0 ->
- unmonitor(R),
+ erlang:demonitor(R, [flush]),
rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies)
end;
rec_nodes_rest(Tag, [N|Tail], Name, Badnodes, Replies) ->
@@ -572,16 +572,6 @@ start_monitor(Node, Name) when is_atom(Node), is_atom(Name) ->
end
end.
-%% Cancels a monitor started with Ref=erlang:monitor(_, _).
-unmonitor(Ref) when is_reference(Ref) ->
- erlang:demonitor(Ref),
- receive
- {'DOWN', Ref, _, _, _} ->
- true
- after 0 ->
- true
- end.
-
%%% ---------------------------------------------------
%%% Message handling functions
%%% ---------------------------------------------------
diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl
index c92e9e3ade..53728237ca 100644
--- a/lib/stdlib/src/io.erl
+++ b/lib/stdlib/src/io.erl
@@ -598,11 +598,7 @@ default_output() ->
wait_io_mon_reply(From, Mref) ->
receive
{io_reply, From, Reply} ->
- erlang:demonitor(Mref),
- receive
- {'DOWN', Mref, _, _, _} -> true
- after 0 -> true
- end,
+ erlang:demonitor(Mref, [flush]),
Reply;
{'EXIT', From, _What} ->
receive
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index 9f93747c3e..54328cd9ff 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -63,7 +63,9 @@
%%--------------------------------------------------------------------------
-record(child, {% pid is undefined when child is not running
- pid = undefined :: child() | {restarting,pid()} | [pid()],
+ pid = undefined :: child()
+ | {restarting, pid() | undefined}
+ | [pid()],
name :: child_id(),
mfargs :: mfargs(),
restart_type :: restart(),
@@ -752,6 +754,9 @@ restart(Child, State) ->
end,
timer:apply_after(0,?MODULE,try_again_restart,[self(),Id]),
{ok,NState2};
+ {try_again, NState2, #child{name=ChName}} ->
+ timer:apply_after(0,?MODULE,try_again_restart,[self(),ChName]),
+ {ok,NState2};
Other ->
Other
end;
@@ -798,10 +803,16 @@ restart(rest_for_one, Child, State) ->
case start_children(ChAfter2, State#state.name) of
{ok, ChAfter3} ->
{ok, State#state{children = ChAfter3 ++ ChBefore}};
- {error, ChAfter3, _Reason} ->
+ {error, ChAfter3, {failed_to_start_child, ChName, _Reason}}
+ when ChName =:= Child#child.name ->
NChild = Child#child{pid=restarting(Child#child.pid)},
NState = State#state{children = ChAfter3 ++ ChBefore},
- {try_again, replace_child(NChild,NState)}
+ {try_again, replace_child(NChild,NState)};
+ {error, ChAfter3, {failed_to_start_child, ChName, _Reason}} ->
+ NChild = lists:keyfind(ChName, #child.name, ChAfter3),
+ NChild2 = NChild#child{pid=?restarting(undefined)},
+ NState = State#state{children = ChAfter3 ++ ChBefore},
+ {try_again, replace_child(NChild2,NState), NChild2}
end;
restart(one_for_all, Child, State) ->
Children1 = del_child(Child#child.pid, State#state.children),
@@ -809,10 +820,16 @@ restart(one_for_all, Child, State) ->
case start_children(Children2, State#state.name) of
{ok, NChs} ->
{ok, State#state{children = NChs}};
- {error, NChs, _Reason} ->
+ {error, NChs, {failed_to_start_child, ChName, _Reason}}
+ when ChName =:= Child#child.name ->
NChild = Child#child{pid=restarting(Child#child.pid)},
NState = State#state{children = NChs},
- {try_again, replace_child(NChild,NState)}
+ {try_again, replace_child(NChild,NState)};
+ {error, NChs, {failed_to_start_child, ChName, _Reason}} ->
+ NChild = lists:keyfind(ChName, #child.name, NChs),
+ NChild2 = NChild#child{pid=?restarting(undefined)},
+ NState = State#state{children = NChs},
+ {try_again, replace_child(NChild2,NState), NChild2}
end.
restarting(Pid) when is_pid(Pid) -> ?restarting(Pid);
diff --git a/lib/stdlib/src/timer.erl b/lib/stdlib/src/timer.erl
index 689e42051f..e11fb046e9 100644
--- a/lib/stdlib/src/timer.erl
+++ b/lib/stdlib/src/timer.erl
@@ -354,7 +354,7 @@ timer_timeout(SysTime) ->
'$end_of_table' ->
infinity;
{Time, _Ref} when Time > SysTime ->
- Timeout = (Time - SysTime) div 1000,
+ Timeout = (Time - SysTime + 999) div 1000,
%% Returned timeout must fit in a small int
erlang:min(Timeout, ?MAX_TIMEOUT);
Key ->
@@ -414,7 +414,7 @@ next_timeout() ->
'$end_of_table' ->
infinity;
{Time, _} ->
- erlang:min(positive((Time - system_time()) div 1000), ?MAX_TIMEOUT)
+ erlang:min(positive((Time - system_time() + 999) div 1000), ?MAX_TIMEOUT)
end.
%% Help functions
diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile
index 6aa09d7bd0..af82f22b21 100644
--- a/lib/stdlib/test/Makefile
+++ b/lib/stdlib/test/Makefile
@@ -66,6 +66,7 @@ MODULES= \
string_SUITE \
supervisor_1 \
supervisor_2 \
+ supervisor_3 \
supervisor_deadlock \
naughty_child \
shell_SUITE \
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index 7ff4c81ea6..18ec17a4bf 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
@@ -992,7 +993,7 @@ otp_10622(Config) when is_list(Config) ->
<<0>>),
check(fun() -> <<"\x{aa}ff"/utf8>> = <<"\x{aa}ff"/utf8>> end,
"<<\"\\x{aa}ff\"/utf8>> = <<\"\\x{aa}ff\"/utf8>>. ",
- <<"�\xaaff">>),
+ <<"Â\xaaff">>),
%% The same bug as last example:
check(fun() -> case <<"foo"/utf8>> of
<<"foo"/utf8>> -> true
diff --git a/lib/stdlib/test/qlc_SUITE_data/join_info_compat.erl b/lib/stdlib/test/qlc_SUITE_data/join_info_compat.erl
index e0db132c47..b93b907392 100644
--- a/lib/stdlib/test/qlc_SUITE_data/join_info_compat.erl
+++ b/lib/stdlib/test/qlc_SUITE_data/join_info_compat.erl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
@@ -515,7 +516,7 @@ create_handle() ->
$.:8/integer-unit:1-unsigned-big,
$x:8/integer-unit:1-unsigned-big,
$\234:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ë:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$N:8/integer-unit:1-unsigned-big,
$a:8/integer-unit:1-unsigned-big,
@@ -523,16 +524,16 @@ create_handle() ->
$-:8/integer-unit:1-unsigned-big,
$):8/integer-unit:1-unsigned-big,
$-:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $È:8/integer-unit:1-unsigned-big,
$I:8/integer-unit:1-unsigned-big,
$M:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $ä:8/integer-unit:1-unsigned-big,
+ $Ê:8/integer-unit:1-unsigned-big,
$a:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ê:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
$\n:8/integer-unit:1-unsigned-big,
$0:8/integer-unit:1-unsigned-big,
@@ -541,19 +542,19 @@ create_handle() ->
$\026:8/integer-unit:1-unsigned-big,
$%:8/integer-unit:1-unsigned-big,
$r:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $¥:8/integer-unit:1-unsigned-big,
$0:8/integer-unit:1-unsigned-big,
$0:8/integer-unit:1-unsigned-big,
$F:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $ :8/integer-unit:1-unsigned-big,
+ $ð:8/integer-unit:1-unsigned-big,
$":8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $³:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $þ:8/integer-unit:1-unsigned-big,
+ $Ì:8/integer-unit:1-unsigned-big,
$\n:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big>>,
+ $É:8/integer-unit:1-unsigned-big>>,
<<$\203:8/integer-unit:1-unsigned-big,
$P:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
@@ -562,7 +563,7 @@ create_handle() ->
$<:8/integer-unit:1-unsigned-big,
$x:8/integer-unit:1-unsigned-big,
$\234:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ë:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$N:8/integer-unit:1-unsigned-big,
$a:8/integer-unit:1-unsigned-big,
@@ -570,16 +571,16 @@ create_handle() ->
$-:8/integer-unit:1-unsigned-big,
$):8/integer-unit:1-unsigned-big,
$-:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $È:8/integer-unit:1-unsigned-big,
$I:8/integer-unit:1-unsigned-big,
$M:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $ä:8/integer-unit:1-unsigned-big,
+ $Ê:8/integer-unit:1-unsigned-big,
$a:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Î:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
$\n:8/integer-unit:1-unsigned-big,
$0:8/integer-unit:1-unsigned-big,
@@ -588,22 +589,22 @@ create_handle() ->
$\026:8/integer-unit:1-unsigned-big,
$%:8/integer-unit:1-unsigned-big,
$r:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $¥:8/integer-unit:1-unsigned-big,
$0:8/integer-unit:1-unsigned-big,
$0:8/integer-unit:1-unsigned-big,
$::8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $¡:8/integer-unit:1-unsigned-big,
+ $ð:8/integer-unit:1-unsigned-big,
$":8/integer-unit:1-unsigned-big,
$P:8/integer-unit:1-unsigned-big,
$x:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $ñ:8/integer-unit:1-unsigned-big,
$Y:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $ª:8/integer-unit:1-unsigned-big,
$9:8/integer-unit:1-unsigned-big,
$\r:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big>>,
+ $ý:8/integer-unit:1-unsigned-big>>,
<<$\203:8/integer-unit:1-unsigned-big,
$P:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
@@ -612,51 +613,51 @@ create_handle() ->
$I:8/integer-unit:1-unsigned-big,
$x:8/integer-unit:1-unsigned-big,
$\234:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ë:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$M:8/integer-unit:1-unsigned-big,
$a:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ê:8/integer-unit:1-unsigned-big,
$/:8/integer-unit:1-unsigned-big,
$H:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $ä:8/integer-unit:1-unsigned-big,
$N:8/integer-unit:1-unsigned-big,
$a:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $¶:8/integer-unit:1-unsigned-big,
+ $µ:8/integer-unit:1-unsigned-big,
+ $²:8/integer-unit:1-unsigned-big,
+ $Í:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$\006:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ò:8/integer-unit:1-unsigned-big,
$e:8/integer-unit:1-unsigned-big,
$\211:8/integer-unit:1-unsigned-big,
$E:8/integer-unit:1-unsigned-big,
$\s:8/integer-unit:1-unsigned-big,
$>:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $£:8/integer-unit:1-unsigned-big,
$\023:8/integer-unit:1-unsigned-big,
$\210:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ç:8/integer-unit:1-unsigned-big,
$\232:8/integer-unit:1-unsigned-big,
$\226:8/integer-unit:1-unsigned-big,
$\223:8/integer-unit:1-unsigned-big,
$\237:8/integer-unit:1-unsigned-big,
$X:8/integer-unit:1-unsigned-big,
$\222:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $È:8/integer-unit:1-unsigned-big,
$\235:8/integer-unit:1-unsigned-big,
$l:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $¨:8/integer-unit:1-unsigned-big,
$g:8/integer-unit:1-unsigned-big,
$i:8/integer-unit:1-unsigned-big,
$d:8/integer-unit:1-unsigned-big,
$\200:8/integer-unit:1-unsigned-big,
$\001:8/integer-unit:1-unsigned-big,
$R:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $µ:8/integer-unit:1-unsigned-big,
$\r:8/integer-unit:1-unsigned-big,
$\214:8/integer-unit:1-unsigned-big,
$\030:8/integer-unit:1-unsigned-big,
@@ -664,7 +665,7 @@ create_handle() ->
$\000:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
$c:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ö:8/integer-unit:1-unsigned-big,
$\017:8/integer-unit:1-unsigned-big,
$=:8/integer-unit:1-unsigned-big>>,
<<$\203:8/integer-unit:1-unsigned-big,
@@ -708,24 +709,24 @@ create_handle() ->
$*:8/integer-unit:1-unsigned-big,
$x:8/integer-unit:1-unsigned-big,
$\234:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ë:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$M:8/integer-unit:1-unsigned-big,
$a:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ê:8/integer-unit:1-unsigned-big,
$/:8/integer-unit:1-unsigned-big,
$H:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $ä:8/integer-unit:1-unsigned-big,
$\005:8/integer-unit:1-unsigned-big,
$R:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $¶:8/integer-unit:1-unsigned-big,
+ $¶:8/integer-unit:1-unsigned-big,
$\031:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ì:8/integer-unit:1-unsigned-big,
$):8/integer-unit:1-unsigned-big,
$\f:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ì:8/integer-unit:1-unsigned-big,
$e:8/integer-unit:1-unsigned-big,
$\211:8/integer-unit:1-unsigned-big,
$E:8/integer-unit:1-unsigned-big,
@@ -737,7 +738,7 @@ create_handle() ->
$/:8/integer-unit:1-unsigned-big,
$\022:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ì:8/integer-unit:1-unsigned-big,
$\205:8/integer-unit:1-unsigned-big,
$\t:8/integer-unit:1-unsigned-big,
$\216:8/integer-unit:1-unsigned-big>>,
@@ -749,33 +750,33 @@ create_handle() ->
$j:8/integer-unit:1-unsigned-big,
$x:8/integer-unit:1-unsigned-big,
$\234:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ë:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$I:8/integer-unit:1-unsigned-big,
$a:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$I:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Î:8/integer-unit:1-unsigned-big,
+ $Ï:8/integer-unit:1-unsigned-big,
$+:8/integer-unit:1-unsigned-big,
$N:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $ú:8/integer-unit:1-unsigned-big,
+ $ÿ:8/integer-unit:1-unsigned-big,
+ $ÿ:8/integer-unit:1-unsigned-big,
+ $ÿ:8/integer-unit:1-unsigned-big,
+ $·:8/integer-unit:1-unsigned-big,
$\f:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $æ:8/integer-unit:1-unsigned-big,
$\024:8/integer-unit:1-unsigned-big,
$\006:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ö:8/integer-unit:1-unsigned-big,
$\222:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ò:8/integer-unit:1-unsigned-big,
$\202:8/integer-unit:1-unsigned-big,
$\234:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ô:8/integer-unit:1-unsigned-big,
$D:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $®:8/integer-unit:1-unsigned-big,
$\034:8/integer-unit:1-unsigned-big,
$\006:8/integer-unit:1-unsigned-big,
$\006:8/integer-unit:1-unsigned-big,
@@ -791,7 +792,7 @@ create_handle() ->
$W:8/integer-unit:1-unsigned-big,
$\n:8/integer-unit:1-unsigned-big,
$\003:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $£:8/integer-unit:1-unsigned-big,
$\023:8/integer-unit:1-unsigned-big,
$\n:8/integer-unit:1-unsigned-big,
$/:8/integer-unit:1-unsigned-big,
@@ -800,18 +801,18 @@ create_handle() ->
$\027:8/integer-unit:1-unsigned-big,
$\237:8/integer-unit:1-unsigned-big,
$\205:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $¤:8/integer-unit:1-unsigned-big,
$\227:8/integer-unit:1-unsigned-big,
$\007:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $¤:8/integer-unit:1-unsigned-big,
$\227:8/integer-unit:1-unsigned-big,
$\021:8/integer-unit:1-unsigned-big,
$.:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ï:8/integer-unit:1-unsigned-big,
$\003:8/integer-unit:1-unsigned-big,
$\224:8/integer-unit:1-unsigned-big,
$\217:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ì:8/integer-unit:1-unsigned-big,
$\002:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
$\203:8/integer-unit:1-unsigned-big,
@@ -1398,7 +1399,7 @@ lookup_handle() ->
$.:8/integer-unit:1-unsigned-big,
$x:8/integer-unit:1-unsigned-big,
$\234:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ë:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$N:8/integer-unit:1-unsigned-big,
$a:8/integer-unit:1-unsigned-big,
@@ -1406,16 +1407,16 @@ lookup_handle() ->
$-:8/integer-unit:1-unsigned-big,
$):8/integer-unit:1-unsigned-big,
$-:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $È:8/integer-unit:1-unsigned-big,
$I:8/integer-unit:1-unsigned-big,
$M:8/integer-unit:1-unsigned-big,
$\024:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ì:8/integer-unit:1-unsigned-big,
$a:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ê:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
$\n:8/integer-unit:1-unsigned-big,
$0:8/integer-unit:1-unsigned-big,
@@ -1424,19 +1425,19 @@ lookup_handle() ->
$\026:8/integer-unit:1-unsigned-big,
$%:8/integer-unit:1-unsigned-big,
$\n:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $¦:8/integer-unit:1-unsigned-big,
$0:8/integer-unit:1-unsigned-big,
$0:8/integer-unit:1-unsigned-big,
$F:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $ :8/integer-unit:1-unsigned-big,
+ $ð:8/integer-unit:1-unsigned-big,
$":8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $³:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
$\206:8/integer-unit:1-unsigned-big,
$\n:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big>>,
+ $Þ:8/integer-unit:1-unsigned-big>>,
<<$\203:8/integer-unit:1-unsigned-big,
$P:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
@@ -1445,7 +1446,7 @@ lookup_handle() ->
$.:8/integer-unit:1-unsigned-big,
$x:8/integer-unit:1-unsigned-big,
$\234:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ë:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$N:8/integer-unit:1-unsigned-big,
$a:8/integer-unit:1-unsigned-big,
@@ -1453,16 +1454,16 @@ lookup_handle() ->
$-:8/integer-unit:1-unsigned-big,
$):8/integer-unit:1-unsigned-big,
$-:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $È:8/integer-unit:1-unsigned-big,
$I:8/integer-unit:1-unsigned-big,
$M:8/integer-unit:1-unsigned-big,
$\024:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ì:8/integer-unit:1-unsigned-big,
$a:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ê:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
$\n:8/integer-unit:1-unsigned-big,
$0:8/integer-unit:1-unsigned-big,
@@ -1471,19 +1472,19 @@ lookup_handle() ->
$\026:8/integer-unit:1-unsigned-big,
$%:8/integer-unit:1-unsigned-big,
$\n:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $¦:8/integer-unit:1-unsigned-big,
$0:8/integer-unit:1-unsigned-big,
$0:8/integer-unit:1-unsigned-big,
$F:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $ :8/integer-unit:1-unsigned-big,
+ $ð:8/integer-unit:1-unsigned-big,
+ $â:8/integer-unit:1-unsigned-big,
+ $³:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
$\222:8/integer-unit:1-unsigned-big,
$\n:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big>>,
+ $ä:8/integer-unit:1-unsigned-big>>,
<<$\203:8/integer-unit:1-unsigned-big,
$h:8/integer-unit:1-unsigned-big,
$\003:8/integer-unit:1-unsigned-big,
@@ -1525,25 +1526,25 @@ lookup_handle() ->
$+:8/integer-unit:1-unsigned-big,
$x:8/integer-unit:1-unsigned-big,
$\234:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ë:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$M:8/integer-unit:1-unsigned-big,
$a:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ê:8/integer-unit:1-unsigned-big,
$/:8/integer-unit:1-unsigned-big,
$H:8/integer-unit:1-unsigned-big,
$\024:8/integer-unit:1-unsigned-big,
$N:8/integer-unit:1-unsigned-big,
$a:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $¶:8/integer-unit:1-unsigned-big,
+ $µ:8/integer-unit:1-unsigned-big,
+ $²:8/integer-unit:1-unsigned-big,
+ $Í:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$\006:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ò:8/integer-unit:1-unsigned-big,
$e:8/integer-unit:1-unsigned-big,
$\211:8/integer-unit:1-unsigned-big,
$E:8/integer-unit:1-unsigned-big,
@@ -1555,10 +1556,10 @@ lookup_handle() ->
$/:8/integer-unit:1-unsigned-big,
$\022:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $×:8/integer-unit:1-unsigned-big,
$\227:8/integer-unit:1-unsigned-big,
$\t:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big>>,
+ $Û:8/integer-unit:1-unsigned-big>>,
<<$\203:8/integer-unit:1-unsigned-big,
$P:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
@@ -1567,33 +1568,33 @@ lookup_handle() ->
$\\:8/integer-unit:1-unsigned-big,
$x:8/integer-unit:1-unsigned-big,
$\234:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ë:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$I:8/integer-unit:1-unsigned-big,
$a:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$I:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Î:8/integer-unit:1-unsigned-big,
+ $Ï:8/integer-unit:1-unsigned-big,
$+:8/integer-unit:1-unsigned-big,
$N:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $ú:8/integer-unit:1-unsigned-big,
+ $ÿ:8/integer-unit:1-unsigned-big,
+ $ÿ:8/integer-unit:1-unsigned-big,
+ $ÿ:8/integer-unit:1-unsigned-big,
+ $û:8/integer-unit:1-unsigned-big,
$\f:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $æ:8/integer-unit:1-unsigned-big,
$\024:8/integer-unit:1-unsigned-big,
$\006:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ö:8/integer-unit:1-unsigned-big,
$\222:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ò:8/integer-unit:1-unsigned-big,
$\202:8/integer-unit:1-unsigned-big,
$\234:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ô:8/integer-unit:1-unsigned-big,
$D:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Á:8/integer-unit:1-unsigned-big,
$\034:8/integer-unit:1-unsigned-big,
$\006:8/integer-unit:1-unsigned-big,
$\006:8/integer-unit:1-unsigned-big,
@@ -1605,7 +1606,7 @@ lookup_handle() ->
$Y:8/integer-unit:1-unsigned-big,
$b:8/integer-unit:1-unsigned-big,
$Q:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $¢:8/integer-unit:1-unsigned-big,
$`:8/integer-unit:1-unsigned-big,
$\n:8/integer-unit:1-unsigned-big,
$\003:8/integer-unit:1-unsigned-big,
@@ -1616,7 +1617,7 @@ lookup_handle() ->
$>:8/integer-unit:1-unsigned-big,
$\v:8/integer-unit:1-unsigned-big,
$I:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $µ:8/integer-unit:1-unsigned-big,
$\020:8/integer-unit:1-unsigned-big,
$H:8/integer-unit:1-unsigned-big,
$5:8/integer-unit:1-unsigned-big,
@@ -1630,7 +1631,7 @@ lookup_handle() ->
$\005:8/integer-unit:1-unsigned-big,
$\000:8/integer-unit:1-unsigned-big,
$\024:8/integer-unit:1-unsigned-big,
- $�:8/integer-unit:1-unsigned-big,
+ $Ù:8/integer-unit:1-unsigned-big,
$\031:8/integer-unit:1-unsigned-big,
$M:8/integer-unit:1-unsigned-big>>}
end,
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index 681c154463..3c49aaa103 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
@@ -2820,7 +2821,7 @@ otp_10302(Config) when is_list(Config) ->
"ok.\n** exception error: an error occurred when evaluating"
" an arithmetic expression\n in operator '/'/2\n"
- " called as <<\"�\">> / <<\"�\">>.\n" = t({Node,Test7}),
+ " called as <<\"ª\">> / <<\"ª\">>.\n" = t({Node,Test7}),
Test8 =
<<"begin
A = [1089],
diff --git a/lib/stdlib/test/supervisor_3.erl b/lib/stdlib/test/supervisor_3.erl
new file mode 100644
index 0000000000..31b3037d6f
--- /dev/null
+++ b/lib/stdlib/test/supervisor_3.erl
@@ -0,0 +1,45 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2011. 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%
+%%
+%% Description: Simulates the behaviour that a child process may have.
+%% Is used by the supervisor_SUITE test suite.
+-module(supervisor_3).
+
+-export([start_child/2, init/1]).
+
+-export([handle_call/3, handle_info/2, terminate/2]).
+
+start_child(Name, Caller) ->
+ gen_server:start_link(?MODULE, [Name, Caller], []).
+
+init([Name, Caller]) ->
+ Caller ! {Name, self()},
+ receive
+ {Result, Caller} ->
+ Result
+ end.
+
+handle_call(Req, _From, State) ->
+ {reply, Req, State}.
+
+handle_info(_, State) ->
+ {noreply, State}.
+
+terminate(_Reason, Time) ->
+ timer:sleep(Time),
+ ok.
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index 569c66959e..ff5be6bb95 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -53,9 +53,10 @@
%% Restart strategy tests
-export([ one_for_one/1,
one_for_one_escalation/1, one_for_all/1,
- one_for_all_escalation/1,
+ one_for_all_escalation/1, one_for_all_other_child_fails_restart/1,
simple_one_for_one/1, simple_one_for_one_escalation/1,
rest_for_one/1, rest_for_one_escalation/1,
+ rest_for_one_other_child_fails_restart/1,
simple_one_for_one_extra/1, simple_one_for_one_shutdown/1]).
%% Misc tests
@@ -107,12 +108,14 @@ groups() ->
{restart_one_for_one, [],
[one_for_one, one_for_one_escalation]},
{restart_one_for_all, [],
- [one_for_all, one_for_all_escalation]},
+ [one_for_all, one_for_all_escalation,
+ one_for_all_other_child_fails_restart]},
{restart_simple_one_for_one, [],
[simple_one_for_one, simple_one_for_one_shutdown,
simple_one_for_one_extra, simple_one_for_one_escalation]},
{restart_rest_for_one, [],
- [rest_for_one, rest_for_one_escalation]}].
+ [rest_for_one, rest_for_one_escalation,
+ rest_for_one_other_child_fails_restart]}].
init_per_suite(Config) ->
Config.
@@ -879,6 +882,57 @@ one_for_all_escalation(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
+%% Test that the supervisor terminates a restarted child when a different
+%% child fails to start.
+one_for_all_other_child_fails_restart(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ Self = self(),
+ Child1 = {child1, {supervisor_3, start_child, [child1, Self]},
+ permanent, 1000, worker, []},
+ Child2 = {child2, {supervisor_3, start_child, [child2, Self]},
+ permanent, 1000, worker, []},
+ Children = [Child1, Child2],
+ StarterFun = fun() ->
+ {ok, SupPid} = start_link({ok, {{one_for_all, 3, 3600}, Children}}),
+ Self ! {sup_pid, SupPid},
+ receive {stop, Self} -> ok end
+ end,
+ StarterPid = spawn_link(StarterFun),
+ Ok = {{ok, undefined}, Self},
+ %% Let the children start.
+ Child1Pid = receive {child1, Pid1} -> Pid1 end,
+ Child1Pid ! Ok,
+ Child2Pid = receive {child2, Pid2} -> Pid2 end,
+ Child2Pid ! Ok,
+ %% Supervisor started.
+ SupPid = receive {sup_pid, Pid} -> Pid end,
+ link(SupPid),
+ exit(Child1Pid, die),
+ %% Let child1 restart but don't let child2.
+ Child1Pid2 = receive {child1, Pid3} -> Pid3 end,
+ Child1Pid2Ref = erlang:monitor(process, Child1Pid2),
+ Child1Pid2 ! Ok,
+ Child2Pid2 = receive {child2, Pid4} -> Pid4 end,
+ Child2Pid2 ! {{stop, normal}, Self},
+ %% Check child1 is terminated.
+ receive
+ {'DOWN', Child1Pid2Ref, _, _, shutdown} ->
+ ok;
+ {_childName, _Pid} ->
+ exit(SupPid, kill),
+ check_exit([StarterPid, SupPid]),
+ test_server:fail({restarting_child_not_terminated, Child1Pid2})
+ end,
+ %% Let the restart complete.
+ Child1Pid3 = receive {child1, Pid5} -> Pid5 end,
+ Child1Pid3 ! Ok,
+ Child2Pid3 = receive {child2, Pid6} -> Pid6 end,
+ Child2Pid3 ! Ok,
+ StarterPid ! {stop, Self},
+ check_exit([StarterPid, SupPid]).
+
+
+%%-------------------------------------------------------------------------
%% Test the simple_one_for_one base case.
simple_one_for_one(Config) when is_list(Config) ->
process_flag(trap_exit, true),
@@ -1044,6 +1098,52 @@ rest_for_one_escalation(Config) when is_list(Config) ->
terminate(SupPid, CPid1, child1, abnormal),
check_exit([CPid2, SupPid]).
+
+%%-------------------------------------------------------------------------
+%% Test that the supervisor terminates a restarted child when a different
+%% child fails to start.
+rest_for_one_other_child_fails_restart(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ Self = self(),
+ Child1 = {child1, {supervisor_3, start_child, [child1, Self]},
+ permanent, 1000, worker, []},
+ Child2 = {child2, {supervisor_3, start_child, [child2, Self]},
+ permanent, 1000, worker, []},
+ Children = [Child1, Child2],
+ StarterFun = fun() ->
+ {ok, SupPid} = start_link({ok, {{rest_for_one, 3, 3600}, Children}}),
+ Self ! {sup_pid, SupPid},
+ receive {stop, Self} -> ok end
+ end,
+ StarterPid = spawn_link(StarterFun),
+ Ok = {{ok, undefined}, Self},
+ %% Let the children start.
+ Child1Pid = receive {child1, Pid1} -> Pid1 end,
+ Child1Pid ! Ok,
+ Child2Pid = receive {child2, Pid2} -> Pid2 end,
+ Child2Pid ! Ok,
+ %% Supervisor started.
+ SupPid = receive {sup_pid, Pid} -> Pid end,
+ link(SupPid),
+ exit(Child1Pid, die),
+ %% Let child1 restart but don't let child2.
+ Child1Pid2 = receive {child1, Pid3} -> Pid3 end,
+ Child1Pid2 ! Ok,
+ Child2Pid2 = receive {child2, Pid4} -> Pid4 end,
+ Child2Pid2 ! {{stop, normal}, Self},
+ %% Let child2 restart.
+ receive
+ {child2, Child2Pid3} ->
+ Child2Pid3 ! Ok;
+ {child1, _Child1Pid3} ->
+ exit(SupPid, kill),
+ check_exit([StarterPid, SupPid]),
+ test_server:fail({restarting_started_child, Child1Pid2})
+ end,
+ StarterPid ! {stop, Self},
+ check_exit([StarterPid, SupPid]).
+
+
%%-------------------------------------------------------------------------
%% Test that the supervisor does not hang forever if the child unliks
%% and then is terminated by the supervisor.
diff --git a/lib/test_server/src/erl2html2.erl b/lib/test_server/src/erl2html2.erl
index 5584c1e50c..d0f40c47a7 100644
--- a/lib/test_server/src/erl2html2.erl
+++ b/lib/test_server/src/erl2html2.erl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
@@ -214,7 +215,7 @@ html_encoding(utf8) ->
%%% from the source.
%%%
%%% Example: if the encoding of the file is utf8, and we have a string
-%%% containing "�" = [229], then we need to convert this to [195,165]
+%%% containing "å" = [229], then we need to convert this to [195,165]
%%% before writing. Note that this conversion is only necessary
%%% because the destination file is not (necessarily) opened with utf8
%%% encoding - it is opened with default encoding in order to allow
diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl
index 21c10adccb..ffa21d054c 100644
--- a/lib/test_server/src/test_server_ctrl.erl
+++ b/lib/test_server/src/test_server_ctrl.erl
@@ -1183,7 +1183,7 @@ init_tester(Mod, Func, Args, Dir, Name, {_,_,MinLev}=Levels,
"<td>~.3fs</td><td><b>~ts</b></td><td>~w Ok, ~w Failed~ts of ~w</td></tr>\n"
"</tfoot>\n",
[Time,SuccessStr,OkN,FailedN,SkipStr,OkN+FailedN+SkippedN]),
- test_server_io:stop().
+ test_server_io:stop([major,html,unexpected_io]).
report_severe_error(Reason) ->
test_server_sup:framework_call(report, [severe_error,Reason]).
@@ -1588,7 +1588,7 @@ do_test_cases(TopCases, SkipCases,
print(major, "=started ~s",
[lists:flatten(timestamp_get(""))]),
- put(test_server_html_footer, Footer),
+ test_server_io:set_footer(Footer),
run_test_cases(TestSpec, Config, TimetrapData)
end;
diff --git a/lib/test_server/src/test_server_io.erl b/lib/test_server/src/test_server_io.erl
index a979deffc3..73d4468bda 100644
--- a/lib/test_server/src/test_server_io.erl
+++ b/lib/test_server/src/test_server_io.erl
@@ -29,7 +29,7 @@
%%
-module(test_server_io).
--export([start_link/0,stop/0,get_gl/1,set_fd/2,
+-export([start_link/0,stop/1,get_gl/1,set_fd/2,
start_transaction/0,end_transaction/0,
print_buffered/1,print/3,print_unexpected/1,
set_footer/1,set_job_name/1,set_gl_props/1]).
@@ -55,10 +55,10 @@ start_link() ->
Other
end.
-stop() ->
+stop(FilesToClose) ->
OldGL = group_leader(),
group_leader(self(), self()),
- req(stop),
+ req({stop,FilesToClose}),
group_leader(OldGL, self()),
ok.
@@ -213,12 +213,21 @@ handle_call({set_job_name,Name}, _From, St) ->
handle_call({set_gl_props,Props}, _From, #st{shared_gl=Shared}=St) ->
test_server_gl:set_props(Shared, Props),
{reply,ok,St#st{gl_props=Props}};
-handle_call(stop, From, #st{shared_gl=SGL,gls=Gls0}=St0) ->
+handle_call({stop,FdTags}, From, #st{fds=Fds,shared_gl=SGL,gls=Gls0}=St0) ->
St = St0#st{gls=gb_sets:insert(SGL, Gls0),stopping=From},
gc(St),
%% Give the users of the surviving group leaders some
%% time to finish.
erlang:send_after(2000, self(), stop_group_leaders),
+ %% close open log files
+ lists:foreach(fun(Tag) ->
+ case gb_trees:lookup(Tag, Fds) of
+ none ->
+ ok;
+ {value,Fd} ->
+ file:close(Fd)
+ end
+ end, FdTags),
{noreply,St}.
handle_info({'EXIT',Pid,normal}, #st{gls=Gls0,stopping=From}=St) ->
diff --git a/lib/test_server/test/test_server_SUITE.erl b/lib/test_server/test/test_server_SUITE.erl
index 3db2f5f9f1..8ad5fcfb5c 100644
--- a/lib/test_server/test/test_server_SUITE.erl
+++ b/lib/test_server/test/test_server_SUITE.erl
@@ -1,3 +1,4 @@
+%% -*- coding: utf-8 -*-
%%
%% %CopyrightBegin%
%%
@@ -187,7 +188,7 @@ test_server_unicode(Config) ->
%% Create and run two test suites - one with filename and content
%% in latin1 (if the default filename mode is latin1) and one with
%% filename and content in utf8. Both have name and content
- %% including letters ���. Check that all logs are generated with
+ %% including letters äöå. Check that all logs are generated with
%% utf8 encoded filenames.
case file:native_name_encoding() of
utf8 ->
@@ -348,7 +349,7 @@ generate_and_run_unicode_test(Config0,Encoding) ->
SuiteHtml = translate_filename(LowerModStr++".src.html",Encoding),
true = filelib:is_regular(filename:join(RunDir,SuiteHtml)),
- TCLog = translate_filename(LowerModStr++".tc_���.html",Encoding),
+ TCLog = translate_filename(LowerModStr++".tc_äöå.html",Encoding),
true = filelib:is_regular(filename:join(RunDir,TCLog)),
ok.
@@ -370,7 +371,7 @@ start_node(Config,Name,Args) ->
end.
create_unicode_test_suite(Dir,Encoding) ->
- ModStr = "test_server_"++atom_to_list(Encoding)++"_���_SUITE",
+ ModStr = "test_server_"++atom_to_list(Encoding)++"_äöå_SUITE",
File = filename:join(Dir,ModStr++".erl"),
Suite =
["%% -*- ",epp:encoding_to_string(Encoding)," -*-\n",
@@ -378,12 +379,12 @@ create_unicode_test_suite(Dir,Encoding) ->
"\n"
"-export([all/1, init_per_suite/1, end_per_suite/1]).\n"
"-export([init_per_testcase/2, end_per_testcase/2]).\n"
- "-export([tc_���/1]).\n"
+ "-export([tc_äöå/1]).\n"
"\n"
"-include_lib(\"test_server/include/test_server.hrl\").\n"
"\n"
"all(suite) ->\n"
- " [tc_���].\n"
+ " [tc_äöå].\n"
"\n"
"init_per_suite(Config) ->\n"
" Config.\n"
@@ -406,7 +407,7 @@ create_unicode_test_suite(Dir,Encoding) ->
" ?t:timetrap_cancel(Dog),\n"
" ok.\n"
"\n"
- "tc_���(Config) when is_list(Config) ->\n"
+ "tc_äöå(Config) when is_list(Config) ->\n"
" true = filelib:is_dir(?config(priv_dir,Config)),\n"
" ok.\n"],
{ok,Fd} = file:open(raw_filename(File,Encoding),[write,{encoding,Encoding}]),
diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl
index 4cbb910f11..877218bda0 100644
--- a/lib/tools/src/fprof.erl
+++ b/lib/tools/src/fprof.erl
@@ -1226,10 +1226,7 @@ spawn_3step(Spawn, FunPrelude, FunAck, FunBody)
MRef = erlang:monitor(process, Parent),
receive
{Parent, Ref, Go} ->
- erlang:demonitor(MRef),
- receive {'DOWN', MRef, _, _, _} -> ok
- after 0 -> ok
- end,
+ erlang:demonitor(MRef, [flush]),
FunBody(Go);
{'DOWN', MRef, _, _, _} ->
ok
@@ -1238,8 +1235,7 @@ spawn_3step(Spawn, FunPrelude, FunAck, FunBody)
MRef = erlang:monitor(process, Child),
receive
{Child, Ref, Ack} ->
- erlang:demonitor(MRef),
- receive {'DOWN', MRef, _, _, _} -> ok after 0 -> ok end,
+ erlang:demonitor(MRef, [flush]),
try FunAck(Ack) of
{Result, Go} ->
catch Child ! {Parent, Ref, Go},