aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/doc/src/erl.xml14
-rw-r--r--erts/doc/src/erlang.xml4
-rw-r--r--erts/doc/src/notes.xml55
-rw-r--r--erts/emulator/beam/bif.c24
-rwxr-xr-xerts/emulator/beam/erl_bif_info.c2
-rw-r--r--erts/emulator/beam/erl_db.c7
-rw-r--r--erts/emulator/beam/erl_init.c16
-rw-r--r--erts/emulator/beam/erl_port_task.c15
-rw-r--r--erts/emulator/beam/erl_process.c153
-rw-r--r--erts/emulator/beam/erl_process.h8
-rw-r--r--erts/emulator/beam/erl_ptab.c9
-rw-r--r--erts/emulator/beam/erl_ptab.h2
-rw-r--r--erts/emulator/beam/erl_thr_progress.c2
-rw-r--r--erts/emulator/beam/erl_thr_progress.h2
-rw-r--r--erts/emulator/beam/io.c7
-rw-r--r--erts/etc/common/erlexec.c1
-rw-r--r--erts/etc/unix/run_erl.c2
-rw-r--r--erts/etc/unix/to_erl.c2
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin54532 -> 54564 bytes
-rw-r--r--erts/preloaded/ebin/erlang.beambin93468 -> 93388 bytes
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin3612 -> 3644 bytes
-rw-r--r--erts/preloaded/ebin/init.beambin48632 -> 48672 bytes
-rw-r--r--erts/preloaded/ebin/otp_ring0.beambin1444 -> 1476 bytes
-rw-r--r--erts/preloaded/ebin/prim_file.beambin44244 -> 44280 bytes
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin70048 -> 70084 bytes
-rw-r--r--erts/preloaded/ebin/prim_zip.beambin23404 -> 23444 bytes
-rw-r--r--erts/preloaded/ebin/zlib.beambin12780 -> 12812 bytes
-rw-r--r--erts/preloaded/src/erlang.erl30
-rw-r--r--lib/common_test/doc/src/ct_run.xml12
-rw-r--r--lib/common_test/doc/src/run_test_chapter.xml31
-rw-r--r--lib/common_test/src/ct.erl3
-rw-r--r--lib/common_test/src/ct_framework.erl70
-rw-r--r--lib/common_test/src/ct_repeat.erl38
-rw-r--r--lib/common_test/src/ct_run.erl14
-rw-r--r--lib/common_test/test/Makefile1
-rw-r--r--lib/common_test/test/ct_repeat_testrun_SUITE.erl378
-rw-r--r--lib/common_test/test/ct_repeat_testrun_SUITE_data/a_test/r1_SUITE.erl75
-rw-r--r--lib/common_test/test/ct_repeat_testrun_SUITE_data/b_test/r2_SUITE.erl75
-rw-r--r--lib/common_test/test/ct_test_support.erl70
-rw-r--r--lib/stdlib/doc/src/sys.xml85
-rw-r--r--lib/stdlib/src/gen_event.erl18
-rw-r--r--lib/stdlib/src/gen_fsm.erl11
-rw-r--r--lib/stdlib/src/gen_server.erl7
-rw-r--r--lib/stdlib/src/sys.erl32
-rw-r--r--lib/stdlib/test/gen_event_SUITE.erl47
-rw-r--r--lib/stdlib/test/gen_fsm_SUITE.erl38
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl48
-rw-r--r--lib/stdlib/test/io_proto_SUITE.erl18
-rw-r--r--lib/test_server/src/test_server_gl.erl2
-rw-r--r--lib/test_server/test/test_server_SUITE.erl2
-rw-r--r--lib/test_server/test/test_server_SUITE_data/test_server_SUITE.erl13
-rw-r--r--system/doc/top/Makefile14
-rw-r--r--system/doc/top/highlights.xml110
-rw-r--r--system/doc/top/incompatible.xml271
54 files changed, 1249 insertions, 589 deletions
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index 622b40a80a..1f7c5b5a7f 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -1009,6 +1009,20 @@
documentation of the <seealso marker="#+sbt">+sbt</seealso> flag.
</p>
</item>
+ <tag><marker id="+swct"><c>+sws very_eager|eager|medium|lazy|very_lazy</c></marker></tag>
+ <item>
+ <p>
+ Set scheduler wake cleanup threshold. Default is <c>medium</c>.
+ This flag controls how eager schedulers should be requesting
+ wake up due to certain cleanup operations. When a lazy setting
+ is used, more outstanding cleanup operations can be left undone
+ while a scheduler is idling. When an eager setting is used,
+ schedulers will more frequently be woken, potentially increasing
+ CPU-utilization.
+ </p>
+ <p><em>NOTE:</em> This flag may be removed or changed at any time without prior notice.
+ </p>
+ </item>
<tag><marker id="+sws"><c>+sws default|legacy</c></marker></tag>
<item>
<p>
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 06fefa8efb..7dc59ea954 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -4477,6 +4477,7 @@ true</pre>
</func>
<func>
<name name="spawn_opt" arity="2"/>
+ <type name="priority_level" />
<fsummary>Create a new process with a fun as entry point</fsummary>
<desc>
<p>Returns the pid of a new process started by the application
@@ -4490,6 +4491,7 @@ true</pre>
</func>
<func>
<name name="spawn_opt" arity="3"/>
+ <type name="priority_level" />
<fsummary>Create a new process with a fun as entry point on a given node</fsummary>
<desc>
<p>Returns the pid of a new process started by the application
@@ -4501,6 +4503,7 @@ true</pre>
</func>
<func>
<name name="spawn_opt" arity="4"/>
+ <type name="priority_level" />
<fsummary>Create a new process with a function as entry point</fsummary>
<desc>
<p>Works exactly like
@@ -4602,6 +4605,7 @@ true</pre>
</func>
<func>
<name name="spawn_opt" arity="5"/>
+ <type name="priority_level" />
<fsummary>Create a new process with a function as entry point on a given node</fsummary>
<desc>
<p>Returns the pid of a new process started by the application
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 7fc61517a8..c9c46afa4b 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -30,6 +30,61 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 5.10.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The BIF <seealso
+ marker="erlang#is_process_alive/1"><c>is_process_alive/1</c></seealso>
+ could prematurely return <c>false</c> while the process
+ being inspected was terminating. This bug was introduced
+ in ERTS-5.10.</p>
+ <p>
+ Own Id: OTP-10926</p>
+ </item>
+ <item>
+ <p>Fix a problem in <c>erlang:delete_element/2</c> where
+ the call could corrupt one word of stack if the heap and
+ stack met during call.</p>
+ <p>
+ Own Id: OTP-10932</p>
+ </item>
+ <item>
+ <p>
+ The <seealso
+ marker="erl#+sws"><c>+sws&lt;value&gt;</c></seealso> and
+ <seealso
+ marker="erl#+swt"><c>+swt&lt;value&gt;</c></seealso>
+ system flags failed if no white space were passed between
+ the parameter and value parts of the flags. Upon failure,
+ the runtime system refused to start.</p>
+ <p>
+ Own Id: OTP-11000</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Scheduler threads will now by default be less eager
+ requesting wakeup due to certain cleanup operations. This
+ can also be controlled using the <seealso
+ marker="erl#+swct"><c>+swct</c></seealso> command line
+ argument of <seealso
+ marker="erl"><c>erl(1)</c></seealso>.</p>
+ <p>
+ Own Id: OTP-10994</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 5.10.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index 9c438679ea..ff237b6a78 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -2543,7 +2543,7 @@ BIF_RETTYPE insert_element_3(BIF_ALIST_3)
Eterm* hp;
Uint arity;
Eterm res;
- Sint ix;
+ Sint ix, c1, c2;
if (is_not_tuple(BIF_ARG_2) || is_not_small(BIF_ARG_1)) {
BIF_ERROR(BIF_P, BADARG);
@@ -2561,14 +2561,12 @@ BIF_RETTYPE insert_element_3(BIF_ALIST_3)
res = make_tuple(hp);
*hp = make_arityval(arity + 1);
- ix--;
- arity -= ix;
-
- while (ix--) { *++hp = *++ptr; }
+ c1 = ix - 1;
+ c2 = arity - ix + 1;
+ while (c1--) { *++hp = *++ptr; }
*++hp = BIF_ARG_3;
-
- while(arity--) { *++hp = *++ptr; }
+ while (c2--) { *++hp = *++ptr; }
BIF_RET(res);
}
@@ -2579,7 +2577,7 @@ BIF_RETTYPE delete_element_2(BIF_ALIST_3)
Eterm* hp;
Uint arity;
Eterm res;
- Sint ix;
+ Sint ix, c1, c2;
if (is_not_tuple(BIF_ARG_2) || is_not_small(BIF_ARG_1)) {
BIF_ERROR(BIF_P, BADARG);
@@ -2597,14 +2595,12 @@ BIF_RETTYPE delete_element_2(BIF_ALIST_3)
res = make_tuple(hp);
*hp = make_arityval(arity - 1);
- ix--;
- arity -= ix;
-
- while (ix--) { *++hp = *++ptr; }
+ c1 = ix - 1;
+ c2 = arity - ix;
+ while (c1--) { *++hp = *++ptr; }
++ptr;
-
- while(arity--) { *++hp = *++ptr; }
+ while (c2--) { *++hp = *++ptr; }
BIF_RET(res);
}
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 8582a8954b..379d3eecc6 100755
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -3077,7 +3077,7 @@ BIF_RETTYPE is_process_alive_1(BIF_ALIST_1)
if (BIF_ARG_1 == BIF_P->common.id)
BIF_RET(am_true);
- rp = erts_proc_lookup(BIF_ARG_1);
+ rp = erts_proc_lookup_raw(BIF_ARG_1);
if (!rp) {
BIF_RET(am_false);
}
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index d0afc8ed8d..98c2988323 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -264,9 +264,10 @@ static void schedule_free_dbtable(DbTable* tb)
* function has returned).
*/
ASSERT(erts_refc_read(&tb->common.ref, 0) == 0);
- erts_schedule_thr_prgr_later_op(free_dbtable,
- (void *) tb,
- &tb->release.data);
+ erts_schedule_thr_prgr_later_cleanup_op(free_dbtable,
+ (void *) tb,
+ &tb->release.data,
+ sizeof(DbTable));
}
static ERTS_INLINE void db_init_lock(DbTable* tb, int use_frequent_read_lock,
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index e7e4030900..e6a96d427f 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -532,6 +532,8 @@ void erts_usage(void)
erts_fprintf(stderr, " see the erl(1) documentation for more info.\n");
erts_fprintf(stderr, "-sws val set scheduler wakeup strategy, valid values are:\n");
erts_fprintf(stderr, " default|legacy.\n");
+ erts_fprintf(stderr, "-swct val set scheduler wake cleanup threshold, valid values are:\n");
+ erts_fprintf(stderr, " very_lazy|lazy|medium|eager|very_eager.\n");
erts_fprintf(stderr, "-swt val set scheduler wakeup threshold, valid values are:\n");
erts_fprintf(stderr, " very_low|low|medium|high|very_high.\n");
erts_fprintf(stderr, "-sss size suggested stack size in kilo words for scheduler threads,\n");
@@ -1413,7 +1415,17 @@ erl_start(int argc, char **argv)
erts_usage();
}
}
- else if (sys_strcmp("wt", sub_param) == 0) {
+ else if (has_prefix("wct", sub_param)) {
+ arg = get_arg(sub_param+3, argv[i+1], &i);
+ if (erts_sched_set_wake_cleanup_threshold(arg) != 0) {
+ erts_fprintf(stderr, "scheduler wake cleanup threshold: %s\n",
+ arg);
+ erts_usage();
+ }
+ VERBOSE(DEBUG_SYSTEM,
+ ("scheduler wake cleanup threshold: %s\n", arg));
+ }
+ else if (has_prefix("wt", sub_param)) {
arg = get_arg(sub_param+2, argv[i+1], &i);
if (erts_sched_set_wakeup_other_thresold(arg) != 0) {
erts_fprintf(stderr, "scheduler wakeup threshold: %s\n",
@@ -1423,7 +1435,7 @@ erl_start(int argc, char **argv)
VERBOSE(DEBUG_SYSTEM,
("scheduler wakeup threshold: %s\n", arg));
}
- else if (sys_strcmp("ws", sub_param) == 0) {
+ else if (has_prefix("ws", sub_param)) {
arg = get_arg(sub_param+2, argv[i+1], &i);
if (erts_sched_set_wakeup_other_type(arg) != 0) {
erts_fprintf(stderr, "scheduler wakeup strategy: %s\n",
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index ce045ec94e..0ed08bee01 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -151,9 +151,10 @@ static ERTS_INLINE void
schedule_port_task_free(ErtsPortTask *ptp)
{
#ifdef ERTS_SMP
- erts_schedule_thr_prgr_later_op(call_port_task_free,
- (void *) ptp,
- &ptp->u.release);
+ erts_schedule_thr_prgr_later_cleanup_op(call_port_task_free,
+ (void *) ptp,
+ &ptp->u.release,
+ sizeof(ErtsPortTask));
#else
port_task_free(ptp);
#endif
@@ -772,9 +773,10 @@ static void
schedule_port_task_handle_list_free(ErtsPortTaskHandleList *pthlp)
{
#ifdef ERTS_SMP
- erts_schedule_thr_prgr_later_op(free_port_task_handle_list,
- (void *) pthlp,
- &pthlp->u.release);
+ erts_schedule_thr_prgr_later_cleanup_op(free_port_task_handle_list,
+ (void *) pthlp,
+ &pthlp->u.release,
+ sizeof(ErtsPortTaskHandleList));
#else
erts_free(ERTS_ALC_T_PT_HNDL_LIST, pthlp);
#endif
@@ -1999,6 +2001,7 @@ begin_port_cleanup(Port *pp, ErtsPortTask **execqp, int *processing_busy_q_p)
* Schedule cleanup of port structure...
*/
#ifdef ERTS_SMP
+ /* Has to be more or less immediate to release any driver */
erts_schedule_thr_prgr_later_op(release_port,
(void *) pp,
&pp->common.u.release);
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 16b6b1c79d..8bf481dbff 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -146,6 +146,14 @@ extern BeamInstr beam_continue_exit[];
int erts_sched_compact_load;
Uint erts_no_schedulers;
+#define ERTS_THR_PRGR_LATER_CLEANUP_OP_THRESHOLD_VERY_LAZY (4*1024*1024)
+#define ERTS_THR_PRGR_LATER_CLEANUP_OP_THRESHOLD_LAZY (512*1024)
+#define ERTS_THR_PRGR_LATER_CLEANUP_OP_THRESHOLD_MEDIUM (64*1024)
+#define ERTS_THR_PRGR_LATER_CLEANUP_OP_THRESHOLD_EAGER (16*1024)
+#define ERTS_THR_PRGR_LATER_CLEANUP_OP_THRESHOLD_VERY_EAGER (1024)
+
+static UWord thr_prgr_later_cleanup_op_threshold = ERTS_THR_PRGR_LATER_CLEANUP_OP_THRESHOLD_MEDIUM;
+
ErtsPTab erts_proc erts_align_attribute(ERTS_CACHE_LINE_SIZE);
int erts_sched_thread_suggested_stack_size = -1;
@@ -496,6 +504,7 @@ erts_init_process(int ncpu, int proc_tab_size)
#endif
(ErtsPTabElementCommon *) &erts_invalid_process.common,
proc_tab_size,
+ sizeof(Process),
"process_table");
last_reductions = 0;
@@ -909,6 +918,53 @@ unset_aux_work_flags(ErtsSchedulerSleepInfo *ssi, erts_aint32_t flgs)
#ifdef ERTS_SMP
static ERTS_INLINE void
+haw_chk_later_cleanup_op_wakeup(ErtsAuxWorkData *awdp, ErtsThrPrgrVal val)
+{
+ if (awdp->later_op.first
+ && erts_thr_progress_cmp(val, awdp->later_op.thr_prgr) >= 0) {
+ awdp->later_op.size = thr_prgr_later_cleanup_op_threshold;
+ }
+}
+
+static ERTS_INLINE void
+haw_thr_prgr_wakeup(ErtsAuxWorkData *awdp, ErtsThrPrgrVal val)
+{
+ int cmp = erts_thr_progress_cmp(val, awdp->latest_wakeup);
+ if (cmp != 0) {
+ if (cmp > 0) {
+ awdp->latest_wakeup = val;
+ haw_chk_later_cleanup_op_wakeup(awdp, val);
+ }
+ erts_thr_progress_wakeup(awdp->esdp, val);
+ }
+}
+
+static ERTS_INLINE void
+haw_thr_prgr_soft_wakeup(ErtsAuxWorkData *awdp, ErtsThrPrgrVal val)
+{
+ if (erts_thr_progress_cmp(val, awdp->latest_wakeup) > 0) {
+ awdp->latest_wakeup = val;
+ haw_chk_later_cleanup_op_wakeup(awdp, val);
+ erts_thr_progress_wakeup(awdp->esdp, val);
+ }
+}
+
+static ERTS_INLINE void
+haw_thr_prgr_later_cleanup_op_wakeup(ErtsAuxWorkData *awdp, ErtsThrPrgrVal val, UWord size)
+{
+ if (erts_thr_progress_cmp(val, awdp->latest_wakeup) > 0) {
+ awdp->later_op.thr_prgr = val;
+ if (awdp->later_op.size > size)
+ awdp->later_op.size -= size;
+ else {
+ awdp->latest_wakeup = val;
+ awdp->later_op.size = thr_prgr_later_cleanup_op_threshold;
+ erts_thr_progress_wakeup(awdp->esdp, val);
+ }
+ }
+}
+
+static ERTS_INLINE void
haw_thr_prgr_current_reset(ErtsAuxWorkData *awdp)
{
awdp->current_thr_prgr = ERTS_THR_PRGR_INVALID;
@@ -1066,8 +1122,7 @@ misc_aux_work_clean(ErtsThrQ_t *q,
case ERTS_THR_Q_NEED_THR_PRGR:
#ifdef ERTS_SMP
set_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_MISC_THR_PRGR);
- erts_thr_progress_wakeup(awdp->esdp,
- erts_thr_q_need_thr_progress(q));
+ haw_thr_prgr_soft_wakeup(awdp, erts_thr_q_need_thr_progress(q));
#endif
case ERTS_THR_Q_CLEAN:
break;
@@ -1225,8 +1280,7 @@ handle_async_ready_clean(ErtsAuxWorkData *awdp,
return aux_work & ~ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN;
#ifdef ERTS_SMP
case ERTS_ASYNC_READY_NEED_THR_PRGR:
- erts_thr_progress_wakeup(awdp->esdp,
- awdp->async_ready.thr_prgr);
+ haw_thr_prgr_soft_wakeup(awdp, awdp->async_ready.thr_prgr);
awdp->async_ready.need_thr_prgr = 1;
return aux_work & ~ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN;
#endif
@@ -1300,7 +1354,7 @@ handle_delayed_dealloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waitin
awdp->dd.thr_prgr = wakeup;
set_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_DD_THR_PRGR);
awdp->dd.thr_prgr = wakeup;
- erts_thr_progress_wakeup(awdp->esdp, wakeup);
+ haw_thr_prgr_soft_wakeup(awdp, wakeup);
}
else if (awdp->dd.completed_callback) {
awdp->dd.completed_callback(awdp->dd.completed_arg);
@@ -1341,7 +1395,7 @@ handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, i
if (wakeup == ERTS_THR_PRGR_INVALID)
wakeup = erts_thr_progress_later(awdp->esdp);
awdp->dd.thr_prgr = wakeup;
- erts_thr_progress_wakeup(awdp->esdp, wakeup);
+ haw_thr_prgr_soft_wakeup(awdp, wakeup);
}
else {
unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_DD_THR_PRGR);
@@ -1376,6 +1430,7 @@ handle_thr_prgr_later_op(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int wait
}
lop->func(lop->data);
if (!awdp->later_op.first) {
+ awdp->later_op.size = thr_prgr_later_cleanup_op_threshold;
awdp->later_op.last = NULL;
unset_aux_work_flags(awdp->ssi,
ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP);
@@ -1386,6 +1441,29 @@ handle_thr_prgr_later_op(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int wait
return aux_work;
}
+static ERTS_INLINE ErtsThrPrgrVal
+enqueue_later_op(ErtsSchedulerData *esdp,
+ void (*later_func)(void *),
+ void *later_data,
+ ErtsThrPrgrLaterOp *lop)
+{
+ ErtsThrPrgrVal later = erts_thr_progress_later(esdp);
+ ASSERT(esdp);
+
+ lop->func = later_func;
+ lop->data = later_data;
+ lop->later = later;
+ lop->next = NULL;
+ if (!esdp->aux_work_data.later_op.last)
+ esdp->aux_work_data.later_op.first = lop;
+ else
+ esdp->aux_work_data.later_op.last->next = lop;
+ esdp->aux_work_data.later_op.last = lop;
+ set_aux_work_flags_wakeup_nob(esdp->ssi,
+ ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP);
+ return later;
+}
+
#endif /* ERTS_SMP */
void
@@ -1396,31 +1474,24 @@ erts_schedule_thr_prgr_later_op(void (*later_func)(void *),
#ifndef ERTS_SMP
later_func(later_data);
#else
- ErtsSchedulerData *esdp;
- ErtsAuxWorkData *awdp;
- int request_wakeup = 1;
-
- esdp = erts_get_scheduler_data();
- ASSERT(esdp);
- awdp = &esdp->aux_work_data;
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ ErtsThrPrgrVal later = enqueue_later_op(esdp, later_func, later_data, lop);
+ haw_thr_prgr_wakeup(&esdp->aux_work_data, later);
+#endif
+}
- lop->func = later_func;
- lop->data = later_data;
- lop->later = erts_thr_progress_later(esdp);
- lop->next = NULL;
- if (!awdp->later_op.last)
- awdp->later_op.first = lop;
- else {
- ErtsThrPrgrLaterOp *last = awdp->later_op.last;
- last->next = lop;
- if (erts_thr_progress_equal(last->later, lop->later))
- request_wakeup = 0;
- }
- awdp->later_op.last = lop;
- set_aux_work_flags_wakeup_nob(awdp->ssi,
- ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP);
- if (request_wakeup)
- erts_thr_progress_wakeup(esdp, lop->later);
+void
+erts_schedule_thr_prgr_later_cleanup_op(void (*later_func)(void *),
+ void *later_data,
+ ErtsThrPrgrLaterOp *lop,
+ UWord size)
+{
+#ifndef ERTS_SMP
+ later_func(later_data);
+#else
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ ErtsThrPrgrVal later = enqueue_later_op(esdp, later_func, later_data, lop);
+ haw_thr_prgr_later_cleanup_op_wakeup(&esdp->aux_work_data, later, size);
#endif
}
@@ -4358,6 +4429,25 @@ erts_sched_set_busy_wait_threshold(char *str)
return 0;
}
+
+int
+erts_sched_set_wake_cleanup_threshold(char *str)
+{
+ if (sys_strcmp(str, "very_lazy") == 0)
+ thr_prgr_later_cleanup_op_threshold = ERTS_THR_PRGR_LATER_CLEANUP_OP_THRESHOLD_VERY_LAZY;
+ else if (sys_strcmp(str, "lazy") == 0)
+ thr_prgr_later_cleanup_op_threshold = ERTS_THR_PRGR_LATER_CLEANUP_OP_THRESHOLD_LAZY;
+ else if (sys_strcmp(str, "medium") == 0)
+ thr_prgr_later_cleanup_op_threshold = ERTS_THR_PRGR_LATER_CLEANUP_OP_THRESHOLD_MEDIUM;
+ else if (sys_strcmp(str, "eager") == 0)
+ thr_prgr_later_cleanup_op_threshold = ERTS_THR_PRGR_LATER_CLEANUP_OP_THRESHOLD_EAGER;
+ else if (sys_strcmp(str, "very_eager") == 0)
+ thr_prgr_later_cleanup_op_threshold = ERTS_THR_PRGR_LATER_CLEANUP_OP_THRESHOLD_VERY_EAGER;
+ else
+ return EINVAL;
+ return 0;
+}
+
static void
init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp)
{
@@ -4365,10 +4455,13 @@ init_aux_work_data(ErtsAuxWorkData *awdp, ErtsSchedulerData *esdp, char *dawwp)
awdp->esdp = esdp;
awdp->ssi = esdp ? esdp->ssi : NULL;
#ifdef ERTS_SMP
+ awdp->latest_wakeup = ERTS_THR_PRGR_VAL_FIRST;
awdp->misc.thr_prgr = ERTS_THR_PRGR_VAL_WAITING;
awdp->dd.thr_prgr = ERTS_THR_PRGR_VAL_WAITING;
awdp->dd.completed_callback = NULL;
awdp->dd.completed_arg = NULL;
+ awdp->later_op.thr_prgr = ERTS_THR_PRGR_VAL_FIRST;
+ awdp->later_op.size = 0;
awdp->later_op.first = NULL;
awdp->later_op.last = NULL;
#endif
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 6d1032c292..3d3579fa7e 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -430,6 +430,7 @@ typedef struct {
ErtsSchedulerSleepInfo *ssi;
#ifdef ERTS_SMP
ErtsThrPrgrVal current_thr_prgr;
+ ErtsThrPrgrVal latest_wakeup;
#endif
struct {
int ix;
@@ -444,6 +445,8 @@ typedef struct {
void (*completed_arg)(void *);
} dd;
struct {
+ ErtsThrPrgrVal thr_prgr;
+ UWord size;
ErtsThrPrgrLaterOp *first;
ErtsThrPrgrLaterOp *last;
} later_op;
@@ -1298,10 +1301,15 @@ ERTS_GLB_INLINE int erts_proclist_is_last(ErtsProcList *list,
int erts_sched_set_wakeup_other_thresold(char *str);
int erts_sched_set_wakeup_other_type(char *str);
int erts_sched_set_busy_wait_threshold(char *str);
+int erts_sched_set_wake_cleanup_threshold(char *);
void erts_schedule_thr_prgr_later_op(void (*)(void *),
void *,
ErtsThrPrgrLaterOp *);
+void erts_schedule_thr_prgr_later_cleanup_op(void (*)(void *),
+ void *,
+ ErtsThrPrgrLaterOp *,
+ UWord);
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
int erts_dbg_check_halloc_lock(Process *p);
diff --git a/erts/emulator/beam/erl_ptab.c b/erts/emulator/beam/erl_ptab.c
index 87beeafa1a..5bbc71c659 100644
--- a/erts/emulator/beam/erl_ptab.c
+++ b/erts/emulator/beam/erl_ptab.c
@@ -421,6 +421,7 @@ erts_ptab_init_table(ErtsPTab *ptab,
void (*release_element)(void *),
ErtsPTabElementCommon *invalid_element,
int size,
+ UWord element_size,
char *name)
{
size_t tab_sz;
@@ -443,6 +444,7 @@ erts_ptab_init_table(ErtsPTab *ptab,
bits = erts_fit_in_bits_int32((Sint32) size - 1);
}
+ ptab->r.o.element_size = element_size;
ptab->r.o.max = size;
tab_sz = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(size*sizeof(erts_smp_atomic_t));
@@ -670,9 +672,10 @@ erts_ptab_delete_element(ErtsPTab *ptab,
}
if (ptab->r.o.release_element)
- erts_schedule_thr_prgr_later_op(ptab->r.o.release_element,
- (void *) ptab_el,
- &ptab_el->u.release);
+ erts_schedule_thr_prgr_later_cleanup_op(ptab->r.o.release_element,
+ (void *) ptab_el,
+ &ptab_el->u.release,
+ ptab->r.o.element_size);
}
/*
diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h
index 8a130f42a3..7fa1251900 100644
--- a/erts/emulator/beam/erl_ptab.h
+++ b/erts/emulator/beam/erl_ptab.h
@@ -109,6 +109,7 @@ typedef struct {
ErtsPTabElementCommon *invalid_element;
Eterm invalid_data;
void (*release_element)(void *);
+ UWord element_size;
} ErtsPTabReadOnlyData;
typedef struct {
@@ -177,6 +178,7 @@ void erts_ptab_init_table(ErtsPTab *ptab,
void (*release_element)(void *),
ErtsPTabElementCommon *invalid_element,
int size,
+ UWord element_size,
char *name);
int erts_ptab_new_element(ErtsPTab *ptab,
ErtsPTabElementCommon *ptab_el,
diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c
index 1292cb0678..cf5e3dc012 100644
--- a/erts/emulator/beam/erl_thr_progress.c
+++ b/erts/emulator/beam/erl_thr_progress.c
@@ -418,7 +418,7 @@ erts_thr_progress_pre_init(void)
{
intrnl = NULL;
erts_tsd_key_create(&erts_thr_prgr_data_key__);
- init_nob(&erts_thr_prgr__.current, 0);
+ init_nob(&erts_thr_prgr__.current, ERTS_THR_PRGR_VAL_FIRST);
}
void
diff --git a/erts/emulator/beam/erl_thr_progress.h b/erts/emulator/beam/erl_thr_progress.h
index 1416aa6166..1aeecf2b06 100644
--- a/erts/emulator/beam/erl_thr_progress.h
+++ b/erts/emulator/beam/erl_thr_progress.h
@@ -108,6 +108,8 @@ struct ErtsThrPrgrLaterOp_ {
#ifdef ERTS_SMP
+/* ERTS_THR_PRGR_VAL_FIRST should only be used when initializing... */
+#define ERTS_THR_PRGR_VAL_FIRST ((ErtsThrPrgrVal) 0)
#define ERTS_THR_PRGR_VAL_WAITING (~((ErtsThrPrgrVal) 0))
#define ERTS_THR_PRGR_INVALID (~((ErtsThrPrgrVal) 0))
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index a9771de7ad..7cadd4aaad 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -2651,11 +2651,17 @@ void erts_init_io(int port_tab_size,
int port_tab_size_ignore_files)
{
ErlDrvEntry** dp;
+ UWord common_element_size;
erts_smp_rwmtx_opt_t drv_list_rwmtx_opts = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER;
drv_list_rwmtx_opts.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ;
drv_list_rwmtx_opts.lived = ERTS_SMP_RWMTX_LONG_LIVED;
+ common_element_size = ERTS_ALC_DATA_ALIGN_SIZE(sizeof(Port));
+ common_element_size += ERTS_ALC_DATA_ALIGN_SIZE(sizeof(ErtsPortTaskBusyPortQ));
+ common_element_size += 10; /* name */
#ifdef ERTS_SMP
+ common_element_size += sizeof(erts_mtx_t);
+
init_xports_list_alloc();
#endif
@@ -2684,6 +2690,7 @@ void erts_init_io(int port_tab_size,
NULL,
(ErtsPTabElementCommon *) &erts_invalid_port.common,
port_tab_size,
+ common_element_size, /* Doesn't need to be excact */
"port_table");
erts_smp_atomic_init_nob(&erts_bytes_out, 0);
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
index 9d674a7c65..31d9b2e0ad 100644
--- a/erts/etc/common/erlexec.c
+++ b/erts/etc/common/erlexec.c
@@ -125,6 +125,7 @@ static char *pluss_val_switches[] = {
"cl",
"ct",
"tbt",
+ "wct",
"wt",
"ws",
"ss",
diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c
index 910be3dce8..a3bcdb85d9 100644
--- a/erts/etc/unix/run_erl.c
+++ b/erts/etc/unix/run_erl.c
@@ -682,7 +682,7 @@ static void pass_on(pid_t childpid)
}
}
- if (!got_some && wfd && buf[0] == '\022') {
+ if (!got_some && wfd && buf[0] == '\014') {
char wbuf[30];
int wlen = sn_printf(wbuf,sizeof(wbuf),"[run_erl v%u-%u]\n",
RUN_ERL_HI_VER, RUN_ERL_LO_VER);
diff --git a/erts/etc/unix/to_erl.c b/erts/etc/unix/to_erl.c
index 754b349338..094006c5fd 100644
--- a/erts/etc/unix/to_erl.c
+++ b/erts/etc/unix/to_erl.c
@@ -353,7 +353,7 @@ int main(int argc, char **argv)
* at the start of every new to_erl-session.
*/
- if (write(wfd, "\022", 1) < 0) {
+ if (write(wfd, "\014", 1) < 0) {
fprintf(stderr, "Error in writing ^R to FIFO.\n");
}
diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index a4ddae3d5d..aee6f631cf 100644
--- a/erts/preloaded/ebin/erl_prim_loader.beam
+++ b/erts/preloaded/ebin/erl_prim_loader.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index 684659cca1..ea80d47eb6 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam
index 74c08fa4c9..6b2593e427 100644
--- a/erts/preloaded/ebin/erts_internal.beam
+++ b/erts/preloaded/ebin/erts_internal.beam
Binary files differ
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index 143d5b18b9..7fe1685ef3 100644
--- a/erts/preloaded/ebin/init.beam
+++ b/erts/preloaded/ebin/init.beam
Binary files differ
diff --git a/erts/preloaded/ebin/otp_ring0.beam b/erts/preloaded/ebin/otp_ring0.beam
index a6b2fdb985..57dafb9ce7 100644
--- a/erts/preloaded/ebin/otp_ring0.beam
+++ b/erts/preloaded/ebin/otp_ring0.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam
index 9460f1da6f..7558a713ae 100644
--- a/erts/preloaded/ebin/prim_file.beam
+++ b/erts/preloaded/ebin/prim_file.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index 8c47d8b611..f836473cb4 100644
--- a/erts/preloaded/ebin/prim_inet.beam
+++ b/erts/preloaded/ebin/prim_inet.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam
index e6f3995b50..35acb59096 100644
--- a/erts/preloaded/ebin/prim_zip.beam
+++ b/erts/preloaded/ebin/prim_zip.beam
Binary files differ
diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam
index cdbaa43d5d..c100d1755b 100644
--- a/erts/preloaded/ebin/zlib.beam
+++ b/erts/preloaded/ebin/zlib.beam
Binary files differ
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 8e4a471a82..a88a8831ea 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -1905,11 +1905,11 @@ setelement(_Index, _Tuple1, _Value) ->
Function :: atom(),
Args :: [term()],
Options :: [Option],
- Option :: link | monitor | {priority, Level}
+ Option :: link | monitor
+ | {priority, Level :: priority_level()}
| {fullsweep_after, Number :: non_neg_integer()}
| {min_heap_size, Size :: non_neg_integer()}
- | {min_bin_vheap_size, VSize :: non_neg_integer()},
- Level :: low | normal | high.
+ | {min_bin_vheap_size, VSize :: non_neg_integer()}.
spawn_opt(_Tuple) ->
erlang:nif_error(undefined).
@@ -2244,11 +2244,11 @@ spawn_monitor(M, F, A) ->
-spec spawn_opt(Fun, Options) -> pid() | {pid(), reference()} when
Fun :: function(),
Options :: [Option],
- Option :: link | monitor | {priority, Level}
+ Option :: link | monitor
+ | {priority, Level :: priority_level()}
| {fullsweep_after, Number :: non_neg_integer()}
| {min_heap_size, Size :: non_neg_integer()}
- | {min_bin_vheap_size, VSize :: non_neg_integer()},
- Level :: low | normal | high.
+ | {min_bin_vheap_size, VSize :: non_neg_integer()}.
spawn_opt(F, O) when erlang:is_function(F) ->
spawn_opt(erlang, apply, [F, []], O);
spawn_opt({M,F}=MF, O) when erlang:is_atom(M), erlang:is_atom(F) ->
@@ -2262,11 +2262,11 @@ spawn_opt(F, O) ->
Node :: node(),
Fun :: function(),
Options :: [Option],
- Option :: link | monitor | {priority, Level}
+ Option :: link | monitor
+ | {priority, Level :: priority_level()}
| {fullsweep_after, Number :: non_neg_integer()}
| {min_heap_size, Size :: non_neg_integer()}
- | {min_bin_vheap_size, VSize :: non_neg_integer()},
- Level :: low | normal | high.
+ | {min_bin_vheap_size, VSize :: non_neg_integer()}.
spawn_opt(N, F, O) when N =:= erlang:node() ->
spawn_opt(F, O);
spawn_opt(N, F, O) when erlang:is_function(F) ->
@@ -2354,11 +2354,11 @@ spawn_link(N,M,F,A) ->
Function :: atom(),
Args :: [term()],
Options :: [Option],
- Option :: link | monitor | {priority, Level}
+ Option :: link | monitor
+ | {priority, Level :: priority_level()}
| {fullsweep_after, Number :: non_neg_integer()}
| {min_heap_size, Size :: non_neg_integer()}
- | {min_bin_vheap_size, VSize :: non_neg_integer()},
- Level :: low | normal | high.
+ | {min_bin_vheap_size, VSize :: non_neg_integer()}.
spawn_opt(M, F, A, Opts) ->
case catch erlang:spawn_opt({M,F,A,Opts}) of
{'EXIT',{Reason,_}} ->
@@ -2374,11 +2374,11 @@ spawn_opt(M, F, A, Opts) ->
Function :: atom(),
Args :: [term()],
Options :: [Option],
- Option :: link | monitor | {priority, Level}
+ Option :: link | monitor
+ | {priority, Level :: priority_level()}
| {fullsweep_after, Number :: non_neg_integer()}
| {min_heap_size, Size :: non_neg_integer()}
- | {min_bin_vheap_size, VSize :: non_neg_integer()},
- Level :: low | normal | high.
+ | {min_bin_vheap_size, VSize :: non_neg_integer()}.
spawn_opt(N, M, F, A, O) when N =:= erlang:node(),
erlang:is_atom(M), erlang:is_atom(F),
erlang:is_list(A), erlang:is_list(O) ->
diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml
index d871908952..c87c765ae7 100644
--- a/lib/common_test/doc/src/ct_run.xml
+++ b/lib/common_test/doc/src/ct_run.xml
@@ -113,9 +113,9 @@
[-muliply_timetraps Multiplier]
[-scale_timetraps]
[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]
- [-repeat N [-force_stop]] |
- [-duration HHMMSS [-force_stop]] |
- [-until [YYMoMoDD]HHMMSS [-force_stop]]
+ [-repeat N] |
+ [-duration HHMMSS [-force_stop [skip_rest]]] |
+ [-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]
[-basic_html]
[-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
CTHModuleN CTHOptsN]
@@ -149,9 +149,9 @@
[-muliply_timetraps Multiplier]
[-scale_timetraps]
[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]
- [-repeat N [-force_stop]] |
- [-duration HHMMSS [-force_stop]] |
- [-until [YYMoMoDD]HHMMSS [-force_stop]]
+ [-repeat N] |
+ [-duration HHMMSS [-force_stop [skip_rest]]] |
+ [-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]
[-basic_html]
[-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and ..
CTHModuleN CTHOptsN]
diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml
index 35f89153d3..afaed29626 100644
--- a/lib/common_test/doc/src/run_test_chapter.xml
+++ b/lib/common_test/doc/src/run_test_chapter.xml
@@ -174,7 +174,7 @@
<item><c><![CDATA[-repeat <n>]]></c>, tells Common Test to repeat the tests n times (see below).</item>
<item><c><![CDATA[-duration <time>]]></c>, tells Common Test to repeat the tests for duration of time (see below).</item>
<item><c><![CDATA[-until <stop_time>]]></c>, tells Common Test to repeat the tests until stop_time (see below).</item>
- <item><c>-force_stop</c>, on timeout, the test run will be aborted when current test job is finished (see below).</item>
+ <item><c>-force_stop [skip_rest]</c>, on timeout, the test run will be aborted when current test job is finished. If <c>skip_rest</c> is provided the rest of the test cases in the current test job will be skipped (see below).</item>
<item><c><![CDATA[-decrypt_key <key>]]></c>, provides a decryption key for
<seealso marker="config_file_chapter#encrypted_config_files">encrypted configuration files</seealso>.</item>
<item><c><![CDATA[-decrypt_file <key_file>]]></c>, points out a file containing a decryption key for
@@ -1273,6 +1273,7 @@
<item><c>-duration DurTime ({duration,DurTime})</c>, where <c>DurTime</c> is the duration, see below.</item>
<item><c>-until StopTime ({until,StopTime})</c>, where <c>StopTime</c> is finish time, see below.</item>
<item><c>-force_stop ({force_stop,true})</c></item>
+ <item><c>-force_stop skip_rest ({force_stop,skip_rest})</c></item>
</list>
<p>The duration time, <c>DurTime</c>, is specified as <c>HHMMSS</c>. Example:
<c>-duration 012030</c> or <c>{duration,"012030"}</c>, means the tests will
@@ -1283,12 +1284,16 @@
Example: <c>-until 071001120000</c> or <c>{until,"071001120000"}</c>, which means the tests
will be executed and (if time allows) repeated, until 12 o'clock on the 1st of Oct 2007.</p>
- <p>When timeout occurs, Common Test will never abort the test run immediately, since
+ <p>When timeout occurs, Common Test will never abort the ongoing test case, since
this might leave the system under test in an undefined, and possibly bad, state.
- Instead Common Test will finish the current test job, or the complete test
- run, before stopping. The latter is the default behaviour. The <c>force_stop</c>
- flag/option tells Common Test to stop as soon as the current test job is finished.
- Note that since Common Test always finishes off the current test job or test session,
+ Instead Common Test will by default finish the current test
+ run before stopping. If the <c>force_stop</c> flag is
+ given, Common Test will stop as soon as the current test job
+ is finished, and if the <c>force_stop</c> flag is given with
+ <c>skip_rest</c> Common Test will only complete the current
+ test case and skip the rest of the tests in the test job.
+ Note that since Common Test always finishes off at least the
+ current test case,
the time specified with <c>duration</c> or <c>until</c> is never definitive!</p>
<p>Log files from every single repeated test run is saved in normal Common Test fashion (see above).
@@ -1312,6 +1317,18 @@
<p>Example 2:</p>
<pre>
+ $ ct_run -dir $TEST_ROOT/to1 $TEST_ROOT/to2 -duration 001000 -forces_stop skip_rest</pre>
+ <p>Here the same test run as in Example 1, but with the
+ <c>force_stop</c> flag set to <c>skip_rest</c>. If the timeout
+ occurs while executing tests in directory to1, the rest of the
+ test cases in to1 will be skipped and then the test will be
+ aborted without running the tests in to2 another time. If the
+ timeout occurs while executing tests in directory to2, then the
+ rest of the test cases in to2 will be skipped and then the test
+ will be aborted.</p>
+
+ <p>Example 3:</p>
+ <pre>
$ date
Fri Sep 28 15:00:00 MEST 2007
@@ -1321,7 +1338,7 @@
Common Test will finish the entire test run before stopping (i.e. the to1 and to2 test
will always both be executed in the same test run).</p>
- <p>Example 3:</p>
+ <p>Example 4:</p>
<pre>
$ ct_run -dir $TEST_ROOT/to1 $TEST_ROOT/to2 -repeat 5</pre>
<p>Here the test run, including both the to1 and the to2 test, will be repeated 5 times.</p>
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index 04a95a53fa..e6732f7fc7 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -153,7 +153,7 @@ run(TestDirs) ->
%%% {auto_compile,Bool} | {create_priv_dir,CreatePrivDir} |
%%% {multiply_timetraps,M} | {scale_timetraps,Bool} |
%%% {repeat,N} | {duration,DurTime} | {until,StopTime} |
-%%% {force_stop,Bool} | {decrypt,DecryptKeyOrFile} |
+%%% {force_stop,ForceStop} | {decrypt,DecryptKeyOrFile} |
%%% {refresh_logs,LogDir} | {logopts,LogOpts} |
%%% {verbosity,VLevels} | {basic_html,Bool} |
%%% {ct_hooks, CTHs} | {enable_builtin_hooks,Bool} |
@@ -184,6 +184,7 @@ run(TestDirs) ->
%%% N = integer()
%%% DurTime = string(HHMMSS)
%%% StopTime = string(YYMoMoDDHHMMSS) | string(HHMMSS)
+%%% ForceStop = skip_rest | Bool
%%% DecryptKeyOrFile = {key,DecryptKey} | {file,DecryptFile}
%%% DecryptKey = string()
%%% DecryptFile = string()
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index 5fe4eaf511..b92fe1555f 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -64,38 +64,46 @@ init_tc(Mod,Func,Config) ->
ok
end,
- case ct_util:get_testdata(curr_tc) of
- {Suite,{suite0_failed,{require,Reason}}} ->
- {skip,{require_failed_in_suite0,Reason}};
- {Suite,{suite0_failed,_}=Failure} ->
- {skip,Failure};
+ case Func=/=end_per_suite
+ andalso Func=/=end_per_group
+ andalso ct_util:get_testdata(skip_rest) of
+ true ->
+ {skip,"Repeated test stopped by force_stop option"};
_ ->
- ct_util:update_testdata(curr_tc,
- fun(undefined) ->
- [{Suite,Func}];
- (Running) ->
- [{Suite,Func}|Running]
- end, [create]),
- case ct_util:read_suite_data({seq,Suite,Func}) of
- undefined ->
- init_tc1(Mod,Suite,Func,Config);
- Seq when is_atom(Seq) ->
- case ct_util:read_suite_data({seq,Suite,Seq}) of
- [Func|TCs] -> % this is the 1st case in Seq
- %% make sure no cases in this seq are
- %% marked as failed from an earlier execution
- %% in the same suite
- lists:foreach(
- fun(TC) ->
- ct_util:save_suite_data({seq,Suite,TC},
- Seq)
- end, TCs);
- _ ->
- ok
- end,
- init_tc1(Mod,Suite,Func,Config);
- {failed,Seq,BadFunc} ->
- {skip,{sequence_failed,Seq,BadFunc}}
+ case ct_util:get_testdata(curr_tc) of
+ {Suite,{suite0_failed,{require,Reason}}} ->
+ {skip,{require_failed_in_suite0,Reason}};
+ {Suite,{suite0_failed,_}=Failure} ->
+ {skip,Failure};
+ _ ->
+ ct_util:update_testdata(curr_tc,
+ fun(undefined) ->
+ [{Suite,Func}];
+ (Running) ->
+ [{Suite,Func}|Running]
+ end, [create]),
+ case ct_util:read_suite_data({seq,Suite,Func}) of
+ undefined ->
+ init_tc1(Mod,Suite,Func,Config);
+ Seq when is_atom(Seq) ->
+ case ct_util:read_suite_data({seq,Suite,Seq}) of
+ [Func|TCs] -> % this is the 1st case in Seq
+ %% make sure no cases in this seq are
+ %% marked as failed from an earlier execution
+ %% in the same suite
+ lists:foreach(
+ fun(TC) ->
+ ct_util:save_suite_data(
+ {seq,Suite,TC},
+ Seq)
+ end, TCs);
+ _ ->
+ ok
+ end,
+ init_tc1(Mod,Suite,Func,Config);
+ {failed,Seq,BadFunc} ->
+ {skip,{sequence_failed,Seq,BadFunc}}
+ end
end
end.
diff --git a/lib/common_test/src/ct_repeat.erl b/lib/common_test/src/ct_repeat.erl
index a47309c6ee..f4d9949776 100644
--- a/lib/common_test/src/ct_repeat.erl
+++ b/lib/common_test/src/ct_repeat.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,7 +23,7 @@
%%% start flags (or equivalent ct:run_test/1 options) are supported:
%%% -until <StopTime>, StopTime = YYMoMoDDHHMMSS | HHMMSS
%%% -duration <DurTime>, DurTime = HHMMSS
-%%% -force_stop
+%%% -force_stop [skip_rest]
%%% -repeat <N>, N = integer()</p>
-module(ct_repeat).
@@ -62,12 +62,15 @@ loop_test(If,Args) when is_list(Args) ->
io:format("\nCommon Test: "
"Will repeat tests for ~s.\n\n",[ts(Secs)]),
TPid =
- case lists:keymember(force_stop,1,Args) of
- true ->
+ case proplists:get_value(force_stop,Args) of
+ False when False==false; False==undefined ->
+ undefined;
+ ForceStop ->
CtrlPid = self(),
- spawn(fun() -> stop_after(CtrlPid,Secs) end);
- false ->
- undefined
+ spawn(
+ fun() ->
+ stop_after(CtrlPid,Secs,ForceStop)
+ end)
end,
Args1 = [{loop_info,[{stop_time,Secs,StopTime,1}]} | Args],
loop(If,stop_time,0,Secs,StopTime,Args1,TPid,[])
@@ -212,7 +215,7 @@ get_stop_time(until,[Y1,Y2,Mo1,Mo2,D1,D2,H1,H2,Mi1,Mi2,S1,S2]) ->
list_to_integer([S1,S2])},
calendar:datetime_to_gregorian_seconds({Date,Time});
-get_stop_time(until,Time) ->
+get_stop_time(until,Time=[_,_,_,_,_,_]) ->
get_stop_time(until,"000000"++Time);
get_stop_time(duration,[H1,H2,Mi1,Mi2,S1,S2]) ->
@@ -227,10 +230,17 @@ cancel(Pid) ->
%% After Secs, abort will make the test_server finish the current
%% job, then empty the job queue and stop.
-stop_after(_CtrlPid,Secs) ->
+stop_after(_CtrlPid,Secs,ForceStop) ->
timer:sleep(Secs*1000),
+ case ForceStop of
+ SkipRest when SkipRest==skip_rest; SkipRest==["skip_rest"] ->
+ ct_util:set_testdata({skip_rest,true});
+ _ ->
+ ok
+ end,
test_server_ctrl:abort().
+
%% Callback from ct_run to print loop info to system log.
log_loop_info(Args) ->
case lists:keysearch(loop_info,1,Args) of
@@ -259,11 +269,11 @@ log_loop_info(Args) ->
io_lib:format("Test time remaining: ~w secs (~w%)\n",
[Secs,trunc((Secs/Secs0)*100)]),
LogStr4 =
- case lists:keymember(force_stop,1,Args) of
- true ->
- io_lib:format("force_stop is enabled",[]);
- _ ->
- ""
+ case proplists:get_value(force_stop,Args) of
+ False when False==false; False==undefined ->
+ "";
+ ForceStop ->
+ io_lib:format("force_stop is set to: ~w",[ForceStop])
end,
ct_logs:log("Test loop info",LogStr1++LogStr2++LogStr3++LogStr4,[])
end.
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index 49f00429ae..57cfab532e 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -771,9 +771,9 @@ script_usage() ->
"\n\t[-scale_timetraps]"
"\n\t[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
"\n\t[-basic_html]"
- "\n\t[-repeat N [-force_stop]] |"
- "\n\t[-duration HHMMSS [-force_stop]] |"
- "\n\t[-until [YYMoMoDD]HHMMSS [-force_stop]]\n\n"),
+ "\n\t[-repeat N] |"
+ "\n\t[-duration HHMMSS [-force_stop [skip_rest]]] |"
+ "\n\t[-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]\n\n"),
io:format("Run tests using test specification:\n\n"
"\tct_run -spec TestSpec1 TestSpec2 .. TestSpecN"
"\n\t[-config ConfigFile1 ConfigFile2 .. ConfigFileN]"
@@ -795,9 +795,9 @@ script_usage() ->
"\n\t[-scale_timetraps]"
"\n\t[-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc]"
"\n\t[-basic_html]"
- "\n\t[-repeat N [-force_stop]] |"
- "\n\t[-duration HHMMSS [-force_stop]] |"
- "\n\t[-until [YYMoMoDD]HHMMSS [-force_stop]]\n\n"),
+ "\n\t[-repeat N] |"
+ "\n\t[-duration HHMMSS [-force_stop [skip_rest]]] |"
+ "\n\t[-until [YYMoMoDD]HHMMSS [-force_stop [skip_rest]]]\n\n"),
io:format("Refresh the HTML index files:\n\n"
"\tct_run -refresh_logs [LogDir]"
"[-logdir LogDir] "
@@ -2933,6 +2933,8 @@ opts2args(EnvStartOpts) ->
[];
({create_priv_dir,PD}) when is_atom(PD) ->
[{create_priv_dir,[atom_to_list(PD)]}];
+ ({force_stop,skip_rest}) ->
+ [{force_stop,["skip_rest"]}];
({force_stop,true}) ->
[{force_stop,[]}];
({force_stop,false}) ->
diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile
index a9ebd8f1d3..94569fa87f 100644
--- a/lib/common_test/test/Makefile
+++ b/lib/common_test/test/Makefile
@@ -38,6 +38,7 @@ MODULES= \
ct_groups_spec_SUITE \
ct_sequence_1_SUITE \
ct_repeat_1_SUITE \
+ ct_repeat_testrun_SUITE \
ct_testspec_1_SUITE \
ct_testspec_2_SUITE \
ct_testspec_3_SUITE \
diff --git a/lib/common_test/test/ct_repeat_testrun_SUITE.erl b/lib/common_test/test/ct_repeat_testrun_SUITE.erl
new file mode 100644
index 0000000000..7ec384c932
--- /dev/null
+++ b/lib/common_test/test/ct_repeat_testrun_SUITE.erl
@@ -0,0 +1,378 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_repeat_test_SUITE
+%%%
+%%% Description:
+%%% Test different options for repeating test runs:
+%%% -repeat N
+%%% -duration T [-force_stop [skip_rest]]
+%%% -until T [-force_stop [skip_rest]]
+%%%
+%%%-------------------------------------------------------------------
+-module(ct_repeat_testrun_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+-define(skip_reason, "Repeated test stopped by force_stop option").
+-define(skipped, {skipped, ?skip_reason}).
+
+
+%% Timers used in this test.
+%% Each test suite consists of
+%%
+%% [tc1,tc2,{group,g,[tc1,tc2]},tc2]
+%%
+%% In r1_SUITE tc1 has a sleep of 10 sec - all other test cases just
+%% return ok.
+%%
+%% => One complete test run of two suites r1_SUITE + r2_SUITE is at
+%% least 20 seconds (10 sec for each r1_SUITE:tc1)
+%%
+-define(t1,30). % time shall expire during second run of r1_SUITE
+-define(t2,6). % time shall expire during first run of tc1
+-define(t3,16). % time shall expire during second run of tc1
+
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config0) ->
+ Config = ct_test_support:init_per_suite(Config0),
+ DataDir = ?config(data_dir, Config),
+ Suite1 = filename:join([DataDir,"a_test","r1_SUITE"]),
+ Suite2 = filename:join([DataDir,"b_test","r2_SUITE"]),
+ Opts0 = ct_test_support:get_opts(Config),
+ Opts1 = Opts0 ++ [{suite,Suite1},{testcase,tc2},{label,timing1}],
+ Opts2 = Opts0 ++ [{suite,Suite2},{testcase,tc2},{label,timing2}],
+
+ %% Make sure both suites are compiled
+ {1,0,{0,0}} = ct_test_support:run(ct,run_test,[Opts1],Config),
+ {1,0,{0,0}} = ct_test_support:run(ct,run_test,[Opts2],Config),
+
+ %% Time the shortest testcase to use for offset
+ {T0,{1,0,{0,0}}} = timer:tc(ct_test_support,run,[ct,run_test,[Opts1],Config]),
+
+ %% -2 is to ensure we hit inside the target test case and not after
+% T = round(T0/1000000)-2,
+ T=0,
+ [{offset,T}|Config].
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [
+ repeat_n,
+ duration,
+ duration_force_stop,
+ duration_force_stop_skip_rest,
+ duration_force_stop_skip_rest_group,
+ until,
+ until_force_stop,
+ until_force_stop_skip_rest,
+ until_force_stop_skip_rest_group
+ ].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+repeat_n(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Dirs = filelib:wildcard(filename:join(DataDir,"*")),
+ {Opts,ERPid} = setup([{dir,Dirs},
+ {label,repeat_n},
+ {repeat,2}],
+ Config),
+ ok = execute(repeat_n, Opts, ERPid, Config).
+
+duration(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Dirs = filelib:wildcard(filename:join(DataDir,"*")),
+ {Opts,ERPid} = setup([{dir,Dirs},
+ {label,duration},
+ {duration,duration_str(?t1,2,Config)}],
+ Config),
+ ok = execute(duration, Opts, ERPid, Config).
+
+duration_force_stop(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Dirs = filelib:wildcard(filename:join(DataDir,"*")),
+ {Opts,ERPid} = setup([{dir,Dirs},
+ {label,duration_force_stop},
+ {duration,duration_str(?t1,2,Config)},
+ {force_stop,true}],
+ Config),
+ ok = execute(duration_force_stop, Opts, ERPid, Config).
+
+duration_force_stop_skip_rest(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Dirs = filelib:wildcard(filename:join(DataDir,"*")),
+ {Opts,ERPid} = setup([{dir,Dirs},
+ {label,duration_force_stop_skip_rest},
+ {duration,duration_str(?t2,1,Config)},
+ {force_stop,skip_rest}],
+ Config),
+ ok = execute(duration_force_stop_skip_rest, Opts, ERPid, Config).
+
+duration_force_stop_skip_rest_group(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Dirs = filelib:wildcard(filename:join(DataDir,"*")),
+ {Opts,ERPid} = setup([{dir,Dirs},
+ {label,duration_force_stop_skip_rest_group},
+ {duration,duration_str(?t3,1,Config)},
+ {force_stop,skip_rest}],
+ Config),
+ ok = execute(duration_force_stop_skip_rest_group, Opts, ERPid, Config).
+
+until(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Dirs = filelib:wildcard(filename:join(DataDir,"*")),
+ {Opts,ERPid} = setup([{dir,Dirs},
+ {label,until}],
+ Config),
+ ExecuteFun =
+ fun() ->
+ [_,_] = ct_test_support:run_ct_run_test(
+ Opts++[{until,until_str(?t1,2,Config)}],Config),
+ 0 = ct_test_support:run_ct_script_start(
+ Opts++[{until,until_str(?t1,2,Config)}],Config)
+ end,
+ ok = execute(ExecuteFun, until, Opts, ERPid, Config).
+
+until_force_stop(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Dirs = filelib:wildcard(filename:join(DataDir,"*")),
+ {Opts,ERPid} = setup([{dir,Dirs},
+ {label,until_force_stop},
+ {force_stop,true}],
+ Config),
+ ExecuteFun =
+ fun() ->
+ [_,_] = ct_test_support:run_ct_run_test(
+ Opts++[{until,until_str(?t1,2,Config)}],Config),
+ 0 = ct_test_support:run_ct_script_start(
+ Opts++[{until,until_str(?t1,2,Config)}],Config)
+ end,
+ ok = execute(ExecuteFun, until_force_stop, Opts, ERPid, Config).
+
+until_force_stop_skip_rest(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Dirs = filelib:wildcard(filename:join(DataDir,"*")),
+ {Opts,ERPid} = setup([{dir,Dirs},
+ {label,until_force_stop_skip_rest},
+ {force_stop,skip_rest}],
+ Config),
+ ExecuteFun =
+ fun() ->
+ [_] = ct_test_support:run_ct_run_test(
+ Opts++[{until,until_str(?t2,1,Config)}],Config),
+ 1 = ct_test_support:run_ct_script_start(
+ Opts++[{until,until_str(?t2,1,Config)}],Config)
+ end,
+ ok = execute(ExecuteFun, until_force_stop_skip_rest,
+ Opts, ERPid, Config).
+
+until_force_stop_skip_rest_group(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Dirs = filelib:wildcard(filename:join(DataDir,"*")),
+ {Opts,ERPid} = setup([{dir,Dirs},
+ {label,until_force_stop_skip_rest_group},
+ {force_stop,skip_rest}],
+ Config),
+ ExecuteFun =
+ fun() ->
+ [_] = ct_test_support:run_ct_run_test(
+ Opts++[{until,until_str(?t3,1,Config)}],Config),
+ 0 = ct_test_support:run_ct_script_start(
+ Opts++[{until,until_str(?t3,1,Config)}],Config)
+ end,
+ ok = execute(ExecuteFun,
+ until_force_stop_skip_rest_group,
+ Opts, ERPid, Config).
+
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+setup(Test, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+%% Execute test, first with ct:run_test, then with ct:script_start
+execute(Name, Opts, ERPid, Config) ->
+ ExecuteFun = fun() -> ok = ct_test_support:run(Opts, Config) end,
+ execute(ExecuteFun, Name, Opts, ERPid, Config).
+
+execute(ExecuteFun, Name, Opts, ERPid, Config) ->
+ ExecuteFun(),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(Name,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config),
+ Opts),
+
+ TestEvents = events_to_check(Name),
+ ct_test_support:verify_events(TestEvents, Events, Config).
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+%% N is the expected number of repeats
+until_str(Secs0,N,Config) ->
+ Offset = ?config(offset,Config),
+ Secs = Secs0 + N*Offset,
+ Now = calendar:datetime_to_gregorian_seconds(calendar:local_time()),
+ {{Y,Mo,D},{H,M,S}} = calendar:gregorian_seconds_to_datetime(Now+Secs),
+ lists:flatten(io_lib:format("~2..0w~2..0w~2..0w~2..0w~2..0w~2..0w",
+ [Y rem 100, Mo, D, H, M, S])).
+
+%% N is the expected number of repeats
+duration_str(Secs0,N,Config) ->
+ Offset = ?config(offset,Config),
+ Secs = Secs0 + N*Offset,
+ "0000" ++ lists:flatten(io_lib:format("~2..0w",[Secs])).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+%% 2 tests (ct:run_test + script_start) is default
+events_to_check(C) when C==repeat_n; C==duration; C==until ->
+ dupl(4, start_logging() ++ all_succ() ++ stop_logging());
+events_to_check(C) when C==duration_force_stop; C==until_force_stop ->
+ dupl(2, start_logging() ++
+ all_succ() ++
+ stop_logging() ++
+ start_logging() ++
+ all_succ(r1_SUITE) ++
+ stop_logging());
+events_to_check(C) when C==duration_force_stop_skip_rest;
+ C==until_force_stop_skip_rest ->
+ dupl(2, start_logging() ++ skip_first_tc1(r1_SUITE) ++ stop_logging());
+events_to_check(C) when C==duration_force_stop_skip_rest_group;
+ C==until_force_stop_skip_rest_group ->
+ dupl(2, start_logging() ++ skip_tc1_in_group(r1_SUITE) ++ stop_logging()).
+
+dupl(N,List) ->
+ lists:flatten(lists:duplicate(N,List)).
+
+start_logging() ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}}].
+stop_logging() ->
+ [{?eh,stop_logging,[]}].
+
+
+all_succ() ->
+ all_succ(r1_SUITE) ++ all_succ(r2_SUITE).
+
+all_succ(Suite) ->
+ [{?eh,tc_start,{Suite,init_per_suite}},
+ {?eh,tc_done,{Suite,init_per_suite,ok}},
+ {?eh,tc_start,{Suite,tc1}},
+ {?eh,tc_done,{Suite,tc1,ok}},
+ {?eh,test_stats,{'_',0,{0,0}}},
+ {?eh,tc_start,{Suite,tc2}},
+ {?eh,tc_done,{Suite,tc2,ok}},
+ {?eh,test_stats,{'_',0,{0,0}}},
+ [{?eh,tc_start,{Suite,{init_per_group,g,[]}}},
+ {?eh,tc_done,{Suite,{init_per_group,g,[]},ok}},
+ {?eh,tc_start,{Suite,tc1}},
+ {?eh,tc_done,{Suite,tc1,ok}},
+ {?eh,test_stats,{'_',0,{0,0}}},
+ {?eh,tc_start,{Suite,tc2}},
+ {?eh,tc_done,{Suite,tc2,ok}},
+ {?eh,test_stats,{'_',0,{0,0}}},
+ {?eh,tc_start,{Suite,{end_per_group,g,[]}}},
+ {?eh,tc_done,{Suite,{end_per_group,g,[]},ok}}],
+ {?eh,tc_start,{Suite,tc2}},
+ {?eh,tc_done,{Suite,tc2,ok}},
+ {?eh,test_stats,{'_',0,{0,0}}},
+ {?eh,tc_start,{Suite,end_per_suite}},
+ {?eh,tc_done,{Suite,end_per_suite,ok}}].
+
+skip_first_tc1(Suite) ->
+ [{?eh,tc_start,{Suite,init_per_suite}},
+ {?eh,tc_done,{Suite,init_per_suite,ok}},
+ {?eh,tc_start,{Suite,tc1}},
+ {?eh,tc_done,{Suite,tc1,ok}},
+ {?eh,test_stats,{'_',0,{0,0}}},
+ {?eh,tc_done,{Suite,tc2,?skipped}},
+ {?eh,test_stats,{'_',0,{1,0}}},
+ {?eh,tc_done,{Suite,{init_per_group,g,[]},?skipped}},
+ {?eh,tc_auto_skip,{Suite,tc1,?skip_reason}},
+ {?eh,test_stats,{'_',0,{1,1}}},
+ {?eh,tc_auto_skip,{Suite,tc2,?skip_reason}},
+ {?eh,test_stats,{'_',0,{1,2}}},
+ {?eh,tc_auto_skip,{Suite,end_per_group,?skip_reason}},
+ {?eh,tc_done,{Suite,tc2,?skipped}},
+ {?eh,test_stats,{'_',0,{2,2}}},
+ {?eh,tc_start,{Suite,end_per_suite}},
+ {?eh,tc_done,{Suite,end_per_suite,ok}}].
+
+
+skip_tc1_in_group(Suite) ->
+ [{?eh,tc_start,{Suite,init_per_suite}},
+ {?eh,tc_done,{Suite,init_per_suite,ok}},
+ {?eh,tc_start,{Suite,tc1}},
+ {?eh,tc_done,{Suite,tc1,ok}},
+ {?eh,test_stats,{'_',0,{0,0}}},
+ {?eh,tc_start,{Suite,tc2}},
+ {?eh,tc_done,{Suite,tc2,ok}},
+ {?eh,test_stats,{'_',0,{0,0}}},
+ [{?eh,tc_start,{Suite,{init_per_group,g,[]}}},
+ {?eh,tc_done,{Suite,{init_per_group,g,[]},ok}},
+ {?eh,tc_start,{Suite,tc1}},
+ {?eh,tc_done,{Suite,tc1,ok}},
+ {?eh,test_stats,{'_',0,{0,0}}},
+ {?eh,tc_done,{Suite,tc2,?skipped}},
+ {?eh,test_stats,{'_',0,{1,0}}},
+ {?eh,tc_start,{Suite,{end_per_group,g,[]}}},
+ {?eh,tc_done,{Suite,{end_per_group,g,[]},ok}}],
+ {?eh,tc_done,{Suite,tc2,?skipped}},
+ {?eh,test_stats,{'_',0,{2,0}}},
+ {?eh,tc_start,{Suite,end_per_suite}},
+ {?eh,tc_done,{Suite,end_per_suite,ok}}].
diff --git a/lib/common_test/test/ct_repeat_testrun_SUITE_data/a_test/r1_SUITE.erl b/lib/common_test/test/ct_repeat_testrun_SUITE_data/a_test/r1_SUITE.erl
new file mode 100644
index 0000000000..3fd5943691
--- /dev/null
+++ b/lib/common_test/test/ct_repeat_testrun_SUITE_data/a_test/r1_SUITE.erl
@@ -0,0 +1,75 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%----------------------------------------------------------------------
+%% File: r1_SUITE.erl
+%%
+%% Description:
+%%
+%%
+%% @author Support
+%% @doc
+%% @end
+%%----------------------------------------------------------------------
+%%----------------------------------------------------------------------
+-module(r1_SUITE).
+-include_lib("common_test/include/ct.hrl").
+
+-compile(export_all).
+
+%% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:seconds(30)).
+
+all() ->
+ testcases() ++ [{group,g}, tc2].
+
+groups() ->
+ [{g,testcases()}].
+
+testcases() ->
+ [tc1,tc2].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ Config.
+
+init_per_group(_, Config) ->
+ Config.
+
+end_per_group(_Group, Config) ->
+ Config.
+
+init_per_testcase(_Case, Config) ->
+ Dog = test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%-----------------------------------------------------------------
+%%% Test cases
+tc1(_Config) ->
+ timer:sleep(10000),
+ ok.
+
+tc2(_Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_repeat_testrun_SUITE_data/b_test/r2_SUITE.erl b/lib/common_test/test/ct_repeat_testrun_SUITE_data/b_test/r2_SUITE.erl
new file mode 100644
index 0000000000..dc9abc2863
--- /dev/null
+++ b/lib/common_test/test/ct_repeat_testrun_SUITE_data/b_test/r2_SUITE.erl
@@ -0,0 +1,75 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%----------------------------------------------------------------------
+%% File: r2_SUITE.erl
+%%
+%% Description:
+%%
+%%
+%% @author Support
+%% @doc
+%% @end
+%%----------------------------------------------------------------------
+%%----------------------------------------------------------------------
+-module(r2_SUITE).
+-include_lib("common_test/include/ct.hrl").
+
+-compile(export_all).
+
+%% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:seconds(30)).
+
+all() ->
+ testcases() ++ [{group,g}, tc2].
+
+groups() ->
+ [{g,testcases()}].
+
+testcases() ->
+ [tc1,tc2].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ Config.
+
+init_per_group(_, Config) ->
+ Config.
+
+end_per_group(_Group, Config) ->
+ Config.
+
+init_per_testcase(_Case, Config) ->
+ Dog = test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%-----------------------------------------------------------------
+%%% Test cases
+tc1(_Config) ->
+ %% timer:sleep(3000),
+ ok.
+
+tc2(_Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 5e109e98e9..70dd087358 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -29,7 +29,8 @@
-export([init_per_suite/1, init_per_suite/2, end_per_suite/1,
init_per_testcase/2, end_per_testcase/2,
write_testspec/2, write_testspec/3,
- run/2, run/3, run/4, get_opts/1, wait_for_ct_stop/1]).
+ run/2, run/3, run/4, run_ct_run_test/2, run_ct_script_start/2,
+ get_opts/1, wait_for_ct_stop/1]).
-export([handle_event/2, start_event_receiver/1, get_events/2,
verify_events/3, verify_events/4, reformat/2, log_events/4,
@@ -224,9 +225,15 @@ get_opts(Config) ->
%%%-----------------------------------------------------------------
%%%
run(Opts, Config) when is_list(Opts) ->
+ %% use ct interface
+ CtRunTestResult=run_ct_run_test(Opts,Config),
+ %% use run_test interface (simulated)
+ ExitStatus=run_ct_script_start(Opts,Config),
+ check_result(CtRunTestResult,ExitStatus,Opts).
+
+run_ct_run_test(Opts,Config) ->
CTNode = proplists:get_value(ct_node, Config),
Level = proplists:get_value(trace_level, Config),
- %% use ct interface
test_server:format(Level, "~n[RUN #1] Calling ct:run_test(~p) on ~p~n",
[Opts, CTNode]),
CtRunTestResult = rpc:call(CTNode, ct, run_test, [Opts]),
@@ -242,7 +249,11 @@ run(Opts, Config) when is_list(Opts) ->
timer:sleep(5000),
undefined = rpc:call(CTNode, erlang, whereis, [ct_util_server])
end,
- %% use run_test interface (simulated)
+ CtRunTestResult.
+
+run_ct_script_start(Opts, Config) ->
+ CTNode = proplists:get_value(ct_node, Config),
+ Level = proplists:get_value(trace_level, Config),
Opts1 = [{halt_with,{?MODULE,ct_test_halt}} | Opts],
test_server:format(Level, "Saving start opts on ~p: ~p~n",
[CTNode, Opts1]),
@@ -253,27 +264,38 @@ run(Opts, Config) when is_list(Opts) ->
ExitStatus = rpc:call(CTNode, ct_run, script_start, []),
test_server:format(Level, "[RUN #2] Got exit status value ~p~n",
[ExitStatus]),
- case {CtRunTestResult,ExitStatus} of
- {{_Ok,Failed,{_UserSkipped,_AutoSkipped}},1} when Failed > 0 ->
- ok;
- {{_Ok,0,{_UserSkipped,AutoSkipped}},ExitStatus} when AutoSkipped > 0 ->
- case proplists:get_value(exit_status, Opts1) of
- ignore_config when ExitStatus == 1 ->
- {error,{wrong_exit_status,ExitStatus}};
- _ ->
- ok
- end;
- {{error,_}=Error,ExitStatus} ->
- if ExitStatus /= 2 ->
- {error,{wrong_exit_status,ExitStatus}};
- ExitStatus == 2 ->
- Error
- end;
- {{_Ok,0,{_UserSkipped,_AutoSkipped}},0} ->
- ok;
- Unexpected ->
- {error,{unexpected_return_value,Unexpected}}
- end.
+ ExitStatus.
+
+check_result({_Ok,Failed,{_UserSkipped,_AutoSkipped}},1,_Opts)
+ when Failed > 0 ->
+ ok;
+check_result({_Ok,0,{_UserSkipped,AutoSkipped}},ExitStatus,Opts)
+ when AutoSkipped > 0 ->
+ case proplists:get_value(exit_status, Opts) of
+ ignore_config when ExitStatus == 1 ->
+ {error,{wrong_exit_status,ExitStatus}};
+ _ ->
+ ok
+ end;
+check_result({error,_}=Error,2,_Opts) ->
+ Error;
+check_result({error,_},ExitStatus,_Opts) ->
+ {error,{wrong_exit_status,ExitStatus}};
+check_result({_Ok,0,{_UserSkipped,_AutoSkipped}},0,_Opts) ->
+ ok;
+check_result(CtRunTestResult,ExitStatus,Opts)
+ when is_list(CtRunTestResult) -> % repeated testruns
+ try check_result(sum_testruns(CtRunTestResult,0,0,0,0),ExitStatus,Opts)
+ catch _:_ ->
+ {error,{unexpected_return_value,{CtRunTestResult,ExitStatus}}}
+ end;
+check_result(CtRunTestResult,ExitStatus,_Opts) ->
+ {error,{unexpected_return_value,{CtRunTestResult,ExitStatus}}}.
+
+sum_testruns([{O,F,{US,AS}}|T],Ok,Failed,UserSkipped,AutoSkipped) ->
+ sum_testruns(T,Ok+O,Failed+F,UserSkipped+US,AutoSkipped+AS);
+sum_testruns([],Ok,Failed,UserSkipped,AutoSkipped) ->
+ {Ok,Failed,{UserSkipped,AutoSkipped}}.
run(M, F, A, Config) ->
run({M,F,A}, [], Config).
diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml
index 073faf2df2..a177b80739 100644
--- a/lib/stdlib/doc/src/sys.xml
+++ b/lib/stdlib/doc/src/sys.xml
@@ -211,18 +211,87 @@
<p>Gets the status of the process.</p>
<p>The value of <c><anno>Misc</anno></c> varies for different types of
processes. For example, a <c>gen_server</c> process returns
- the callback module's state, and a <c>gen_fsm</c> process
- returns information such as its current state name. Callback
- modules for <c>gen_server</c> and <c>gen_fsm</c> can also
- customise the value of <c><anno>Misc</anno></c> by exporting
- a <c>format_status/2</c> function that contributes
- module-specific information;
- see <seealso marker="gen_server#Module:format_status/2">gen_server:format_status/2</seealso>
- and <seealso marker="gen_fsm#Module:format_status/2">gen_fsm:format_status/2</seealso>
+ the callback module's state, a <c>gen_fsm</c> process
+ returns information such as its current state name and state data,
+ and a <c>gen_event</c> process returns information about each of its
+ registered handlers. Callback modules for <c>gen_server</c>,
+ <c>gen_fsm</c>, and <c>gen_event</c> can also customise the value
+ of <c><anno>Misc</anno></c> by exporting a <c>format_status/2</c>
+ function that contributes module-specific information;
+ see <seealso marker="gen_server#Module:format_status/2">gen_server:format_status/2</seealso>,
+ <seealso marker="gen_fsm#Module:format_status/2">gen_fsm:format_status/2</seealso>, and
+ <seealso marker="gen_event#Module:format_status/2">gen_event:format_status/2</seealso>
for more details.</p>
</desc>
</func>
<func>
+ <name name="get_state" arity="1"/>
+ <name name="get_state" arity="2"/>
+ <fsummary>Get the state of the process</fsummary>
+ <desc>
+ <p>Gets the state of the process.</p>
+ <note>
+ <p>These functions are intended only to help with debugging. They are provided for
+ convenience, allowing developers to avoid having to create their own state extraction
+ functions and also avoid having to interactively extract state from the return values of
+ <c><seealso marker="get_status/1">get_status/1</seealso></c> or
+ <c><seealso marker="get_status/2">get_status/2</seealso></c> while debugging.</p>
+ </note>
+ <p>The value of <c><anno>State</anno></c> varies for different types of
+ processes. For a <c>gen_server</c> process, the returned <c><anno>State</anno></c>
+ is simply the callback module's state. For a <c>gen_fsm</c> process,
+ <c><anno>State</anno></c> is the tuple <c>{CurrentStateName, CurrentStateData}</c>.
+ For a <c>gen_event</c> process, <c><anno>State</anno></c> a list of tuples,
+ where each tuple corresponds to an event handler registered in the process and contains
+ <c>{Module, Id, HandlerState}</c>, where <c>Module</c> is the event handler's module name,
+ <c>Id</c> is the handler's ID (which is the value <c>false</c> if it was registered without
+ an ID), and <c>HandlerState</c> is the handler's state.</p>
+ <p>To obtain more information about a process, including its state, see
+ <seealso marker="get_status/1">get_status/1</seealso> and
+ <seealso marker="get_status/2">get_status/2</seealso>.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="replace_state" arity="2"/>
+ <name name="replace_state" arity="3"/>
+ <fsummary>Replace the state of the process</fsummary>
+ <desc>
+ <p>Replaces the state of the process, and returns the new state.</p>
+ <note>
+ <p>These functions are intended only to help with debugging, and they should not be
+ be called from normal code. They are provided for convenience, allowing developers
+ to avoid having to create their own custom state replacement functions.</p>
+ </note>
+ <p>The <c><anno>StateFun</anno></c> function provides a new state for the process.
+ The <c><anno>State</anno></c> argument and <c><anno>NewState</anno></c> return value
+ of <c><anno>StateFun</anno></c> vary for different types of processes. For a
+ <c>gen_server</c> process, <c><anno>State</anno></c> is simply the callback module's
+ state, and <c><anno>NewState</anno></c> is a new instance of that state. For a
+ <c>gen_fsm</c> process, <c><anno>State</anno></c> is the tuple
+ <c>{CurrentStateName, CurrentStateData}</c>, and <c><anno>NewState</anno></c>
+ is a similar tuple that may contain a new state name, new state data, or both.
+ For a <c>gen_event</c> process, <c><anno>State</anno></c> is the tuple
+ <c>{Module, Id, HandlerState}</c> where <c>Module</c> is the event handler's module name,
+ <c>Id</c> is the handler's ID (which is the value <c>false</c> if it was registered without
+ an ID), and <c>HandlerState</c> is the handler's state. <c><anno>NewState</anno></c> is a
+ similar tuple where <c>Module</c> and <c>Id</c> shall have the same values as in
+ <c><anno>State</anno></c> but the value of <c>HandlerState</c> may be different. Returning
+ a <c><anno>NewState</anno></c> whose <c>Module</c> or <c>Id</c> values differ from those of
+ <c><anno>State</anno></c> will result in the event handler's state remaining unchanged. For a
+ <c>gen_event</c> process, <c><anno>StateFun</anno></c> is called once for each event handler
+ registered in the <c>gen_event</c> process.</p>
+ <p>If a <c><anno>StateFun</anno></c> function decides not to effect any change in process
+ state, then regardless of process type, it may simply return its <c><anno>State</anno></c>
+ argument.</p>
+ <p>If a <c><anno>StateFun</anno></c> function crashes or throws an exception, then
+ for <c>gen_server</c> and <c>gen_fsm</c> processes, the original state of the process is
+ unchanged. For <c>gen_event</c> processes, a crashing or failing <c><anno>StateFun</anno></c>
+ function means that only the state of the particular event handler it was working on when it
+ failed or crashed is unchanged; it can still succeed in changing the states of other event
+ handlers registered in the same <c>gen_event</c> process.</p>
+ </desc>
+ </func>
+ <func>
<name name="install" arity="2"/>
<name name="install" arity="3"/>
<fsummary>Install a debug function in the process</fsummary>
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index 2b8ba86909..bfebf29080 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -229,6 +229,24 @@ wake_hib(Parent, ServerName, MSL, Debug) ->
fetch_msg(Parent, ServerName, MSL, Debug, Hib) ->
receive
+ {system, From, get_state} ->
+ States = [{Mod,Id,State} || #handler{module=Mod, id=Id, state=State} <- MSL],
+ sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug,
+ {States, [ServerName, MSL, Hib]}, Hib);
+ {system, From, {replace_state, StateFun}} ->
+ {NMSL, NStates} =
+ lists:unzip([begin
+ Cur = {Mod,Id,State},
+ try
+ NState = {Mod,Id,NS} = StateFun(Cur),
+ {HS#handler{state=NS}, NState}
+ catch
+ _:_ ->
+ {HS, Cur}
+ end
+ end || #handler{module=Mod, id=Id, state=State}=HS <- MSL]),
+ sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug,
+ {NStates, [ServerName, NMSL, Hib]}, Hib);
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
[ServerName, MSL, Hib],Hib);
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index e480e2ac11..d9411e58cf 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -422,6 +422,17 @@ wake_hib(Parent, Name, StateName, StateData, Mod, Debug) ->
decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, Debug, Hib) ->
case Msg of
+ {system, From, get_state} ->
+ Misc = [Name, StateName, StateData, Mod, Time],
+ sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug,
+ {{StateName, StateData}, Misc}, Hib);
+ {system, From, {replace_state, StateFun}} ->
+ State = {StateName, StateData},
+ NState = {NStateName, NStateData} = try StateFun(State)
+ catch _:_ -> State end,
+ NMisc = [Name, NStateName, NStateData, Mod, Time],
+ sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug,
+ {NState, NMisc}, Hib);
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
[Name, StateName, StateData, Mod, Time], Hib);
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index 04308a51b7..9c4b95acf6 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -372,6 +372,13 @@ wake_hib(Parent, Name, State, Mod, Debug) ->
decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) ->
case Msg of
+ {system, From, get_state} ->
+ sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug,
+ {State, [Name, State, Mod, Time]}, Hib);
+ {system, From, {replace_state, StateFun}} ->
+ NState = try StateFun(State) catch _:_ -> State end,
+ sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug,
+ {NState, [Name, NState, Mod, Time]}, Hib);
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
[Name, State, Mod, Time], Hib);
diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl
index 2d6287814e..bffeb44179 100644
--- a/lib/stdlib/src/sys.erl
+++ b/lib/stdlib/src/sys.erl
@@ -21,6 +21,8 @@
%% External exports
-export([suspend/1, suspend/2, resume/1, resume/2,
get_status/1, get_status/2,
+ get_state/1, get_state/2,
+ replace_state/2, replace_state/3,
change_code/4, change_code/5,
log/2, log/3, trace/2, trace/3, statistics/2, statistics/3,
log_to_file/2, log_to_file/3, no_debug/1, no_debug/2,
@@ -97,6 +99,32 @@ get_status(Name) -> send_system_msg(Name, get_status).
| (Misc :: term()).
get_status(Name, Timeout) -> send_system_msg(Name, get_status, Timeout).
+-spec get_state(Name) -> State when
+ Name :: name(),
+ State :: term().
+get_state(Name) -> send_system_msg(Name, get_state).
+
+-spec get_state(Name, Timeout) -> State when
+ Name :: name(),
+ Timeout :: timeout(),
+ State :: term().
+get_state(Name, Timeout) -> send_system_msg(Name, get_state, Timeout).
+
+-spec replace_state(Name, StateFun) -> NewState when
+ Name :: name(),
+ StateFun :: fun((State :: term()) -> NewState :: term()),
+ NewState :: term().
+replace_state(Name, StateFun) ->
+ send_system_msg(Name, {replace_state, StateFun}).
+
+-spec replace_state(Name, StateFun, Timeout) -> NewState when
+ Name :: name(),
+ StateFun :: fun((State :: term()) -> NewState :: term()),
+ Timeout :: timeout(),
+ NewState :: term().
+replace_state(Name, StateFun, Timeout) ->
+ send_system_msg(Name, {replace_state, StateFun}, Timeout).
+
-spec change_code(Name, Module, OldVsn, Extra) -> 'ok' | {error, Reason} when
Name :: name(),
Module :: module(),
@@ -362,6 +390,10 @@ do_cmd(_, suspend, _Parent, _Mod, Debug, Misc) ->
{suspended, ok, Debug, Misc};
do_cmd(_, resume, _Parent, _Mod, Debug, Misc) ->
{running, ok, Debug, Misc};
+do_cmd(SysState, get_state, _Parent, _Mod, Debug, {State, Misc}) ->
+ {SysState, State, Debug, Misc};
+do_cmd(SysState, replace_state, _Parent, _Mod, Debug, {State, Misc}) ->
+ {SysState, State, Debug, Misc};
do_cmd(SysState, get_status, Parent, Mod, Debug, Misc) ->
Res = get_status(SysState, Parent, Mod, Debug, Misc),
{SysState, Res, Debug, Misc};
diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl
index 5c51e12e35..6be5a299b6 100644
--- a/lib/stdlib/test/gen_event_SUITE.erl
+++ b/lib/stdlib/test/gen_event_SUITE.erl
@@ -26,13 +26,14 @@
delete_handler/1, swap_handler/1, swap_sup_handler/1,
notify/1, sync_notify/1, call/1, info/1, hibernate/1,
call_format_status/1, call_format_status_anon/1,
- error_format_status/1]).
+ error_format_status/1, get_state/1, replace_state/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[start, {group, test_all}, hibernate,
- call_format_status, call_format_status_anon, error_format_status].
+ call_format_status, call_format_status_anon, error_format_status,
+ get_state, replace_state].
groups() ->
[{test_all, [],
@@ -956,3 +957,45 @@ error_format_status(Config) when is_list(Config) ->
?line ok = gen_event:stop(Pid),
process_flag(trap_exit, OldFl),
ok.
+
+get_state(suite) ->
+ [];
+get_state(doc) ->
+ ["Test that sys:get_state/1,2 return the gen_event state"];
+get_state(Config) when is_list(Config) ->
+ {ok, Pid} = gen_event:start({local, my_dummy_handler}),
+ State1 = self(),
+ ok = gen_event:add_handler(my_dummy_handler, dummy1_h, [State1]),
+ [{dummy1_h,false,State1}] = sys:get_state(Pid),
+ [{dummy1_h,false,State1}] = sys:get_state(Pid, 5000),
+ State2 = {?MODULE, self()},
+ ok = gen_event:add_handler(my_dummy_handler, {dummy1_h,id}, [State2]),
+ Result1 = sys:get_state(Pid),
+ [{dummy1_h,false,State1},{dummy1_h,id,State2}] = lists:sort(Result1),
+ Result2 = sys:get_state(Pid, 5000),
+ [{dummy1_h,false,State1},{dummy1_h,id,State2}] = lists:sort(Result2),
+ ok = gen_event:stop(Pid),
+ ok.
+
+replace_state(suite) ->
+ [];
+replace_state(doc) ->
+ ["Test that replace_state/2,3 replace the gen_event state"];
+replace_state(Config) when is_list(Config) ->
+ {ok, Pid} = gen_event:start({local, my_dummy_handler}),
+ State1 = self(),
+ ok = gen_event:add_handler(my_dummy_handler, dummy1_h, [State1]),
+ [{dummy1_h,false,State1}] = sys:get_state(Pid),
+ NState1 = "replaced",
+ Replace1 = fun({dummy1_h,false,_}=S) -> setelement(3,S,NState1) end,
+ [{dummy1_h,false,NState1}] = sys:replace_state(Pid, Replace1),
+ [{dummy1_h,false,NState1}] = sys:get_state(Pid),
+ NState2 = "replaced again",
+ Replace2 = fun({dummy1_h,false,_}=S) -> setelement(3,S,NState2) end,
+ [{dummy1_h,false,NState2}] = sys:replace_state(Pid, Replace2, 5000),
+ [{dummy1_h,false,NState2}] = sys:get_state(Pid),
+ %% verify no change in state if replace function crashes
+ Replace3 = fun(_) -> exit(fail) end,
+ [{dummy1_h,false,NState2}] = sys:replace_state(Pid, Replace3),
+ [{dummy1_h,false,NState2}] = sys:get_state(Pid),
+ ok.
diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl
index a637a8543b..fd15838b7d 100644
--- a/lib/stdlib/test/gen_fsm_SUITE.erl
+++ b/lib/stdlib/test/gen_fsm_SUITE.erl
@@ -31,7 +31,7 @@
-export([shutdown/1]).
--export([ sys1/1, call_format_status/1, error_format_status/1]).
+-export([ sys1/1, call_format_status/1, error_format_status/1, get_state/1, replace_state/1]).
-export([hibernate/1,hiber_idle/3,hiber_wakeup/3,hiber_idle/2,hiber_wakeup/2]).
@@ -66,7 +66,7 @@ groups() ->
start8, start9, start10, start11, start12]},
{abnormal, [], [abnormal1, abnormal2]},
{sys, [],
- [sys1, call_format_status, error_format_status]}].
+ [sys1, call_format_status, error_format_status, get_state, replace_state]}].
init_per_suite(Config) ->
Config.
@@ -413,6 +413,40 @@ error_format_status(Config) when is_list(Config) ->
process_flag(trap_exit, OldFl),
ok.
+get_state(Config) when is_list(Config) ->
+ State = self(),
+ {ok, Pid} = gen_fsm:start(?MODULE, {state_data, State}, []),
+ {idle, State} = sys:get_state(Pid),
+ {idle, State} = sys:get_state(Pid, 5000),
+ stop_it(Pid),
+
+ %% check that get_state can handle a name being an atom (pid is
+ %% already checked by the previous test)
+ {ok, Pid2} = gen_fsm:start({local, gfsm}, gen_fsm_SUITE, {state_data, State}, []),
+ {idle, State} = sys:get_state(gfsm),
+ {idle, State} = sys:get_state(gfsm, 5000),
+ stop_it(Pid2),
+ ok.
+
+replace_state(Config) when is_list(Config) ->
+ State = self(),
+ {ok, Pid} = gen_fsm:start(?MODULE, {state_data, State}, []),
+ {idle, State} = sys:get_state(Pid),
+ NState1 = "replaced",
+ Replace1 = fun({StateName, _}) -> {StateName, NState1} end,
+ {idle, NState1} = sys:replace_state(Pid, Replace1),
+ {idle, NState1} = sys:get_state(Pid),
+ NState2 = "replaced again",
+ Replace2 = fun({idle, _}) -> {state0, NState2} end,
+ {state0, NState2} = sys:replace_state(Pid, Replace2, 5000),
+ {state0, NState2} = sys:get_state(Pid),
+ %% verify no change in state if replace function crashes
+ Replace3 = fun(_) -> error(fail) end,
+ {state0, NState2} = sys:replace_state(Pid, Replace3),
+ {state0, NState2} = sys:get_state(Pid),
+ stop_it(Pid),
+ ok.
+
%% Hibernation
hibernate(suite) -> [];
hibernate(Config) when is_list(Config) ->
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index dffeadb423..3b6a3f38bc 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -32,7 +32,7 @@
spec_init_local_registered_parent/1,
spec_init_global_registered_parent/1,
otp_5854/1, hibernate/1, otp_7669/1, call_format_status/1,
- error_format_status/1, call_with_huge_message_queue/1
+ error_format_status/1, get_state/1, replace_state/1, call_with_huge_message_queue/1
]).
% spawn export
@@ -57,6 +57,7 @@ all() ->
spec_init_local_registered_parent,
spec_init_global_registered_parent, otp_5854, hibernate,
otp_7669, call_format_status, error_format_status,
+ get_state, replace_state,
call_with_huge_message_queue].
groups() ->
@@ -1033,6 +1034,51 @@ error_format_status(Config) when is_list(Config) ->
process_flag(trap_exit, OldFl),
ok.
+%% Verify that sys:get_state correctly returns gen_server state
+%%
+get_state(suite) ->
+ [];
+get_state(doc) ->
+ ["Test that sys:get_state/1,2 return the gen_server state"];
+get_state(Config) when is_list(Config) ->
+ State = self(),
+ {ok, _Pid} = gen_server:start_link({local, get_state},
+ ?MODULE, {state,State}, []),
+ State = sys:get_state(get_state),
+ State = sys:get_state(get_state, 5000),
+ {ok, Pid} = gen_server:start_link(?MODULE, {state,State}, []),
+ State = sys:get_state(Pid),
+ State = sys:get_state(Pid, 5000),
+ ok.
+
+%% Verify that sys:replace_state correctly replaces gen_server state
+%%
+replace_state(suite) ->
+ [];
+replace_state(doc) ->
+ ["Test that sys:replace_state/1,2 replace the gen_server state"];
+replace_state(Config) when is_list(Config) ->
+ State = self(),
+ {ok, _Pid} = gen_server:start_link({local, replace_state},
+ ?MODULE, {state,State}, []),
+ State = sys:get_state(replace_state),
+ NState1 = "replaced",
+ Replace1 = fun(_) -> NState1 end,
+ NState1 = sys:replace_state(replace_state, Replace1),
+ NState1 = sys:get_state(replace_state),
+ {ok, Pid} = gen_server:start_link(?MODULE, {state,NState1}, []),
+ NState1 = sys:get_state(Pid),
+ Suffix = " again",
+ NState2 = NState1 ++ Suffix,
+ Replace2 = fun(S) -> S ++ Suffix end,
+ NState2 = sys:replace_state(Pid, Replace2, 5000),
+ NState2 = sys:get_state(Pid, 5000),
+ %% verify no change in state if replace function crashes
+ Replace3 = fun(_) -> throw(fail) end,
+ NState2 = sys:replace_state(Pid, Replace3),
+ NState2 = sys:get_state(Pid, 5000),
+ ok.
+
%% Test that the time for a huge message queue is not
%% significantly slower than with an empty message queue.
call_with_huge_message_queue(Config) when is_list(Config) ->
diff --git a/lib/stdlib/test/io_proto_SUITE.erl b/lib/stdlib/test/io_proto_SUITE.erl
index e16ba55481..76a8109a8d 100644
--- a/lib/stdlib/test/io_proto_SUITE.erl
+++ b/lib/stdlib/test/io_proto_SUITE.erl
@@ -147,8 +147,7 @@ unicode_prompt(Config) when is_list(Config) ->
%% And one with oldshell
?line rtnode([{putline,""},
{putline, "2."},
- {getline_re, ".*2."},
- {getline, "2"},
+ {getline_re, ".*2$"},
{putline, "shell:prompt_func({io_proto_SUITE,uprompt})."},
{getline_re, ".*default"},
{putline, "io:get_line('')."},
@@ -263,8 +262,7 @@ setopts_getopts(Config) when is_list(Config) ->
%% And one with oldshell
?line rtnode([{putline,""},
{putline, "2."},
- {getline_re, ".*2."},
- {getline, "2"},
+ {getline_re, ".*2$"},
{putline, "lists:keyfind(binary,1,io:getopts())."},
{getline_re, ".*{binary,false}"},
{putline, "io:get_line('')."},
@@ -467,8 +465,7 @@ unicode_options(Config) when is_list(Config) ->
end,
?line rtnode([{putline,""},
{putline, "2."},
- {getline_re, ".*2."},
- {getline, "2"},
+ {getline_re, ".*2$"},
{putline, "lists:keyfind(encoding,1,io:getopts())."},
{getline_re, ".*{encoding,latin1}"},
{putline, "io:format(\"~ts~n\",[[1024]])."},
@@ -701,8 +698,7 @@ binary_options(Config) when is_list(Config) ->
old ->
ok;
new ->
- ?line rtnode([{putline,""},
- {putline, "2."},
+ ?line rtnode([{putline, "2."},
{getline, "2"},
{putline, "lists:keyfind(binary,1,io:getopts())."},
{getline, "{binary,false}"},
@@ -720,10 +716,8 @@ binary_options(Config) when is_list(Config) ->
],[])
end,
%% And one with oldshell
- ?line rtnode([{putline,""},
- {putline, "2."},
- {getline_re, ".*2."},
- {getline, "2"},
+ ?line rtnode([{putline, "2."},
+ {getline_re, ".*2$"},
{putline, "lists:keyfind(binary,1,io:getopts())."},
{getline_re, ".*{binary,false}"},
{putline, "io:get_line('')."},
diff --git a/lib/test_server/src/test_server_gl.erl b/lib/test_server/src/test_server_gl.erl
index 766a4537a2..2e4f223811 100644
--- a/lib/test_server/src/test_server_gl.erl
+++ b/lib/test_server/src/test_server_gl.erl
@@ -197,7 +197,7 @@ handle_info({io_request,From,ReplyAs,Req}=IoReq, St) ->
From ! {io_reply,ReplyAs,ok}
catch
_:_ ->
- {io_reply,ReplyAs,{error,arguments}}
+ From ! {io_reply,ReplyAs,{error,arguments}}
end,
{noreply,St};
handle_info({structured_io,ClientPid,{Detail,Str}}, St) ->
diff --git a/lib/test_server/test/test_server_SUITE.erl b/lib/test_server/test/test_server_SUITE.erl
index 1a2fc632da..cf1df6df34 100644
--- a/lib/test_server/test/test_server_SUITE.erl
+++ b/lib/test_server/test/test_server_SUITE.erl
@@ -104,7 +104,7 @@ test_server_SUITE(Config) ->
% rpc:call(Node,dbg, tpl,[test_server_ctrl,x]),
run_test_server_tests("test_server_SUITE",
[{test_server_SUITE,skip_case7,"SKIPPED!"}],
- 38, 1, 30, 19, 9, 1, 11, 2, 25, Config).
+ 39, 1, 31, 20, 9, 1, 11, 2, 26, Config).
test_server_parallel01_SUITE(Config) ->
run_test_server_tests("test_server_parallel01_SUITE", [],
diff --git a/lib/test_server/test/test_server_SUITE_data/test_server_SUITE.erl b/lib/test_server/test/test_server_SUITE_data/test_server_SUITE.erl
index fc2adcd651..6c50efa712 100644
--- a/lib/test_server/test/test_server_SUITE_data/test_server_SUITE.erl
+++ b/lib/test_server/test/test_server_SUITE_data/test_server_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -38,7 +38,8 @@
conf_init/1, check_new_conf/1, conf_cleanup/1,
check_old_conf/1, conf_init_fail/1, start_stop_node/1,
cleanup_nodes_init/1, check_survive_nodes/1, cleanup_nodes_fin/1,
- commercial/1]).
+ commercial/1,
+ io_invalid_data/1]).
-export([dummy_function/0,dummy_function/1,doer/1]).
@@ -47,7 +48,7 @@ all(suite) ->
[config, comment, timetrap, timetrap_cancel, multiply_timetrap,
init_per_s, init_per_tc, end_per_tc,
timeconv, msgs, capture, timecall, do_times, skip_cases,
- commercial,
+ commercial, io_invalid_data,
{conf, conf_init, [check_new_conf], conf_cleanup},
check_old_conf,
{conf, conf_init_fail,[conf_member_skip],conf_cleanup_skip},
@@ -497,4 +498,8 @@ commercial(Config) when is_list(Config) ->
true -> {comment,"Commercial build"}
end.
-
+io_invalid_data(Config) when is_list(Config) ->
+ ok = io:put_chars("valid: " ++ [42]),
+ %% OTP-10991 caused this to hang and produce a timetrap timeout:
+ {'EXIT',{badarg,_}} = (catch io:put_chars("invalid: " ++ [42.0])),
+ ok.
diff --git a/system/doc/top/Makefile b/system/doc/top/Makefile
index 37466fa2d9..20ef9fe781 100644
--- a/system/doc/top/Makefile
+++ b/system/doc/top/Makefile
@@ -38,13 +38,6 @@ INFO_FILES = ../../README ../../COPYRIGHT PR.template
TOPDOCDIR=.
-TOP_HTML_GEN_FILES = \
- $(HTMLDIR)/incompatible.html \
- $(HTMLDIR)/highlights.html
-
-TOP_HTML_FILES = \
- $(TOP_HTML_GEN_FILES)
-
include ../installation_guide/xmlfiles.mk
include ../system_principles/xmlfiles.mk
include ../embedded/xmlfiles.mk
@@ -229,7 +222,7 @@ $(TOP_PDF_FILE): $(XML_FILES)
pdf: $(TOP_PDF_FILE)
-html: $(INDEX_FILES) $(TOP_HTML_FILES) \
+html: $(INDEX_FILES) \
$(MAN_INDEX) $(JAVASCRIPT)
debug opt:
@@ -237,7 +230,7 @@ debug opt:
clean:
rm -rf ../html/js
rm -f PR.template
- rm -f $(INDEX_FILES) $(TOP_HTML_FILES) $(MAN_INDEX)
+ rm -f $(INDEX_FILES) $(MAN_INDEX)
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f $(INDEX_SCRIPT) $(GLOSSARY_SCRIPT) \
$(JAVASCRIPT_BUILD_SCRIPT)
@@ -256,11 +249,10 @@ release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/pdf
$(INSTALL_DATA) \
$(TOP_PDF_FILE) $(RELSYSDIR)/pdf
- $(INSTALL_DATA) $(TOP_HTML_FILES) $(RELSYSDIR)
$(INSTALL_DIR) $(RELSYSDIR)/js
$(INSTALL_DATA) \
$(JAVASCRIPT) $(RELSYSDIR)/js
- $(INSTALL_DATA) $(INDEX_FILES) $(MAN_INDEX) $(TOP_HTML_FILES) $(RELSYSDIR)
+ $(INSTALL_DATA) $(INDEX_FILES) $(MAN_INDEX) $(RELSYSDIR)
$(INSTALL_DIR) $(RELSYSDIR)/docbuild
$(INSTALL_DATA) $(INDEX_SCRIPT) $(MAN_INDEX_SCRIPT) $(JAVASCRIPT_BUILD_SCRIPT) \
$(INDEX_SRC) $(MAN_INDEX_SRC) $(JAVASCRIPT_BUILD_SCRIPT_SRC) \
diff --git a/system/doc/top/highlights.xml b/system/doc/top/highlights.xml
deleted file mode 100644
index 251bb1a47d..0000000000
--- a/system/doc/top/highlights.xml
+++ /dev/null
@@ -1,110 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
-
-<chapter>
- <header>
- <copyright>
- <year>2006</year><year>2010</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>Highlights</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev>A</rev>
- <file>highlights.xml</file>
- </header>
- <p>This document lists some highlights of Erlang 5.8/OTP R14A
- (unpatched), compared to the previous version Erlang 5.7.1/OTP R13B,
- with focus on things not already released as R13B patches.</p>
- <p>Note: This document was compiled at the time when R14A was released
- and does not list any features introduced in R14 patches.</p>
-
- <section>
- <title>Erlang Language and Run-time System</title>
- <section>
- <title>New NIF features</title>
- <list type="bulleted">
- <item><p>Send messages from a NIF, or from thread created by NIF, to
- any local process (enif_send)</p></item>
-
- <item><p>Store terms between NIF calls (enif_alloc_env,
- enif_make_copy)</p></item>
-
- <item><p>Create binary terms with user defined memory management
- (enif_make_resource_binary)</p></item>
-
- <item><p>Some incompatible changes made to the API. For more
- information see the warning text in erl_nif(3).</p></item>
- </list>
- </section>
- <section>
- <title>Receive optimizations</title>
- <p>Receive statements that can only read out a newly created
- reference are now specially optimized so that it will execute
- in constant time regardless of the number of messages in the
- receive queue for the process. That optimization will benefit
- calls to gen_server:call(). (See gen:do_call/4 for an example
- of a receive statement that will be optimized.)</p>
- </section>
- <section>
- <title>Improved eprof</title>
- <p>eprof has been reimplemented with support in the Erlang
- virtual machine and is now both faster (i.e. slows down the
- code being measured less) and scales much better. In
- measurements we saw speed-ups compared to the old eprof
- ranging from 6 times (for sequential code that only uses one
- scheduler/core) up to 84 times (for parallel code that uses 8
- cores).</p>
-
- <p>Note: The API for the eprof has been cleaned up and extended.
- See the documentation.</p>
- </section>
- </section>
- <section>
- <title>New features in Existing Applications</title>
-
- <section>
- <title>Common_test</title>
-
- <p>
- It is now possible for the user to provide specific callback
- modules that handle test configuration data, so that data on
- arbitray form can be accessed (e.g. by reading files or by
- communicating with a configuration server process). Two
- default callback modules have been introduced in Common Test:
- ct_config_plain and ct_config_xml. The former is used to
- handle the traditional Common Test configuration files (with
- terms on key-value tuple form) and the latter to handle
- configuration data on XML representation.
- </p>
- <p>
- The run_test script has been replaced by a program (with the
- same name) which can be executed without explicit
- installation. The start flags are the same as for the legacy
- start script.
- </p>
- </section>
- <section>
- <title>STDLIB</title>
- <p>
- The module binary from EEP31 (and EEP9) is implemented.
- </p>
- </section>
- </section>
-</chapter>
-
diff --git a/system/doc/top/incompatible.xml b/system/doc/top/incompatible.xml
deleted file mode 100644
index e09fa7272f..0000000000
--- a/system/doc/top/incompatible.xml
+++ /dev/null
@@ -1,271 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
-
-<chapter>
- <header>
- <copyright>
- <year>2006</year><year>2010</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>Potential Incompatibilities</title>
- <prepared></prepared>
- <docno></docno>
- <date></date>
- <rev></rev>
- <file>incompatible.xml</file>
- </header>
- <p>This document contains a list of potential incompatibilities
- between Erlang 5.8/OTP R14A and Erl 5.7.5/OTP R13B04,
- and is an extract from the release notes for the respective applications.</p>
-
- <section>
- <title>Tools</title>
- <list type="bulleted">
- <item>
- <p>
- Xref has been updated to use the re module instead of the
- deprecated regexp module.</p>
- <p>*** INCOMPATIBILITY with R12B ***</p>
- <p>
- Own Id: OTP-8472</p>
- </item>
- <item>
- <p>eprof has been reimplemented with support in the Erlang
- virtual machine and is now both faster (i.e. slows down the
- code being measured less) and scales much better. In
- measurements we saw speed-ups compared to the old eprof
- ranging from 6 times (for sequential code that only uses one
- scheduler/core) up to 84 times (for parallel code that uses 8
- cores).</p>
-
- <p>Note: The API for the eprof has been cleaned up and extended.
- See the documentation.</p>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-8706</p>
- </item>
- </list>
- </section>
-
- <section>
- <title>Odbc</title>
- <list type="bulleted">
- <item>
- <p>
- Now supports SQL_TYPE_TIMESTAMP on the format {{YY, MM, DD},
- {HH, MM, SS}}. Thanks to Juhani Ränkimies.
- </p>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-8511</p>
- </item>
- </list>
- </section>
-
- <section>
- <title>Ssh</title>
- <list type="bulleted">
- <item>
- <p>
- The configuration parameter ip_v6_disabled is now available,
- which makes it possible for the user to alter the IP version
- SSH shall use.</p>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-8535</p>
- </item>
- <item>
- <p>
- Removed deprecated modules (ssh_ssh, ssh_sshd and ssh_cm) and
- functions (ssh_sftp:connect and ssh_sftp:stop).</p>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-8596</p>
- </item>
- <item>
- <p>
- Aligned error message with used version (SSH_FX_FAILURE vs
- SSH_FX_NOT_A_DIRECTORY, the latter introduced in version 6).</p>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-8644</p>
- </item>
- </list>
- </section>
-
- <section>
- <title>ERTS</title>
- <list>
- <item>
- <p>Some incompatible changes made to the NIF API. For more
- information see the warning text in erl_nif(3).</p>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-8555</p>
- </item>
- </list>
- </section>
-
- <section>
- <title>STDLIB</title>
- <list type="bulleted">
- <item>
- <p>When defining macros the closing right parenthesis before the
- dot is now mandatory.</p>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-8562</p>
- </item>
- </list>
- </section>
-
- <section>
- <title>Compiler and STDLIB</title>
- <list type="bulleted">
- <item>
- <p>
- Local and imported functions now override the auto-imported
- BIFs when the names clash. The pre R14 behaviour was that
- auto-imported BIFs would override local functions. To avoid
- that old programs change behaviour, the following will
- generate an error:</p>
-
- <list type="bulleted">
- <item>Doing a call without explicit module name to a local function
- having a name clashing with the name of an auto-imported BIF
- that was present (and auto-imported) before OTP R14A</item>
-
- <item>Explicitly importing a function having a name clashing with
- the name of an autoimported BIF that was present (and
- autoimported) before OTP R14A</item>
-
- <item>Using any form of the old compiler directive nowarn_bif_clash</item>
- </list>
-
- <p>If the BIF was added or auto-imported in OTP R14A or later,
- overriding it with an import or a local function will only
- result in a warning.</p>
-
- <p>To resolve clashes, you can either use the explicit module
- name erlang to call the BIF, or you can remove the
- auto-import of that specific BIF by using the new compiler
- directive -compile({no_auto_import,[F/A]})., which makes all
- calls to the local or imported function without explicit
- module name pass without warnings or errors.</p>
-
- <p>The change makes it possible to add auto-imported BIFs
- without breaking or silently changing old code in the future.
- However some current code ingeniously utilizing the old
- behaviour or the nowarn_bif_clash compiler directive, might
- need changing to be accepted by the compiler.</p>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-8579</p>
- </item>
- <item>
- <p>
- The recently added BIFs erlang:min/2, erlang:max/2 and
- erlang:port_command/3 are now auto-imported (as they were
- originally intended to be). Due to the recent compiler change
- (OTP-8579), the only impact on old code defining it's own
- min/2, max/2 or port_command/3 functions will be a warning,
- the local functions will still be used. The warning can be
- removed by using
- -compile({no_auto_import,[min/2,max/2,port_command/3]}). in
- the source file.</p>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-8669</p>
- </item>
- <item>
- <p>
- Now, binary_to_term/2 is auto-imported. This will cause a
- compile warning if and only if a module has got a local
- function with that name.</p>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-8671</p>
- </item>
- </list>
- </section>
-
- <section>
- <title>Erl_interface</title>
- <list type="bulleted">
- <item>
- <p>Compact IEEE 754 double encoding in external binary format
- for ei</p>
-
- <p>Implement the compact IEEE 754 double encoding in external
- binary format for ei. Encoding for ei now always produces the
- NEW_FLOAT_EXT format. Decoding and term printing handle both
- the old ERL_FLOAT_EXT encoding and the new NEW_FLOAT_EXT
- encoding.</p>
-
- <p>Legacy erl_interface code also handles the new encoding, but
- still produces the ERL_FLOAT_EXT encoding by default.</p>
-
- <p>Also enable the DFLAG_NEW_FLOATS distribution flag.</p>
-
- <p>ei_get_type() will return ERL_FLOAT_EXT regardless if the
- external format is encoded with ERL_FLOAT_EXT or
- NEW_FLOAT_EXT for doubles.</p>
-
- <p>Reduce the number of copies of the code for encoding and
- decoding doubles throughout ei and erl_interface by instead
- calling the ei encoding and decoding functions wherever
- possible.</p>
-
- <p>Restore commented-out float tests in ei_decode_SUITE and
- ei_encode_SUITE in lib/erl_interface/test. Modify them to
- make them match the style of other tests in the same suites.</p>
-
- <p>These changes are based on an ei float patch from Serge
- Aleynikov originally submitted against R12B-2 in July 2008
- and reworked by Steve Vinoski May 2010.</p>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-8684</p>
- </item>
- </list>
- </section>
- <section>
- <title>Debugger and STDLIB</title>
- <list type="bulleted">
- <item>
- <p> The predefined builtin type tid() has been removed. Instead,
- ets:tid() should be used.</p>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-8687</p>
- </item>
- </list>
- </section>
-</chapter>
-