aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/doc/src/erl_nif.xml57
-rw-r--r--erts/doc/src/notes.xml44
-rw-r--r--erts/emulator/beam/beam_bif_load.c4
-rw-r--r--erts/emulator/beam/erl_gc.c36
-rw-r--r--erts/emulator/beam/erl_nif.c58
-rw-r--r--erts/emulator/beam/erl_nif_api_funcs.h4
-rw-r--r--erts/emulator/beam/erl_process.c11
-rw-r--r--erts/emulator/beam/erl_process.h1
-rw-r--r--erts/emulator/drivers/common/inet_drv.c5
-rw-r--r--erts/emulator/test/Makefile1
-rw-r--r--erts/emulator/test/binary_SUITE.erl14
-rw-r--r--erts/emulator/test/bs_construct_SUITE.erl2
-rw-r--r--erts/emulator/test/call_trace_SUITE.erl2
-rw-r--r--erts/emulator/test/code_SUITE.erl44
-rw-r--r--erts/emulator/test/ddll_SUITE.erl2
-rw-r--r--erts/emulator/test/dirty_nif_SUITE.erl140
-rw-r--r--erts/emulator/test/dirty_nif_SUITE_data/Makefile.src2
-rw-r--r--erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c172
-rw-r--r--erts/emulator/test/dirty_nif_SUITE_data/echo_drv.c62
-rw-r--r--erts/emulator/test/distribution_SUITE.erl132
-rw-r--r--erts/emulator/test/evil_SUITE.erl2
-rw-r--r--erts/emulator/test/exception_SUITE.erl2
-rw-r--r--erts/emulator/test/lttng_SUITE.erl2
-rw-r--r--erts/emulator/test/match_spec_SUITE.erl2
-rw-r--r--erts/emulator/test/nested_SUITE.erl2
-rw-r--r--erts/emulator/test/nif_SUITE.erl173
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c262
-rw-r--r--erts/emulator/test/old_scheduler_SUITE.erl384
-rw-r--r--erts/emulator/test/port_SUITE.erl2
-rw-r--r--erts/emulator/test/port_trace_SUITE.erl2
-rw-r--r--erts/emulator/test/process_SUITE.erl83
-rw-r--r--erts/emulator/test/receive_SUITE.erl55
-rw-r--r--erts/emulator/test/timer_bif_SUITE.erl24
-rw-r--r--erts/emulator/test/trace_SUITE.erl10
-rw-r--r--erts/emulator/test/trace_port_SUITE.erl2
-rw-r--r--lib/kernel/src/inet_parse.erl98
-rw-r--r--lib/kernel/test/inet_SUITE.erl158
-rw-r--r--lib/reltool/doc/src/reltool_examples.xml627
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml2
-rw-r--r--otp_versions.table2
40 files changed, 1799 insertions, 888 deletions
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 3eb3e04f33..5a69bed34c 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -3002,6 +3002,63 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) {
<c>erl_drv_tsd_set</c></seealso>.</p>
</desc>
</func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_whereis_pid(ErlNifEnv *env,
+ ERL_NIF_TERM name, ErlNifPid *pid)</nametext></name>
+ <fsummary>Looks up a process by its registered name.</fsummary>
+ <desc>
+ <p>Looks up a process by its registered name.</p>
+ <taglist>
+ <tag><c>env</c></tag>
+ <item>The environment of the calling process. Must be <c>NULL</c>
+ only if calling from a created thread.</item>
+ <tag><c>name</c></tag>
+ <item>The name of a registered process, as an atom.</item>
+ <tag><c>*pid</c></tag>
+ <item>The <seealso marker="#ErlNifPid"><c>ErlNifPid</c></seealso>
+ in which the resolved process id is stored.</item>
+ </taglist>
+ <p>On success, sets <c>*pid</c> to the local process registered with
+ <c>name</c> and returns <c>true</c>. If <c>name</c> is not a
+ registered process, or is not an atom, <c>false</c> is returned and
+ <c>*pid</c> is unchanged.</p>
+ <p>Works as <seealso marker="erlang#whereis-1">
+ <c>erlang:whereis/1</c></seealso>, but restricted to processes. See
+ <seealso marker="#enif_whereis_port"><c>enif_whereis_port</c></seealso>
+ to resolve registered ports.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret>
+ <nametext>enif_whereis_port(ErlNifEnv *env,
+ ERL_NIF_TERM name, ErlNifPort *port)</nametext></name>
+ <fsummary>Looks up a port by its registered name.</fsummary>
+ <desc>
+ <p>Looks up a port by its registered name.</p>
+ <taglist>
+ <tag><c>env</c></tag>
+ <item>The environment of the calling process. Must be <c>NULL</c>
+ only if calling from a created thread.</item>
+ <tag><c>name</c></tag>
+ <item>The name of a registered port, as an atom.</item>
+ <tag><c>*port</c></tag>
+ <item>The <seealso marker="#ErlNifPort"><c>ErlNifPort</c></seealso>
+ in which the resolved port id is stored.</item>
+ </taglist>
+ <p>On success, sets <c>*port</c> to the port registered with
+ <c>name</c> and returns <c>true</c>. If <c>name</c> is not a
+ registered port, or is not an atom, <c>false</c> is returned and
+ <c>*port</c> is unchanged.</p>
+ <p>Works as <seealso marker="erlang#whereis-1">
+ <c>erlang:whereis/1</c></seealso>, but restricted to ports. See
+ <seealso marker="#enif_whereis_pid"><c>enif_whereis_pid</c></seealso>
+ to resolve registered processes.</p>
+ </desc>
+ </func>
+
</funcs>
<section>
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 8ae87ac280..e61114c504 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -31,6 +31,34 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 8.3.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Active-mode TCP sockets are now cleaned up properly on
+ send/shutdown errors.</p>
+ <p>
+ Own Id: OTP-14441 Aux Id: ERL-430 </p>
+ </item>
+ <item>
+ <p>
+ A code purge operation could under certain circumstances
+ expand the size of hibernated processes.</p>
+ <p>
+ Own Id: OTP-14444 Aux Id: ERIERL-24 </p>
+ </item>
+ <item>
+ <p>
+ Fix so that the ERL_ZZ_SIGTERM_KILL introduced in
+ erts-8.3.4 works.</p>
+ <p>
+ Own Id: OTP-14451</p>
+ </item>
+ </list>
+ </section>
+
+</section>
<section><title>Erts 8.3.4</title>
@@ -533,6 +561,22 @@
</section>
+<section><title>Erts 8.1.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A code purge operation could under certain circumstances
+ expand the size of hibernated processes.</p>
+ <p>
+ Own Id: OTP-14444 Aux Id: ERIERL-24 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 8.1.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index 5aceae8ffe..007bf99b6e 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -1068,10 +1068,10 @@ return_ok:
literal_gc:
if (!gc_allowed)
- return am_need_gc;
+ return am_need_gc;
if (c_p->flags & F_DISABLE_GC)
- return THE_NON_VALUE;
+ return THE_NON_VALUE;
*redsp += erts_garbage_collect_literals(c_p, (Eterm *) literals, lit_bsize,
oh, fcalls);
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index 2ff49c97b3..3c8bdaa62e 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -834,7 +834,7 @@ do_major_collection:
esdp->gc_info.reclaimed += reclaimed_now;
}
- FLAGS(p) &= ~F_FORCE_GC;
+ FLAGS(p) &= ~(F_FORCE_GC|F_HIBERNATED);
p->live_hf_end = ERTS_INVALID_HFRAG_PTR;
ERTS_MSACC_POP_STATE_M();
@@ -891,8 +891,8 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
* Place all living data on a the new heap; deallocate any old heap.
* Meant to be used by hibernate/3.
*/
-void
-erts_garbage_collect_hibernate(Process* p)
+static int
+garbage_collect_hibernate(Process* p, int check_long_gc)
{
Uint heap_size;
Eterm* heap;
@@ -909,13 +909,13 @@ erts_garbage_collect_hibernate(Process* p)
#ifdef ERTS_DIRTY_SCHEDULERS
if (ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(p)))
p->flags &= ~(F_DIRTY_GC_HIBERNATE|F_DIRTY_MAJOR_GC|F_DIRTY_MINOR_GC);
- else {
+ else if (check_long_gc) {
Uint flags = p->flags;
p->flags |= F_NEED_FULLSWEEP;
check_for_possibly_long_gc(p, (p->htop - p->heap) + p->mbuf_sz);
if (p->flags & (F_DIRTY_MAJOR_GC|F_DIRTY_MINOR_GC)) {
p->flags = flags|F_DIRTY_GC_HIBERNATE;
- return;
+ return 1;
}
p->flags = flags;
}
@@ -1012,12 +1012,20 @@ erts_garbage_collect_hibernate(Process* p)
ErtsGcQuickSanityCheck(p);
+ p->flags |= F_HIBERNATED;
+
erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
reds = gc_cost(actual_size, actual_size);
- BUMP_REDS(p, reds);
+ return reds;
}
+void
+erts_garbage_collect_hibernate(Process* p)
+{
+ int reds = garbage_collect_hibernate(p, 1);
+ BUMP_REDS(p, reds);
+}
/*
* HiPE native code stack scanning procedures:
@@ -1090,6 +1098,7 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
Uint ygen_usage = 0;
struct erl_off_heap_header** prev = NULL;
Sint64 reds;
+ int hibernated = !!(p->flags & F_HIBERNATED);
if (p->flags & (F_DISABLE_GC|F_DELAY_GC))
ERTS_INTERNAL_ERROR("GC disabled");
@@ -1104,10 +1113,13 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
if (ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(p)))
p->flags &= ~F_DIRTY_CLA;
else {
+ Uint size = byte_lit_size/sizeof(Uint);
ygen_usage = young_gen_usage(p);
- check_for_possibly_long_gc(p,
- (byte_lit_size/sizeof(Uint)
- + 2*ygen_usage));
+ if (hibernated)
+ size = size*2 + 3*ygen_usage;
+ else
+ size = size + 2*ygen_usage;
+ check_for_possibly_long_gc(p, size);
if (p->flags & F_DIRTY_MAJOR_GC) {
p->flags |= F_DIRTY_CLA;
return 10;
@@ -1274,6 +1286,12 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
erts_smp_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
reds += (Sint64) gc_cost((p->htop - p->heap) + byte_lit_size/sizeof(Uint), 0);
+
+ if (hibernated) {
+ /* Restore the process into hibernated state... */
+ reds += garbage_collect_hibernate(p, 0);
+ }
+
if (reds > INT_MAX)
return INT_MAX;
return (int) reds;
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index ea835d1b64..4815e5e7bb 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -879,6 +879,64 @@ enif_port_command(ErlNifEnv *env, const ErlNifPort* to_port,
return res;
}
+/*
+ * env must be the caller's environment in a scheduler or NULL in a
+ * non-scheduler thread.
+ * name must be an atom - anything else will just waste time.
+ */
+static Eterm call_whereis(ErlNifEnv *env, Eterm name)
+{
+ Process *c_p;
+ Eterm res;
+ int scheduler;
+ int unlock;
+
+ execution_state(env, &c_p, &scheduler);
+ ASSERT((c_p && scheduler) || (!c_p && !scheduler));
+
+ unlock = 0;
+ if (scheduler < 0) {
+ /* dirty scheduler */
+ if (ERTS_PROC_IS_EXITING(c_p))
+ return 0;
+
+ if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) {
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+ unlock = 1;
+ }
+ }
+ res = erts_whereis_name_to_id(c_p, name);
+
+ if (unlock)
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+
+ return res;
+}
+
+int enif_whereis_pid(ErlNifEnv *env, ERL_NIF_TERM name, ErlNifPid *pid)
+{
+ Eterm res;
+
+ if (is_not_atom(name))
+ return 0;
+
+ res = call_whereis(env, name);
+ /* enif_get_local_ functions check the type */
+ return enif_get_local_pid(env, res, pid);
+}
+
+int enif_whereis_port(ErlNifEnv *env, ERL_NIF_TERM name, ErlNifPort *port)
+{
+ Eterm res;
+
+ if (is_not_atom(name))
+ return 0;
+
+ res = call_whereis(env, name);
+ /* enif_get_local_ functions check the type */
+ return enif_get_local_port(env, res, port);
+}
+
ERL_NIF_TERM enif_make_copy(ErlNifEnv* dst_env, ERL_NIF_TERM src_term)
{
Uint sz;
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index c305732d63..94c04cd126 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -181,6 +181,8 @@ ERL_NIF_API_FUNC_DECL(int, enif_monitor_process,(ErlNifEnv*,void* obj,const ErlN
ERL_NIF_API_FUNC_DECL(int, enif_demonitor_process,(ErlNifEnv*,void* obj,const ErlNifMonitor *monitor));
ERL_NIF_API_FUNC_DECL(int, enif_compare_monitors,(const ErlNifMonitor*,const ErlNifMonitor*));
ERL_NIF_API_FUNC_DECL(ErlNifUInt64,enif_hash,(ErlNifHash type, ERL_NIF_TERM term, ErlNifUInt64 salt));
+ERL_NIF_API_FUNC_DECL(int, enif_whereis_pid, (ErlNifEnv *env, ERL_NIF_TERM name, ErlNifPid *pid));
+ERL_NIF_API_FUNC_DECL(int, enif_whereis_port, (ErlNifEnv *env, ERL_NIF_TERM name, ErlNifPort *port));
/*
** ADD NEW ENTRIES HERE (before this comment) !!!
@@ -344,6 +346,8 @@ ERL_NIF_API_FUNC_DECL(ErlNifUInt64,enif_hash,(ErlNifHash type, ERL_NIF_TERM term
# define enif_demonitor_process ERL_NIF_API_FUNC_MACRO(enif_demonitor_process)
# define enif_compare_monitors ERL_NIF_API_FUNC_MACRO(enif_compare_monitors)
# define enif_hash ERL_NIF_API_FUNC_MACRO(enif_hash)
+# define enif_whereis_pid ERL_NIF_API_FUNC_MACRO(enif_whereis_pid)
+# define enif_whereis_port ERL_NIF_API_FUNC_MACRO(enif_whereis_port)
/*
** ADD NEW ENTRIES HERE (before this comment)
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index d4385e3987..fc2b34e70f 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -11220,8 +11220,9 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
reds--;
}
else {
- if (!minor_gc
- || (!major_gc && type == ERTS_PSTT_GC_MAJOR)) {
+ if ((!minor_gc
+ || (!major_gc && type == ERTS_PSTT_GC_MAJOR))
+ && !(c_p->flags & F_HIBERNATED)) {
if (type == ERTS_PSTT_GC_MAJOR) {
FLAGS(c_p) |= F_NEED_FULLSWEEP;
}
@@ -13932,7 +13933,11 @@ erts_continue_exit_process(Process *p)
erts_set_gc_state(p, 1);
state = erts_smp_atomic32_read_acqb(&p->state);
- if (state & ERTS_PSFLG_ACTIVE_SYS) {
+ if (state & ERTS_PSFLG_ACTIVE_SYS
+#ifdef ERTS_DIRTY_SCHEDULERS
+ || p->dirty_sys_tasks
+#endif
+ ) {
if (cleanup_sys_tasks(p, state, CONTEXT_REDS) >= CONTEXT_REDS/2)
goto yield;
}
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index d44e8c252d..9d7ba27c50 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -1422,6 +1422,7 @@ extern int erts_system_profile_ts_type;
#define F_DIRTY_GC_HIBERNATE (1 << 22) /* Dirty GC hibernate scheduled */
#define F_DIRTY_MAJOR_GC (1 << 23) /* Dirty major GC scheduled */
#define F_DIRTY_MINOR_GC (1 << 24) /* Dirty minor GC scheduled */
+#define F_HIBERNATED (1 << 25) /* Hibernated */
/*
* F_DISABLE_GC and F_DELAY_GC are similar. Both will prevent
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index bfebff5706..13ee935e45 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -10490,6 +10490,9 @@ static int tcp_send_or_shutdown_error(tcp_descriptor* desc, int err)
set_busy_port(desc->inet.port, 0);
}
+ tcp_clear_output(desc);
+ tcp_clear_input(desc);
+
/*
* We used to handle "expected errors" differently from unexpected ones.
* Now we handle all errors in the same way (unless the show_econnreset
@@ -10512,8 +10515,6 @@ static int tcp_send_or_shutdown_error(tcp_descriptor* desc, int err)
else
desc_close(INETP(desc));
} else {
- tcp_clear_output(desc);
- tcp_clear_input(desc);
tcp_close_check(desc);
erl_inet_close(INETP(desc));
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index 2479ccc01f..fcd7244ae9 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -117,7 +117,6 @@ MODULES= \
tracer_SUITE \
tracer_test \
scheduler_SUITE \
- old_scheduler_SUITE \
port_trace_SUITE \
unique_SUITE \
z_SUITE \
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index 355be7a36d..4d17276e5c 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -1358,17 +1358,19 @@ do_trapping(N, Bif, ArgFun) ->
io:format("N=~p: Do ~p ~s gc.\n", [N, Bif, case N rem 2 of 0 -> "with"; 1 -> "without" end]),
Pid = spawn(?MODULE,trapping_loop,[Bif, ArgFun, 1000, self()]),
receive ok -> ok end,
- receive after 100 -> ok end,
Ref = make_ref(),
case N rem 2 of
- 0 -> erlang:garbage_collect(Pid, [{async,Ref}]),
- receive after 100 -> ok end;
+ 0 ->
+ erlang:garbage_collect(Pid, [{async,Ref}]),
+ receive after 1 -> ok end;
1 -> void
end,
- exit(Pid,kill),
+ exit(Pid, kill),
case N rem 2 of
- 0 -> receive {garbage_collect, Ref, _} -> ok end;
- 1 -> void
+ 0 ->
+ receive {garbage_collect, Ref, _} -> ok end;
+ 1 ->
+ void
end,
receive after 1 -> ok end,
do_trapping(N-1, Bif, ArgFun).
diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl
index ed03284a5b..779d81daa5 100644
--- a/erts/emulator/test/bs_construct_SUITE.erl
+++ b/erts/emulator/test/bs_construct_SUITE.erl
@@ -35,7 +35,7 @@
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap, {seconds, 10}}].
+ {timetrap, {minutes, 1}}].
all() ->
[test1, test2, test3, test4, test5, testf, not_used,
diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl
index 95171d04ce..1251d644ae 100644
--- a/erts/emulator/test/call_trace_SUITE.erl
+++ b/erts/emulator/test/call_trace_SUITE.erl
@@ -43,7 +43,7 @@
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap, {seconds, 30}}].
+ {timetrap, {minutes, 2}}].
all() ->
Common = [errors, on_load],
diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl
index 35f7baf2cf..ab0fc0d42c 100644
--- a/erts/emulator/test/code_SUITE.erl
+++ b/erts/emulator/test/code_SUITE.erl
@@ -358,9 +358,40 @@ constant_pools(Config) when is_list(Config) ->
erlang:purge_module(literals),
OldHeap ! done,
receive
- {'EXIT',OldHeap,{A,B,C,[1,2,3|_]=Seq}} when length(Seq) =:= 16 ->
- ok
- end.
+ {'EXIT',OldHeap,{A,B,C,[1,2,3|_]=Seq}} when length(Seq) =:= 16 ->
+ ok
+ end,
+
+ {module,literals} = erlang:load_module(literals, Code),
+ %% Have a hibernated process that references the literals
+ %% in the 'literals' module.
+ {Hib, Mon} = spawn_monitor(fun() -> hibernated(Self) end),
+ receive go -> ok end,
+ [{heap_size,OldHeapSz},
+ {total_heap_size,OldTotHeapSz}] = process_info(Hib, [heap_size,
+ total_heap_size]),
+ OldHeapSz = OldTotHeapSz,
+ io:format("OldHeapSz=~p OldTotHeapSz=~p~n", [OldHeapSz, OldTotHeapSz]),
+ true = erlang:delete_module(literals),
+ false = erlang:check_process_code(Hib, literals),
+ erlang:check_process_code(self(), literals),
+ erlang:purge_module(literals),
+ receive after 1000 -> ok end,
+ [{heap_size,HeapSz},
+ {total_heap_size,TotHeapSz}] = process_info(Hib, [heap_size,
+ total_heap_size]),
+ io:format("HeapSz=~p TotHeapSz=~p~n", [HeapSz, TotHeapSz]),
+ Hib ! hej,
+ receive
+ {'DOWN', Mon, process, Hib, Reason} ->
+ {undef, [{no_module,
+ no_function,
+ [{A,B,C,[1,2,3|_]=Seq}], _}]} = Reason,
+ 16 = length(Seq)
+ end,
+ HeapSz = TotHeapSz, %% Ensure restored to hibernated state...
+ true = HeapSz > OldHeapSz,
+ ok.
no_old_heap(Parent) ->
A = literals:a(),
@@ -383,6 +414,13 @@ old_heap(Parent) ->
exit(Res)
end.
+hibernated(Parent) ->
+ A = literals:a(),
+ B = literals:b(),
+ Res = {A,B,literals:huge_bignum(),lists:seq(1, 16)},
+ Parent ! go,
+ erlang:hibernate(no_module, no_function, [Res]).
+
create_old_heap() ->
case process_info(self(), [heap_size,total_heap_size]) of
[{heap_size,Sz},{total_heap_size,Total}] when Sz < Total ->
diff --git a/erts/emulator/test/ddll_SUITE.erl b/erts/emulator/test/ddll_SUITE.erl
index 0b9f76a892..031b05790d 100644
--- a/erts/emulator/test/ddll_SUITE.erl
+++ b/erts/emulator/test/ddll_SUITE.erl
@@ -55,7 +55,7 @@
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap, {seconds, 10}}].
+ {timetrap, {minutes, 1}}].
all() ->
[ddll_test, errors, reference_count, kill_port,
diff --git a/erts/emulator/test/dirty_nif_SUITE.erl b/erts/emulator/test/dirty_nif_SUITE.erl
index f62f1e9dce..13806fd5c4 100644
--- a/erts/emulator/test/dirty_nif_SUITE.erl
+++ b/erts/emulator/test/dirty_nif_SUITE.erl
@@ -34,7 +34,8 @@
dirty_scheduler_exit/1, dirty_call_while_terminated/1,
dirty_heap_access/1, dirty_process_info/1,
dirty_process_register/1, dirty_process_trace/1,
- code_purge/1, dirty_nif_send_traced/1]).
+ code_purge/1, dirty_nif_send_traced/1,
+ nif_whereis/1, nif_whereis_parallel/1, nif_whereis_proxy/1]).
-define(nif_stub,nif_stub_error(?LINE)).
@@ -51,7 +52,9 @@ all() ->
dirty_process_register,
dirty_process_trace,
code_purge,
- dirty_nif_send_traced].
+ dirty_nif_send_traced,
+ nif_whereis,
+ nif_whereis_parallel].
init_per_suite(Config) ->
case erlang:system_info(dirty_cpu_schedulers) of
@@ -531,6 +534,137 @@ mcall(Node, Funs) ->
end
end, Refs).
+%% Test enif_whereis_...
+%% These tests are mostly identical to their counterparts in nif_SUITE.erl,
+%% with just name and count changes in the first few lines.
+
+nif_whereis(Config) when is_list(Config) ->
+ erl_ddll:try_load(?config(data_dir, Config), echo_drv, []),
+
+ RegName = dirty_nif_whereis_test_thing,
+ undefined = erlang:whereis(RegName),
+ false = whereis_term(pid, RegName),
+
+ Mgr = self(),
+ Ref = make_ref(),
+ ProcMsg = {Ref, ?LINE},
+ PortMsg = ?MODULE_STRING " whereis hello\n",
+
+ {Pid, Mon} = spawn_monitor(?MODULE, nif_whereis_proxy, [Ref]),
+ true = register(RegName, Pid),
+ Pid = erlang:whereis(RegName),
+ Pid = whereis_term(pid, RegName),
+ false = whereis_term(port, RegName),
+ false = whereis_term(pid, [RegName]),
+
+ ok = whereis_send(pid, RegName, {forward, Mgr, ProcMsg}),
+ ok = receive ProcMsg -> ok end,
+
+ Pid ! {Ref, quit},
+ ok = receive {'DOWN', Mon, process, Pid, normal} -> ok end,
+ undefined = erlang:whereis(RegName),
+ false = whereis_term(pid, RegName),
+
+ Port = open_port({spawn, echo_drv}, [eof]),
+ true = register(RegName, Port),
+ Port = erlang:whereis(RegName),
+ Port = whereis_term(port, RegName),
+ false = whereis_term(pid, RegName),
+ false = whereis_term(port, [RegName]),
+
+ ok = whereis_send(port, RegName, PortMsg),
+ ok = receive {Port, {data, PortMsg}} -> ok end,
+
+ port_close(Port),
+ undefined = erlang:whereis(RegName),
+ false = whereis_term(port, RegName),
+ ok.
+
+nif_whereis_parallel(Config) when is_list(Config) ->
+
+ %% try to be at least a little asymetric
+ NProcs = trunc(3.5 * erlang:system_info(schedulers)),
+ NSeq = lists:seq(1, NProcs),
+ Names = [list_to_atom("dirty_nif_whereis_proc_" ++ integer_to_list(N))
+ || N <- NSeq],
+ Mgr = self(),
+ Ref = make_ref(),
+
+ NotReg = fun(Name) ->
+ erlang:whereis(Name) == undefined
+ end,
+ PidReg = fun({Name, Pid, _Mon}) ->
+ erlang:whereis(Name) == Pid andalso whereis_term(pid, Name) == Pid
+ end,
+ RecvDown = fun({_Name, Pid, Mon}) ->
+ receive {'DOWN', Mon, process, Pid, normal} -> true
+ after 1500 -> false end
+ end,
+ RecvNum = fun(N) ->
+ receive {N, Ref} -> true
+ after 1500 -> false end
+ end,
+
+ true = lists:all(NotReg, Names),
+
+ %% {Name, Pid, Mon}
+ Procs = lists:map(
+ fun(N) ->
+ Name = lists:nth(N, Names),
+ Prev = lists:nth((if N == 1 -> NProcs; true -> (N - 1) end), Names),
+ Next = lists:nth((if N == NProcs -> 1; true -> (N + 1) end), Names),
+ {Pid, Mon} = spawn_monitor(
+ ?MODULE, nif_whereis_proxy, [{N, Ref, Mgr, [Prev, Next]}]),
+ true = register(Name, Pid),
+ {Name, Pid, Mon}
+ end, NSeq),
+
+ true = lists:all(PidReg, Procs),
+
+ %% tell them all to 'fire' as fast as we can
+ [P ! {Ref, send_proc} || {_, P, _} <- Procs],
+
+ %% each gets forwarded through two processes
+ true = lists:all(RecvNum, NSeq),
+ true = lists:all(RecvNum, NSeq),
+
+ %% tell them all to 'quit' by name
+ [N ! {Ref, quit} || {N, _, _} <- Procs],
+ true = lists:all(RecvDown, Procs),
+ true = lists:all(NotReg, Names),
+ ok.
+
+%% exported to be spawned by MFA by whereis tests
+nif_whereis_proxy({N, Ref, Mgr, Targets} = Args) ->
+ receive
+ {forward, To, Data} ->
+ To ! Data,
+ nif_whereis_proxy(Args);
+ {Ref, quit} ->
+ ok;
+ {Ref, send_port} ->
+ Msg = ?MODULE_STRING " whereis " ++ integer_to_list(N) ++ "\n",
+ lists:foreach(
+ fun(T) ->
+ ok = whereis_send(port, T, Msg)
+ end, Targets),
+ nif_whereis_proxy(Args);
+ {Ref, send_proc} ->
+ lists:foreach(
+ fun(T) ->
+ ok = whereis_send(pid, T, {forward, Mgr, {N, Ref}})
+ end, Targets),
+ nif_whereis_proxy(Args)
+ end;
+nif_whereis_proxy(Ref) ->
+ receive
+ {forward, To, Data} ->
+ To ! Data,
+ nif_whereis_proxy(Ref);
+ {Ref, quit} ->
+ ok
+ end.
+
%% The NIFs:
lib_loaded() -> false.
call_dirty_nif(_,_,_) -> ?nif_stub.
@@ -542,6 +676,8 @@ dirty_call_while_terminated_nif(_) -> ?nif_stub.
dirty_sleeper() -> ?nif_stub.
dirty_sleeper(_) -> ?nif_stub.
dirty_heap_access_nif(_) -> ?nif_stub.
+whereis_term(_Type,_Name) -> ?nif_stub.
+whereis_send(_Type,_Name,_Msg) -> ?nif_stub.
nif_stub_error(Line) ->
exit({nif_not_loaded,module,?MODULE,line,Line}).
diff --git a/erts/emulator/test/dirty_nif_SUITE_data/Makefile.src b/erts/emulator/test/dirty_nif_SUITE_data/Makefile.src
index e9301753b0..4462afd815 100644
--- a/erts/emulator/test/dirty_nif_SUITE_data/Makefile.src
+++ b/erts/emulator/test/dirty_nif_SUITE_data/Makefile.src
@@ -1,6 +1,6 @@
NIF_LIBS = dirty_nif_SUITE@dll@
-all: $(NIF_LIBS)
+all: $(NIF_LIBS) echo_drv@dll@
@SHLIB_RULES@
diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
index caf99c952f..1ab39466db 100644
--- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
+++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
@@ -25,8 +25,35 @@
#include <unistd.h>
#endif
+/*
+ * Hack to get around this function missing from the NIF API.
+ * TODO: Add this function/macro in the appropriate place, probably with
+ * enif_make_pid() in erl_nif_api_funcs.h
+ */
+#ifndef enif_make_port
+#define enif_make_port(ENV, PORT) ((void)(ENV),(const ERL_NIF_TERM)((PORT)->port_id))
+#endif
+
+static ERL_NIF_TERM atom_badarg;
+static ERL_NIF_TERM atom_error;
+static ERL_NIF_TERM atom_false;
+static ERL_NIF_TERM atom_lookup;
+static ERL_NIF_TERM atom_ok;
+static ERL_NIF_TERM atom_pid;
+static ERL_NIF_TERM atom_port;
+static ERL_NIF_TERM atom_send;
+
static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
{
+ atom_badarg = enif_make_atom(env, "badarg");
+ atom_error = enif_make_atom(env, "error");
+ atom_false = enif_make_atom(env,"false");
+ atom_lookup = enif_make_atom(env, "lookup");
+ atom_ok = enif_make_atom(env,"ok");
+ atom_pid = enif_make_atom(env, "pid");
+ atom_port = enif_make_atom(env, "port");
+ atom_send = enif_make_atom(env, "send");
+
return 0;
}
@@ -257,6 +284,147 @@ static ERL_NIF_TERM dirty_heap_access_nif(ErlNifEnv* env, int argc, const ERL_NI
return res;
}
+/*
+ * enif_whereis_... tests
+ * subset of the functions in nif_SUITE.c
+ */
+
+enum {
+ /* results */
+ WHEREIS_SUCCESS,
+ WHEREIS_ERROR_TYPE,
+ WHEREIS_ERROR_LOOKUP,
+ WHEREIS_ERROR_SEND,
+ /* types */
+ WHEREIS_LOOKUP_PID, /* enif_whereis_pid() */
+ WHEREIS_LOOKUP_PORT /* enif_whereis_port() */
+};
+
+typedef union {
+ ErlNifPid pid;
+ ErlNifPort port;
+} whereis_term_data_t;
+
+static int whereis_type(ERL_NIF_TERM type)
+{
+ if (enif_is_identical(type, atom_pid))
+ return WHEREIS_LOOKUP_PID;
+
+ if (enif_is_identical(type, atom_port))
+ return WHEREIS_LOOKUP_PORT;
+
+ return WHEREIS_ERROR_TYPE;
+}
+
+static int whereis_lookup_internal(
+ ErlNifEnv* env, int type, ERL_NIF_TERM name, whereis_term_data_t* out)
+{
+ if (type == WHEREIS_LOOKUP_PID)
+ return enif_whereis_pid(env, name, & out->pid)
+ ? WHEREIS_SUCCESS : WHEREIS_ERROR_LOOKUP;
+
+ if (type == WHEREIS_LOOKUP_PORT)
+ return enif_whereis_port(env, name, & out->port)
+ ? WHEREIS_SUCCESS : WHEREIS_ERROR_LOOKUP;
+
+ return WHEREIS_ERROR_TYPE;
+}
+
+static int whereis_send_internal(
+ ErlNifEnv* env, int type, whereis_term_data_t* to, ERL_NIF_TERM msg)
+{
+ if (type == WHEREIS_LOOKUP_PID)
+ return enif_send(env, & to->pid, NULL, msg)
+ ? WHEREIS_SUCCESS : WHEREIS_ERROR_SEND;
+
+ if (type == WHEREIS_LOOKUP_PORT)
+ return enif_port_command(env, & to->port, NULL, msg)
+ ? WHEREIS_SUCCESS : WHEREIS_ERROR_SEND;
+
+ return WHEREIS_ERROR_TYPE;
+}
+
+static int whereis_lookup_term(
+ ErlNifEnv* env, int type, ERL_NIF_TERM name, ERL_NIF_TERM* out)
+{
+ whereis_term_data_t res;
+ int rc = whereis_lookup_internal(env, type, name, &res);
+ if (rc == WHEREIS_SUCCESS) {
+ switch (type) {
+ case WHEREIS_LOOKUP_PID:
+ *out = enif_make_pid(env, & res.pid);
+ break;
+ case WHEREIS_LOOKUP_PORT:
+ *out = enif_make_port(env, & res.port);
+ break;
+ default:
+ rc = WHEREIS_ERROR_TYPE;
+ break;
+ }
+ }
+ return rc;
+}
+
+static ERL_NIF_TERM whereis_result_term(ErlNifEnv* env, int result)
+{
+ ERL_NIF_TERM err;
+ switch (result)
+ {
+ case WHEREIS_SUCCESS:
+ return atom_ok;
+ case WHEREIS_ERROR_LOOKUP:
+ err = atom_lookup;
+ break;
+ case WHEREIS_ERROR_SEND:
+ err = atom_send;
+ break;
+ case WHEREIS_ERROR_TYPE:
+ err = atom_badarg;
+ break;
+ default:
+ err = enif_make_int(env, -result);
+ break;
+ }
+ return enif_make_tuple2(env, atom_error, err);
+}
+
+/* whereis_term(Type, Name) -> pid() | port() | false */
+static ERL_NIF_TERM
+whereis_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM ret;
+ int type, rc;
+
+ if (argc != 2) /* allow non-atom name for testing */
+ return enif_make_badarg(env);
+
+ if ((type = whereis_type(argv[0])) == WHEREIS_ERROR_TYPE)
+ return enif_make_badarg(env);
+
+ rc = whereis_lookup_term(env, type, argv[1], &ret);
+ return (rc == WHEREIS_SUCCESS) ? ret : atom_false;
+}
+
+/* whereis_send(Type, Name, Message) -> ok | {error, Reason} */
+static ERL_NIF_TERM
+whereis_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ whereis_term_data_t to;
+ int type, rc;
+
+ if (argc != 3 || !enif_is_atom(env, argv[1]))
+ return enif_make_badarg(env);
+
+ if ((type = whereis_type(argv[0])) == WHEREIS_ERROR_TYPE)
+ return enif_make_badarg(env);
+
+ rc = whereis_lookup_internal(env, type, argv[1], & to);
+ if (rc == WHEREIS_SUCCESS)
+ rc = whereis_send_internal(env, type, & to, argv[2]);
+
+ return whereis_result_term(env, rc);
+}
+
static ErlNifFunc nif_funcs[] =
{
@@ -269,7 +437,9 @@ static ErlNifFunc nif_funcs[] =
{"dirty_sleeper", 0, dirty_sleeper, ERL_NIF_DIRTY_JOB_IO_BOUND},
{"dirty_sleeper", 1, dirty_sleeper, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"dirty_call_while_terminated_nif", 1, dirty_call_while_terminated_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND},
- {"dirty_heap_access_nif", 1, dirty_heap_access_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND}
+ {"dirty_heap_access_nif", 1, dirty_heap_access_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND},
+ {"whereis_send", 3, whereis_send, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"whereis_term", 2, whereis_term, ERL_NIF_DIRTY_JOB_CPU_BOUND}
};
ERL_NIF_INIT(dirty_nif_SUITE,nif_funcs,load,NULL,NULL,NULL)
diff --git a/erts/emulator/test/dirty_nif_SUITE_data/echo_drv.c b/erts/emulator/test/dirty_nif_SUITE_data/echo_drv.c
new file mode 100644
index 0000000000..2b3510c641
--- /dev/null
+++ b/erts/emulator/test/dirty_nif_SUITE_data/echo_drv.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include "erl_driver.h"
+
+static ErlDrvPort erlang_port;
+static ErlDrvData echo_start(ErlDrvPort, char *);
+static void from_erlang(ErlDrvData, char*, ErlDrvSizeT);
+static ErlDrvSSizeT echo_call(ErlDrvData drv_data, unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen, unsigned *ret_flags);
+static ErlDrvEntry echo_driver_entry = {
+ NULL, /* Init */
+ echo_start,
+ NULL, /* Stop */
+ from_erlang,
+ NULL, /* Ready input */
+ NULL, /* Ready output */
+ "echo_drv",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ echo_call,
+ NULL,
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ 0,
+ NULL,
+ NULL,
+ NULL
+};
+
+DRIVER_INIT(echo_drv)
+{
+ return &echo_driver_entry;
+}
+
+static ErlDrvData
+echo_start(ErlDrvPort port, char *buf)
+{
+ return (ErlDrvData) port;
+}
+
+static void
+from_erlang(ErlDrvData data, char *buf, ErlDrvSizeT count)
+{
+ driver_output((ErlDrvPort) data, buf, count);
+}
+
+static ErlDrvSSizeT
+echo_call(ErlDrvData drv_data, unsigned int command,
+ char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen,
+ unsigned *ret_flags)
+{
+ *rbuf = buf;
+ *ret_flags |= DRIVER_CALL_KEEP_BUFFER;
+ return len;
+}
+
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index 434e729310..dc7afa381b 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -62,8 +62,7 @@
-export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0,
roundtrip/1, bounce/1, do_dist_auto_connect/1, inet_rpc_server/1,
dist_parallel_sender/3, dist_parallel_receiver/0,
- dist_evil_parallel_receiver/0,
- sendersender/4, sendersender2/4]).
+ dist_evil_parallel_receiver/0]).
%% epmd_module exports
-export([start_link/0, register_node/2, register_node/3, port_please/2]).
@@ -129,52 +128,53 @@ bulk_send_small(Config) when is_list(Config) ->
bulk_send_big(Config) when is_list(Config) ->
bulk_send(32, 64).
-bulk_send_bigbig(Config) when is_list(Config) ->
- bulk_sendsend(32*5, 4).
-
bulk_send(Terms, BinSize) ->
ct:timetrap({seconds, 30}),
io:format("Sending ~w binaries, each of size ~w K", [Terms, BinSize]),
{ok, Node} = start_node(bulk_receiver),
Recv = spawn(Node, erlang, apply, [fun receiver/2, [0, 0]]),
- Bin = list_to_binary(lists:duplicate(BinSize*1024, 253)),
+ Bin = binary:copy(<<253>>, BinSize*1024),
Size = Terms*size(Bin),
{Elapsed, {Terms, Size}} = test_server:timecall(?MODULE, sender,
[Recv, Bin, Terms]),
stop_node(Node),
- {comment, integer_to_list(trunc(Size/1024/max(1,Elapsed)+0.5)) ++ " K/s"}.
+ {comment, integer_to_list(round(Size/1024/max(1,Elapsed))) ++ " K/s"}.
-bulk_sendsend(Terms, BinSize) ->
+sender(To, _Bin, 0) ->
+ To ! {done, self()},
+ receive
+ Any ->
+ Any
+ end;
+sender(To, Bin, Left) ->
+ To ! {term, Bin},
+ sender(To, Bin, Left-1).
+
+bulk_send_bigbig(Config) when is_list(Config) ->
+ Terms = 32*5,
+ BinSize = 4,
{Rate1, MonitorCount1} = bulk_sendsend2(Terms, BinSize, 5),
{Rate2, MonitorCount2} = bulk_sendsend2(Terms, BinSize, 995),
Ratio = if MonitorCount2 == 0 -> MonitorCount1 / 1.0;
true -> MonitorCount1 / MonitorCount2
end,
- Comment = integer_to_list(Rate1) ++ " K/s, " ++
- integer_to_list(Rate2) ++ " K/s, " ++
- integer_to_list(MonitorCount1) ++ " monitor msgs, " ++
- integer_to_list(MonitorCount2) ++ " monitor msgs, " ++
- float_to_list(Ratio) ++ " monitor ratio",
- if
- %% A somewhat arbitrary ratio, but hopefully one that will
- %% accommodate a wide range of CPU speeds.
- Ratio > 8.0 ->
- {comment,Comment};
- true ->
- io:put_chars(Comment),
- ct:fail(ratio_too_low)
- end.
+ Comment0 = io_lib:format("~p K/s, ~p K/s, "
+ "~p monitor msgs, ~p monitor msgs, "
+ "~.1f monitor ratio",
+ [Rate1,Rate2,MonitorCount1,
+ MonitorCount2,Ratio]),
+ Comment = lists:flatten(Comment0),
+ {comment,Comment}.
bulk_sendsend2(Terms, BinSize, BusyBufSize) ->
ct:timetrap({seconds, 30}),
- io:format("Sending ~w binaries, each of size ~w K",
+ io:format("\nSending ~w binaries, each of size ~w K",
[Terms, BinSize]),
{ok, NodeRecv} = start_node(bulk_receiver),
Recv = spawn(NodeRecv, erlang, apply, [fun receiver/2, [0, 0]]),
- Bin = list_to_binary(lists:duplicate(BinSize*1024, 253)),
- %%Size = Terms*size(Bin),
+ Bin = binary:copy(<<253>>, BinSize*1024),
%% SLF LEFT OFF HERE.
%% When the caller uses small hunks, like 4k via
@@ -185,74 +185,62 @@ bulk_sendsend2(Terms, BinSize, BusyBufSize) ->
%% default busy size and "+zdbbl 5", and if the 5 case gets
%% "many many more" monitor messages, then we know we're working.
- {ok, NodeSend} = start_node(bulk_sender, "+zdbbl " ++ integer_to_list(BusyBufSize)),
- _Send = spawn(NodeSend, erlang, apply, [fun sendersender/4, [self(), Recv, Bin, Terms]]),
+ {ok, NodeSend} = start_node(bulk_sender, "+zdbbl " ++
+ integer_to_list(BusyBufSize)),
+ _Send = spawn(NodeSend, erlang, apply,
+ [fun sendersender/4, [self(), Recv, Bin, Terms]]),
{Elapsed, {_TermsN, SizeN}, MonitorCount} =
- receive
- %% On some platforms (windows), the time taken is 0 so we
- %% simulate that some little time has passed.
- {sendersender, {0.0,T,MC}} ->
- {0.0015, T, MC};
- {sendersender, BigRes} ->
- BigRes
- end,
+ receive
+ %% On some platforms (Windows), the time taken is 0 so we
+ %% simulate that some little time has passed.
+ {sendersender, {0.0,T,MC}} ->
+ {0.0015, T, MC};
+ {sendersender, BigRes} ->
+ BigRes
+ end,
stop_node(NodeRecv),
stop_node(NodeSend),
- {trunc(SizeN/1024/Elapsed+0.5), MonitorCount}.
-
-sender(To, _Bin, 0) ->
- To ! {done, self()},
- receive
- Any ->
- Any
- end;
-sender(To, Bin, Left) ->
- To ! {term, Bin},
- sender(To, Bin, Left-1).
+ {round(SizeN/1024/Elapsed), MonitorCount}.
%% Sender process to be run on a slave node
sendersender(Parent, To, Bin, Left) ->
erlang:system_monitor(self(), [busy_dist_port]),
- [spawn(fun() -> sendersender2(To, Bin, Left, false) end) ||
- _ <- lists:seq(1,1)],
+ _ = spawn(fun() ->
+ sendersender_send(To, Bin, Left),
+ exit(normal)
+ end),
{USec, {Res, MonitorCount}} =
- timer:tc(?MODULE, sendersender2, [To, Bin, Left, true]),
+ timer:tc(fun() ->
+ sendersender_send(To, Bin, Left),
+ To ! {done, self()},
+ count_monitors(0)
+ end),
Parent ! {sendersender, {USec/1000000, Res, MonitorCount}}.
-sendersender2(To, Bin, Left, SendDone) ->
- sendersender3(To, Bin, Left, SendDone, 0).
+sendersender_send(_To, _Bin, 0) ->
+ ok;
+sendersender_send(To, Bin, Left) ->
+ To ! {term, Bin},
+ sendersender_send(To, Bin, Left-1).
-sendersender3(To, _Bin, 0, SendDone, MonitorCount) ->
- if SendDone ->
- To ! {done, self()};
- true ->
- ok
- end,
+count_monitors(MonitorCount) ->
receive
{monitor, _Pid, _Type, _Info} ->
- sendersender3(To, _Bin, 0, SendDone, MonitorCount + 1)
+ count_monitors(MonitorCount + 1)
after 0 ->
- if SendDone ->
- receive
- Any when is_tuple(Any), size(Any) == 2 ->
- {Any, MonitorCount}
- end;
- true ->
- exit(normal)
- end
- end;
-sendersender3(To, Bin, Left, SendDone, MonitorCount) ->
- To ! {term, Bin},
- %%timer:sleep(50),
- sendersender3(To, Bin, Left-1, SendDone, MonitorCount).
+ receive
+ {_,_}=Any ->
+ {Any,MonitorCount}
+ end
+ end.
%% Receiver process to be run on a slave node.
receiver(Terms, Size) ->
receive
{term, Bin} ->
- receiver(Terms+1, Size+size(Bin));
+ receiver(Terms+1, Size+byte_size(Bin));
{done, ReplyTo} ->
ReplyTo ! {Terms, Size}
end.
diff --git a/erts/emulator/test/evil_SUITE.erl b/erts/emulator/test/evil_SUITE.erl
index 9416ac7a02..fb1954ce37 100644
--- a/erts/emulator/test/evil_SUITE.erl
+++ b/erts/emulator/test/evil_SUITE.erl
@@ -34,7 +34,7 @@
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap, {seconds, 30}}].
+ {timetrap, {minutes, 1}}].
all() ->
[heap_frag, encode_decode_ext, decode_integer_ext,
diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl
index 76e3556bc4..ad6d8c890f 100644
--- a/erts/emulator/test/exception_SUITE.erl
+++ b/erts/emulator/test/exception_SUITE.erl
@@ -33,7 +33,7 @@
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap, {seconds, 10}}].
+ {timetrap, {minutes, 1}}].
all() ->
[badmatch, pending_errors, nil_arith, stacktrace,
diff --git a/erts/emulator/test/lttng_SUITE.erl b/erts/emulator/test/lttng_SUITE.erl
index c12f63706a..1c1952f912 100644
--- a/erts/emulator/test/lttng_SUITE.erl
+++ b/erts/emulator/test/lttng_SUITE.erl
@@ -40,7 +40,7 @@
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap, {seconds, 10}}].
+ {timetrap, {minutes, 1}}].
all() ->
[t_lttng_list,
diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl
index eb189c2c33..92ddc23592 100644
--- a/erts/emulator/test/match_spec_SUITE.erl
+++ b/erts/emulator/test/match_spec_SUITE.erl
@@ -42,7 +42,7 @@
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap, {seconds, 30}}].
+ {timetrap, {minutes, 1}}].
all() ->
case test_server:is_native(match_spec_SUITE) of
diff --git a/erts/emulator/test/nested_SUITE.erl b/erts/emulator/test/nested_SUITE.erl
index 7af2873ce2..5059317172 100644
--- a/erts/emulator/test/nested_SUITE.erl
+++ b/erts/emulator/test/nested_SUITE.erl
@@ -27,7 +27,7 @@
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap, {seconds, 10}}].
+ {timetrap, {minutes, 1}}].
all() ->
[case_in_case, case_in_after, catch_in_catch,
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index bcea9e3539..05c250125d 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -59,7 +59,9 @@
nif_snprintf/1,
nif_internal_hash/1,
nif_internal_hash_salted/1,
- nif_phash2/1
+ nif_phash2/1,
+ nif_whereis/1, nif_whereis_parallel/1,
+ nif_whereis_threaded/1, nif_whereis_proxy/1
]).
-export([many_args_100/100]).
@@ -96,7 +98,8 @@ all() ->
nif_snprintf,
nif_internal_hash,
nif_internal_hash_salted,
- nif_phash2].
+ nif_phash2,
+ nif_whereis, nif_whereis_parallel, nif_whereis_threaded].
groups() ->
[{G, [], api_repeaters()} || G <- api_groups()]
@@ -134,6 +137,11 @@ init_per_testcase(hipe, Config) ->
undefined -> {skip, "HiPE is disabled"};
_ -> Config
end;
+init_per_testcase(nif_whereis_threaded, Config) ->
+ case erlang:system_info(threads) of
+ true -> Config;
+ false -> {skip, "No thread support"}
+ end;
init_per_testcase(select, Config) ->
case os:type() of
{win32,_} ->
@@ -2791,6 +2799,161 @@ random_pid() ->
Processes = erlang:processes(),
lists:nth(rand:uniform(length(Processes)), Processes).
+%% Test enif_whereis_...
+
+nif_whereis(Config) when is_list(Config) ->
+ ensure_lib_loaded(Config),
+
+ RegName = nif_whereis_test_thing,
+ undefined = erlang:whereis(RegName),
+ false = whereis_term(pid, RegName),
+
+ Mgr = self(),
+ Ref = make_ref(),
+ ProcMsg = {Ref, ?LINE},
+ PortMsg = ?MODULE_STRING " whereis hello\n",
+
+ {Pid, Mon} = spawn_monitor(?MODULE, nif_whereis_proxy, [Ref]),
+ true = register(RegName, Pid),
+ Pid = erlang:whereis(RegName),
+ Pid = whereis_term(pid, RegName),
+ false = whereis_term(port, RegName),
+ false = whereis_term(pid, [RegName]),
+
+ ok = whereis_send(pid, RegName, {forward, Mgr, ProcMsg}),
+ ok = receive ProcMsg -> ok end,
+
+ Pid ! {Ref, quit},
+ ok = receive {'DOWN', Mon, process, Pid, normal} -> ok end,
+ undefined = erlang:whereis(RegName),
+ false = whereis_term(pid, RegName),
+
+ Port = open_port({spawn, echo_drv}, [eof]),
+ true = register(RegName, Port),
+ Port = erlang:whereis(RegName),
+ Port = whereis_term(port, RegName),
+ false = whereis_term(pid, RegName),
+ false = whereis_term(port, [RegName]),
+
+ ok = whereis_send(port, RegName, PortMsg),
+ ok = receive {Port, {data, PortMsg}} -> ok end,
+
+ port_close(Port),
+ undefined = erlang:whereis(RegName),
+ false = whereis_term(port, RegName),
+ ok.
+
+nif_whereis_parallel(Config) when is_list(Config) ->
+ ensure_lib_loaded(Config),
+
+ %% try to be at least a little asymetric
+ NProcs = trunc(3.7 * erlang:system_info(schedulers)),
+ NSeq = lists:seq(1, NProcs),
+ Names = [list_to_atom("nif_whereis_proc_" ++ integer_to_list(N))
+ || N <- NSeq],
+ Mgr = self(),
+ Ref = make_ref(),
+
+ NotReg = fun(Name) ->
+ erlang:whereis(Name) == undefined
+ end,
+ PidReg = fun({Name, Pid, _Mon}) ->
+ erlang:whereis(Name) == Pid andalso whereis_term(pid, Name) == Pid
+ end,
+ RecvDown = fun({_Name, Pid, Mon}) ->
+ receive {'DOWN', Mon, process, Pid, normal} -> true
+ after 1500 -> false end
+ end,
+ RecvNum = fun(N) ->
+ receive {N, Ref} -> true
+ after 1500 -> false end
+ end,
+
+ true = lists:all(NotReg, Names),
+
+ %% {Name, Pid, Mon}
+ Procs = lists:map(
+ fun(N) ->
+ Name = lists:nth(N, Names),
+ Prev = lists:nth((if N == 1 -> NProcs; true -> (N - 1) end), Names),
+ Next = lists:nth((if N == NProcs -> 1; true -> (N + 1) end), Names),
+ {Pid, Mon} = spawn_monitor(
+ ?MODULE, nif_whereis_proxy, [{N, Ref, Mgr, [Prev, Next]}]),
+ true = register(Name, Pid),
+ {Name, Pid, Mon}
+ end, NSeq),
+
+ true = lists:all(PidReg, Procs),
+
+ %% tell them all to 'fire' as fast as we can
+ [P ! {Ref, send_proc} || {_, P, _} <- Procs],
+
+ %% each gets forwarded through two processes
+ true = lists:all(RecvNum, NSeq),
+ true = lists:all(RecvNum, NSeq),
+
+ %% tell them all to 'quit' by name
+ [N ! {Ref, quit} || {N, _, _} <- Procs],
+ true = lists:all(RecvDown, Procs),
+ true = lists:all(NotReg, Names),
+ ok.
+
+nif_whereis_threaded(Config) when is_list(Config) ->
+ ensure_lib_loaded(Config),
+
+ RegName = nif_whereis_test_threaded,
+ undefined = erlang:whereis(RegName),
+
+ Ref = make_ref(),
+ {Pid, Mon} = spawn_monitor(?MODULE, nif_whereis_proxy, [Ref]),
+ true = register(RegName, Pid),
+
+ {ok, ProcThr} = whereis_thd_lookup(pid, RegName),
+ {ok, Pid} = whereis_thd_result(ProcThr),
+
+ Pid ! {Ref, quit},
+ ok = receive {'DOWN', Mon, process, Pid, normal} -> ok end,
+
+ Port = open_port({spawn, echo_drv}, [eof]),
+ true = register(RegName, Port),
+
+ {ok, PortThr} = whereis_thd_lookup(port, RegName),
+ {ok, Port} = whereis_thd_result(PortThr),
+
+ port_close(Port),
+ ok.
+
+%% exported to be spawned by MFA by whereis tests
+nif_whereis_proxy({N, Ref, Mgr, Targets} = Args) ->
+ receive
+ {forward, To, Data} ->
+ To ! Data,
+ nif_whereis_proxy(Args);
+ {Ref, quit} ->
+ ok;
+ {Ref, send_port} ->
+ Msg = ?MODULE_STRING " whereis " ++ integer_to_list(N) ++ "\n",
+ lists:foreach(
+ fun(T) ->
+ ok = whereis_send(port, T, Msg)
+ end, Targets),
+ nif_whereis_proxy(Args);
+ {Ref, send_proc} ->
+ lists:foreach(
+ fun(T) ->
+ ok = whereis_send(pid, T, {forward, Mgr, {N, Ref}})
+ end, Targets),
+ nif_whereis_proxy(Args)
+ end;
+nif_whereis_proxy(Ref) ->
+ receive
+ {forward, To, Data} ->
+ To ! Data,
+ nif_whereis_proxy(Ref);
+ {Ref, quit} ->
+ ok
+ end.
+
%% The NIFs:
lib_version() -> undefined.
call_history() -> ?nif_stub.
@@ -2866,6 +3029,12 @@ demonitor_process_nif(_,_) -> ?nif_stub.
compare_monitors_nif(_,_) -> ?nif_stub.
monitor_frenzy_nif(_,_,_,_) -> ?nif_stub.
+%% whereis
+whereis_send(_Type,_Name,_Msg) -> ?nif_stub.
+whereis_term(_Type,_Name) -> ?nif_stub.
+whereis_thd_lookup(_Type,_Name) -> ?nif_stub.
+whereis_thd_result(_Thd) -> ?nif_stub.
+
%% maps
is_map_nif(_) -> ?nif_stub.
get_map_size_nif(_) -> ?nif_stub.
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 15d31162ed..307d1c390f 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -52,6 +52,15 @@ static ErlNifMutex* dbg_trace_lock;
#define DBG_TRACE4(FMT, A, B, C, D)
#endif
+/*
+ * Hack to get around this function missing from the NIF API.
+ * TODO: Add this function/macro in the appropriate place, probably with
+ * enif_make_pid() in erl_nif_api_funcs.h
+ */
+#ifndef enif_make_port
+#define enif_make_port(ENV, PORT) ((void)(ENV),(const ERL_NIF_TERM)((PORT)->port_id))
+#endif
+
static int static_cntA; /* zero by default */
static int static_cntB = NIF_SUITE_LIB_VER * 100;
@@ -76,6 +85,11 @@ static ERL_NIF_TERM atom_stats;
static ERL_NIF_TERM atom_done;
static ERL_NIF_TERM atom_stop;
static ERL_NIF_TERM atom_null;
+static ERL_NIF_TERM atom_pid;
+static ERL_NIF_TERM atom_port;
+static ERL_NIF_TERM atom_send;
+static ERL_NIF_TERM atom_lookup;
+static ERL_NIF_TERM atom_badarg;
typedef struct
{
@@ -170,6 +184,9 @@ static ErlNifResourceTypeInit frenzy_rt_init = {
frenzy_resource_down
};
+static ErlNifResourceType* whereis_resource_type;
+static void whereis_thread_resource_dtor(ErlNifEnv* env, void* obj);
+
static int get_pointer(ErlNifEnv* env, ERL_NIF_TERM term, void** pp)
{
ErlNifBinary bin;
@@ -223,6 +240,9 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
&frenzy_rt_init,
ERL_NIF_RT_CREATE, NULL);
+ whereis_resource_type = enif_open_resource_type(env, NULL, "nif_SUITE.whereis",
+ whereis_thread_resource_dtor, ERL_NIF_RT_CREATE, NULL);
+
atom_false = enif_make_atom(env,"false");
atom_true = enif_make_atom(env,"true");
atom_self = enif_make_atom(env,"self");
@@ -244,6 +264,11 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
atom_done = enif_make_atom(env,"done");
atom_stop = enif_make_atom(env,"stop");
atom_null = enif_make_atom(env,"null");
+ atom_pid = enif_make_atom(env, "pid");
+ atom_port = enif_make_atom(env, "port");
+ atom_send = enif_make_atom(env, "send");
+ atom_lookup = enif_make_atom(env, "lookup");
+ atom_badarg = enif_make_atom(env, "badarg");
*priv_data = data;
return 0;
@@ -1161,6 +1186,237 @@ static void fill(void* dst, unsigned bytes, int seed)
}
}
+/* enif_whereis_... tests */
+
+enum {
+ /* results */
+ WHEREIS_SUCCESS,
+ WHEREIS_ERROR_TYPE,
+ WHEREIS_ERROR_LOOKUP,
+ WHEREIS_ERROR_SEND,
+ /* types */
+ WHEREIS_LOOKUP_PID, /* enif_whereis_pid() */
+ WHEREIS_LOOKUP_PORT /* enif_whereis_port() */
+};
+
+typedef union {
+ ErlNifPid pid;
+ ErlNifPort port;
+} whereis_term_data_t;
+
+/* single use, no cross-thread access/serialization */
+typedef struct {
+ ErlNifEnv* env;
+ ERL_NIF_TERM name;
+ whereis_term_data_t res;
+ ErlNifTid tid;
+ int type;
+} whereis_thread_resource_t;
+
+static whereis_thread_resource_t* whereis_thread_resource_create(void)
+{
+ whereis_thread_resource_t* rp = (whereis_thread_resource_t*)
+ enif_alloc_resource(whereis_resource_type, sizeof(*rp));
+ memset(rp, 0, sizeof(*rp));
+ rp->env = enif_alloc_env();
+
+ return rp;
+}
+
+static void whereis_thread_resource_dtor(ErlNifEnv* env, void* obj)
+{
+ whereis_thread_resource_t* rp = (whereis_thread_resource_t*) obj;
+ enif_free_env(rp->env);
+}
+
+static int whereis_type(ERL_NIF_TERM type)
+{
+ if (enif_is_identical(type, atom_pid))
+ return WHEREIS_LOOKUP_PID;
+
+ if (enif_is_identical(type, atom_port))
+ return WHEREIS_LOOKUP_PORT;
+
+ return WHEREIS_ERROR_TYPE;
+}
+
+static int whereis_lookup_internal(
+ ErlNifEnv* env, int type, ERL_NIF_TERM name, whereis_term_data_t* out)
+{
+ if (type == WHEREIS_LOOKUP_PID)
+ return enif_whereis_pid(env, name, & out->pid)
+ ? WHEREIS_SUCCESS : WHEREIS_ERROR_LOOKUP;
+
+ if (type == WHEREIS_LOOKUP_PORT)
+ return enif_whereis_port(env, name, & out->port)
+ ? WHEREIS_SUCCESS : WHEREIS_ERROR_LOOKUP;
+
+ return WHEREIS_ERROR_TYPE;
+}
+
+static int whereis_send_internal(
+ ErlNifEnv* env, int type, whereis_term_data_t* to, ERL_NIF_TERM msg)
+{
+ if (type == WHEREIS_LOOKUP_PID)
+ return enif_send(env, & to->pid, NULL, msg)
+ ? WHEREIS_SUCCESS : WHEREIS_ERROR_SEND;
+
+ if (type == WHEREIS_LOOKUP_PORT)
+ return enif_port_command(env, & to->port, NULL, msg)
+ ? WHEREIS_SUCCESS : WHEREIS_ERROR_SEND;
+
+ return WHEREIS_ERROR_TYPE;
+}
+
+static int whereis_resolved_term(
+ ErlNifEnv* env, int type, whereis_term_data_t* res, ERL_NIF_TERM* out)
+{
+ switch (type) {
+ case WHEREIS_LOOKUP_PID:
+ *out = enif_make_pid(env, & res->pid);
+ break;
+ case WHEREIS_LOOKUP_PORT:
+ *out = enif_make_port(env, & res->port);
+ break;
+ default:
+ return WHEREIS_ERROR_TYPE;
+ }
+ return WHEREIS_SUCCESS;
+}
+
+static ERL_NIF_TERM whereis_result_term(ErlNifEnv* env, int result)
+{
+ ERL_NIF_TERM err;
+ switch (result)
+ {
+ case WHEREIS_SUCCESS:
+ return atom_ok;
+ case WHEREIS_ERROR_LOOKUP:
+ err = atom_lookup;
+ break;
+ case WHEREIS_ERROR_SEND:
+ err = atom_send;
+ break;
+ case WHEREIS_ERROR_TYPE:
+ err = atom_badarg;
+ break;
+ default:
+ err = enif_make_int(env, -result);
+ break;
+ }
+ return enif_make_tuple2(env, atom_error, err);
+}
+
+static void* whereis_lookup_thread(void* arg)
+{
+ whereis_thread_resource_t* rp = (whereis_thread_resource_t*) arg;
+ int rc;
+
+ /* enif_whereis_xxx should work with allocated or null env */
+ rc = whereis_lookup_internal(
+ ((rp->type == WHEREIS_LOOKUP_PID) ? NULL : rp->env),
+ rp->type, rp->name, & rp->res);
+
+ return (((char*) NULL) + rc);
+}
+
+/* whereis_term(Type, Name) -> pid() | port() | false */
+static ERL_NIF_TERM
+whereis_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ whereis_term_data_t res;
+ ERL_NIF_TERM ret;
+ int type, rc;
+
+ if (argc != 2) /* allow non-atom name for testing */
+ return enif_make_badarg(env);
+
+ if ((type = whereis_type(argv[0])) == WHEREIS_ERROR_TYPE)
+ return enif_make_badarg(env);
+
+ rc = whereis_lookup_internal(env, type, argv[1], & res);
+ if (rc == WHEREIS_SUCCESS) {
+ rc = whereis_resolved_term(env, type, & res, & ret);
+ }
+ return (rc == WHEREIS_SUCCESS) ? ret : atom_false;
+}
+
+/* whereis_send(Type, Name, Message) -> ok | {error, Reason} */
+static ERL_NIF_TERM
+whereis_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ whereis_term_data_t to;
+ int type, rc;
+
+ if (argc != 3 || !enif_is_atom(env, argv[1]))
+ return enif_make_badarg(env);
+
+ if ((type = whereis_type(argv[0])) == WHEREIS_ERROR_TYPE)
+ return enif_make_badarg(env);
+
+ rc = whereis_lookup_internal(env, type, argv[1], & to);
+ if (rc == WHEREIS_SUCCESS)
+ rc = whereis_send_internal(env, type, & to, argv[2]);
+
+ return whereis_result_term(env, rc);
+}
+
+/* whereis_thd_lookup(Type, Name) -> {ok, Resource} | {error, SysErrno} */
+static ERL_NIF_TERM
+whereis_thd_lookup(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ whereis_thread_resource_t* rp;
+ int type, rc;
+
+ if (argc != 2 || !enif_is_atom(env, argv[1]))
+ return enif_make_badarg(env);
+
+ if ((type = whereis_type(argv[0])) == WHEREIS_ERROR_TYPE)
+ return enif_make_badarg(env);
+
+ rp = whereis_thread_resource_create();
+ rp->type = type;
+ rp->name = enif_make_copy(rp->env, argv[1]);
+
+ rc = enif_thread_create(
+ "nif_SUITE:whereis_thd", & rp->tid, whereis_lookup_thread, rp, NULL);
+
+ if (rc == 0) {
+ return enif_make_tuple2(env, atom_ok, enif_make_resource(env, rp));
+ }
+ else {
+ enif_release_resource(rp);
+ return enif_make_tuple2(env, atom_error, enif_make_int(env, rc));
+ }
+}
+
+/* whereis_thd_result(Resource) -> {ok, pid() | port()} | {error, ErrNum} */
+static ERL_NIF_TERM
+whereis_thd_result(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ whereis_thread_resource_t* rp;
+ ERL_NIF_TERM ret;
+ char* thdret; /* so we can keep compilers happy converting to int */
+ int rc;
+
+ if (argc != 1
+ || !enif_get_resource(env, argv[0], whereis_resource_type, (void**) & rp))
+ return enif_make_badarg(env);
+
+ if ((rc = enif_thread_join(rp->tid, (void**) & thdret)) != 0)
+ return enif_make_tuple2(env, atom_error, enif_make_int(env, rc));
+
+ rc = (int)(thdret - ((char*) NULL));
+ if (rc == WHEREIS_SUCCESS) {
+ rc = whereis_resolved_term(env, rp->type, & rp->res, & ret);
+ }
+ ret = (rc == WHEREIS_SUCCESS)
+ ? enif_make_tuple2(env, atom_ok, ret) : whereis_result_term(env, rc);
+
+ enif_release_resource(rp);
+ return ret;
+}
+
#define MAKE_TERM_REUSE_LEN 16
struct make_term_info
{
@@ -2995,7 +3251,11 @@ static ErlNifFunc nif_funcs[] =
{"monitor_process_nif", 4, monitor_process_nif},
{"demonitor_process_nif", 2, demonitor_process_nif},
{"compare_monitors_nif", 2, compare_monitors_nif},
- {"monitor_frenzy_nif", 4, monitor_frenzy_nif}
+ {"monitor_frenzy_nif", 4, monitor_frenzy_nif},
+ {"whereis_send", 3, whereis_send},
+ {"whereis_term", 2, whereis_term},
+ {"whereis_thd_lookup", 2, whereis_thd_lookup},
+ {"whereis_thd_result", 1, whereis_thd_result}
};
ERL_NIF_INIT(nif_SUITE,nif_funcs,load,NULL,upgrade,unload)
diff --git a/erts/emulator/test/old_scheduler_SUITE.erl b/erts/emulator/test/old_scheduler_SUITE.erl
deleted file mode 100644
index 8515a87df8..0000000000
--- a/erts/emulator/test/old_scheduler_SUITE.erl
+++ /dev/null
@@ -1,384 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
--module(old_scheduler_SUITE).
-
--include_lib("common_test/include/ct.hrl").
-
--export([all/0, suite/0,
- init_per_testcase/2, end_per_testcase/2]).
--export([equal/1, many_low/1, few_low/1, max/1, high/1]).
-
-suite() ->
- [{ct_hooks,[ts_install_cth]},
- {timetrap, {minutes, 11}}].
-
-all() ->
- case catch erlang:system_info(modified_timing_level) of
- Level when is_integer(Level) ->
- {skipped,
- "Modified timing (level " ++
- integer_to_list(Level) ++
- ") is enabled. Testcases gets messed "
- "up by modfied timing."};
- _ -> [equal, many_low, few_low, max, high]
- end.
-
-
-%%-----------------------------------------------------------------------------------
-%% TEST SUITE DESCRIPTION
-%%
-%% The test case function spawns two controlling processes: Starter and Receiver.
-%% Starter spawns a number of prio A and a number of prio B test processes. Each
-%% test process loops for a number of times, sends a report to the Receiver, then
-%% loops again. For each report, the Receiver increases a counter that corresponds
-%% to the priority of the sender. After a certain amount of time, the Receiver
-%% sends the collected data to the main test process and waits for the test case
-%% to terminate. From this data, it's possible to calculate the average run time
-%% relationship between the prio A and B test processes.
-%%
-%% Note that in order to be able to run tests with high or max prio test processes,
-%% the main test process and the Receiver needs to run at max prio, or they will
-%% be starved by the test processes. The controlling processes must not wait for
-%% messages from a normal (or low) prio process while max or high prio test processes
-%% are running (which happens e.g. if an io function is called).
-%%-----------------------------------------------------------------------------------
-
-init_per_testcase(_Case, Config) ->
- %% main test process needs max prio
- Prio = process_flag(priority, max),
- MS = erlang:system_flag(multi_scheduling, block_normal),
- [{prio,Prio},{multi_scheduling, MS}|Config].
-
-end_per_testcase(_Case, Config) ->
- erlang:system_flag(multi_scheduling, unblock_normal),
- Prio=proplists:get_value(prio, Config),
- process_flag(priority, Prio),
- ok.
-
-ok(Config) when is_list(Config) ->
- case proplists:get_value(multi_scheduling, Config) of
- blocked ->
- {comment,
- "Multi-scheduling blocked during test. This testcase was not "
- "written to work with multiple schedulers."};
- _ -> ok
- end.
-
-%% Run equal number of low and normal prio processes.
-
-equal(Config) when is_list(Config) ->
- Self = self(),
-
- %% specify number of test processes to run
- Normal = {normal,500},
- Low = {low,500},
-
- %% specify time of test (in seconds)
- Time = 30,
-
- %% start controllers
- Receiver =
- spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end),
- Starter =
- spawn(fun() -> starter(Normal, Low, Receiver) end),
-
- %% receive test data from Receiver
- {NRs,NAvg,LRs,LAvg,Ratio} =
- receive
- {Receiver,Res} -> Res
- end,
-
- %% stop controllers and test processes
- exit(Starter, kill),
- exit(Receiver, kill),
-
- io:format("Reports: ~w normal (~w/proc), ~w low (~w/proc). Ratio: ~w~n",
- [NRs,NAvg,LRs,LAvg,Ratio]),
-
- %% runtime ratio between normal and low should be ~8
- if Ratio < 7.5 ; Ratio > 8.5 ->
- ct:fail({bad_ratio,Ratio});
- true ->
- ok(Config)
- end.
-
-
-%% Run many low and few normal prio processes.
-
-many_low(Config) when is_list(Config) ->
- Self = self(),
- Normal = {normal,1},
- Low = {low,1000},
-
- %% specify time of test (in seconds)
- Time = 30,
-
- Receiver =
- spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end),
- Starter =
- spawn(fun() -> starter(Normal, Low, Receiver) end),
- {NRs,NAvg,LRs,LAvg,Ratio} =
- receive
- {Receiver,Res} -> Res
- end,
- exit(Starter, kill),
- exit(Receiver, kill),
- io:format("Reports: ~w normal (~w/proc), ~w low (~w/proc). Ratio: ~w~n",
- [NRs,NAvg,LRs,LAvg,Ratio]),
- if Ratio < 7.5 ; Ratio > 8.5 ->
- ct:fail({bad_ratio,Ratio});
- true ->
- ok(Config)
- end.
-
-
-%% Run few low and many normal prio processes.
-
-few_low(Config) when is_list(Config) ->
- Self = self(),
- Normal = {normal,1000},
- Low = {low,1},
-
- %% specify time of test (in seconds)
- Time = 30,
-
- Receiver =
- spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Normal, Low) end),
- Starter =
- spawn(fun() -> starter(Normal, Low, Receiver) end),
- {NRs,NAvg,LRs,LAvg,Ratio} =
- receive
- {Receiver,Res} -> Res
- end,
- exit(Starter, kill),
- exit(Receiver, kill),
- io:format("Reports: ~w normal (~w/proc), ~w low (~w/proc). Ratio: ~w~n",
- [NRs,NAvg,LRs,LAvg,Ratio]),
- if Ratio < 7.0 ; Ratio > 8.5 ->
- ct:fail({bad_ratio,Ratio});
- true ->
- ok(Config)
- end.
-
-
-%% Run max prio processes and verify they get at least as much
-%% runtime as high, normal and low.
-
-max(Config) when is_list(Config) ->
- max = process_flag(priority, max), % should already be max (init_per_tc)
- Self = self(),
- Max = {max,2},
- High = {high,2},
- Normal = {normal,100},
- Low = {low,100},
-
- %% specify time of test (in seconds)
- Time = 30,
-
- Receiver1 =
- spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, High) end),
- Starter1 =
- spawn(fun() -> starter(Max, High, Receiver1) end),
- {M1Rs,M1Avg,HRs,HAvg,Ratio1} =
- receive
- {Receiver1,Res1} -> Res1
- end,
- exit(Starter1, kill),
- exit(Receiver1, kill),
- io:format("Reports: ~w max (~w/proc), ~w high (~w/proc). Ratio: ~w~n",
- [M1Rs,M1Avg,HRs,HAvg,Ratio1]),
- if Ratio1 < 1.0 ->
- ct:fail({bad_ratio,Ratio1});
- true ->
- ok(Config)
- end,
-
- Receiver2 =
- spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, Normal) end),
- Starter2 =
- spawn(fun() -> starter(Max, Normal, Receiver2) end),
- {M2Rs,M2Avg,NRs,NAvg,Ratio2} =
- receive
- {Receiver2,Res2} -> Res2
- end,
- exit(Starter2, kill),
- exit(Receiver2, kill),
- io:format("Reports: ~w max (~w/proc), ~w normal (~w/proc). Ratio: ~w~n",
- [M2Rs,M2Avg,NRs,NAvg,Ratio2]),
- if Ratio2 < 1.0 ->
- ct:fail({bad_ratio,Ratio2});
- true ->
- ok
- end,
-
- Receiver3 =
- spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, Max, Low) end),
- Starter3 =
- spawn(fun() -> starter(Max, Low, Receiver3) end),
- {M3Rs,M3Avg,LRs,LAvg,Ratio3} =
- receive
- {Receiver3,Res3} -> Res3
- end,
- exit(Starter3, kill),
- exit(Receiver3, kill),
- io:format("Reports: ~w max (~w/proc), ~w low (~w/proc). Ratio: ~w~n",
- [M3Rs,M3Avg,LRs,LAvg,Ratio3]),
- if Ratio3 < 1.0 ->
- ct:fail({bad_ratio,Ratio3});
- true ->
- ok(Config)
- end.
-
-
-%% Run high prio processes and verify they get at least as much
-%% runtime as normal and low.
-
-high(Config) when is_list(Config) ->
- max = process_flag(priority, max), % should already be max (init_per_tc)
- Self = self(),
- High = {high,2},
- Normal = {normal,100},
- Low = {low,100},
-
- %% specify time of test (in seconds)
- Time = 30,
-
- Receiver1 =
- spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, High, Normal) end),
- Starter1 =
- spawn(fun() -> starter(High, Normal, Receiver1) end),
- {H1Rs,H1Avg,NRs,NAvg,Ratio1} =
- receive
- {Receiver1,Res1} -> Res1
- end,
- exit(Starter1, kill),
- exit(Receiver1, kill),
- io:format("Reports: ~w high (~w/proc), ~w normal (~w/proc). Ratio: ~w~n",
- [H1Rs,H1Avg,NRs,NAvg,Ratio1]),
- if Ratio1 < 1.0 ->
- ct:fail({bad_ratio,Ratio1});
- true ->
- ok
- end,
-
- Receiver2 =
- spawn(fun() -> receiver(erlang:monotonic_time(), Time, Self, High, Low) end),
- Starter2 =
- spawn(fun() -> starter(High, Low, Receiver2) end),
- {H2Rs,H2Avg,LRs,LAvg,Ratio2} =
- receive
- {Receiver2,Res2} -> Res2
- end,
- exit(Starter2, kill),
- exit(Receiver2, kill),
- io:format("Reports: ~w high (~w/proc), ~w low (~w/proc). Ratio: ~w~n",
- [H2Rs,H2Avg,LRs,LAvg,Ratio2]),
- if Ratio2 < 1.0 ->
- ct:fail({bad_ratio,Ratio2});
- true ->
- ok(Config)
- end.
-
-
-%%-----------------------------------------------------------------------------------
-%% Controller processes and help functions
-%%-----------------------------------------------------------------------------------
-
-receiver(T0, TimeSec, Main, {P1,P1N}, {P2,P2N}) ->
- %% prio should be max so that mailbox doesn't overflow
- process_flag(priority, max),
- receiver(T0, TimeSec*1000, Main, P1,P1N,0, P2,P2N,0, 100000).
-
-%% uncomment lines below to get life sign (debug)
-receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 0) ->
- % T = erlang:convert_time_unit(erlang:monotonic_time() - T0, native, millisecond),
- % erlang:display({round(T/1000),P1Rs,P2Rs}),
- receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 100000);
-
-receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, C) ->
- Remain = Time - erlang:convert_time_unit(erlang:monotonic_time() - T0,
- native, millisecond), % test time remaining
- Remain1 = if Remain < 0 ->
- 0;
- true ->
- Remain
- end,
- {P1Rs1,P2Rs1} =
- receive
- {_Pid,P1} -> % report from a P1 process
- {P1Rs+1,P2Rs};
- {_Pid,P2} -> % report from a P2 process
- {P1Rs,P2Rs+1}
- after Remain1 ->
- {P1Rs,P2Rs}
- end,
- if Remain > 0 -> % keep going
- receiver(T0, Time, Main, P1,P1N,P1Rs1, P2,P2N,P2Rs1, C-1);
- true -> % finish
- %% calculate results and send to main test process
- P1Avg = P1Rs1/P1N,
- P2Avg = P2Rs1/P2N,
- Ratio = if P2Avg < 1.0 -> P1Avg;
- true -> P1Avg/P2Avg
- end,
- Main ! {self(),{P1Rs1,round(P1Avg),P2Rs1,round(P2Avg),Ratio}},
- flush_loop()
- end.
-
-starter({P1,P1N}, {P2,P2N}, Receiver) ->
- %% start N1 processes with prio P1
- start_p(P1, P1N, Receiver),
- %% start N2 processes with prio P2
- start_p(P2, P2N, Receiver),
- erlang:display({started,P1N+P2N}),
- flush_loop().
-
-start_p(_, 0, _) ->
- ok;
-start_p(Prio, N, Receiver) ->
- spawn_link(fun() -> p(Prio, Receiver) end),
- start_p(Prio, N-1, Receiver).
-
-p(Prio, Receiver) ->
- %% set process priority
- process_flag(priority, Prio),
- p_loop(0, Prio, Receiver).
-
-p_loop(100, Prio, Receiver) ->
- receive after 0 -> ok end,
- %% if Receiver gone, we're done
- case is_process_alive(Receiver) of
- false -> exit(bye);
- true -> ok
- end,
- %% send report
- Receiver ! {self(),Prio},
- p_loop(0, Prio, Receiver);
-
-p_loop(N, Prio, Receiver) ->
- p_loop(N+1, Prio, Receiver).
-
-
-flush_loop() ->
- receive _ ->
- ok
- end,
- flush_loop().
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index 94ee9851dd..fccbaf13ee 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -153,7 +153,7 @@
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap, {seconds, 10}}].
+ {timetrap, {minutes, 1}}].
all() ->
[otp_6224, {group, stream}, basic_ping, slow_writes,
diff --git a/erts/emulator/test/port_trace_SUITE.erl b/erts/emulator/test/port_trace_SUITE.erl
index 03efdc15db..bfc3c8cb51 100644
--- a/erts/emulator/test/port_trace_SUITE.erl
+++ b/erts/emulator/test/port_trace_SUITE.erl
@@ -52,7 +52,7 @@
-define(ECHO_DRV_REMOTE_SEND_TERM, 15).
suite() -> [{ct_hooks,[ts_install_cth]},
- {timetrap, {seconds, 30}}].
+ {timetrap, {minutes, 2}}].
all() ->
[port_specs, ports, open_close,
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index e14185e881..4204d12eb3 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -134,6 +134,11 @@ init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
[{testcase, Func}|Config].
end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
+ %% Restore max_heap_size to default value.
+ erlang:system_flag(max_heap_size,
+ #{size => 0,
+ kill => true,
+ error_logger => true}),
ok.
fun_spawn(Fun) ->
@@ -1024,36 +1029,48 @@ bump_big(Prev, Limit) ->
%% Priority 'low' should be mixed with 'normal' using a factor of
%% about 8. (OTP-2644)
low_prio(Config) when is_list(Config) ->
- case erlang:system_info(schedulers_online) of
- 1 ->
- ok = low_prio_test(Config);
- _ ->
- erlang:system_flag(multi_scheduling, block_normal),
- ok = low_prio_test(Config),
- erlang:system_flag(multi_scheduling, unblock_normal),
- {comment,
- "Test not written for SMP runtime system. "
- "Multi scheduling blocked during test."}
- end.
+ erlang:system_flag(multi_scheduling, block_normal),
+ Prop = low_prio_test(Config),
+ erlang:system_flag(multi_scheduling, unblock_normal),
+ Str = lists:flatten(io_lib:format("Low/high proportion is ~.3f",
+ [Prop])),
+ {comment,Str}.
low_prio_test(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- S = spawn_link(?MODULE, prio_server, [0, 0]),
+
+ %% Spawn the server running with high priority. The server must
+ %% not run at normal priority as that would skew the results for
+ %% two reasons:
+ %%
+ %% 1. There would be one more normal-priority processes than
+ %% low-priority processes.
+ %%
+ %% 2. The receive queue would grow faster than the server process
+ %% could process it. That would in turn trigger the reduction
+ %% punishment for the clients.
+ S = spawn_opt(?MODULE, prio_server, [0, 0], [link,{priority,high}]),
+
+ %% Spawn the clients and let them run for a while.
PCs = spawn_prio_clients(S, erlang:system_info(schedulers_online)),
- ct:sleep({seconds,3}),
+ ct:sleep({seconds,2}),
lists:foreach(fun (P) -> exit(P, kill) end, PCs),
+
+ %% Stop the server and retrieve the result.
S ! exit,
- receive {'EXIT', S, {A, B}} -> check_prio(A, B) end,
- ok.
+ receive
+ {'EXIT', S, {A, B}} ->
+ check_prio(A, B)
+ end.
check_prio(A, B) ->
Prop = A/B,
ok = io:format("Low=~p, High=~p, Prop=~p\n", [A, B, Prop]),
- %% It isn't 1/8, it's more like 0.3, but let's check that
- %% the low-prio processes get some little chance to run at all.
- true = (Prop < 1.0),
- true = (Prop > 1/32).
+ %% Prop is expected to be appr. 1/8. Allow a reasonable margin.
+ true = Prop < 1/4,
+ true = Prop > 1/16,
+ Prop.
prio_server(A, B) ->
receive
@@ -2057,6 +2074,7 @@ max_heap_size_test(Option, Size, Kill, ErrorLogger) ->
end,
if ErrorLogger ->
receive
+ %% There must be at least one error message.
{error, _, {emulator, _, [Pid|_]}} ->
ok
end;
@@ -2069,22 +2087,33 @@ max_heap_size_test(Option, Size, Kill, ErrorLogger) ->
{'DOWN', Ref, process, Pid, die} ->
ok
end,
- flush();
+ %% If the process was not killed, the limit may have
+ %% been reached more than once and there may be
+ %% more {error, ...} messages left.
+ receive_error_messages(Pid);
true ->
ok
end,
+
+ %% Make sure that there are no unexpected messages.
+ receive_unexpected().
+
+receive_error_messages(Pid) ->
receive
- M ->
- ct:fail({unexpected_message, M})
- after 10 ->
+ {error, _, {emulator, _, [Pid|_]}} ->
+ receive_error_messages(Pid)
+ after 1000 ->
ok
end.
-flush() ->
+receive_unexpected() ->
receive
- _M ->
- flush()
- after 1000 ->
+ {info_report, _, _} ->
+ %% May be an alarm message from os_mon. Ignore.
+ receive_unexpected();
+ M ->
+ ct:fail({unexpected_message, M})
+ after 10 ->
ok
end.
diff --git a/erts/emulator/test/receive_SUITE.erl b/erts/emulator/test/receive_SUITE.erl
index 83653a7a36..c7d5a3f5a0 100644
--- a/erts/emulator/test/receive_SUITE.erl
+++ b/erts/emulator/test/receive_SUITE.erl
@@ -39,25 +39,43 @@ groups() ->
call_with_huge_message_queue(Config) when is_list(Config) ->
Pid = spawn_link(fun echo_loop/0),
-
- {Time,ok} = tc(fun() -> calls(10, Pid) end),
-
- [self() ! {msg,N} || N <- lists:seq(1, 500000)],
+ _WarmUpTime = time_calls(Pid),
+ Time = time_calls(Pid),
+ _ = [self() ! {msg,N} || N <- lists:seq(1, 500000)],
+ io:format("Time for empty message queue: ~p", [Time]),
erlang:garbage_collect(),
- {NewTime1,ok} = tc(fun() -> calls(10, Pid) end),
- {NewTime2,ok} = tc(fun() -> calls(10, Pid) end),
+ call_with_huge_message_queue_1(Pid, Time, 5).
+
+call_with_huge_message_queue_1(_Pid, _Time, 0) ->
+ ct:fail(bad_ratio);
+call_with_huge_message_queue_1(Pid, Time, NumTries) ->
+ HugeTime = time_calls(Pid),
+ io:format("Time for huge message queue: ~p", [HugeTime]),
+
+ case (HugeTime+1) / (Time+1) of
+ Q when Q < 10 ->
+ ok;
+ Q ->
+ io:format("Too high ratio: ~p\n", [Q]),
+ call_with_huge_message_queue_1(Pid, Time, NumTries-1)
+ end.
- io:format("Time for empty message queue: ~p", [Time]),
- io:format("Time1 for huge message queue: ~p", [NewTime1]),
- io:format("Time2 for huge message queue: ~p", [NewTime2]),
-
- case hd(lists:sort([(NewTime1+1) / (Time+1), (NewTime2+1) / (Time+1)])) of
- Q when Q < 10 ->
- ok;
- Q ->
- ct:fail("Best Q = ~p", [Q])
- end,
- ok.
+%% Time a number calls. Try to avoid returning a zero time.
+time_calls(Pid) ->
+ time_calls(Pid, 10).
+
+time_calls(_Pid, 0) ->
+ 0;
+time_calls(Pid, NumTries) ->
+ case timer:tc(fun() -> calls(Pid) end) of
+ {0,ok} ->
+ time_calls(Pid, NumTries-1);
+ {Time,ok} ->
+ Time
+ end.
+
+calls(Pid) ->
+ calls(100, Pid).
calls(0, _) -> ok;
calls(N, Pid) ->
@@ -108,6 +126,3 @@ echo_loop() ->
Pid ! {Ref,Msg},
echo_loop()
end.
-
-tc(Fun) ->
- timer:tc(erlang, apply, [Fun,[]]).
diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl
index 7cbd93a0f3..a977eb41c4 100644
--- a/erts/emulator/test/timer_bif_SUITE.erl
+++ b/erts/emulator/test/timer_bif_SUITE.erl
@@ -488,24 +488,40 @@ registered_process(Config) when is_list(Config) ->
same_time_yielding(Config) when is_list(Config) ->
Mem = mem(),
+ Ref = make_ref(),
SchdlrsOnln = erlang:system_info(schedulers_online),
Tmo = erlang:monotonic_time(millisecond) + 3000,
Tmrs = lists:map(fun (I) ->
process_flag(scheduler, (I rem SchdlrsOnln) + 1),
- erlang:start_timer(Tmo, self(), hej, [{abs, true}])
+ erlang:start_timer(Tmo, self(), Ref, [{abs, true}])
end,
lists:seq(1, (?TIMEOUT_YIELD_LIMIT*3+1)*SchdlrsOnln)),
true = mem_larger_than(Mem),
- lists:foreach(fun (Tmr) -> receive {timeout, Tmr, hej} -> ok end end, Tmrs),
+ receive_all_timeouts(length(Tmrs), Ref),
Done = erlang:monotonic_time(millisecond),
true = Done >= Tmo,
+ MsAfterTmo = Done - Tmo,
+ io:format("Done ~p ms after Tmo\n", [MsAfterTmo]),
case erlang:system_info(build_type) of
- opt -> true = Done < Tmo + 200;
- _ -> true = Done < Tmo + 1000
+ opt ->
+ true = MsAfterTmo < 200;
+ _ ->
+ true = MsAfterTmo < 1000
end,
Mem = mem(),
ok.
+%% Read out all timeouts in receive queue order. This is efficient
+%% even if there are very many messages.
+
+receive_all_timeouts(0, _Ref) ->
+ ok;
+receive_all_timeouts(N, Ref) ->
+ receive
+ {timeout, _Tmr, Ref} ->
+ receive_all_timeouts(N-1, Ref)
+ end.
+
same_time_yielding_with_cancel(Config) when is_list(Config) ->
same_time_yielding_with_cancel_test(false, false).
diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl
index 643c2e0472..bd0ea22de9 100644
--- a/erts/emulator/test/trace_SUITE.erl
+++ b/erts/emulator/test/trace_SUITE.erl
@@ -46,7 +46,7 @@
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap, {seconds, 5}}].
+ {timetrap, {minutes, 1}}].
all() ->
[cpu_timestamp, receive_trace, link_receive_call_correlation,
@@ -184,10 +184,10 @@ receive_trace(Config) when is_list(Config) ->
{'EXIT', Intruder, {badarg, _}} = receive_first(),
%% Untrace the process; we should not receive anything.
- ?line 1 = erlang:trace(Receiver, false, ['receive']),
- ?line Receiver ! {hello, there},
- ?line Receiver ! any_garbage,
- ?line receive_nothing(),
+ 1 = erlang:trace(Receiver, false, ['receive']),
+ Receiver ! {hello, there},
+ Receiver ! any_garbage,
+ receive_nothing(),
%% Verify restrictions in matchspec for 'receive'
F3 = fun (Pat) -> {'EXIT', {badarg,_}} = (catch erlang:trace_pattern('receive', Pat, [])) end,
diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl
index e4db368ea1..5eb27a7b68 100644
--- a/erts/emulator/test/trace_port_SUITE.erl
+++ b/erts/emulator/test/trace_port_SUITE.erl
@@ -37,7 +37,7 @@
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap, {seconds, 30}}].
+ {timetrap, {minutes, 2}}].
all() ->
[call_trace, return_trace, send, receive_trace,
diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl
index 0f5dc40553..29804dc50b 100644
--- a/lib/kernel/src/inet_parse.erl
+++ b/lib/kernel/src/inet_parse.erl
@@ -644,8 +644,12 @@ ipv6_addr(Cs) ->
ipv6_addr(hex(Cs), [], 0).
%% Before "::"
+ipv6_addr({Cs0,"%"++Cs1}, A, N) when N == 7 ->
+ ipv6_addr_scope(Cs1, [hex_to_int(Cs0)|A], [], N+1, []);
ipv6_addr({Cs0,[]}, A, N) when N == 7 ->
ipv6_addr_done([hex_to_int(Cs0)|A]);
+ipv6_addr({Cs0,"::%"++Cs1}, A, N) when N =< 6 ->
+ ipv6_addr_scope(Cs1, [hex_to_int(Cs0)|A], [], N+1, []);
ipv6_addr({Cs0,"::"}, A, N) when N =< 6 ->
ipv6_addr_done([hex_to_int(Cs0)|A], [], N+1);
ipv6_addr({Cs0,"::"++Cs1}, A, N) when N =< 5 ->
@@ -658,6 +662,8 @@ ipv6_addr(_, _, _) ->
erlang:error(badarg).
%% After "::"
+ipv6_addr({Cs0,"%"++Cs1}, A, B, N) when N =< 6 ->
+ ipv6_addr_scope(Cs1, A, [hex_to_int(Cs0)|B], N+1, []);
ipv6_addr({Cs0,[]}, A, B, N) when N =< 6 ->
ipv6_addr_done(A, [hex_to_int(Cs0)|B], N+1);
ipv6_addr({Cs0,":"++Cs1}, A, B, N) when N =< 5 ->
@@ -667,6 +673,43 @@ ipv6_addr({Cs0,"."++_=Cs1}, A, B, N) when N =< 5 ->
ipv6_addr(_, _, _, _) ->
erlang:error(badarg).
+%% After "%"
+ipv6_addr_scope([], Ar, Br, N, Sr) ->
+ ScopeId =
+ case lists:reverse(Sr) of
+ %% Empty scope id
+ "" -> 0;
+ %% Scope id starts with 0
+ "0"++S -> dec16(S);
+ _ -> 0
+ end,
+ %% Suggested formats for scope id parsing:
+ %% "" -> "0"
+ %% "0" -> Scope id 0
+ %% "1" - "9", "10" - "99" -> "0"++S
+ %% "0"++DecimalScopeId -> decimal scope id
+ %% "25"++PercentEncoded -> Percent encoded interface name
+ %% S -> Interface name (Unicode?)
+ %% Missing: translation from interface name into integer scope id.
+ %% XXX: scope id is actually 32 bit, but we only have room for
+ %% 16 bit in the second address word - ignore or fix (how)?
+ ipv6_addr_scope(ScopeId, Ar, Br, N);
+ipv6_addr_scope([C|Cs], Ar, Br, N, Sr) ->
+ ipv6_addr_scope(Cs, Ar, Br, N, [C|Sr]).
+%%
+ipv6_addr_scope(ScopeId, [P], Br, N)
+ when N =< 7, P =:= 16#fe80;
+ N =< 7, P =:= 16#ff02 ->
+ %% Optimized special case
+ ipv6_addr_done([ScopeId,P], Br, N+1);
+ipv6_addr_scope(ScopeId, Ar, Br, N) ->
+ case lists:reverse(Br++dup(8-N, 0, Ar)) of
+ [P,0|Xs] when P =:= 16#fe80; P =:= 16#ff02 ->
+ list_to_tuple([P,ScopeId|Xs]);
+ _ ->
+ erlang:error(badarg)
+ end.
+
ipv6_addr_done(Ar, Br, N, {D1,D2,D3,D4}) ->
ipv6_addr_done(Ar, [((D3 bsl 8) bor D4),((D1 bsl 8) bor D2)|Br], N+2).
@@ -690,6 +733,19 @@ hex(Cs, [_|_]=R, _) when is_list(Cs) ->
hex(_, _, _) ->
erlang:error(badarg).
+%% Parse a reverse decimal integer string, empty is 0
+dec16(Cs) -> dec16(Cs, 0).
+%%
+dec16([], I) -> I;
+dec16([C|Cs], I) when C >= $0, C =< $9 ->
+ case 10*I + (C - $0) of
+ J when 16#ffff < J ->
+ erlang:error(badarg);
+ J ->
+ dec16(Cs, J)
+ end;
+dec16(_, _) -> erlang:error(badarg).
+
%% Hex string to integer
hex_to_int(Cs) -> erlang:list_to_integer(Cs, 16).
@@ -703,7 +759,7 @@ dup(N, E, L) when is_integer(N), N >= 1 ->
%% Convert IPv4 address to ascii
%% Convert IPv6 / IPV4 address to ascii (plain format)
-ntoa({A,B,C,D}) ->
+ntoa({A,B,C,D}) when (A band B band C band D band (bnot 16#ff)) =:= 0 ->
integer_to_list(A) ++ "." ++ integer_to_list(B) ++ "." ++
integer_to_list(C) ++ "." ++ integer_to_list(D);
%% ANY
@@ -711,13 +767,25 @@ ntoa({0,0,0,0,0,0,0,0}) -> "::";
%% LOOPBACK
ntoa({0,0,0,0,0,0,0,1}) -> "::1";
%% IPV4 ipv6 host address
-ntoa({0,0,0,0,0,0,A,B}) -> "::" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B);
+ntoa({0,0,0,0,0,0,A,B}) when (A band B band (bnot 16#ffff)) =:= 0 ->
+ "::" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B);
%% IPV4 non ipv6 host address
-ntoa({0,0,0,0,0,16#ffff,A,B}) ->
- "::FFFF:" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B);
-ntoa({_,_,_,_,_,_,_,_}=T) ->
- %% Find longest sequence of zeros, at least 2, to replace with "::"
- ntoa(tuple_to_list(T), []);
+ntoa({0,0,0,0,0,16#ffff,A,B}) when (A band B band (bnot 16#ffff)) =:= 0 ->
+ "::ffff:" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B);
+ntoa({A,B,C,D,E,F,G,H})
+ when (A band B band C band D band E band F band G band H band
+ (bnot 16#ffff)) =:= 0 ->
+ if
+ A =:= 16#fe80, B =/= 0;
+ A =:= 16#ff02, B =/= 0 ->
+ %% Find longest sequence of zeros, at least 2,
+ %% to replace with "::"
+ ntoa([A,0,C,D,E,F,G,H], []) ++ "%0" ++ integer_to_list(B);
+ true ->
+ %% Find longest sequence of zeros, at least 2,
+ %% to replace with "::"
+ ntoa([A,B,C,D,E,F,G,H], [])
+ end;
ntoa(_) ->
{error, einval}.
@@ -780,9 +848,19 @@ dig_to_dec(X) ->
integer_to_list((X bsr 8) band 16#ff) ++ "." ++
integer_to_list(X band 16#ff).
-%% Convert a integer to hex string
-dig_to_hex(X) ->
- erlang:integer_to_list(X, 16).
+%% Convert a integer to hex string (lowercase)
+dig_to_hex(0) -> "0";
+dig_to_hex(X) when is_integer(X), 0 < X ->
+ dig_to_hex(X, "").
+%%
+dig_to_hex(0, Acc) -> Acc;
+dig_to_hex(X, Acc) ->
+ dig_to_hex(
+ X bsr 4,
+ [case X band 15 of
+ D when D < 10 -> D + $0;
+ D -> D - 10 + $a
+ end|Acc]).
%%
%% Count number of '.' in a name
diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl
index 97f789b61c..3b502be8b8 100644
--- a/lib/kernel/test/inet_SUITE.erl
+++ b/lib/kernel/test/inet_SUITE.erl
@@ -447,91 +447,125 @@ parse_hosts(Config) when is_list(Config) ->
inet_parse:resolv(ResolvErr1).
parse_address(Config) when is_list(Config) ->
- V4Strict =
+ V4Reversable =
[{{0,0,0,0},"0.0.0.0"},
- {{1,2,3,4},"1.2.3.4"},
+ {{1,2,3,4},"1.2.3.4"},
{{253,252,251,250},"253.252.251.250"},
{{1,2,255,254},"1.2.255.254"}],
- V6Strict =
+ V6Reversable =
[{{0,0,0,0,0,0,0,0},"::"},
+ {{0,0,0,0,0,0,0,1},"::1"},
+ {{0,0,0,0,0,0,0,2},"::0.0.0.2"},
{{15,0,0,0,0,0,0,2},"f::2"},
- {{15,16#f11,0,0,0,0,256,2},"f:f11::0100:2"},
- {{0,0,0,0,0,0,0,16#17},"::17"},
- {{16#700,0,0,0,0,0,0,0},"0700::"},
- {{0,0,0,0,0,0,2,1},"::2:1"},
+ {{15,16#f11,0,0,0,0,256,2},"f:f11::100:2"},
+ {{16#700,0,0,0,0,0,0,0},"700::"},
+ {{0,0,0,0,0,0,2,1},"::0.2.0.1"},
{{0,0,0,0,0,3,2,1},"::3:2:1"},
{{0,0,0,0,4,3,2,1},"::4:3:2:1"},
{{0,0,0,5,4,3,2,1},"::5:4:3:2:1"},
{{0,0,6,5,4,3,2,1},"::6:5:4:3:2:1"},
- {{0,7,6,5,4,3,2,1},"::7:6:5:4:3:2:1"},
+ {{0,7,6,5,4,3,2,1},"0:7:6:5:4:3:2:1"},
{{7,0,0,0,0,0,0,0},"7::"},
{{7,6,0,0,0,0,0,0},"7:6::"},
{{7,6,5,0,0,0,0,0},"7:6:5::"},
{{7,6,5,4,0,0,0,0},"7:6:5:4::"},
{{7,6,5,4,3,0,0,0},"7:6:5:4:3::"},
{{7,6,5,4,3,2,0,0},"7:6:5:4:3:2::"},
- {{7,6,5,4,3,2,1,0},"7:6:5:4:3:2:1::"},
+ {{7,6,5,4,3,2,1,0},"7:6:5:4:3:2:1:0"},
+ {{0,0,6,5,4,3,0,0},"::6:5:4:3:0:0"},
+ {{0,0,6,5,4,0,0,0},"0:0:6:5:4::"},
+ {{8,0,0,5,4,0,0,1},"8::5:4:0:0:1"},
+ {{8,0,0,5,0,0,0,1},"8:0:0:5::1"},
+ {{0,7,6,5,4,3,2,0},"0:7:6:5:4:3:2:0"},
+ {{0,0,6,5,4,3,0,0},"::6:5:4:3:0:0"},
+ {{0,0,0,5,4,0,0,0},"::5:4:0:0:0"},
+ {{0,0,0,0,4,0,0,0},"::4:0:0:0"},
+ {{0,0,0,5,0,0,0,0},"0:0:0:5::"},
{{16#c11,16#c22,16#5c33,16#c440,16#55c0,16#c66c,16#77,16#88},
- "c11:0c22:5c33:c440:55c0:c66c:77:0088"},
+ "c11:c22:5c33:c440:55c0:c66c:77:88"},
+ {{0,16#c22,16#5c33,16#c440,16#55c0,16#c66c,16#77,16#88},
+ "0:c22:5c33:c440:55c0:c66c:77:88"},
{{16#c11,0,16#5c33,16#c440,16#55c0,16#c66c,16#77,16#88},
- "c11::5c33:c440:55c0:c66c:77:0088"},
+ "c11:0:5c33:c440:55c0:c66c:77:88"},
{{16#c11,16#c22,0,16#c440,16#55c0,16#c66c,16#77,16#88},
- "c11:0c22::c440:55c0:c66c:77:0088"},
+ "c11:c22:0:c440:55c0:c66c:77:88"},
{{16#c11,16#c22,16#5c33,0,16#55c0,16#c66c,16#77,16#88},
- "c11:0c22:5c33::55c0:c66c:77:0088"},
+ "c11:c22:5c33:0:55c0:c66c:77:88"},
{{16#c11,16#c22,16#5c33,16#c440,0,16#c66c,16#77,16#88},
- "c11:0c22:5c33:c440::c66c:77:0088"},
+ "c11:c22:5c33:c440:0:c66c:77:88"},
{{16#c11,16#c22,16#5c33,16#c440,16#55c0,0,16#77,16#88},
- "c11:0c22:5c33:c440:55c0::77:0088"},
+ "c11:c22:5c33:c440:55c0:0:77:88"},
{{16#c11,16#c22,16#5c33,16#c440,16#55c0,16#c66c,0,16#88},
- "c11:0c22:5c33:c440:55c0:c66c::0088"},
+ "c11:c22:5c33:c440:55c0:c66c:0:88"},
+ {{16#c11,16#c22,16#5c33,16#c440,16#55c0,16#c66c,16#77,0},
+ "c11:c22:5c33:c440:55c0:c66c:77:0"},
+ {{0,0,16#5c33,16#c440,16#55c0,16#c66c,16#77,16#88},
+ "::5c33:c440:55c0:c66c:77:88"},
{{16#c11,0,0,16#c440,16#55c0,16#c66c,16#77,16#88},
- "c11::c440:55c0:c66c:77:0088"},
+ "c11::c440:55c0:c66c:77:88"},
{{16#c11,16#c22,0,0,16#55c0,16#c66c,16#77,16#88},
- "c11:0c22::55c0:c66c:77:0088"},
+ "c11:c22::55c0:c66c:77:88"},
{{16#c11,16#c22,16#5c33,0,0,16#c66c,16#77,16#88},
- "c11:0c22:5c33::c66c:77:0088"},
+ "c11:c22:5c33::c66c:77:88"},
{{16#c11,16#c22,16#5c33,16#c440,0,0,16#77,16#88},
- "c11:0c22:5c33:c440::77:0088"},
+ "c11:c22:5c33:c440::77:88"},
{{16#c11,16#c22,16#5c33,16#c440,16#55c0,0,0,16#88},
- "c11:0c22:5c33:c440:55c0::0088"},
+ "c11:c22:5c33:c440:55c0::88"},
+ {{16#c11,16#c22,16#5c33,16#c440,16#55c0,16#c66c,0,0},
+ "c11:c22:5c33:c440:55c0:c66c::"},
+ {{0,0,0,16#c440,16#55c0,16#c66c,16#77,16#88},
+ "::c440:55c0:c66c:77:88"},
{{16#c11,0,0,0,16#55c0,16#c66c,16#77,16#88},
- "c11::55c0:c66c:77:0088"},
+ "c11::55c0:c66c:77:88"},
{{16#c11,16#c22,0,0,0,16#c66c,16#77,16#88},
- "c11:0c22::c66c:77:0088"},
+ "c11:c22::c66c:77:88"},
{{16#c11,16#c22,16#5c33,0,0,0,16#77,16#88},
- "c11:0c22:5c33::77:0088"},
+ "c11:c22:5c33::77:88"},
{{16#c11,16#c22,16#5c33,16#c440,0,0,0,16#88},
- "c11:0c22:5c33:c440::0088"},
+ "c11:c22:5c33:c440::88"},
+ {{16#c11,16#c22,16#5c33,16#c440,16#55c0,0,0,0},
+ "c11:c22:5c33:c440:55c0::"},
+ {{0,0,0,0,16#55c0,16#c66c,16#77,16#88},
+ "::55c0:c66c:77:88"},
{{16#c11,0,0,0,0,16#c66c,16#77,16#88},
- "c11::c66c:77:0088"},
+ "c11::c66c:77:88"},
{{16#c11,16#c22,0,0,0,0,16#77,16#88},
- "c11:0c22::77:0088"},
+ "c11:c22::77:88"},
{{16#c11,16#c22,16#5c33,0,0,0,0,16#88},
- "c11:0c22:5c33::0088"},
+ "c11:c22:5c33::88"},
+ {{16#c11,16#c22,16#5c33,16#c440,0,0,0,0},
+ "c11:c22:5c33:c440::"},
+ {{0,0,0,0,0,16#c66c,16#77,16#88},
+ "::c66c:77:88"},
{{16#c11,0,0,0,0,0,16#77,16#88},
- "c11::77:0088"},
+ "c11::77:88"},
{{16#c11,16#c22,0,0,0,0,0,16#88},
- "c11:0c22::0088"},
- {{0,0,0,0,0,65535,258,65534},"::FFFF:1.2.255.254"},
+ "c11:c22::88"},
+ {{16#c11,16#c22,16#5c33,0,0,0,0,0},
+ "c11:c22:5c33::"},
+ {{0,0,0,0,0,65535,258,65534},"::ffff:1.2.255.254"},
+ {{16#fe80,12345,0,0,0,0,0,16#12},"fe80::12%012345"},
{{16#ffff,16#ffff,16#ffff,16#ffff,16#ffff,16#ffff,16#ffff,16#ffff},
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"}
- |[{{D2,0,0,0,0,P,(D1 bsl 8) bor D2,(D3 bsl 8) bor D4},
- erlang:integer_to_list(D2, 16)++"::"++Q++S}
- || {{D1,D2,D3,D4},S} <- V4Strict,
- {P,Q} <- [{0,""},{16#17,"17:"},{16#ff0,"0ff0:"}]]],
+ |[{list_to_tuple(P++[(D1 bsl 8) bor D2,(D3 bsl 8) bor D4]),
+ Q++S}
+ || {{D1,D2,D3,D4},S} <-
+ tl(V4Reversable),
+ {P,Q} <-
+ [{[0,0,0,0,0,16#ffff],"::ffff:"},
+ {[0,0,0,0,0,0],"::"}]]],
V4Sloppy =
[{{10,1,16#98,16#76},"10.0x019876"},
{{8#12,1,8#130,8#321},"012.01.054321"},
- {{255,255,255,255},"255.255.255.0377"},
- {{255,255,255,255},"0Xff.000000000377.0x0000ff.255"},
- {{255,255,255,255},"255.255.65535"},
- {{255,255,255,255},"255.0xFF.0177777"},
- {{255,255,255,255},"255.16777215"},
- {{255,255,255,255},"00377.0XFFFFFF"},
- {{255,255,255,255},"4294967295"},
- {{255,255,255,255},"0xffffffff"},
- {{255,255,255,255},"00000000000037777777777"},
+ {{252,253,254,255},"252.253.254.0377"},
+ {{252,253,254,255},"0Xfc.000000000375.0x0000fe.255"},
+ {{252,253,254,255},"252.253.65279"},
+ {{252,253,254,255},"252.0xFD.0177377"},
+ {{252,253,254,255},"252.16645887"},
+ {{252,253,254,255},"00374.0XFDFEFF"},
+ {{252,253,254,255},"4244504319"},
+ {{252,253,254,255},"0xfcfdfeff"},
+ {{252,253,254,255},"00000000000037477377377"},
{{16#12,16#34,16#56,16#78},"0x12345678"},
{{16#12,16#34,16#56,16#78},"0x12.0x345678"},
{{16#12,16#34,16#56,16#78},"0x12.0X34.0x5678"},
@@ -543,8 +577,14 @@ parse_address(Config) when is_list(Config) ->
{{0,0,0,0},"0.00.0.0"},
{{0,0,0,0},"0.0.000000000000.0"}],
V6Sloppy =
- [{{0,0,0,0,0,65535,(D1 bsl 8) bor D2,(D3 bsl 8) bor D4},S}
- || {{D1,D2,D3,D4},S} <- V4Strict++V4Sloppy],
+ [{{16#a,16#b,16#c,16#0,16#0,16#d,16#e,16#f},"A:B:C::d:e:f"},
+ {{16#fe80,0,0,0,0,0,0,16#12},"fe80::12%XXXXXXX"}]
+ ++
+ [{{P,0,0,0,0,D2,(D1 bsl 8) bor D2,(D3 bsl 8) bor D4},
+ Q++erlang:integer_to_list(D2, 16)++":"++S}
+ || {{D1,D2,D3,D4},S} <- V4Reversable,
+ {P,Q} <-
+ [{16#2001,"2001::"},{16#177,"177::"},{16#ff0,"Ff0::"}]],
V4Err =
["0.256.0.1",
"1.2.3.4.5",
@@ -588,28 +628,36 @@ parse_address(Config) when is_list(Config) ->
"fec0::fFfF:127.0.0.1."],
t_parse_address
(parse_ipv6_address,
- V6Strict++V6Sloppy++V6Err++V4Err),
+ false,
+ V6Reversable++V6Sloppy++V6Err++V4Err),
t_parse_address
(parse_ipv6strict_address,
- V6Strict++V6Err++V4Err++[S || {_,S} <- V6Sloppy]),
+ true,
+ V6Reversable++V6Err++V4Err),
t_parse_address
(parse_ipv4_address,
- V4Strict++V4Sloppy++V4Err++V6Err++[S || {_,S} <- V6Strict]),
+ false,
+ V4Reversable++V4Sloppy++V4Err++V6Err++[S || {_,S} <- V6Reversable]),
t_parse_address
(parse_ipv4strict_address,
- V4Strict++V4Err++V6Err++[S || {_,S} <- V4Sloppy++V6Strict]).
+ true,
+ V4Reversable++V4Err++V6Err++[S || {_,S} <- V4Sloppy++V6Reversable]).
-t_parse_address(Func, []) ->
+t_parse_address(Func, _Reversable, []) ->
io:format("~p done.~n", [Func]),
ok;
-t_parse_address(Func, [{Addr,String}|L]) ->
+t_parse_address(Func, Reversable, [{Addr,String}|L]) ->
io:format("~p = ~p.~n", [Addr,String]),
{ok,Addr} = inet:Func(String),
- t_parse_address(Func, L);
-t_parse_address(Func, [String|L]) ->
+ case Reversable of
+ true ->String = inet:ntoa(Addr);
+ false -> ok
+ end,
+ t_parse_address(Func, Reversable, L);
+t_parse_address(Func, Reversable, [String|L]) ->
io:format("~p.~n", [String]),
{error,einval} = inet:Func(String),
- t_parse_address(Func, L).
+ t_parse_address(Func, Reversable, L).
parse_strict_address(Config) when is_list(Config) ->
{ok, {127,0,0,1}} =
diff --git a/lib/reltool/doc/src/reltool_examples.xml b/lib/reltool/doc/src/reltool_examples.xml
index 83eee6017a..49bde56964 100644
--- a/lib/reltool/doc/src/reltool_examples.xml
+++ b/lib/reltool/doc/src/reltool_examples.xml
@@ -41,30 +41,31 @@
<p>The main process in Reltool is the server. It can be used as it
is or be used via the GUI frontend process. When the GUI is
started, a server process will automatically be started. The GUI
- process is started with <c>reltool:start/0</c>,
- <c>reltool:start/1</c> or <c>reltool:start_link/1</c>. The pid of
- its server can be obtained with <c>reltool:get_server/1</c></p>
+ process is started with
+ <seealso marker="reltool#start-0"><c>reltool:start/0</c></seealso>,
+ <seealso marker="reltool#start-1"><c>reltool:start/1</c></seealso> or
+ <seealso marker="reltool#start_link-1"><c>reltool:start_link/1</c></seealso>.
+ The pid of its server can be obtained with
+ <seealso marker="reltool#start_link-1"><c>reltool:get_server/1</c></seealso>
+ </p>
<pre>
-Erlang R13B02 (erts-5.7.3) [source] [64-bit] [smp:4:4] [rq:4]
- [async-threads:0] [kernel-poll:false]
-
-Eshell V5.7.3 (abort with ^G)
+Erlang/OTP 20 [erts-9.0] [source-c13b302] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10]
+[hipe] [kernel-poll:false]
+Eshell V9.0 (abort with ^G)
+1&gt;
1&gt; {ok, Win} = reltool:start([]).
{ok,&lt;0.36.01&gt;}
-2&gt; {ok, Server} = reltool:get_server([]).
+2&gt; {ok, Server} = reltool:get_server(Win).
{ok,&lt;0.37.01&gt;}
3&gt; reltool:get_config(Server).
{ok,{sys,[]}}
-4&gt; reltool:stop(Win).
-ok
-
-
-5&gt; {ok, Server2} = reltool:start_server([]).
+4&gt;
+4&gt; {ok, Server2} = reltool:start_server([]).
{ok,&lt;0.6535.01&gt;}
-6&gt; reltool:get_config(Server2).
+5&gt; reltool:get_config(Server2).
{ok,{sys,[]}}
-7&gt; reltool:stop(Server2).
+6&gt; reltool:stop(Server2).
ok
</pre>
@@ -74,13 +75,11 @@ ok
<title>Inspecting the configuration</title>
<pre>
-Erlang R13B02 (erts-5.7.3) [source] [64-bit] [smp:4:4] [rq:4]
- [async-threads:0] [kernel-poll:false]
-
-Eshell V5.7.3 (abort with ^G)
-1&gt; Config = {sys, [{escript,
- "examples/display_args",
- [{incl_cond, include}]},
+Erlang/OTP 20 [erts-9.0] [source-c13b302] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10]
+[hipe] [kernel-poll:false]
+Eshell V9.0 (abort with ^G)
+1&gt;
+1&gt; Config = {sys, [{escript, "examples/display_args", [{incl_cond, include}]},
{app, inets, [{incl_cond, include}]},
{app, mnesia, [{incl_cond, exclude}]},
{app, ssl, [{incl_cond, exclude}]},
@@ -92,88 +91,105 @@ Eshell V5.7.3 (abort with ^G)
{app,ssl,[{incl_cond,exclude}]},
{app,runtime_tools,[{incl_cond,exclude}]},
{app,syntax_tools,[{incl_cond,exclude}]}]}
-
-
-
+2&gt;
2&gt; {ok, Server} = reltool:start_server([Config]).
-{ok,&lt;0.35.0&gt;}
+{ok,&lt;0.66.0&gt;}
+3&gt;
3&gt; reltool:get_config(Server).
-{ok,{sys,[{escript,"/clearcase/otp/tools/reltool/examples/display_args",
- [{incl_cond,include}]}]}}
+{ok,{sys,[{escript,"/usr/local/lib/erlang/lib/reltool-0.7.3/examples/display_args",
+ [{incl_cond,include}]},
+ {app,inets,[{incl_cond,include}]},
+ {app,mnesia,[{incl_cond,exclude}]},
+ {app,runtime_tools,[{incl_cond,exclude}]},
+ {app,ssl,[{incl_cond,exclude}]},
+ {app,syntax_tools,[{incl_cond,exclude}]}]}}
+4&gt;
4&gt; reltool:get_config(Server, false, false).
-{ok,{sys,[{escript,"/clearcase/otp/tools/reltool/examples/display_args",
- [{incl_cond,include}]}]}}
-
-
-
+{ok,{sys,[{escript,"/usr/local/lib/erlang/lib/reltool-0.7.3/examples/display_args",
+ [{incl_cond,include}]},
+ {app,inets,[{incl_cond,include}]},
+ {app,mnesia,[{incl_cond,exclude}]},
+ {app,runtime_tools,[{incl_cond,exclude}]},
+ {app,ssl,[{incl_cond,exclude}]},
+ {app,syntax_tools,[{incl_cond,exclude}]}]}}
+5&gt;
5&gt; reltool:get_config(Server, true, false).
-{ok,{sys,[{root_dir,"/ldisk/hakan/otp_test"},
+{ok,{sys,[{root_dir,"/usr/local/lib/erlang"},
{lib_dirs,[]},
- {escript,"/clearcase/otp/tools/reltool/examples/display_args",
+ {escript,"/usr/local/lib/erlang/lib/reltool-0.7.3/examples/display_args",
[{incl_cond,include}]},
{mod_cond,all},
{incl_cond,derived},
+ {app,inets,
+ [{incl_cond,include},{vsn,undefined},{lib_dir,undefined}]},
+ {app,mnesia,[{incl_cond,exclude}]},
+ {app,runtime_tools,[{incl_cond,exclude}]},
+ {app,ssl,[{incl_cond,exclude}]},
+ {app,syntax_tools,[{incl_cond,exclude}]},
{boot_rel,"start_clean"},
+ {rel,"start_clean","1.0",[]},
+ {rel,"start_sasl","1.0",[sasl]},
{emu_name,"beam"},
{relocatable,true},
{profile,development},
- {incl_sys_files,[".*"]},
- {excl_sys_files,[]},
- {incl_app_files,[".*"]},
- {excl_app_files,[]},
- {incl_archive_dirs,[".*"]},
- {excl_archive_dirs,["^include$","^priv$"]},
+ {incl_sys_filters,[".*"]},
+ {excl_sys_filters,[]},
+ {incl_app_filters,[".*"]},
+ {excl_app_filters,[]},
+ {incl_archive_filters,[".*"]},
+ {excl_archive_filters,[[...]|...]},
{archive_opts,[]},
- {app_type,permanent},
- {app_file,keep},
- {debug_info,keep}]}}
-
-
-
+ {rel_app_type,...},
+ {...}|...]}}
+6&gt;
6&gt; reltool:get_config(Server, true, true).
-{ok,{sys,[{root_dir,"/ldisk/hakan/otp_test"},
+{ok,{sys,[{root_dir,"/usr/local/lib/erlang"},
{lib_dirs,[]},
- {escript,"/clearcase/otp/tools/reltool/examples/display_args",
+ {escript,"/usr/local/lib/erlang/lib/reltool-0.7.3/examples/display_args",
[{incl_cond,include}]},
{mod_cond,all},
{incl_cond,derived},
- {erts,[{vsn,"5.7.3"},
- {mod,erl_prim_loader,[]},
- {mod,erlang,[]},
- {mod,init,[]},
- {mod,otp_ring0,[]},
- {mod,prim_file,[]},
- {mod,prim_inet,[]},
- {mod,prim_zip,[]},
- {mod,zlib,[]}]},
+ {erts,[{app,erts,
+ [{vsn,"9.0"},
+ {lib_dir,"/usr/local/lib/erlang/lib/erts-9.0"},
+ {mod,erl_prim_loader,[]},
+ {mod,erl_tracer,[]},
+ {mod,erlang,[]},
+ {mod,erts_code_purger,[]},
+ {mod,erts_dirty_process_code_checker,[]},
+ {mod,erts_internal,[]},
+ {mod,erts_literal_area_collector,[]},
+ {mod,init,[]},
+ {mod,otp_ring0,...},
+ {mod,...},
+ {...}|...]}]},
{app,compiler,
- [{vsn,"4.6.3"},
+ [{vsn,"7.0.4"},
+ {lib_dir,"/usr/local/lib/erlang/lib/compiler-7.0.4"},
+ {mod,beam_a,[]},
{mod,beam_asm,[]},
{mod,beam_block,[]},
- {mod,beam_bool,[]},
+ {mod,beam_bs,[]},
{mod,beam_bsm,[]},
{mod,beam_clean,[]},
{mod,beam_dead,[]},
{mod,beam_dict,[]},
{mod,beam_disasm,[]},
- {mod,beam_flatten,[]},
- {mod,beam_jump,[]},
- {mod,beam_listing,[]},
- {mod,beam_opcodes,...},
+ {mod,beam_except,[]},
+ {mod,beam_flatten,...},
{mod,...},
{...}|...]},
{app,crypto,
- [{vsn,"1.6.1"},
+ [{vsn,"3.7.4"},
+ {lib_dir,"/usr/local/lib/erlang/lib/crypto-3.7.4"},
{mod,crypto,[]},
- {mod,crypto_app,[]},
- {mod,crypto_server,[]},
- {mod,crypto_sup,[]}]},
+ {mod,crypto_ec_curves,[]}]},
{app,hipe,
- [{vsn,"3.7.3"},
+ [{vsn,"3.15.4"},
+ {lib_dir,"/usr/local/lib/erlang/lib/hipe-3.15.4"},
{mod,cerl_cconv,[]},
{mod,cerl_closurean,[]},
{mod,cerl_hipeify,[]},
- {mod,cerl_hybrid_transform,[]},
{mod,cerl_lib,[]},
{mod,cerl_messagean,[]},
{mod,cerl_pmatch,[]},
@@ -182,65 +198,110 @@ Eshell V5.7.3 (abort with ^G)
{mod,cerl_typean,...},
{mod,...},
{...}|...]},
+ {app,inets,
+ [{incl_cond,include},
+ {vsn,"6.3.9"},
+ {lib_dir,"/usr/local/lib/erlang/lib/inets-6.3.9"},
+ {mod,ftp,[]},
+ {mod,ftp_progress,[]},
+ {mod,ftp_response,[]},
+ {mod,ftp_sup,[]},
+ {mod,http_chunk,[]},
+ {mod,http_request,[]},
+ {mod,http_response,...},
+ {mod,...},
+ {...}|...]},
{app,kernel,
- [{vsn,"2.13.3"},
+ [{vsn,"5.2"},
+ {lib_dir,"/usr/local/lib/erlang/lib/kernel-5.2"},
{mod,application,[]},
{mod,application_controller,[]},
{mod,application_master,[]},
{mod,application_starter,[]},
{mod,auth,[]},
{mod,code,[]},
- {mod,code_server,[]},
- {mod,disk_log,[]},
- {mod,disk_log_1,...},
+ {mod,code_server,...},
+ {mod,...},
+ {...}|...]},
+ {app,mnesia,[{incl_cond,exclude}]},
+ {app,runtime_tools,[{incl_cond,exclude}]},
+ {app,sasl,
+ [{vsn,"3.0.3"},
+ {lib_dir,"/usr/local/lib/erlang/lib/sasl-3.0.3"},
+ {mod,alarm_handler,[]},
+ {mod,erlsrv,[]},
+ {mod,format_lib_supp,[]},
+ {mod,misc_supp,...},
{mod,...},
{...}|...]},
+ {app,ssl,[{incl_cond,exclude}]},
{app,stdlib,
- [{vsn,"1.16.3"},
+ [{vsn,"3.3"},
+ {lib_dir,"/usr/local/lib/erlang/lib/stdlib-3.3"},
{mod,array,[]},
- {mod,base64,[]},
- {mod,beam_lib,[]},
- {mod,c,[]},
- {mod,calendar,[]},
- {mod,dets,[]},
- {mod,dets_server,[]},
- {mod,dets_sup,...},
+ {mod,base64,...},
{mod,...},
{...}|...]},
+ {app,syntax_tools,[{incl_cond,exclude}]},
+ {app,tools,
+ [{vsn,"2.9.1"},{lib_dir,[...]},{mod,...},{...}|...]},
{boot_rel,"start_clean"},
+ {rel,"start_clean","1.0",[]},
+ {rel,"start_sasl","1.0",[...]},
{emu_name,"beam"},
{relocatable,true},
- {profile,development},
- {incl_sys_files,[".*"]},
- {excl_sys_files,[]},
- {incl_app_files,[".*"]},
- {excl_app_files,[]},
- {incl_archive_dirs,[".*"]},
- {excl_archive_dirs,["^include$",[...]]},
- {archive_opts,[]},
- {app_type,permanent},
- {app_file,...},
- {...}]}}
-
-
-
-7&gt; reltool:get_config([{sys,[{profile, embedded}]}]).
-{ok,{sys,[{profile,embedded},
+ {profile,...},
+ {...}|...]}}
+7&gt;
+7&gt; reltool:get_config([{sys, [{profile, embedded}]}], true, false).
+{ok,{sys,[{root_dir,"/usr/local/lib/erlang"},
+ {lib_dirs,[]},
+ {mod_cond,all},
+ {incl_cond,derived},
+ {boot_rel,"start_clean"},
+ {rel,"start_clean","1.0",[]},
+ {rel,"start_sasl","1.0",[sasl]},
+ {emu_name,"beam"},
+ {relocatable,true},
+ {profile,embedded},
{incl_sys_filters,["^bin","^erts","^lib","^releases"]},
{excl_sys_filters,["^bin/(erlc|dialyzer|typer)(|\\.exe)$",
"^erts.*/bin/(erlc|dialyzer|typer)(|\\.exe)$",
"^erts.*/bin/.*(debug|pdb)"]},
- {incl_app_filters,["^ebin","^include","^priv"]}]}}
-8&gt; reltool:get_config([{sys,[{profile, standalone}]}]).
-{ok,{sys,[{profile,standalone},
+ {incl_app_filters,["^ebin","^include","^priv"]},
+ {excl_app_filters,[]},
+ {incl_archive_filters,[".*"]},
+ {excl_archive_filters,["^include$","^priv$"]},
+ {archive_opts,[]},
+ {rel_app_type,permanent},
+ {embedded_app_type,load},
+ {app_file,keep},
+ {debug_info,keep}]}}
+8&gt;
+8&gt; reltool:get_config([{sys, [{profile, standalone}]}], true, false).
+{ok,{sys,[{root_dir,"/usr/local/lib/erlang"},
+ {lib_dirs,[]},
+ {mod_cond,all},
+ {incl_cond,derived},
+ {boot_rel,"start_clean"},
+ {rel,"start_clean","1.0",[]},
+ {rel,"start_sasl","1.0",[sasl]},
+ {emu_name,"beam"},
+ {relocatable,true},
+ {profile,standalone},
{incl_sys_filters,["^bin/(erl|epmd)(|\\.exe|\\.ini)$",
"^bin/start(|_clean).boot$","^erts.*/bin","^lib$"]},
{excl_sys_filters,["^erts.*/bin/(erlc|dialyzer|typer)(|\\.exe)$",
"^erts.*/bin/(start|escript|to_erl|run_erl)(|\\.exe)$",
"^erts.*/bin/.*(debug|pdb)"]},
{incl_app_filters,["^ebin","^priv"]},
- {excl_app_filters,["^ebin/.*\\.appup$"]}]}}
-
+ {excl_app_filters,["^ebin/.*\\.appup$"]},
+ {incl_archive_filters,[".*"]},
+ {excl_archive_filters,["^include$","^priv$"]},
+ {archive_opts,[]},
+ {rel_app_type,permanent},
+ {app_file,keep},
+ {debug_info,keep}]}}
</pre>
</section>
@@ -248,43 +309,51 @@ Eshell V5.7.3 (abort with ^G)
<section>
<title>Generate release and script files</title>
<pre>
-5&gt; {ok, Server} = reltool:start_server([{config, {sys, [{boot_rel, "NAME"},
+Erlang/OTP 20 [erts-9.0] [source-c13b302] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10]
+[hipe] [kernel-poll:false]
+Eshell V9.0 (abort with ^G)
+1&gt;
+1&gt; {ok, Server} = reltool:start_server([{config, {sys, [{boot_rel, "NAME"},
{rel, "NAME", "VSN",
[sasl]}]}}]).
{ok,&lt;0.1288.0&gt;}
-6&gt; reltool:get_config(Server).
+2&gt;
+2&gt; reltool:get_config(Server).
{ok,{sys,[{boot_rel,"NAME"},
{rel,"NAME","VSN",[sasl]}]}}
-7&gt; reltool:get_rel(Server, "NAME").
+3&gt;
+3&gt; reltool:get_rel(Server, "NAME").
{ok,{release,{"NAME","VSN"},
- {erts,"5.7"},
- [{kernel,"2.13"},{stdlib,"1.16"},{sasl,"2.1.6"}]}}
-8&gt; reltool:get_script(Server, "NAME").
+ {erts,"9.0"},
+ [{kernel,"5.2"},{stdlib,"3.3"},{sasl,"3.0.3"}]}}
+4&gt;
+4&gt; reltool:get_script(Server, "NAME").
{ok,{script,{"NAME","VSN"},
- [{preLoaded,[erl_prim_loader,erlang,init,otp_ring0,
- prim_eval,prim_file,prim_inet,prim_zip,
- zlib]},
+ [{preLoaded,[erl_prim_loader,erl_tracer,erlang,
+ erts_code_purger,erts_dirty_process_code_checker,
+ erts_internal,erts_literal_area_collector,init,otp_ring0,
+ prim_eval,prim_file,prim_inet,prim_zip,zlib]},
{progress,preloaded},
- {path,["$ROOT/lib/kernel-2.13/ebin",
- "$ROOT/lib/stdlib-1.16/ebin"]},
+ {path,["$ROOT/lib/kernel-5.2/ebin",
+ "$ROOT/lib/stdlib-3.3/ebin"]},
{primLoad,[error_handler]},
{kernel_load_completed},
{progress,kernel_load_completed},
- {path,["$ROOT/lib/kernel-2.13/ebin"]},
+ {path,["$ROOT/lib/kernel-5.2/ebin"]},
{primLoad,[application,application_controller,
application_master,application_starter,auth,code,
code_server,disk_log,disk_log_1,disk_log_server,
disk_log_sup,dist_ac,dist_util,erl_boot_server|...]},
- {path,["$ROOT/lib/stdlib-1.16/ebin"]},
- {primLoad,[array,base64,beam_lib,c,calendar,dets,
+ {path,["$ROOT/lib/stdlib-3.3/ebin"]},
+ {primLoad,[array,base64,beam_lib,binary,c,calendar,dets,
dets_server,dets_sup,dets_utils,dets_v9,dict|...]},
- {path,["$ROOT/lib/sasl-2.1.6/ebin"]},
+ {path,["$ROOT/lib/sasl-3.0.3/ebin"]},
{primLoad,[alarm_handler,erlsrv,format_lib_supp,misc_supp,
- overload,rb,rb_format_supp,release_handler,
- release_handler_1,sasl|...]},
+ rb,rb_format_supp,release_handler,release_handler_1,sasl,
+ sasl_report|...]},
{progress,modules_loaded},
- {path,["$ROOT/lib/kernel-2.13/ebin",
- "$ROOT/lib/stdlib-1.16/ebin","$ROOT/lib/sasl-2.1.6/ebin"]},
+ {path,["$ROOT/lib/kernel-5.2/ebin",
+ "$ROOT/lib/stdlib-3.3/ebin","$ROOT/lib/sasl-3.0.3/ebin"]},
{kernelProcess,heart,{heart,start,[]}},
{kernelProcess,error_logger,{error_logger,start_link,[]}},
{kernelProcess,application_controller,
@@ -296,7 +365,8 @@ Eshell V5.7.3 (abort with ^G)
{apply,{...}},
{apply,...},
{...}|...]}}
-9&gt; reltool:stop(Server).
+5&gt;
+5&gt; reltool:stop(Server).
ok
</pre>
</section>
@@ -304,13 +374,11 @@ ok
<section>
<title>Create a target system</title>
<pre>
-Erlang R13B02 (erts-5.7.3) [source] [64-bit] [smp:4:4] [rq:4]
- [async-threads:0] [kernel-poll:false]
-
-Eshell V5.7.3 (abort with ^G)
-1&gt; Config = {sys, [{escript,
- "examples/display_args",
- [{incl_cond, include}]},
+Erlang/OTP 20 [erts-9.0] [source-c13b302] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10]
+[hipe] [kernel-poll:false]
+Eshell V9.0 (abort with ^G)
+1&gt;
+1&gt; Config = {sys, [{escript, "examples/display_args", [{incl_cond, include}]},
{app, inets, [{incl_cond, include}]},
{app, mnesia, [{incl_cond, exclude}]},
{app, ssl, [{incl_cond, exclude}]},
@@ -322,156 +390,183 @@ Eshell V5.7.3 (abort with ^G)
{app,ssl,[{incl_cond,exclude}]},
{app,runtime_tools,[{incl_cond,exclude}]},
{app,syntax_tools,[{incl_cond,exclude}]}]}
-
-
-
+2&gt;
2&gt; {ok, Spec} = reltool:get_target_spec([Config]).
{ok,[{create_dir,"releases",
- [{write_file,"start_erl.data","5.7.3 1.0"},
- {create_dir,"1.0",
- [{write_file,"start_clean.rel",
- [37,37,32,114,101,108,32,103,101,110,101|...]},
- {write_file,"start_clean.script",
- [37,37,32,115,99,114,105,112,116,32|...]},
- {write_file,"start_clean.boot",
- &lt;&lt;131,104,3,100,0,6,115,99,114,...&gt;&gt;},
- {write_file,"start_sasl.rel",
- [37,37,32,114,101,108,32,103,101,110,101|...]},
- {write_file,"start_sasl.script",
- [37,37,32,115,99,114,105,112,116,32|...]},
- {write_file,"start_sasl.boot",
- &lt;&lt;131,104,3,100,0,6,115,99,114,...&gt;&gt;}]}]},
+ [{write_file,"start_erl.data","9.0 1.0\n"},
+ {create_dir,"1.0",
+ [{write_file,"start_clean.rel",
+ [37,37,32,114,101,108,32,103,101,110,101,114,97,116|...]},
+ {write_file,"start_clean.script",
+ [37,37,32,115,99,114,105,112,116,32,103,101,110|...]},
+ {write_file,"start_clean.boot",
+ &lt;&lt;131,104,3,119,6,115,99,114,105,112,116,104,...&gt;&gt;},
+ {write_file,"start_sasl.rel",
+ [37,37,32,114,101,108,32,103,101,110,101|...]},
+ {write_file,"start_sasl.script",
+ [37,37,32,115,99,114,105,112,116,32|...]},
+ {write_file,"start_sasl.boot",
+ &lt;&lt;131,104,3,119,6,115,99,114,105,...&gt;&gt;}]}]},
{create_dir,"bin",
- [{copy_file,"display_args.escript",
- "/clearcase/otp/tools/reltool/examples/display_args"},
- {copy_file,"display_args","erts-5.7.3/bin/escript"},
- {copy_file,"start","erts-5.7.3/bin/start"},
- {copy_file,"erl","erts-5.7.3/bin/dyn_erl"},
- {copy_file,"epmd","erts-5.7.3/bin/epmd"},
- {copy_file,"to_erl","erts-5.7.3/bin/to_erl"},
- {copy_file,"run_erl","erts-5.7.3/bin/run_erl"},
- {copy_file,"escript","erts-5.7.3/bin/escript"},
- {copy_file,"erlc","erts-5.7.3/bin/erlc"},
- {copy_file,"dialyzer","erts-5.7.3/bin/dialyzer"},
- {copy_file,"typer","erts-5.7.3/bin/typer"},
- {write_file,"start_clean.boot",
- &lt;&lt;131,104,3,100,0,6,115,...&gt;&gt;},
- {write_file,"start_sasl.boot",&lt;&lt;131,104,3,100,0,6,...&gt;&gt;},
- {write_file,"start.boot",&lt;&lt;131,104,3,100,0,...&gt;&gt;}]},
- {create_dir,"misc",
- [{copy_file,"makewhatis"},{copy_file,"format_man_pages"}]},
+ [{copy_file,"display_args.escript",
+ "/usr/local/lib/erlang/lib/reltool-0.7.3/examples/display_args"},
+ {copy_file,"display_args","erts-9.0/bin/escript"},
+ {copy_file,"start","erts-9.0/bin/start"},
+ {copy_file,"ct_run","erts-9.0/bin/ct_run"},
+ {copy_file,"dialyzer","erts-9.0/bin/dialyzer"},
+ {copy_file,"run_erl","erts-9.0/bin/run_erl"},
+ {copy_file,"erl","erts-9.0/bin/dyn_erl"},
+ {copy_file,"to_erl","erts-9.0/bin/to_erl"},
+ {copy_file,"epmd","erts-9.0/bin/epmd"},
+ {copy_file,"erlc","erts-9.0/bin/erlc"},
+ {copy_file,"typer","erts-9.0/bin/typer"},
+ {copy_file,"escript","erts-9.0/bin/escript"},
+ {write_file,"start_clean.boot",&lt;&lt;131,104,3,119,6,115,...&gt;&gt;},
+ {write_file,"start_sasl.boot",&lt;&lt;131,104,3,119,6,...&gt;&gt;},
+ {write_file,"start.boot",&lt;&lt;131,104,3,119,...&gt;&gt;}]},
{copy_file,"Install"},
+ {create_dir,"misc",
+ [{copy_file,"makewhatis"},{copy_file,"format_man_pages"}]},
{create_dir,"usr",
- [{create_dir,"lib",
- [{copy_file,"liberts_r.a"},{copy_file,"liberts.a"}]},
- {create_dir,"include",
- [{copy_file,"erl_fixed_size_int_types.h"},
- {copy_file,"erl_int_sizes_config.h"},
- {copy_file,"erl_memory_trace_parser.h"},
- {create_dir,"obsolete",[{copy_file,"driver.h"}]},
- {copy_file,"driver_int.h"},
- {copy_file,"erl_driver.h"}]}]},
- {create_dir,"erts-5.7.3",
- [{create_dir,"lib",
- [{create_dir,"internal",
- [{copy_file,"liberts_internal_r.a"},
- {copy_file,"liberts_internal.a"},
- {copy_file,"libethread.a"},
- {copy_file,"README"}]},
- {copy_file,"liberts_r.a"},
- {copy_file,"liberts.a"}]},
- {create_dir,"bin",
- [{copy_file,"start"},
- {copy_file,"erl","erts-5.7.3/bin/dyn_erl"},
- {copy_file,"epmd"},
- {copy_file,"to_erl"},
- {copy_file,"run_erl"},
- {copy_file,"escript"},
- {copy_file,"erlc"},
- {copy_file,"dialyzer"},
- {copy_file,"typer"},
- {copy_file,"erlexec"},
- {copy_file,[...]},
- {copy_file,...},
- {...}|...]},
- {create_dir,"doc",[]},
- {create_dir,"man",[]},
- {create_dir,"include",
- [{create_dir,"internal",
- [{create_dir,"tile",[{copy_file,...},{...}]},
- {create_dir,"sparc64",[{...}]},
- {create_dir,"sparc32",[...]},
- {create_dir,[...],...},
- {create_dir,...},
- {...}|...]},
- {copy_file,"erl_fixed_size_int_types.h"},
- {copy_file,"erl_int_sizes_config.h"},
- {copy_file,"erl_memory_trace_parser.h"},
- {copy_file,"driver_int.h"},
- {copy_file,"erl_driver.h"}]},
- {create_dir,"src",[{copy_file,"setuid_socket_wrap.c"}]}]},
+ [{create_dir,"lib",
+ [{copy_file,"liberts.a"},
+ {copy_file,"liberl_interface_st.a"},
+ {copy_file,"liberts_r.a"},
+ {copy_file,"libic.a"},
+ {copy_file,"liberl_interface.a"},
+ {copy_file,"libei_st.a"},
+ {copy_file,"libei.a"}]},
+ {create_dir,"include",
+ [{copy_file,"erl_memory_trace_parser.h"},
+ {copy_file,"driver_int.h"},
+ {copy_file,"ei_connect.h"},
+ {copy_file,"ei.h"},
+ {copy_file,"erl_nif_api_funcs.h"},
+ {copy_file,"erl_fixed_size_int_types.h"},
+ {copy_file,"erl_int_sizes_config.h"},
+ {copy_file,"erl_interface.h"},
+ {copy_file,"eicode.h"},
+ {copy_file,"erl_driver.h"},
+ {copy_file,"erlang.idl"},
+ {copy_file,[...]},
+ {copy_file,...},
+ {...}]}]},
+ {create_dir,"erts-9.0",
+ [{create_dir,"bin",
+ [{copy_file,"start"},
+ {copy_file,"ct_run"},
+ {copy_file,"erlexec"},
+ {copy_file,"dialyzer"},
+ {copy_file,"beam.smp"},
+ {copy_file,"run_erl"},
+ {copy_file,"erl","erts-9.0/bin/dyn_erl"},
+ {copy_file,"to_erl"},
+ {copy_file,"epmd"},
+ {copy_file,"erl_child_setup"},
+ {copy_file,"heart"},
+ {copy_file,[...]},
+ {copy_file,...},
+ {...}|...]},
+ {create_dir,"lib",
+ [{create_dir,"internal",
+ [{copy_file,"liberts_internal.a"},
+ {copy_file,"liberts_internal_r.a"},
+ {copy_file,"libethread.a"},
+ {copy_file,"README"}]},
+ {copy_file,"liberts.a"},
+ {copy_file,"liberts_r.a"}]},
+ {create_dir,"src",[{copy_file,"setuid_socket_wrap.c"}]},
+ {create_dir,"doc",[]},
+ {create_dir,"man",[]},
+ {create_dir,"include",
+ [{create_dir,"internal",
+ [{create_dir,"i386",[{...}|...]},
+ {copy_file,"erl_errno.h"},
+ {copy_file,[...]},
+ {copy_file,...},
+ {...}|...]},
+ {copy_file,"erl_memory_trace_parser.h"},
+ {copy_file,"driver_int.h"},
+ {copy_file,"erl_nif_api_funcs.h"},
+ {copy_file,"erl_fixed_size_int_types.h"},
+ {copy_file,"erl_int_sizes_config.h"},
+ {copy_file,[...]},
+ {copy_file,...},
+ {...}]}]},
{create_dir,"lib",
- [{archive,"compiler-4.6.3.ez",[],
- [{create_dir,"compiler-4.6.3",
- [{create_dir,"ebin",
- [{copy_file,"compiler.appup"},
- {copy_file,[...]},
- {copy_file,...},
- {...}|...]},
- {create_dir,"src",
- [{copy_file,[...]},
- {copy_file,...},{...}|...]}]}]},
- {archive,"crypto-1.6.1.ez",[],
- [{create_dir,"crypto-1.6.1",
- [{create_dir,"ebin",
- [{copy_file,[...]},
- {copy_file,...},{...}|...]},
- {create_dir,"src",[{copy_file,...},{...}|...]}]}]},
- {create_dir,"crypto-1.6.1",
- [{create_dir,"priv",
- [{create_dir,"lib",[{copy_file,[...]}]},
- {create_dir,"obj",[{copy_file,...},{...}]}]}]},
- {archive,"erts-5.7.3.ez",[],
- [{create_dir,"erts-5.7.3",
- [{create_dir,"ebin",[{...}|...]},
- {create_dir,"src",[...]}]}]},
- {archive,"hipe-3.7.3.ez",[],
- [{create_dir,"hipe-3.7.3",
- [{create_dir,"util",[...]},
- {create_dir,[...],...},
- {create_dir,...},
- {...}|...]}]},
- {archive,"kernel-2.13.3.ez",[],
- [{create_dir,"kernel-2.13.3",
- [{create_dir,[...],...},{create_dir,...},{...}]}]},
- {create_dir,"kernel-2.13.3",
- [{create_dir,"include",
- [{copy_file,[...]},{copy_file,...},{...}]}]},
- {archive,"stdlib-1.16.3.ez",[],
- [{create_dir,"stdlib-1.16.3",[{...}|...]}]},
- {create_dir,"stdlib-1.16.3",
- [{create_dir,"include",[{...}|...]}]}]}]}
-
-
-
-3&gt; TargetDir = "my_target_dir".
-"my_target_dir"
+ [{archive,"compiler-7.0.4.ez",[],
+ [{create_dir,"compiler-7.0.4",
+ [{create_dir,"src",
+ [{copy_file,"beam_flatten.erl"},
+ {copy_file,[...]},
+ {copy_file,...},
+ {...}|...]},
+ {create_dir,"ebin",
+ [{copy_file,[...]},{copy_file,...},{...}|...]}]}]},
+ {archive,"crypto-3.7.4.ez",[],
+ [{create_dir,"crypto-3.7.4",
+ [{create_dir,"src",[{copy_file,[...]},{copy_file,...}]},
+ {create_dir,"ebin",[{copy_file,...},{...}|...]}]}]},
+ {create_dir,"crypto-3.7.4",
+ [{create_dir,"priv",
+ [{create_dir,"lib",[{copy_file,[...]},{copy_file,...}]},
+ {create_dir,"obj",[{copy_file,...},{...}|...]}]}]},
+ {archive,"erts-9.0.ez",[],
+ [{create_dir,"erts-9.0",
+ [{create_dir,"src",[{...}|...]},
+ {create_dir,"ebin",[...]}]}]},
+ {archive,"hipe-3.15.4.ez",[],
+ [{create_dir,"hipe-3.15.4",
+ [{create_dir,"flow",[...]},
+ {copy_file,[...]},
+ {create_dir,...},
+ {...}|...]}]},
+ {archive,"inets-6.3.9.ez",[],
+ [{create_dir,"inets-6.3.9",
+ [{create_dir,[...],...},{create_dir,...},{...}]}]},
+ {create_dir,"inets-6.3.9",
+ [{create_dir,"priv",[{create_dir,[...],...}]},
+ {create_dir,"include",[{copy_file,...},{...}]}]},
+ {archive,"kernel-5.2.ez",[],
+ [{create_dir,"kernel-5.2",[{...}|...]}]},
+ {create_dir,"kernel-5.2",
+ [{create_dir,"include",[{...}|...]}]},
+ {archive,"sasl-3.0.3.ez",[],[{create_dir,[...],...}]},
+ {archive,"stdlib-3.3.ez",[],[{create_dir,...}]},
+ {create_dir,"stdlib-3.3",[{create_dir,...}]},
+ {archive,"tools-2.9.1.ez",[],[...]}]}]}
+3&gt;
+3&gt; TargetDir = "/tmp/my_target_dir".
+"/tmp/my_target_dir"
+4&gt;
4&gt; reltool:eval_target_spec(Spec, code:root_dir(), TargetDir).
-{error,"/clearcase/otp/tools/reltool/my_target_dir: no such file or directory"}
-5&gt; file:make_dir("my_target_dir").
+{error,"/tmp/my_target_dir: no such file or directory"}
+5&gt;
+5&gt; file:make_dir(TargetDir).
ok
+6&gt;
6&gt; reltool:eval_target_spec(Spec, code:root_dir(), TargetDir).
ok
+7&gt;
7&gt; file:list_dir(TargetDir).
-{ok,["lib","erts-5.7.3","usr","Install","misc","bin","releases"]}
+{ok,["bin","Install","lib","misc","usr","erts-9.0",
+ "releases"]}
+8&gt;
8&gt; file:list_dir(filename:join([TargetDir,"lib"])).
-{ok,["stdlib-1.16.3","stdlib-1.16.3.ez","kernel-2.13.3",
- "kernel-2.13.3.ez","hipe-3.7.3.ez","erts-5.7.3.ez",
- "crypto-1.6.1","crypto-1.6.1.ez","compiler-4.6.3.ez"]}
-9&gt; file:make_dir("yet_another_target_dir").
+{ok,["tools-2.9.1.ez","kernel-5.2.ez","inets-6.3.9.ez",
+ "kernel-5.2","sasl-3.0.3.ez","hipe-3.15.4.ez","inets-6.3.9",
+ "crypto-3.7.4","crypto-3.7.4.ez","stdlib-3.3.ez",
+ "erts-9.0.ez","stdlib-3.3","compiler-7.0.4.ez"]}
+9&gt;
+9&gt; file:make_dir("/tmp/yet_another_target_dir").
ok
-10&gt; reltool:create_target(Config, "yet_another_target_dir").
+10&gt;
+10&gt; reltool:create_target([Config], "/tmp/yet_another_target_dir").
ok
+11&gt;
+11&gt; file:list_dir("/tmp/yet_another_target_dir").
+{ok,["bin","Install","lib","misc","usr","erts-9.0",
+ "releases"]}
</pre>
</section>
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index ad7f2f2e95..a7caa71dcb 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -1098,7 +1098,7 @@ handle_event(_, _, State, Data) ->
<seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
or
<seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>
- would be weird on the border of whichcraft
+ would be weird on the border of witchcraft
since there has been no earlier call to a
<seealso marker="#state callback">state callback</seealso>
in this server.
diff --git a/otp_versions.table b/otp_versions.table
index e86ace6e02..cb52cbd51e 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,4 @@
+OTP-19.3.6 : erts-8.3.5 # asn1-4.0.4 common_test-1.14 compiler-7.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 ssl-8.1.3 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.14 :
OTP-19.3.5 : erts-8.3.4 xmerl-1.3.14 # asn1-4.0.4 common_test-1.14 compiler-7.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 inets-6.3.9 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 ssl-8.1.3 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 :
OTP-19.3.4 : inets-6.3.9 ssl-8.1.3 # asn1-4.0.4 common_test-1.14 compiler-7.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 dialyzer-3.1.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 erts-8.3.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.13 :
OTP-19.3.3 : dialyzer-3.1.1 erts-8.3.3 inets-6.3.8 # asn1-4.0.4 common_test-1.14 compiler-7.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.4 debugger-4.2.1 diameter-1.12.2 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.3 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.4 ic-4.4.2 jinterface-1.7.1 kernel-5.2 megaco-3.18.1 mnesia-4.14.3 observer-2.3.1 odbc-2.12 orber-3.8.2 os_mon-2.4.2 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.4 reltool-0.7.3 runtime_tools-1.11.1 sasl-3.0.3 snmp-5.2.5 ssh-4.4.2 ssl-8.1.2 stdlib-3.3 syntax_tools-2.1.1 tools-2.9.1 typer-0.9.12 wx-1.8 xmerl-1.3.13 :
@@ -8,6 +9,7 @@ OTP-19.2.3 : erts-8.2.2 inets-6.3.5 # asn1-4.0.4 common_test-1.13 compiler-7.0.3
OTP-19.2.2 : mnesia-4.14.3 # asn1-4.0.4 common_test-1.13 compiler-7.0.3 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.2 debugger-4.2.1 dialyzer-3.0.3 diameter-1.12.1 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.2 erts-8.2.1 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.3 ic-4.4.2 inets-6.3.4 jinterface-1.7.1 kernel-5.1.1 megaco-3.18.1 observer-2.3 odbc-2.12 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.3 reltool-0.7.2 runtime_tools-1.11 sasl-3.0.2 snmp-5.2.4 ssh-4.4 ssl-8.1 stdlib-3.2 syntax_tools-2.1.1 tools-2.9 typer-0.9.11 wx-1.8 xmerl-1.3.12 :
OTP-19.2.1 : erts-8.2.1 # asn1-4.0.4 common_test-1.13 compiler-7.0.3 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.2 debugger-4.2.1 dialyzer-3.0.3 diameter-1.12.1 edoc-0.8.1 eldap-1.2.2 erl_docgen-0.6.1 erl_interface-3.9.2 et-1.6 eunit-2.3.2 gs-1.6.2 hipe-3.15.3 ic-4.4.2 inets-6.3.4 jinterface-1.7.1 kernel-5.1.1 megaco-3.18.1 mnesia-4.14.2 observer-2.3 odbc-2.12 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.4 percept-0.9 public_key-1.3 reltool-0.7.2 runtime_tools-1.11 sasl-3.0.2 snmp-5.2.4 ssh-4.4 ssl-8.1 stdlib-3.2 syntax_tools-2.1.1 tools-2.9 typer-0.9.11 wx-1.8 xmerl-1.3.12 :
OTP-19.2 : common_test-1.13 compiler-7.0.3 crypto-3.7.2 dialyzer-3.0.3 edoc-0.8.1 erl_docgen-0.6.1 erl_interface-3.9.2 erts-8.2 eunit-2.3.2 hipe-3.15.3 inets-6.3.4 kernel-5.1.1 mnesia-4.14.2 observer-2.3 odbc-2.12 parsetools-2.1.4 public_key-1.3 runtime_tools-1.11 sasl-3.0.2 ssh-4.4 ssl-8.1 stdlib-3.2 syntax_tools-2.1.1 tools-2.9 wx-1.8 # asn1-4.0.4 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 debugger-4.2.1 diameter-1.12.1 eldap-1.2.2 et-1.6 gs-1.6.2 ic-4.4.2 jinterface-1.7.1 megaco-3.18.1 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 percept-0.9 reltool-0.7.2 snmp-5.2.4 typer-0.9.11 xmerl-1.3.12 :
+OTP-19.1.6.1 : erts-8.1.1.1 # asn1-4.0.4 common_test-1.12.3 compiler-7.0.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.1 debugger-4.2.1 dialyzer-3.0.2 diameter-1.12.1 edoc-0.8 eldap-1.2.2 erl_docgen-0.6 erl_interface-3.9.1 et-1.6 eunit-2.3.1 gs-1.6.2 hipe-3.15.2 ic-4.4.2 inets-6.3.3 jinterface-1.7.1 kernel-5.1 megaco-3.18.1 mnesia-4.14.1 observer-2.2.2 odbc-2.11.3 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.3 percept-0.9 public_key-1.2 reltool-0.7.2 runtime_tools-1.10.1 sasl-3.0.1 snmp-5.2.4 ssh-4.3.6 ssl-8.0.3 stdlib-3.1 syntax_tools-2.1 tools-2.8.6 typer-0.9.11 wx-1.7.1 xmerl-1.3.12 :
OTP-19.1.6 : erts-8.1.1 # asn1-4.0.4 common_test-1.12.3 compiler-7.0.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.1 debugger-4.2.1 dialyzer-3.0.2 diameter-1.12.1 edoc-0.8 eldap-1.2.2 erl_docgen-0.6 erl_interface-3.9.1 et-1.6 eunit-2.3.1 gs-1.6.2 hipe-3.15.2 ic-4.4.2 inets-6.3.3 jinterface-1.7.1 kernel-5.1 megaco-3.18.1 mnesia-4.14.1 observer-2.2.2 odbc-2.11.3 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.3 percept-0.9 public_key-1.2 reltool-0.7.2 runtime_tools-1.10.1 sasl-3.0.1 snmp-5.2.4 ssh-4.3.6 ssl-8.0.3 stdlib-3.1 syntax_tools-2.1 tools-2.8.6 typer-0.9.11 wx-1.7.1 xmerl-1.3.12 :
OTP-19.1.5 : ssh-4.3.6 # asn1-4.0.4 common_test-1.12.3 compiler-7.0.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.1 debugger-4.2.1 dialyzer-3.0.2 diameter-1.12.1 edoc-0.8 eldap-1.2.2 erl_docgen-0.6 erl_interface-3.9.1 erts-8.1 et-1.6 eunit-2.3.1 gs-1.6.2 hipe-3.15.2 ic-4.4.2 inets-6.3.3 jinterface-1.7.1 kernel-5.1 megaco-3.18.1 mnesia-4.14.1 observer-2.2.2 odbc-2.11.3 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.3 percept-0.9 public_key-1.2 reltool-0.7.2 runtime_tools-1.10.1 sasl-3.0.1 snmp-5.2.4 ssl-8.0.3 stdlib-3.1 syntax_tools-2.1 tools-2.8.6 typer-0.9.11 wx-1.7.1 xmerl-1.3.12 :
OTP-19.1.4 : ssh-4.3.5 # asn1-4.0.4 common_test-1.12.3 compiler-7.0.2 cosEvent-2.2.1 cosEventDomain-1.2.1 cosFileTransfer-1.2.1 cosNotification-1.2.2 cosProperty-1.2.1 cosTime-1.2.2 cosTransactions-1.3.2 crypto-3.7.1 debugger-4.2.1 dialyzer-3.0.2 diameter-1.12.1 edoc-0.8 eldap-1.2.2 erl_docgen-0.6 erl_interface-3.9.1 erts-8.1 et-1.6 eunit-2.3.1 gs-1.6.2 hipe-3.15.2 ic-4.4.2 inets-6.3.3 jinterface-1.7.1 kernel-5.1 megaco-3.18.1 mnesia-4.14.1 observer-2.2.2 odbc-2.11.3 orber-3.8.2 os_mon-2.4.1 otp_mibs-1.1.1 parsetools-2.1.3 percept-0.9 public_key-1.2 reltool-0.7.2 runtime_tools-1.10.1 sasl-3.0.1 snmp-5.2.4 ssl-8.0.3 stdlib-3.1 syntax_tools-2.1 tools-2.8.6 typer-0.9.11 wx-1.7.1 xmerl-1.3.12 :