diff options
40 files changed, 1220 insertions, 589 deletions
diff --git a/erts/emulator/internal_doc/dec.erl b/erts/emulator/internal_doc/dec.erl index 0315f2a52d..255018abe0 100644 --- a/erts/emulator/internal_doc/dec.erl +++ b/erts/emulator/internal_doc/dec.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -25,10 +26,10 @@ %% {RevList,Translation} %% Where 'RevList' is a reversed list of the denormalized repressentation of %% the character 'Translation'. An example would be the swedish character -%% '�', which would be represented in the file as: +%% 'ö', which would be represented in the file as: %% {[776,111],246}, as the denormalized representation of codepoint 246 %% is [111,776] (i.e an 'o' followed by the "double dot accent character 776), -%% while '�' instead is represented as {[776,97],228}, as the denormalized +%% while 'ä' instead is represented as {[776,97],228}, as the denormalized %% form would be [97,776] (same accent but an 'a' instead). %% The datafile is generated from the table on Apple's developer connection %% http://developer.apple.com/library/mac/#technotes/tn/tn1150table.html diff --git a/erts/emulator/test/code_parallel_load_SUITE.erl b/erts/emulator/test/code_parallel_load_SUITE.erl index aa9e4c96c6..d2c80c1ca0 100644 --- a/erts/emulator/test/code_parallel_load_SUITE.erl +++ b/erts/emulator/test/code_parallel_load_SUITE.erl @@ -16,7 +16,6 @@ %% %% %CopyrightEnd% %% -%% Author: Björn-Egil Dahlberg -module(code_parallel_load_SUITE). -export([ diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl index 752033fdff..79db1ae65c 100644 --- a/lib/common_test/src/ct_logs.erl +++ b/lib/common_test/src/ct_logs.erl @@ -59,6 +59,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 +69,10 @@ -define(abs(Name), filename:absname(Name)). +-record(log_cache, {version, + all_runs = [], + tests = []}). + %%%----------------------------------------------------------------- %%% @spec init(Mode) -> Result %%% Mode = normal | interactive @@ -93,14 +98,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 +124,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 +161,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; @@ -561,9 +592,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 +719,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 -> @@ -957,29 +989,27 @@ close_ctlog(Fd) -> 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 @@ -1008,6 +1038,7 @@ make_last_run_index1(StartTime,IndexName) -> %% 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()], + case force_write_file(IndexName, unicode:characters_to_binary(Index)) of ok -> ok; @@ -1046,22 +1077,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 +1105,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 -> - [" <a href=\"", uri(CrashDumpName), + [" <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 +1156,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 +1167,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 +1210,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} = @@ -1580,35 +1634,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 +1807,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 +1827,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 +1967,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 +2033,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 +2107,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 +2127,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 +2348,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 +2366,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()], 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,37 +2433,49 @@ 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), + Index0 = make_all_suites_ix_temp1(AllTestLogDirs1, + all_suites_index_header(IndexDir), + 0, 0, 0, 0, 0), Index = [Index0|index_footer()], case force_write_file(AbsIndexName, unicode:characters_to_binary(Index)) of ok -> @@ -2008,51 +2487,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/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/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/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/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/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/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/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/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/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/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/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/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}, |