aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator')
-rw-r--r--erts/emulator/beam/beam_bif_load.c12
-rwxr-xr-xerts/emulator/beam/erl_bif_info.c13
-rw-r--r--erts/emulator/beam/erl_debug.c3
-rw-r--r--erts/emulator/beam/erl_gc.c13
-rw-r--r--erts/emulator/beam/erl_process.c251
-rw-r--r--erts/emulator/beam/erl_process.h15
-rwxr-xr-xerts/emulator/beam/global.h5
-rw-r--r--erts/emulator/test/process_SUITE.erl79
8 files changed, 364 insertions, 27 deletions
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index 5239940a50..3f92c5b025 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -495,6 +495,8 @@ BIF_RETTYPE erts_internal_check_process_code_2(BIF_ALIST_2)
res = erts_check_process_code(BIF_P, BIF_ARG_1, allow_gc, &reds);
+ ASSERT(is_value(res));
+
BIF_RET2(res, reds);
badarg:
@@ -784,6 +786,16 @@ check_process_code(Process* rp, Module* modp, int allow_gc, int *redsp)
}
}
+ if (rp->flags & F_DISABLE_GC) {
+ /*
+ * Cannot proceed. Process has disabled gc in order to
+ * safely leave inconsistent data on the heap and/or
+ * off heap lists. Need to wait for gc to be enabled
+ * again.
+ */
+ return THE_NON_VALUE;
+ }
+
/*
* See if there are funs that refer to the old version of the module.
*/
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 673dfc658c..f5893f9291 100755
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -3593,6 +3593,19 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
BIF_RET(am_true);
}
}
+ else if (ERTS_IS_ATOM_STR("gc_state", BIF_ARG_1)) {
+ /* Used by process_SUITE (emulator) */
+ int res, enable;
+
+ switch (BIF_ARG_2) {
+ case am_true: enable = 1; break;
+ case am_false: enable = 0; break;
+ default: BIF_ERROR(BIF_P, BADARG); break;
+ }
+
+ res = erts_set_gc_state(BIF_P, enable);
+ BIF_RET(res ? am_true : am_false);
+ }
else if (ERTS_IS_ATOM_STR("send_fake_exit_signal", BIF_ARG_1)) {
/* Used by signal_SUITE (emulator) */
diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c
index b90d00f236..dc79d45be7 100644
--- a/erts/emulator/beam/erl_debug.c
+++ b/erts/emulator/beam/erl_debug.c
@@ -299,6 +299,9 @@ void erts_check_for_holes(Process* p)
ErlHeapFragment* hf;
Eterm* start;
+ if (p->flags & F_DISABLE_GC)
+ return;
+
start = p->last_htop ? p->last_htop : HEAP_START(p);
check_memory(start, HEAP_TOP(p));
p->last_htop = HEAP_TOP(p);
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index 8ba94d89e9..da254286c4 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -400,10 +400,16 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
Uint reclaimed_now = 0;
int done = 0;
Uint ms1, s1, us1;
- ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ ErtsSchedulerData *esdp;
#ifdef USE_VM_PROBES
DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
#endif
+
+ if (p->flags & F_DISABLE_GC)
+ return 1;
+
+ esdp = erts_get_scheduler_data();
+
if (IS_TRACED_FL(p, F_TRACE_GC)) {
trace_gc(p, am_gc_start);
}
@@ -532,6 +538,9 @@ erts_garbage_collect_hibernate(Process* p)
Uint area_size;
Sint offs;
+ if (p->flags & F_DISABLE_GC)
+ ERTS_INTERNAL_ERROR("GC disabled");
+
/*
* Preliminaries.
*/
@@ -667,6 +676,8 @@ erts_garbage_collect_literals(Process* p, Eterm* literals,
Uint n;
struct erl_off_heap_header** prev;
+ if (p->flags & F_DISABLE_GC)
+ return;
/*
* Set GC state.
*/
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index ad806da660..13b18e9e0e 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -513,6 +513,11 @@ erts_pre_init_process(void)
erts_psd_required_locks[ERTS_PSD_CALL_TIME_BP].set_locks
= ERTS_PSD_CALL_TIME_BP_SET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].get_locks
+ = ERTS_PSD_DELAYED_GC_TASK_QS_GET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].set_locks
+ = ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS;
+
/* Check that we have locks for all entries */
for (ix = 0; ix < ERTS_PSD_SIZE; ix++) {
ERTS_SMP_LC_ASSERT(erts_psd_required_locks[ix].get_locks);
@@ -7033,7 +7038,7 @@ erts_set_process_priority(Process *p, Eterm value)
oprio = ERTS_PSFLGS_GET_USR_PRIO(a);
n = e = a;
- if (!(a & ERTS_PSFLG_ACTIVE_SYS))
+ if (!(a & (ERTS_PSFLG_ACTIVE_SYS|ERTS_PSFLG_DELAYED_SYS)))
aprio = nprio;
else {
int max_qbit;
@@ -7043,7 +7048,15 @@ erts_set_process_priority(Process *p, Eterm value)
slocked = 1;
}
- max_qbit = p->sys_task_qs->qmask;
+ max_qbit = 0;
+ if (a & ERTS_PSFLG_ACTIVE_SYS)
+ max_qbit |= p->sys_task_qs->qmask;
+ if (a & ERTS_PSFLG_DELAYED_SYS) {
+ ErtsProcSysTaskQs *qs;
+ qs = ERTS_PROC_GET_DELAYED_GC_TASK_QS(p);
+ ASSERT(qs);
+ max_qbit |= qs->qmask;
+ }
max_qbit &= -max_qbit;
switch (max_qbit) {
case MAX_BIT:
@@ -7731,12 +7744,14 @@ notify_sys_task_executed(Process *c_p, ErtsProcSysTask *st, Eterm st_result)
}
static ERTS_INLINE ErtsProcSysTask *
-fetch_sys_task(Process *c_p, erts_aint32_t state, int *qmaskp)
+fetch_sys_task(Process *c_p, erts_aint32_t state, int *qmaskp, int *priop)
{
ErtsProcSysTaskQs *unused_qs = NULL;
int qbit, qmask;
ErtsProcSysTask *st, **qp;
+ *priop = -1; /* Shut up annoying erroneous warning */
+
erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_STATUS);
if (!c_p->sys_task_qs) {
@@ -7770,20 +7785,24 @@ fetch_sys_task(Process *c_p, erts_aint32_t state, int *qmaskp)
switch (qbit) {
case MAX_BIT:
qp = &c_p->sys_task_qs->q[PRIORITY_MAX];
+ *priop = PRIORITY_MAX;
break;
case HIGH_BIT:
qp = &c_p->sys_task_qs->q[PRIORITY_HIGH];
+ *priop = PRIORITY_HIGH;
break;
case NORMAL_BIT:
if (!(qmask & PRIORITY_LOW)
|| ++c_p->sys_task_qs->ncount <= RESCHEDULE_LOW) {
qp = &c_p->sys_task_qs->q[PRIORITY_NORMAL];
+ *priop = PRIORITY_NORMAL;
break;
}
c_p->sys_task_qs->ncount = 0;
/* Fall through */
case LOW_BIT:
qp = &c_p->sys_task_qs->q[PRIORITY_LOW];
+ *priop = PRIORITY_LOW;
break;
default:
ERTS_INTERNAL_ERROR("Invalid qmask");
@@ -7807,6 +7826,12 @@ fetch_sys_task(Process *c_p, erts_aint32_t state, int *qmaskp)
qmask2 = qmask;
+ if (state & ERTS_PSFLG_DELAYED_SYS) {
+ ErtsProcSysTaskQs *qs = ERTS_PROC_GET_DELAYED_GC_TASK_QS(c_p);
+ ASSERT(qs);
+ qmask2 |= qs->qmask;
+ }
+
switch (qmask2 & -qmask2) {
case MAX_BIT:
st_prio = PRIORITY_MAX;
@@ -7818,17 +7843,18 @@ fetch_sys_task(Process *c_p, erts_aint32_t state, int *qmaskp)
st_prio = PRIORITY_NORMAL;
break;
case LOW_BIT:
- st_prio = PRIORITY_LOW;
- break;
case 0:
st_prio = PRIORITY_LOW;
- unused_qs = c_p->sys_task_qs;
- c_p->sys_task_qs = NULL;
break;
default:
ERTS_INTERNAL_ERROR("Invalid qmask");
}
+ if (!qmask) {
+ unused_qs = c_p->sys_task_qs;
+ c_p->sys_task_qs = NULL;
+ }
+
a = state;
do {
erts_aint32_t prio = ERTS_PSFLGS_GET_USR_PRIO(a);
@@ -7862,6 +7888,8 @@ done:
return st;
}
+static void save_gc_task(Process *c_p, ErtsProcSysTask *st, int prio);
+
static int
execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
{
@@ -7875,6 +7903,7 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
do {
ErtsProcSysTask *st;
+ int st_prio;
Eterm st_res;
if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) {
@@ -7886,24 +7915,39 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds)
break;
}
- st = fetch_sys_task(c_p, state, &qmask);
+ st = fetch_sys_task(c_p, state, &qmask, &st_prio);
if (!st)
break;
switch (st->type) {
case ERTS_PSTT_GC:
- if (!garbage_collected) {
- FLAGS(c_p) |= F_NEED_FULLSWEEP;
- reds += erts_garbage_collect(c_p, 0, c_p->arg_reg, c_p->arity);
- garbage_collected = 1;
+ if (c_p->flags & F_DISABLE_GC) {
+ save_gc_task(c_p, st, st_prio);
+ st = NULL;
+ reds++;
+ }
+ else {
+ if (!garbage_collected) {
+ FLAGS(c_p) |= F_NEED_FULLSWEEP;
+ reds += erts_garbage_collect(c_p,
+ 0,
+ c_p->arg_reg,
+ c_p->arity);
+ garbage_collected = 1;
+ }
+ st_res = am_true;
}
- st_res = am_true;
break;
case ERTS_PSTT_CPC:
st_res = erts_check_process_code(c_p,
st->arg[0],
st->arg[1] == am_true,
&reds);
+ if (is_non_value(st_res)) {
+ /* Needed gc, but gc was disabled */
+ save_gc_task(c_p, st, st_prio);
+ st = NULL;
+ }
break;
default:
ERTS_INTERNAL_ERROR("Invalid process sys task type");
@@ -7934,8 +7978,9 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds)
do {
ErtsProcSysTask *st;
Eterm st_res;
+ int st_prio;
- st = fetch_sys_task(c_p, state, &qmask);
+ st = fetch_sys_task(c_p, state, &qmask, &st_prio);
if (!st)
break;
@@ -8168,6 +8213,183 @@ badarg:
BIF_ERROR(BIF_P, BADARG);
}
+static void
+save_gc_task(Process *c_p, ErtsProcSysTask *st, int prio)
+{
+ erts_aint32_t state;
+ ErtsProcSysTaskQs *qs;
+
+ ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p));
+
+ qs = ERTS_PROC_GET_DELAYED_GC_TASK_QS(c_p);
+ if (!qs) {
+ st->next = st->prev = st;
+ qs = proc_sys_task_queues_alloc();
+ qs->qmask = 1 << prio;
+ qs->ncount = 0;
+ qs->q[PRIORITY_MAX] = NULL;
+ qs->q[PRIORITY_HIGH] = NULL;
+ qs->q[PRIORITY_NORMAL] = NULL;
+ qs->q[PRIORITY_LOW] = NULL;
+ qs->q[prio] = st;
+ (void) ERTS_PROC_SET_DELAYED_GC_TASK_QS(c_p, ERTS_PROC_LOCK_MAIN, qs);
+ }
+ else {
+ if (!qs->q[prio]) {
+ st->next = st->prev = st;
+ qs->q[prio] = st;
+ qs->qmask |= 1 << prio;
+ }
+ else {
+ st->next = qs->q[prio];
+ st->prev = qs->q[prio]->prev;
+ st->next->prev = st;
+ st->prev->next = st;
+ ASSERT(qs->qmask & (1 << prio));
+ }
+ }
+
+ state = erts_smp_atomic32_read_nob(&c_p->state);
+ ASSERT((ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS) & state);
+
+ while (!(state & ERTS_PSFLG_DELAYED_SYS)
+ || prio < ERTS_PSFLGS_GET_ACT_PRIO(state)) {
+ erts_aint32_t n, e;
+
+ n = e = state;
+ n |= ERTS_PSFLG_DELAYED_SYS;
+ if (prio < ERTS_PSFLGS_GET_ACT_PRIO(state)) {
+ n &= ~ERTS_PSFLGS_ACT_PRIO_MASK;
+ n |= prio << ERTS_PSFLGS_ACT_PRIO_OFFSET;
+ }
+ state = erts_smp_atomic32_cmpxchg_relb(&c_p->state, n, e);
+ if (state == e)
+ break;
+ }
+}
+
+int
+erts_set_gc_state(Process *c_p, int enable)
+{
+ int res;
+ ErtsProcSysTaskQs *dgc_tsk_qs;
+ ASSERT(c_p == erts_get_current_process());
+ ASSERT((ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS)
+ & erts_smp_atomic32_read_nob(&c_p->state));
+ ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p));
+
+ res = !(c_p->flags & F_DISABLE_GC);
+
+ if (!enable) {
+ c_p->flags |= F_DISABLE_GC;
+ return res;
+ }
+
+ c_p->flags &= ~F_DISABLE_GC;
+
+ dgc_tsk_qs = ERTS_PROC_GET_DELAYED_GC_TASK_QS(c_p);
+ if (!dgc_tsk_qs)
+ return res;
+
+ /* Move delayed gc tasks into sys tasks queues. */
+
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_STATUS);
+
+ if (!c_p->sys_task_qs) {
+ c_p->sys_task_qs = dgc_tsk_qs;
+ dgc_tsk_qs = NULL;
+ }
+ else {
+ ErtsProcSysTaskQs *stsk_qs;
+ int prio;
+
+ /*
+ * We push delayed tasks to the front of the queue
+ * since they have already made it to the front
+ * once and then been delayed after that.
+ */
+
+ stsk_qs = c_p->sys_task_qs;
+
+ while (dgc_tsk_qs->qmask) {
+ int qbit = dgc_tsk_qs->qmask & -dgc_tsk_qs->qmask;
+ dgc_tsk_qs->qmask &= ~qbit;
+ switch (qbit) {
+ case MAX_BIT:
+ prio = PRIORITY_MAX;
+ break;
+ case HIGH_BIT:
+ prio = PRIORITY_HIGH;
+ break;
+ case NORMAL_BIT:
+ prio = PRIORITY_NORMAL;
+ break;
+ case LOW_BIT:
+ prio = PRIORITY_LOW;
+ break;
+ default:
+ ERTS_INTERNAL_ERROR("Invalid qmask");
+ prio = -1;
+ break;
+ }
+
+ ASSERT(dgc_tsk_qs->q[prio]);
+
+ if (!stsk_qs->q[prio]) {
+ stsk_qs->q[prio] = dgc_tsk_qs->q[prio];
+ stsk_qs->qmask |= 1 << prio;
+ }
+ else {
+ ErtsProcSysTask *first1, *last1, *first2, *last2;
+
+ ASSERT(stsk_qs->qmask & (1 << prio));
+ first1 = dgc_tsk_qs->q[prio];
+ last1 = first1->prev;
+ first2 = stsk_qs->q[prio];
+ last2 = first1->prev;
+
+ last1->next = first2;
+ first2->prev = last1;
+
+ first1->prev = last2;
+ last2->next = first1;
+
+ stsk_qs->q[prio] = first1;
+ }
+
+ }
+ }
+
+#ifdef DEBUG
+ {
+ int qmask;
+ erts_aint32_t aprio, state =
+#endif
+
+ erts_smp_atomic32_read_bset_nob(&c_p->state,
+ (ERTS_PSFLG_DELAYED_SYS
+ | ERTS_PSFLG_ACTIVE_SYS),
+ ERTS_PSFLG_ACTIVE_SYS);
+
+#ifdef DEBUG
+ ASSERT(state & ERTS_PSFLG_DELAYED_SYS);
+ qmask = c_p->sys_task_qs->qmask;
+ aprio = ERTS_PSFLGS_GET_ACT_PRIO(state);
+ ASSERT(ERTS_PSFLGS_GET_USR_PRIO(state) >= aprio);
+ ASSERT((qmask & -qmask) >= (1 << aprio));
+ }
+#endif
+
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_STATUS);
+
+ (void) ERTS_PROC_SET_DELAYED_GC_TASK_QS(c_p, ERTS_PROC_LOCK_MAIN, NULL);
+
+ if (dgc_tsk_qs)
+ proc_sys_task_queues_free(dgc_tsk_qs);
+
+ return res;
+}
+
void
erts_sched_stat_modify(int what)
{
@@ -9839,6 +10061,7 @@ erts_continue_exit_process(Process *p)
p->flags &= ~F_USING_DB;
}
+ erts_set_gc_state(p, 1);
state = erts_smp_atomic32_read_acqb(&p->state);
if (state & ERTS_PSFLG_ACTIVE_SYS) {
if (cleanup_sys_tasks(p, state, CONTEXT_REDS) >= CONTEXT_REDS/2)
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 814e3d34df..0b8d2976ed 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -631,8 +631,9 @@ erts_smp_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi)
#define ERTS_PSD_SCHED_ID 2
#define ERTS_PSD_DIST_ENTRY 3
#define ERTS_PSD_CALL_TIME_BP 4
+#define ERTS_PSD_DELAYED_GC_TASK_QS 5
-#define ERTS_PSD_SIZE 5
+#define ERTS_PSD_SIZE 6
typedef struct {
void *data[ERTS_PSD_SIZE];
@@ -656,6 +657,9 @@ typedef struct {
#define ERTS_PSD_CALL_TIME_BP_GET_LOCKS ERTS_PROC_LOCK_MAIN
#define ERTS_PSD_CALL_TIME_BP_SET_LOCKS ERTS_PROC_LOCK_MAIN
+#define ERTS_PSD_DELAYED_GC_TASK_QS_GET_LOCKS ERTS_PROC_LOCK_MAIN
+#define ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS ERTS_PROC_LOCK_MAIN
+
typedef struct {
ErtsProcLocks get_locks;
ErtsProcLocks set_locks;
@@ -859,6 +863,7 @@ struct process {
Uint64 bin_old_vheap; /* Virtual old heap size for binaries */
ErtsProcSysTaskQs *sys_task_qs;
+
erts_smp_atomic32_t state; /* Process state flags (see ERTS_PSFLG_*) */
#ifdef ERTS_SMP
@@ -976,6 +981,7 @@ void erts_check_for_holes(Process* p);
#define ERTS_PSFLG_ACTIVE_SYS ERTS_PSFLG_BIT(14)
#define ERTS_PSFLG_RUNNING_SYS ERTS_PSFLG_BIT(15)
#define ERTS_PSFLG_PROXY ERTS_PSFLG_BIT(16)
+#define ERTS_PSFLG_DELAYED_SYS ERTS_PSFLG_BIT(17)
#define ERTS_PSFLGS_IN_PRQ_MASK (ERTS_PSFLG_IN_PRQ_MAX \
| ERTS_PSFLG_IN_PRQ_HIGH \
@@ -1102,6 +1108,7 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags;
#define F_HAVE_BLCKD_MSCHED (1 << 8) /* Process has blocked multi-scheduling */
#define F_P2PNR_RESCHED (1 << 9) /* Process has been rescheduled via erts_pid2proc_not_running() */
#define F_FORCE_GC (1 << 10) /* Force gc at process in-scheduling */
+#define F_DISABLE_GC (1 << 11) /* Disable GC */
/* process trace_flags */
#define F_SENSITIVE (1 << 0)
@@ -1192,6 +1199,7 @@ void erts_late_init_process(void);
void erts_early_init_scheduling(int);
void erts_init_scheduling(int, int);
+int erts_set_gc_state(Process *c_p, int enable);
Eterm erts_sched_wall_time_request(Process *c_p, int set, int enable);
Eterm erts_gc_info_request(Process *c_p);
Uint64 erts_get_proc_interval(void);
@@ -1637,6 +1645,11 @@ erts_psd_set(Process *p, ErtsProcLocks plocks, int ix, void *data)
#define ERTS_PROC_SET_CALL_TIME(P, L, PBT) \
((process_breakpoint_time_t *) erts_psd_set((P), (L), ERTS_PSD_CALL_TIME_BP, (void *) (PBT)))
+#define ERTS_PROC_GET_DELAYED_GC_TASK_QS(P) \
+ ((ErtsProcSysTaskQs *) erts_psd_get((P), ERTS_PSD_DELAYED_GC_TASK_QS))
+#define ERTS_PROC_SET_DELAYED_GC_TASK_QS(P, L, PBT) \
+ ((ErtsProcSysTaskQs *) erts_psd_set((P), (L), ERTS_PSD_DELAYED_GC_TASK_QS, (void *) (PBT)))
+
ERTS_GLB_INLINE Eterm erts_proc_get_error_handler(Process *p);
ERTS_GLB_INLINE Eterm erts_proc_set_error_handler(Process *p,
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 885f092aca..6b720b53d8 100755
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -1114,7 +1114,12 @@ erts_alloc_message_heap_state(Uint size,
if (statep)
*statep = state;
if ((state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT))
+ || (receiver->flags & F_DISABLE_GC)
|| HEAP_LIMIT(receiver) - HEAP_TOP(receiver) <= size) {
+ /*
+ * The heap is either potentially in an inconsistent
+ * state, or not large enough.
+ */
#ifdef ERTS_SMP
if (locked_main) {
*receiver_locks &= ~ERTS_PROC_LOCK_MAIN;
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index e66c6f09b6..bf31655066 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -55,7 +55,9 @@
no_priority_inversion/1,
no_priority_inversion2/1,
system_task_blast/1,
- system_task_on_suspended/1]).
+ system_task_on_suspended/1,
+ gc_request_when_gc_disabled/1,
+ gc_request_blast_when_gc_disabled/1]).
-export([prio_server/2, prio_client/2]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -95,7 +97,8 @@ groups() ->
otp_7738_resume]},
{system_task, [],
[no_priority_inversion, no_priority_inversion2,
- system_task_blast, system_task_on_suspended]}].
+ system_task_blast, system_task_on_suspended,
+ gc_request_when_gc_disabled, gc_request_blast_when_gc_disabled]}].
init_per_suite(Config) ->
A0 = case application:start(sasl) of
@@ -2361,15 +2364,69 @@ system_task_on_suspended(Config) when is_list(Config) ->
ok
end.
-gc_req(_Pid, 0) ->
- [];
-gc_req(Pid, N) ->
- R0 = erts_internal:request_system_task(Pid, low, garbage_collect),
- R1 = erts_internal:request_system_task(Pid, normal, garbage_collect),
- R2 = erts_internal:request_system_task(Pid, high, garbage_collect),
- R3 = erts_internal:request_system_task(Pid, max, garbage_collect),
- [R0, R1, R2, R3 | gc_req(Pid, N-1)].
-
+gc_request_when_gc_disabled(Config) when is_list(Config) ->
+ Master = self(),
+ AIS = erts_debug:set_internal_state(available_internal_state, true),
+ {P, M} = spawn_opt(fun () ->
+ true = erts_debug:set_internal_state(gc_state,
+ false),
+ Master ! {self(), gc_state, false},
+ receive after 1000 -> ok end,
+ Master ! {self(), gc_state, true},
+ false = erts_debug:set_internal_state(gc_state,
+ true),
+ receive after 100 -> ok end
+ end, [monitor, link]),
+ receive {P, gc_state, false} -> ok end,
+ ReqId = make_ref(),
+ async = garbage_collect(P, [{async, ReqId}]),
+ receive
+ {garbage_collect, ReqId, Result} ->
+ ?t:fail({unexpected_gc, Result});
+ {P, gc_state, true} ->
+ ok
+ end,
+ receive {garbage_collect, ReqId, true} -> ok end,
+ erts_debug:set_internal_state(available_internal_state, AIS),
+ receive {'DOWN', M, process, P, _Reason} -> ok end,
+ ok.
+
+gc_request_blast_when_gc_disabled(Config) when is_list(Config) ->
+ Master = self(),
+ AIS = erts_debug:set_internal_state(available_internal_state, true),
+ {P, M} = spawn_opt(fun () ->
+ true = erts_debug:set_internal_state(gc_state,
+ false),
+ Master ! {self(), gc_state, false},
+ receive after 1000 -> ok end,
+ false = erts_debug:set_internal_state(gc_state,
+ true),
+ receive after 100 -> ok end
+ end, [monitor, link]),
+ receive {P, gc_state, false} -> ok end,
+ PMs = lists:map(fun (N) ->
+ Prio = case N rem 4 of
+ 0 -> max;
+ 1 -> high;
+ 2 -> normal;
+ 3 -> low
+ end,
+ spawn_opt(fun () ->
+ erlang:garbage_collect(P)
+ end, [monitor, link, {priority, Prio}])
+ end, lists:seq(1, 10000)),
+ lists:foreach(fun ({Proc, Mon}) ->
+ receive
+ {'DOWN', Mon, process, Proc, normal} ->
+ ok
+ end
+ end,
+ PMs),
+ erts_debug:set_internal_state(available_internal_state, AIS),
+ receive {'DOWN', M, process, P, _Reason} -> ok end,
+ ok.
+
+
%% Internal functions
wait_until(Fun) ->