diff options
Diffstat (limited to 'erts/emulator/beam')
-rw-r--r-- | erts/emulator/beam/atom.names | 1 | ||||
-rw-r--r-- | erts/emulator/beam/beam_bif_load.c | 493 | ||||
-rw-r--r-- | erts/emulator/beam/erl_alloc.types | 1 | ||||
-rw-r--r-- | erts/emulator/beam/erl_process.c | 65 | ||||
-rw-r--r-- | erts/emulator/beam/global.h | 3 |
5 files changed, 480 insertions, 83 deletions
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 7b12e5432d..348d75303d 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -698,6 +698,7 @@ atom values atom version atom visible atom wait +atom wait_release_literal_area_switch atom waiting atom wall_clock atom warning diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 023ee3ef4b..d7e74cd622 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -68,23 +68,6 @@ Process *erts_code_purger = NULL; #ifdef ERTS_DIRTY_SCHEDULERS Process *erts_dirty_process_code_checker; #endif -erts_smp_atomic_t erts_copy_literal_area__; -#define ERTS_SET_COPY_LITERAL_AREA(LA) \ - erts_smp_atomic_set_nob(&erts_copy_literal_area__, \ - (erts_aint_t) (LA)) -Process *erts_literal_area_collector = NULL; - -typedef struct ErtsLiteralAreaRef_ ErtsLiteralAreaRef; -struct ErtsLiteralAreaRef_ { - ErtsLiteralAreaRef *next; - ErtsLiteralArea *literal_area; -}; - -struct { - erts_smp_mtx_t mtx; - ErtsLiteralAreaRef *first; - ErtsLiteralAreaRef *last; -} release_literal_areas; static void set_default_trace_pattern(Eterm module); static Eterm check_process_code(Process* rp, Module* modp, int *redsp, int fcalls); @@ -116,17 +99,13 @@ init_purge_state(void) purge_state.saved_old.code_hdr = 0; } +static void +init_release_literal_areas(void); + void erts_beam_bif_load_init(void) { - erts_smp_mtx_init(&release_literal_areas.mtx, "release_literal_areas", NIL, - ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC); - - release_literal_areas.first = NULL; - release_literal_areas.last = NULL; - erts_smp_atomic_init_nob(&erts_copy_literal_area__, - (erts_aint_t) NULL); - + init_release_literal_areas(); init_purge_state(); } @@ -1314,6 +1293,68 @@ hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp, } } +/* + * Release of literal areas... + * + * Overview over how literal areas are released. + * + * - A literal area to remove is placed in the release_literal_areas.first + * queue. + * - The erts_literal_area_collector process is woken and calls + * erts_internal:release_literal_area_switch() which publishes the + * area to release available to the emulator + * (ERTS_COPY_LITERAL_AREA()). + * - The literal area collector process gets suspended waiting thread + * progress in order to ensure all schedulers see the newly published + * area to release. + * - When the literal area collector process is resumed after thread + * progress has completed, erts_internal:release_literal_area_switch() + * returns 'true'. + * - The literal area collector process sends copy-literals requests + * to all processes in the system. + * - Processes inspects their heap for literals in the area, if + * such are found do a literal-gc to make copies on the heap + * of all those literals, and then send replies to the + * literal area collector process. + * - Processes that terminates replies even though they might need to + * access literal areas. When a process that might need to access a + * literal area terminates, it blocks release of literal areas + * by incrementing a counter, and later when termination has + * completed decrements that counter. The increment is performed + * before replying to the copy-literals request. + * - When all processes has responded, the literal area collector + * process calls erts_internal:release_literal_area_switch() again + * in order to switch to the next area. + * - erts_internal:release_literal_area_switch() changes the set of + * counters that blocks release of literal areas + * - The literal area collector process gets suspended waiting thread + * progress in order to ensure that the change of counters is visable + * by all schedulers. + * - When the literal area collector process is resumed after thread + * progress has completed, erts_internal:release_literal_area_switch() + * inspects all counters in previously used set ensuring that no + * terminating processes (which began termination before the change + * of counters) are lingering. If needed the literal area collector + * process will be blocked in + * erts_internal:release_literal_area_switch() waiting for all + * terminating processes to complete. + * - When counter inspection is complete + * erts_internal:release_literal_area_switch() returns 'true' if + * a new area was set for release and 'false' if no more areas have + * been scheduled for release. + * + * When multiple literal areas have been queued for release, + * erts_internal:release_literal_area_switch() will time the thread + * progress waits so each wait period will be utilized both for + * ensuring that a new area is seen by all schedulers, and ensuring + * that a change of counters is seen by all schedulers. By this only + * one thread progress wait will be done per literal area collected + * until the last literal area which will need two thread progress + * wait periods. + */ + +static Export *wait_release_literal_area_switch; + #ifdef ERTS_SMP ErtsThrPrgrLaterOp later_literal_area_switch; @@ -1323,87 +1364,385 @@ typedef struct { ErtsLiteralArea *la; } ErtsLaterReleasLiteralArea; +#endif + +erts_smp_atomic_t erts_copy_literal_area__; +#define ERTS_SET_COPY_LITERAL_AREA(LA) \ + erts_smp_atomic_set_nob(&erts_copy_literal_area__, \ + (erts_aint_t) (LA)) +Process *erts_literal_area_collector = NULL; + +typedef struct ErtsLiteralAreaRef_ ErtsLiteralAreaRef; +struct ErtsLiteralAreaRef_ { + ErtsLiteralAreaRef *next; + ErtsLiteralArea *literal_area; +}; + +typedef struct { + erts_smp_atomic_t counter[2]; +} ErtsReleaseLiteralAreaBlockCounters; + +typedef struct { + union { + ErtsReleaseLiteralAreaBlockCounters block; + char align__[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(ErtsReleaseLiteralAreaBlockCounters))]; + } u; +} ErtsAlignedReleaseLiteralAreaBlockCounters; + +typedef enum { + ERTS_RLA_BLOCK_STATE_NONE, + ERTS_RLA_BLOCK_STATE_SWITCHED_IX, + ERTS_RLA_BLOCK_STATE_WAITING +} ErtsReleaseLiteralAreaBlockState; + +static struct { + erts_smp_mtx_t mtx; + ErtsLiteralAreaRef *first; + ErtsLiteralAreaRef *last; + ErtsAlignedReleaseLiteralAreaBlockCounters *bc; + erts_smp_atomic32_t block_ix; + int wait_sched_ix; + ErtsReleaseLiteralAreaBlockState block_state; + ErtsLiteralArea *block_area; +} release_literal_areas; + static void -later_release_literal_area(void *vlrlap) +init_release_literal_areas(void) { - ErtsLaterReleasLiteralArea *lrlap; - lrlap = (ErtsLaterReleasLiteralArea *) vlrlap; - erts_release_literal_area(lrlap->la); - erts_free(ERTS_ALC_T_RELEASE_LAREA, vlrlap); + int i; + erts_smp_mtx_init(&release_literal_areas.mtx, "release_literal_areas", NIL, + ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC); + + release_literal_areas.first = NULL; + release_literal_areas.last = NULL; + erts_smp_atomic_init_nob(&erts_copy_literal_area__, + (erts_aint_t) NULL); + + erts_smp_atomic32_init_nob(&release_literal_areas.block_ix, 0); + release_literal_areas.wait_sched_ix = 0; + release_literal_areas.block_state = ERTS_RLA_BLOCK_STATE_NONE; + release_literal_areas.block_area = NULL; + + release_literal_areas.bc = + erts_alloc_permanent_cache_aligned(ERTS_ALC_T_RLA_BLOCK_CNTRS, + sizeof(ErtsAlignedReleaseLiteralAreaBlockCounters) + * erts_no_schedulers); + /* + * The literal-area-collector has an increment in all block counters + * which it only removes when waiting for other increments to disappear. + */ + for (i = 0; i < erts_no_schedulers; i++) { + erts_smp_atomic_init_nob(&release_literal_areas.bc[i].u.block.counter[0], 1); + erts_smp_atomic_init_nob(&release_literal_areas.bc[i].u.block.counter[1], 1); + } + + wait_release_literal_area_switch = erts_export_put(am_erts_internal, + am_wait_release_literal_area_switch, + 1); } +#ifdef ERTS_SMP static void -complete_literal_area_switch(void *literal_area) +rla_resume(void *literal_area) { - Process *p = erts_literal_area_collector; - erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); - erts_resume(p, ERTS_PROC_LOCK_STATUS); - erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); - if (literal_area) - erts_release_literal_area((ErtsLiteralArea *) literal_area); + erts_resume(erts_literal_area_collector, 0); } #endif -BIF_RETTYPE erts_internal_release_literal_area_switch_0(BIF_ALIST_0) + +static ERTS_INLINE Sint +rla_bc_read(int sched_ix, int block_ix) { - ErtsLiteralArea *unused_la; - ErtsLiteralAreaRef *la_ref; + return (Sint) erts_smp_atomic_read_nob( + &release_literal_areas.bc[sched_ix].u.block.counter[block_ix]); +} - if (BIF_P != erts_literal_area_collector) - BIF_ERROR(BIF_P, EXC_NOTSUP); +static ERTS_INLINE Sint +rla_bc_read_acqb(int sched_ix, int block_ix) +{ + return (Sint) erts_smp_atomic_read_acqb( + &release_literal_areas.bc[sched_ix].u.block.counter[block_ix]); +} - erts_smp_mtx_lock(&release_literal_areas.mtx); +static ERTS_INLINE Sint +rla_bc_dec_read_acqb(int sched_ix, int block_ix) +{ + return (Sint) erts_smp_atomic_dec_read_acqb( + &release_literal_areas.bc[sched_ix].u.block.counter[block_ix]); +} + +static ERTS_INLINE Sint +rla_bc_dec_read_relb(int sched_ix, int block_ix) +{ + return (Sint) erts_smp_atomic_dec_read_relb( + &release_literal_areas.bc[sched_ix].u.block.counter[block_ix]); +} + +static ERTS_INLINE void +rla_bc_inc(int sched_ix, int block_ix) +{ + erts_smp_atomic_inc_nob( + &release_literal_areas.bc[sched_ix].u.block.counter[block_ix]); +} + + +Uint32 +erts_block_release_literal_area(void) +{ + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + int sched_ix; + int block_ix; + + ASSERT(esdp->type == ERTS_SCHED_NORMAL); + + sched_ix = ((int) esdp->no) - 1; + ASSERT((sched_ix & ~0xffff) == 0); + + ASSERT(0 <= sched_ix && sched_ix <= erts_no_schedulers); + + block_ix = (int) erts_smp_atomic32_read_nob(&release_literal_areas.block_ix); + ASSERT(block_ix == 0 || block_ix == 1); + + rla_bc_inc(sched_ix, block_ix); + /* + * The returned value needs to be non-zero, so the user can + * use zero as a marker for not having blocked. + * + * Both block_ix and sched_ix can be zero so we set + * the highest (unused) bits to 0xfed00000 + */ + return (Uint32) 0xfed00000 | ((block_ix << 16) | sched_ix); +} + +static void +wakeup_literal_area_collector(void *unused) +{ + erts_queue_message(erts_literal_area_collector, + 0, + erts_alloc_message(0, NULL), + am_copy_literals, + am_system); +} + +void +erts_unblock_release_literal_area(Uint32 sched_block_ix) +{ + Sint block_count; + int block_ix = (int) ((sched_block_ix >> 16) & 0xf); + int sched_ix = (int) (sched_block_ix & 0xffff); + + ASSERT((sched_block_ix & ((Uint32) 0xfff00000)) + == (Uint32) 0xfed00000); + + ASSERT(block_ix == 0 || block_ix == 1); + + block_count = rla_bc_dec_read_relb(sched_ix, block_ix); + + ASSERT(block_count >= 0); + + if (!block_count) { + /* + * Wakeup literal collector so it can continue... + * + * We don't know what locks we have here, so schedule + * the operation... + */ + int sid = 1; + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + if (esdp && esdp->type == ERTS_SCHED_NORMAL) + sid = (int) esdp->no; + erts_schedule_misc_aux_work(sid, + wakeup_literal_area_collector, + NULL); + } +} + +static void +rla_switch_area(void) +{ + ErtsLiteralAreaRef *la_ref; + + erts_smp_mtx_lock(&release_literal_areas.mtx); la_ref = release_literal_areas.first; if (la_ref) { release_literal_areas.first = la_ref->next; if (!release_literal_areas.first) release_literal_areas.last = NULL; } - erts_smp_mtx_unlock(&release_literal_areas.mtx); - unused_la = ERTS_COPY_LITERAL_AREA(); + if (!la_ref) + ERTS_SET_COPY_LITERAL_AREA(NULL); + else { + ERTS_SET_COPY_LITERAL_AREA(la_ref->literal_area); + erts_free(ERTS_ALC_T_LITERAL_REF, la_ref); + } +} + +BIF_RETTYPE erts_internal_release_literal_area_switch_0(BIF_ALIST_0) +{ + ErtsLiteralArea *new_area, *old_area; + int wait_ix = 0; + int sched_ix = 0; + + if (BIF_P != erts_literal_area_collector) + BIF_ERROR(BIF_P, EXC_NOTSUP); + + while (1) { + int six; + + switch (release_literal_areas.block_state) { + case ERTS_RLA_BLOCK_STATE_NONE: { + + old_area = ERTS_COPY_LITERAL_AREA(); + + rla_switch_area(); + + if (old_area) { + int block_ix; + /* + * Switch block index. + */ + block_ix = (int) erts_smp_atomic32_read_nob(&release_literal_areas.block_ix); + erts_smp_atomic32_set_nob(&release_literal_areas.block_ix, + (erts_aint32_t) !block_ix); + release_literal_areas.block_state = ERTS_RLA_BLOCK_STATE_SWITCHED_IX; + ASSERT(!release_literal_areas.block_area); + release_literal_areas.block_area = old_area; + } + + new_area = ERTS_COPY_LITERAL_AREA(); - if (!la_ref) { - ERTS_SET_COPY_LITERAL_AREA(NULL); - if (unused_la) { -#ifdef ERTS_SMP - ErtsLaterReleasLiteralArea *lrlap; - lrlap = erts_alloc(ERTS_ALC_T_RELEASE_LAREA, - sizeof(ErtsLaterReleasLiteralArea)); - lrlap->la = unused_la; - erts_schedule_thr_prgr_later_cleanup_op( - later_release_literal_area, - (void *) lrlap, - &lrlap->lop, - (sizeof(ErtsLaterReleasLiteralArea) - + sizeof(ErtsLiteralArea) - + ((unused_la->end - - &unused_la->start[0]) - - 1)*(sizeof(Eterm)))); + if (!old_area && !new_area) + BIF_RET(am_false); + + publish_new_info: + +#ifndef ERTS_SMP + if (new_area) + BIF_RET(am_true); + /* fall through... */ #else - erts_release_literal_area(unused_la); + /* + * Waiting 'thread progress' will ensure that all schedulers are + * guaranteed to see the new block index and the new area before + * we continue... + */ + erts_schedule_thr_prgr_later_op(rla_resume, + NULL, + &later_literal_area_switch); + erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL); + if (new_area) { + /* + * If we also got a new block_area, we will + * take care of that the next time we come back + * after all processes has responded on + * copy-literals requests... + */ + ERTS_BIF_YIELD_RETURN(BIF_P, + am_true); + } + + ASSERT(old_area); + ERTS_VBUMP_ALL_REDS(BIF_P); + BIF_TRAP0(BIF_P, + bif_export[BIF_erts_internal_release_literal_area_switch_0]); #endif - } - BIF_RET(am_false); - } + } - ERTS_SET_COPY_LITERAL_AREA(la_ref->literal_area); + case ERTS_RLA_BLOCK_STATE_SWITCHED_IX: + wait_ix = !erts_smp_atomic32_read_nob(&release_literal_areas.block_ix); + /* + * Now all counters in the old index will monotonically + * decrease towards 1 (our own increment). Check that we + * have no other increments, than our own, in all counters + * of the old block index. Wait for other increments to + * be decremented if necessary... + */ + sched_ix = 0; + break; + + case ERTS_RLA_BLOCK_STATE_WAITING: + wait_ix = !erts_smp_atomic32_read_nob(&release_literal_areas.block_ix); + /* + * Woken after being waiting for a counter to reach zero... + */ + sched_ix = release_literal_areas.wait_sched_ix; + /* restore "our own increment" */ + rla_bc_inc(sched_ix, wait_ix); + break; + } - erts_free(ERTS_ALC_T_LITERAL_REF, la_ref); + ASSERT(0 <= sched_ix && sched_ix < erts_no_schedulers); -#ifdef ERTS_SMP - erts_schedule_thr_prgr_later_op(complete_literal_area_switch, - unused_la, - &later_literal_area_switch); - erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL); - ERTS_BIF_YIELD_RETURN(BIF_P, am_true); -#else - erts_release_literal_area(unused_la); - BIF_RET(am_true); +#ifdef DEBUG + for (six = 0; six < sched_ix; six++) { + ASSERT(1 == rla_bc_read(six, wait_ix)); + } #endif + for (six = sched_ix; six < erts_no_schedulers; six++) { + Sint block_count = rla_bc_read_acqb(six, wait_ix); + ASSERT(block_count >= 1); + if (block_count == 1) + continue; + + block_count = rla_bc_dec_read_acqb(six, wait_ix); + if (!block_count) { + /* + * We brought it down to zero ourselves, so no need to wait. + * Since the counter is guaranteed to be monotonically + * decreasing (disregarding our own operations) it is safe + * to continue. Restore "our increment" in preparation for + * next switch. + */ + rla_bc_inc(six, wait_ix); + continue; + } + + /* + * Wait for counter to be brought down to zero. The one bringing + * the counter down to zero will wake us up. We might also be + * woken later in erts_internal:wait_release_literal_area_switch() + * if a new area appears (handled here below). + */ + release_literal_areas.wait_sched_ix = six; + release_literal_areas.block_state = ERTS_RLA_BLOCK_STATE_WAITING; + if (!ERTS_COPY_LITERAL_AREA()) { + rla_switch_area(); + new_area = ERTS_COPY_LITERAL_AREA(); + if (new_area) { + /* + * A new area showed up. Start the work with that area + * and come back and check block counters when that has + * been handled. + */ + old_area = release_literal_areas.block_area; + goto publish_new_info; + } + } + + /* + * Wait for block_counter to reach zero or a new literal area + * to handle... + */ + BIF_TRAP1(wait_release_literal_area_switch, BIF_P, am_copy_literals); + } + + /* Done checking all block counters, release the literal area... */ + + release_literal_areas.block_state = ERTS_RLA_BLOCK_STATE_NONE; + erts_release_literal_area(release_literal_areas.block_area); + release_literal_areas.block_area = NULL; + +#ifdef DEBUG + /* All counters should be at 1; ready for next switch... */ + for (six = 0; six < erts_no_schedulers; six++) { + ASSERT(1 == rla_bc_read(six, wait_ix)); + } +#endif + } } void diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index af1133b853..7156a8e47d 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -286,6 +286,7 @@ type MREF_TAB_BKTS STANDARD SYSTEM magic_ref_table_buckets type MREF_TAB LONG_LIVED SYSTEM magic_ref_table type MINDIRECTION FIXED_SIZE SYSTEM magic_indirection type CRASH_DUMP STANDARD SYSTEM crash_dump +type RLA_BLOCK_CNTRS LONG_LIVED SYSTEM release_literal_area_block_counters +if threads_no_smp # Need thread safe allocs, but std_alloc and fix_alloc are not; diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index be94418755..cd95ef1a8a 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -11748,6 +11748,7 @@ request_system_task(Process *c_p, Eterm requester, Eterm target, goto badarg; st->type = ERTS_PSTT_CLA; noproc_res = am_ok; + fail_state = ERTS_PSFLG_FREE; if (!rp) goto noproc; break; @@ -11758,7 +11759,7 @@ request_system_task(Process *c_p, Eterm requester, Eterm target, if (!schedule_process_sys_task(rp, prio, st, &fail_state)) { Eterm failure; - if (fail_state & ERTS_PSFLG_EXITING) { + if (fail_state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_FREE)) { noproc: failure = noproc_res; } @@ -12960,6 +12961,7 @@ delete_process(Process* p) ErtsPSD *psd; struct saved_calls *scb; process_breakpoint_time_t *pbt; + Uint32 block_rla_ref = (Uint32) (Uint) p->u.terminate; VERBOSE(DEBUG_PROCESSES, ("Removing process: %T\n",p->common.id)); VERBOSE(DEBUG_SHCOPY, ("[pid=%T] delete process: %p %p %p %p\n", p->common.id, @@ -13031,6 +13033,9 @@ delete_process(Process* p) ASSERT(!p->suspend_monitors); p->fvalue = NIL; + + if (block_rla_ref) + erts_unblock_release_literal_area(block_rla_ref); } static ERTS_INLINE void @@ -13977,6 +13982,7 @@ erts_continue_exit_process(Process *p) } ASSERT(erts_proc_read_refc(p) > 0); p->bif_timers = NULL; + ASSERT(!p->u.terminate); } #ifdef ERTS_SMP @@ -14002,7 +14008,9 @@ erts_continue_exit_process(Process *p) erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error: %d\n", __FILE__, __LINE__, (int) ssr); } + ASSERT(!p->u.terminate); } + if (p->flags & F_HAVE_BLCKD_NMSCHED) { ErtsSchedSuspendResult ssr; ssr = erts_block_multi_scheduling(p, ERTS_PROC_LOCK_MAIN, 0, 1, 1); @@ -14022,6 +14030,7 @@ erts_continue_exit_process(Process *p) erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error: %d\n", __FILE__, __LINE__, (int) ssr); } + ASSERT(!p->u.terminate); } #endif @@ -14029,10 +14038,33 @@ erts_continue_exit_process(Process *p) if (erts_db_process_exiting(p, ERTS_PROC_LOCK_MAIN)) goto yield; p->flags &= ~F_USING_DB; + ASSERT(!p->u.terminate); } - erts_set_gc_state(p, 1); state = erts_smp_atomic32_read_acqb(&p->state); + /* + * If we might access any literals on the heap after this point, + * we need to block release of literal areas. After this point, + * since cleanup of sys-tasks reply to copy-literals requests. + * Note that we do not only have to prevent release of + * currently processed literal area, but also future processed + * literal areas, until we are guaranteed not to access any + * literal areas at all. + * + * - A non-immediate exit reason may refer to literals. + * - A process executing dirty while terminated, might access + * any term on the heap, and therfore literals, until it has + * stopped executing dirty. + */ + if (!p->u.terminate + && (is_not_immed(reason) + || (state & (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)))) { + Uint32 block_rla_ref = erts_block_release_literal_area(); + p->u.terminate = (void *) (Uint) block_rla_ref; + } + + erts_set_gc_state(p, 1); if (state & ERTS_PSFLG_ACTIVE_SYS #ifdef ERTS_DIRTY_SCHEDULERS || p->dirty_sys_tasks @@ -14044,7 +14076,6 @@ erts_continue_exit_process(Process *p) #ifdef DEBUG erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); - ASSERT(p->sys_task_qs == NULL); ASSERT(ERTS_PROC_GET_DELAYED_GC_TASK_QS(p) == NULL); #ifdef ERTS_DIRTY_SCHEDULERS ASSERT(p->dirty_sys_tasks == NULL); @@ -14140,15 +14171,16 @@ erts_continue_exit_process(Process *p) ASSERT(a & ERTS_PSFLG_EXITING); n |= ERTS_PSFLG_FREE; n &= ~(ERTS_PSFLG_ACTIVE - | ERTS_PSFLG_ACTIVE_SYS | ERTS_PSFLG_DIRTY_ACTIVE_SYS); if ((n & ERTS_PSFLG_IN_RUNQ) && !refc_inced) { erts_proc_inc_refc(p); refc_inced = 1; } a = erts_smp_atomic32_cmpxchg_mb(&p->state, n, e); - if (a == e) + if (a == e) { + state = n; break; + } } #ifdef ERTS_DIRTY_SCHEDULERS @@ -14169,7 +14201,28 @@ erts_continue_exit_process(Process *p) dep = (p->flags & F_DISTRIBUTION) ? erts_this_dist_entry : NULL; - erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL); + /* + * It might show up copy-literals tasks until we + * have entered free state. Cleanup such tasks now. + */ + if (!(state & ERTS_PSFLG_ACTIVE_SYS)) + erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL); + else { + erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR); + + do { + (void) cleanup_sys_tasks(p, state, CONTEXT_REDS); + state = erts_atomic32_read_acqb(&p->state); + } while (state & ERTS_PSFLG_ACTIVE_SYS); + + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN); + } + +#ifdef DEBUG + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); + ASSERT(p->sys_task_qs == NULL); + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); +#endif if (dep) { erts_do_net_exits(dep, reason); diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 87777d14e9..c5fdb46590 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -901,6 +901,9 @@ Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2); Eterm erts_check_process_code(Process *c_p, Eterm module, int *redsp, int fcalls); Eterm erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed); +Uint32 erts_block_release_literal_area(void); +void erts_unblock_release_literal_area(Uint32); + typedef struct ErtsLiteralArea_ { struct erl_off_heap_header *off_heap; Eterm *end; |