From 5b740336b0d7d0aec7c4df3b3e5c968ec0456766 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Tue, 20 Mar 2012 09:49:09 +0100 Subject: erts: Implement erl_halt --- erts/emulator/beam/erl_init.c | 48 ++++++++++------ erts/emulator/beam/erl_process.c | 118 ++++++++++++++++++++++++++++++++++++--- erts/emulator/beam/erl_process.h | 6 +- erts/emulator/beam/global.h | 5 +- erts/emulator/beam/io.c | 7 +++ erts/emulator/beam/sys.h | 4 +- 6 files changed, 160 insertions(+), 28 deletions(-) (limited to 'erts/emulator/beam') diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 717315d8bd..d866a1fe3f 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2011. All Rights Reserved. + * Copyright Ericsson AB 1997-2012. 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 @@ -1510,7 +1510,7 @@ __decl_noreturn void erts_thr_fatal_error(int err, char *what) #endif static void -system_cleanup(int exit_code) +system_cleanup(int flush_async) { /* * Make sure only one thread exits the runtime system. @@ -1542,7 +1542,7 @@ system_cleanup(int exit_code) * (in threaded non smp case). */ - if (exit_code != 0 + if (!flush_async || !erts_initialized #if defined(USE_THREADS) && !defined(ERTS_SMP) || !erts_equal_tids(main_thread, erts_thr_self()) @@ -1632,14 +1632,12 @@ __decl_noreturn void erl_exit0(char *file, int line, int n, char *fmt,...) exit(an); } -__decl_noreturn void erl_exit(int n, char *fmt,...) +static __decl_noreturn void __noreturn +erl_exit_vv(int n, int flush_async, char *fmt, va_list args1, va_list args2) { unsigned int an; - va_list args; - va_start(args, fmt); - - system_cleanup(n); + system_cleanup(flush_async); save_statistics(); @@ -1649,18 +1647,13 @@ __decl_noreturn void erl_exit(int n, char *fmt,...) erts_mtrace_exit((Uint32) an); /* Produce an Erlang core dump if error */ - if (n > 0 && erts_initialized && - (erts_no_crash_dump == 0 || n == ERTS_DUMP_EXIT)) { - erl_crash_dump_v((char*) NULL, 0, fmt, args); + if (((n > 0 && erts_no_crash_dump == 0) || n == ERTS_DUMP_EXIT) + && erts_initialized) { + erl_crash_dump_v((char*) NULL, 0, fmt, args1); } - /* need to reinitialize va_args thing */ - va_end(args); - va_start(args, fmt); - if (fmt != NULL && *fmt != '\0') - erl_error(fmt, args); /* Print error message. */ - va_end(args); + erl_error(fmt, args2); /* Print error message. */ sys_tty_reset(n); if (n == ERTS_INTR_EXIT) @@ -1672,3 +1665,24 @@ __decl_noreturn void erl_exit(int n, char *fmt,...) exit(an); } +/* Exit without flushing async threads */ +__decl_noreturn void __noreturn erl_exit(int n, char *fmt, ...) +{ + va_list args1, args2; + va_start(args1, fmt); + va_start(args2, fmt); + erl_exit_vv(n, 0, fmt, args1, args2); + va_end(args2); + va_end(args1); +} + +/* Exit after flushing async threads */ +__decl_noreturn void __noreturn erl_exit_flush_async(int n, char *fmt, ...) +{ + va_list args1, args2; + va_start(args1, fmt); + va_start(args2, fmt); + erl_exit_vv(n, 1, fmt, args1, args2); + va_end(args2); + va_end(args1); +} diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 138acfeb2c..45f6dc800c 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -104,6 +104,9 @@ do { \ #define ERTS_EMPTY_RUNQ(RQ) \ ((RQ)->len == 0 && (RQ)->misc.start == NULL) +#define ERTS_EMPTY_RUNQ_PORTS(RQ) \ + ((RQ)->ports.info.len == 0 && (RQ)->misc.start == NULL) + extern BeamInstr beam_apply[]; extern BeamInstr beam_exit[]; extern BeamInstr beam_continue_exit[]; @@ -366,6 +369,9 @@ dbg_chk_aux_work_val(erts_aint32_t value) #ifdef ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN valid |= ERTS_SSI_AUX_WORK_CHECK_CHILDREN; #endif +#ifdef ERTS_SSI_AUX_WORK_REAP_PORTS + valid |= ERTS_SSI_AUX_WORK_REAP_PORTS; +#endif if (~valid & value) erl_exit(ERTS_ABORT_EXIT, @@ -861,8 +867,6 @@ set_aux_work_flags_wakeup_nob(ErtsSchedulerSleepInfo *ssi, } } -#if 0 /* Currently not used */ - static ERTS_INLINE void set_aux_work_flags_wakeup_relb(ErtsSchedulerSleepInfo *ssi, erts_aint32_t flgs) @@ -882,8 +886,6 @@ set_aux_work_flags_wakeup_relb(ErtsSchedulerSleepInfo *ssi, } } -#endif - static ERTS_INLINE erts_aint32_t set_aux_work_flags(ErtsSchedulerSleepInfo *ssi, erts_aint32_t flgs) { @@ -1351,6 +1353,65 @@ handle_check_children(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) #endif +static void +notify_reap_ports_relb(void) +{ + int i; + for (i = 0; i < erts_no_schedulers; i++) { + set_aux_work_flags_wakeup_relb(ERTS_SCHED_SLEEP_INFO_IX(i), + ERTS_SSI_AUX_WORK_REAP_PORTS); + } +} + +erts_smp_atomic32_t erts_halt_progress; +int erts_halt_code; + +static ERTS_INLINE erts_aint32_t +handle_reap_ports(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) +{ + unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_REAP_PORTS); + awdp->esdp->run_queue->halt_in_progress = 1; + if (erts_smp_atomic32_dec_read_acqb(&erts_halt_progress) == 0) { + int i; + erts_smp_atomic32_set_nob(&erts_halt_progress, 1); + for (i = 0; i < erts_max_ports; i++) { + Port *prt = &erts_port[i]; + erts_smp_port_state_lock(prt); + if ((prt->status & (ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP + | ERTS_PORT_SFLG_HALT))) { + erts_smp_port_state_unlock(prt); + continue; + } + /* We need to set the halt flag - get the port lock */ +#ifdef ERTS_SMP + erts_smp_atomic_inc_nob(&prt->refc); +#endif + erts_smp_port_state_unlock(prt); +#ifdef ERTS_SMP + erts_smp_mtx_lock(prt->lock); +#endif + if ((prt->status & (ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP + | ERTS_PORT_SFLG_HALT))) { + erts_port_release(prt); + continue; + } + erts_port_status_bor_set(prt, ERTS_PORT_SFLG_HALT); + erts_smp_atomic32_inc_nob(&erts_halt_progress); + if (prt->status & (ERTS_PORT_SFLG_EXITING + | ERTS_PORT_SFLG_CLOSING)) { + erts_port_release(prt); + continue; + } + erts_do_exit_port(prt, prt->id, am_killed); + erts_port_release(prt); + } + if (erts_smp_atomic32_dec_read_nob(&erts_halt_progress) == 0) { + erl_exit_flush_async(erts_halt_code, ""); + } + } + return aux_work & ~ERTS_SSI_AUX_WORK_REAP_PORTS; +} + #if HAVE_ERTS_MSEG static ERTS_INLINE erts_aint32_t @@ -1451,6 +1512,9 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work) handle_mseg_cache_check); #endif + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_REAP_PORTS, + handle_reap_ports); + ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); return aux_work; @@ -2716,6 +2780,9 @@ try_steal_task_from_victim(ErtsRunQueue *rq, int *rq_lockedp, ErtsRunQueue *vrq) ERTS_SMP_LC_CHK_RUNQ_LOCK(rq, *rq_lockedp); ERTS_SMP_LC_CHK_RUNQ_LOCK(vrq, vrq_locked); + if (rq->halt_in_progress) + goto try_steal_port; + /* * Check for a runnable process to steal... */ @@ -2802,6 +2869,8 @@ try_steal_task_from_victim(ErtsRunQueue *rq, int *rq_lockedp, ErtsRunQueue *vrq) vrq_locked = 1; } + try_steal_port: + ERTS_SMP_LC_CHK_RUNQ_LOCK(rq, *rq_lockedp); ERTS_SMP_LC_CHK_RUNQ_LOCK(vrq, vrq_locked); @@ -2917,7 +2986,8 @@ try_steal_task(ErtsRunQueue *rq) erts_smp_runq_lock(rq); if (!res) - res = !ERTS_EMPTY_RUNQ(rq); + res = rq->halt_in_progress ? + !ERTS_EMPTY_RUNQ_PORTS(rq) : !ERTS_EMPTY_RUNQ(rq); return res; } @@ -3583,6 +3653,7 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) rq->len = 0; rq->wakeup_other = 0; rq->wakeup_other_reds = 0; + rq->halt_in_progress = 0; rq->procs.len = 0; rq->procs.pending_exiters = NULL; @@ -3777,6 +3848,9 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online) ERTS_VERIFY_UNUSED_TEMP_ALLOC(NULL); #endif #endif + + erts_smp_atomic32_init_relb(&erts_halt_progress, -1); + erts_halt_code = 0; } ErtsRunQueue * @@ -6343,7 +6417,9 @@ Process *schedule(Process *p, int calls) ASSERT(rq->len == rq->procs.len + rq->ports.info.len); - if (rq->len == 0 && !rq->misc.start) { + if ((rq->len == 0 && !rq->misc.start) + || (rq->halt_in_progress + && rq->ports.info.len == 0 && !rq->misc.start)) { #ifdef ERTS_SMP @@ -6441,7 +6517,8 @@ Process *schedule(Process *p, int calls) if (rq->ports.info.len) { int have_outstanding_io; have_outstanding_io = erts_port_task_execute(rq, &esdp->current_port); - if (have_outstanding_io && fcalls > 2*input_reductions) { + if ((have_outstanding_io && fcalls > 2*input_reductions) + || rq->halt_in_progress) { /* * If we have performed more than 2*INPUT_REDUCTIONS since * last call to erl_sys_schedule() and we still haven't @@ -9867,3 +9944,30 @@ debug_processes_assert_error(char* expr, char* file, int line) /* *\ * End of the processes/0 BIF implementation. * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * A nice system halt closing all open port goes as follows: + * 1) This function schedules the aux work ERTS_SSI_AUX_WORK_REAP_PORTS + * on all schedulers, then schedules itself out. + * 2) All shedulers detect this and set the flag halt_in_progress + * on their run queue. The last scheduler sets all non-closed ports + * ERTS_PORT_SFLG_HALT. Global atomic erts_halt_progress is used + * as refcount to determine which is last. + * 3) While the run ques has flag halt_in_progress no processes + * will be scheduled, only ports. + * 4) When the last port closes that scheduler calls erlang:halt/1. + * The same global atomic is used as refcount. + * + * A BIF that calls this should make sure to schedule out to never come back: + * erl_halt((int)(- code)); + * ERTS_BIF_YIELD1(bif_export[BIF_erlang_halt_1], BIF_P, NIL); + */ +void erl_halt(int code) +{ + if (-1 == erts_smp_atomic32_cmpxchg_acqb(&erts_halt_progress, + erts_no_schedulers, + -1)) { + erts_halt_code = code; + notify_reap_ports_relb(); + } +} diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index c23810f15a..d671638ce8 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -264,6 +264,7 @@ typedef enum { #define ERTS_SSI_AUX_WORK_CHECK_CHILDREN (((erts_aint32_t) 1) << 8) #define ERTS_SSI_AUX_WORK_SET_TMO (((erts_aint32_t) 1) << 9) #define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK (((erts_aint32_t) 1) << 10) +#define ERTS_SSI_AUX_WORK_REAP_PORTS (((erts_aint32_t) 1) << 11) typedef struct ErtsSchedulerSleepInfo_ ErtsSchedulerSleepInfo; @@ -341,6 +342,7 @@ struct ErtsRunQueue_ { int len; int wakeup_other; int wakeup_other_reds; + int halt_in_progress; struct { int len; @@ -1659,4 +1661,6 @@ erts_sched_poke(ErtsSchedulerSleepInfo *ssi) #endif - +void erl_halt(int code); +extern erts_smp_atomic32_t erts_halt_progress; +extern int erts_halt_code; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index f1335f600d..34eb984925 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2011. All Rights Reserved. + * Copyright Ericsson AB 1996-2012. 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 @@ -806,6 +806,8 @@ do { \ /* Port uses port specific locking (opposed to driver specific locking) */ #define ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK ((Uint32) (1 << 13)) #define ERTS_PORT_SFLG_INVALID ((Uint32) (1 << 14)) +/* Last port to terminate halts the emulator */ +#define ERTS_PORT_SFLG_HALT ((Uint32) (1 << 15)) #ifdef DEBUG /* Only debug: make sure all flags aren't cleared unintentionally */ #define ERTS_PORT_SFLG_PORT_DEBUG ((Uint32) (1 << 31)) @@ -900,6 +902,7 @@ void loaded(int, void *); __decl_noreturn void __noreturn erl_exit(int n, char*, ...); __decl_noreturn void __noreturn erl_exit0(char *, int, int n, char*, ...); +__decl_noreturn void __noreturn erl_exit_flush_async(int n, char*, ...); void erl_error(char*, va_list); #define ERL_EXIT0(n,f) erl_exit0(__FILE__, __LINE__, n, f) diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index b23b1f628d..591eb834e6 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1837,6 +1837,7 @@ terminate_port(Port *prt) Eterm send_closed_port_id; Eterm connected_id = NIL /* Initialize to silence compiler */; erts_driver_t *drv; + int halt; ERTS_SMP_CHK_NO_PROC_LOCKS; ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); @@ -1844,6 +1845,8 @@ terminate_port(Port *prt) ASSERT(!prt->nlinks); ASSERT(!prt->monitors); + /* prt->status may be altered by kill_port()below */ + halt = (prt->status & ERTS_PORT_SFLG_HALT) != 0; if (prt->status & ERTS_PORT_SFLG_SEND_CLOSED) { erts_port_status_band_set(prt, ~ERTS_PORT_SFLG_SEND_CLOSED); send_closed_port_id = prt->id; @@ -1895,6 +1898,10 @@ terminate_port(Port *prt) * We don't want to send the closed message until after the * port has been removed from the port table (in kill_port()). */ + if (halt && (erts_smp_atomic32_dec_read_nob(&erts_halt_progress) == 0)) { + erts_smp_port_unlock(prt); /* We will exit and never return */ + erl_exit_flush_async(erts_halt_code, ""); + } if (is_internal_port(send_closed_port_id)) deliver_result(send_closed_port_id, connected_id, am_closed); diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index eb6f2f8516..7b2bb81f62 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2011. All Rights Reserved. + * Copyright Ericsson AB 1996-2012. 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 @@ -511,7 +511,7 @@ __decl_noreturn void __noreturn erl_exit(int n, char*, ...); /* Some special erl_exit() codes: */ #define ERTS_INTR_EXIT INT_MIN /* called from signal handler */ #define ERTS_ABORT_EXIT (INT_MIN + 1) /* no crash dump; only abort() */ -#define ERTS_DUMP_EXIT (127) /* crash dump; then exit() */ +#define ERTS_DUMP_EXIT (INT_MIN + 2) /* crash dump; then exit() */ Eterm erts_check_io_info(void *p); -- cgit v1.2.3