diff options
60 files changed, 2624 insertions, 763 deletions
diff --git a/Makefile.in b/Makefile.in index 2ded1b4356..0b1f0930ca 100644 --- a/Makefile.in +++ b/Makefile.in @@ -220,8 +220,10 @@ BOOTSTRAP_ROOT = $(ERL_TOP) LOCAL_PATH = $(ERL_TOP)/erts/bin/$(TARGET):$(ERL_TOP)/erts/bin ifeq ($(TARGET),win32) BOOT_PREFIX=$(WIN32_WRAPPER_PATH):$(BOOTSTRAP_ROOT)/bootstrap/bin: +TEST_PATH_PREFIX=$(WIN32_WRAPPER_PATH):$(ERL_TOP)/bin: else BOOT_PREFIX=$(BOOTSTRAP_ROOT)/bootstrap/bin: +TEST_PATH_PREFIX=$(ERL_TOP)/bin: endif # ---------------------------------------------------------------------- @@ -880,7 +882,7 @@ tests release_tests: $(TEST_DIRS) $(TEST_DIRS): if test -f $@/Makefile; then \ (cd $@; $(MAKE) TESTROOT=$(TESTSUITE_ROOT) \ - PATH=$(ERL_TOP)/bin:$(BOOT_PREFIX)$${PATH} release_tests) || exit $$?; \ + PATH=$(TEST_PATH_PREFIX)$(BOOT_PREFIX)$${PATH} release_tests) || exit $$?; \ fi # diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 976f05c990..25ac790d81 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -1844,6 +1844,7 @@ dsize_t big_bytes(Eterm x) /* ** Load a bignum from bytes ** xsz is the number of bytes in xp +** *r is untouched if number fits in small */ Eterm bytes_to_big(byte *xp, dsize_t xsz, int xsgn, Eterm *r) { @@ -1852,7 +1853,7 @@ Eterm bytes_to_big(byte *xp, dsize_t xsz, int xsgn, Eterm *r) ErtsDigit d; int i; - while(xsz >= sizeof(ErtsDigit)) { + while(xsz > sizeof(ErtsDigit)) { d = 0; for(i = sizeof(ErtsDigit); --i >= 0;) d = (d << 8) | xp[i]; @@ -1867,11 +1868,20 @@ Eterm bytes_to_big(byte *xp, dsize_t xsz, int xsgn, Eterm *r) d = 0; for(i = xsz; --i >= 0;) d = (d << 8) | xp[i]; + if (++rsz == 1 && IS_USMALL(xsgn,d)) { + if (xsgn) d = -d; + return make_small(d); + } *rwp = d; rwp++; - rsz++; } - return big_norm(r, rsz, (short) xsgn); + if (xsgn) { + *r = make_neg_bignum_header(rsz); + } + else { + *r = make_pos_bignum_header(rsz); + } + return make_big(r); } /* diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 0d3b6a4dba..6f5020dc14 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -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 @@ -646,6 +646,9 @@ bin_check(void) void erl_crash_dump_v(char *file, int line, char* fmt, va_list args) { +#ifdef ERTS_SMP + ErtsThrPrgrData tpd_buf; /* in case we aren't a managed thread... */ +#endif int fd; time_t now; size_t dumpnamebufsize = MAXPATHLEN; @@ -663,7 +666,7 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args) * We do not release system again. We expect an exit() or abort() after * dump has been written. */ - erts_thr_progress_fatal_error_block(60000); + erts_thr_progress_fatal_error_block(60000, &tpd_buf); /* Either worked or not... */ /* Allow us to pass certain places without locking... */ diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 4d02a67d54..df27186680 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2011. All Rights Reserved. + * Copyright Ericsson AB 2002-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 @@ -1577,9 +1577,11 @@ erts_alloc_register_scheduler(void *vesdp) } } +#ifdef ERTS_SMP void erts_alloc_scheduler_handle_delayed_dealloc(void *vesdp, int *need_thr_progress, + ErtsThrPrgrVal *thr_prgr_p, int *more_work) { ErtsSchedulerData *esdp = (ErtsSchedulerData *) vesdp; @@ -1599,10 +1601,12 @@ erts_alloc_scheduler_handle_delayed_dealloc(void *vesdp, erts_alcu_check_delayed_dealloc(allctr, 1, need_thr_progress, + thr_prgr_p, more_work); } } } +#endif erts_aint32_t erts_alloc_fix_alloc_shrink(int ix, erts_aint32_t flgs) diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h index 991061c48e..e475f9d8a2 100644 --- a/erts/emulator/beam/erl_alloc.h +++ b/erts/emulator/beam/erl_alloc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2011. All Rights Reserved. + * Copyright Ericsson AB 2002-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 @@ -21,6 +21,10 @@ #define ERL_ALLOC_H__ #include "erl_alloc_types.h" +#undef ERL_THR_PROGRESS_TSD_TYPE_ONLY +#define ERL_THR_PROGRESS_TSD_TYPE_ONLY +#include "erl_thr_progress.h" +#undef ERL_THR_PROGRESS_TSD_TYPE_ONLY #include "erl_alloc_util.h" #ifdef USE_THREADS #include "erl_threads.h" @@ -132,9 +136,12 @@ typedef struct { extern ErtsAllocatorThrSpec_t erts_allctr_thr_spec[ERTS_ALC_A_MAX+1]; void erts_alloc_register_scheduler(void *vesdp); +#ifdef ERTS_SMP void erts_alloc_scheduler_handle_delayed_dealloc(void *vesdp, int *need_thr_progress, + ErtsThrPrgrVal *thr_prgr_p, int *more_work); +#endif erts_aint32_t erts_alloc_fix_alloc_shrink(int ix, erts_aint32_t flgs); __decl_noreturn void erts_alloc_enomem(ErtsAlcType_t,Uint) diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 2e1a92f61d..e0d525bdde 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2011. All Rights Reserved. + * Copyright Ericsson AB 2002-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 @@ -949,7 +949,6 @@ ddq_check_incoming(ErtsAllctrDDQueue_t *ddq) ERTS_THR_MEMORY_BARRIER; else { ddq->head.next.unref_end = (ErtsAllctrDDBlock_t *) ilast; - ERTS_THR_MEMORY_BARRIER; ddq->head.next.thr_progress = erts_thr_progress_later(); erts_atomic32_set_relb(&ddq->tail.data.um_refc_ix, um_refc_ix); @@ -961,12 +960,24 @@ ddq_check_incoming(ErtsAllctrDDQueue_t *ddq) return 1; } +static ERTS_INLINE void +store_earliest_thr_prgr(ErtsThrPrgrVal *prev_val, ErtsAllctrDDQueue_t *ddq) +{ + if (!ddq->head.next.thr_progress_reached + && (*prev_val == ERTS_THR_PRGR_INVALID + || erts_thr_progress_cmp(ddq->head.next.thr_progress, + *prev_val) < 0)) { + *prev_val = ddq->head.next.thr_progress; + } +} + static ERTS_INLINE int handle_delayed_dealloc(Allctr_t *allctr, int allctr_locked, int use_limit, int ops_limit, int *need_thr_progress, + ErtsThrPrgrVal *thr_prgr_p, int *need_more_work) { int need_thr_prgr = 0; @@ -1008,8 +1019,12 @@ handle_delayed_dealloc(Allctr_t *allctr, if (have_checked_incoming) break; need_thr_prgr = ddq_check_incoming(ddq); - if (need_thr_progress) + if (need_thr_progress) { *need_thr_progress |= need_thr_prgr; + if (need_thr_prgr) + store_earliest_thr_prgr(thr_prgr_p, ddq); + + } have_checked_incoming = 1; goto dequeue; } @@ -1067,6 +1082,8 @@ handle_delayed_dealloc(Allctr_t *allctr, if (need_thr_progress && !(need_thr_prgr | need_mr_wrk)) { need_thr_prgr = ddq_check_incoming(ddq); *need_thr_progress |= need_thr_prgr; + if (need_thr_prgr) + store_earliest_thr_prgr(thr_prgr_p, ddq); } if (allctr->thread_safe && !allctr_locked) @@ -1086,25 +1103,27 @@ enqueue_dealloc_other_instance(ErtsAlcType_t type, Allctr_t *allctr, void *ptr) #endif +#ifdef ERTS_SMP void erts_alcu_check_delayed_dealloc(Allctr_t *allctr, int limit, int *need_thr_progress, + ErtsThrPrgrVal *thr_prgr_p, int *more_work) { -#ifdef ERTS_SMP handle_delayed_dealloc(allctr, 0, limit, ERTS_ALCU_DD_OPS_LIM_HIGH, need_thr_progress, + thr_prgr_p, more_work); -#endif } +#endif #define ERTS_ALCU_HANDLE_DD_IN_OP(Allctr, Locked) \ handle_delayed_dealloc((Allctr), (Locked), 1, \ - ERTS_ALCU_DD_OPS_LIM_LOW, NULL, NULL) + ERTS_ALCU_DD_OPS_LIM_LOW, NULL, NULL, NULL) /* Multi block carrier alloc/realloc/free ... */ diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index fc1eddb116..cedf4ccf85 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2011. All Rights Reserved. + * Copyright Ericsson AB 2002-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 @@ -170,7 +170,9 @@ Eterm erts_alcu_info(Allctr_t *, int, int *, void *, Uint **, Uint *); void erts_alcu_init(AlcUInit_t *); void erts_alcu_current_size(Allctr_t *, AllctrSize_t *, ErtsAlcUFixInfo_t *, int); -void erts_alcu_check_delayed_dealloc(Allctr_t *, int, int *, int *); +#ifdef ERTS_SMP +void erts_alcu_check_delayed_dealloc(Allctr_t *, int, int *, ErtsThrPrgrVal *, int *); +#endif erts_aint32_t erts_alcu_fix_alloc_shrink(Allctr_t *, erts_aint32_t); #endif diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index eb89baf1c9..51bdf53823 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -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 @@ -251,7 +251,7 @@ free_dbtable(DbTable* tb) ASSERT(is_immed(tb->common.heir_data)); erts_db_free(ERTS_ALC_T_DB_TABLE, tb, (void *) tb, sizeof(DbTable)); ERTS_ETS_MISC_MEM_ADD(-sizeof(DbTable)); - ERTS_THR_MEMORY_BARRIER; + ERTS_SMP_MEMORY_BARRIER; } #ifdef ERTS_SMP @@ -3816,7 +3816,7 @@ void db_info(int to, void *to_arg, int show) /* Called by break handler */ Uint erts_get_ets_misc_mem_size(void) { - ERTS_THR_MEMORY_BARRIER; + ERTS_SMP_MEMORY_BARRIER; /* Memory not allocated in ets_alloc */ return (Uint) erts_smp_atomic_read_nob(&erts_ets_misc_mem_size); } diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 038a667b06..c726be5fb4 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2011. All Rights Reserved. + * Copyright Ericsson AB 1998-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 @@ -105,11 +105,7 @@ #define NSEG_2 256 /* Size of second segment table */ #define NSEG_INC 128 /* Number of segments to grow after that */ -#ifdef ETHR_ORDERED_READ_DEPEND -#define SEGTAB(tb) ((struct segment**)erts_smp_atomic_read_nob(&(tb)->segtab)) -#else -#define SEGTAB(tb) ((struct segment**)erts_smp_atomic_read_rb(&(tb)->segtab)) -#endif +#define SEGTAB(tb) ((struct segment**)erts_smp_atomic_read_ddrb(&(tb)->segtab)) #define NACTIVE(tb) ((int)erts_smp_atomic_read_nob(&(tb)->nactive)) #define NITEMS(tb) ((int)erts_smp_atomic_read_nob(&(tb)->common.nitems)) diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index eedbb35486..138acfeb2c 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -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 @@ -347,40 +347,25 @@ dbg_chk_aux_work_val(erts_aint32_t value) { erts_aint32_t valid = 0; -#ifdef ERTS_SSI_AUX_WORK_SET_TMO valid |= ERTS_SSI_AUX_WORK_SET_TMO; -#endif -#ifdef ERTS_SSI_AUX_WORK_CHECK_CHILDREN - valid |= ERTS_SSI_AUX_WORK_CHECK_CHILDREN; -#endif -#ifdef ERTS_SSI_AUX_WORK_MISC valid |= ERTS_SSI_AUX_WORK_MISC; -#endif -#ifdef ERTS_SSI_AUX_WORK_MISC_THR_PRGR - valid |= ERTS_SSI_AUX_WORK_MISC_THR_PRGR; -#endif -#ifdef ERTS_SSI_AUX_WORK_ASYNC_READY - valid |= ERTS_SSI_AUX_WORK_ASYNC_READY; -#endif -#ifdef ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN - valid |= ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN; -#endif - -#ifdef ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM valid |= ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM; -#endif -#ifdef ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC valid |= ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC; +#if ERTS_USE_ASYNC_READY_Q + valid |= ERTS_SSI_AUX_WORK_ASYNC_READY; + valid |= ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN; #endif -#ifdef ERTS_SSI_AUX_WORK_DD +#ifdef ERTS_SMP + valid |= ERTS_SSI_AUX_WORK_MISC_THR_PRGR; valid |= ERTS_SSI_AUX_WORK_DD; -#endif -#ifdef ERTS_SSI_AUX_WORK_DD valid |= ERTS_SSI_AUX_WORK_DD_THR_PRGR; #endif -#ifdef ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK +#if HAVE_ERTS_MSEG valid |= ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK; #endif +#ifdef ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN + valid |= ERTS_SSI_AUX_WORK_CHECK_CHILDREN; +#endif if (~valid & value) erl_exit(ERTS_ABORT_EXIT, @@ -911,6 +896,27 @@ unset_aux_work_flags(ErtsSchedulerSleepInfo *ssi, erts_aint32_t flgs) return erts_atomic32_read_band_nob(&ssi->aux_work, ~flgs); } +#ifdef ERTS_SMP + +static ERTS_INLINE void +thr_prgr_current_reset(ErtsAuxWorkData *awdp) +{ + awdp->current_thr_prgr = ERTS_THR_PRGR_INVALID; +} + +static ERTS_INLINE ErtsThrPrgrVal +thr_prgr_current(ErtsAuxWorkData *awdp) +{ + ErtsThrPrgrVal current = awdp->current_thr_prgr; + if (current == ERTS_THR_PRGR_INVALID) { + current = erts_thr_progress_current(); + awdp->current_thr_prgr = current; + } + return current; +} + +#endif + typedef struct erts_misc_aux_work_t_ erts_misc_aux_work_t; struct erts_misc_aux_work_t_ { void (*func)(void *); @@ -983,7 +989,7 @@ misc_aux_work_clean(ErtsThrQ_t *q, return aux_work; } -static erts_aint32_t +static ERTS_INLINE erts_aint32_t handle_misc_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) { @@ -1003,12 +1009,13 @@ handle_misc_aux_work(ErtsAuxWorkData *awdp, #ifdef ERTS_SMP -static erts_aint32_t +static ERTS_INLINE erts_aint32_t handle_misc_aux_work_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) { - if (!erts_thr_progress_has_reached(awdp->misc.thr_prgr)) - return aux_work; + if (!erts_thr_progress_has_reached_this(thr_prgr_current(awdp), + awdp->misc.thr_prgr)) + return aux_work & ~ERTS_SSI_AUX_WORK_MISC_THR_PRGR; unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_MISC_THR_PRGR); @@ -1081,7 +1088,7 @@ erts_notify_check_async_ready_queue(void *vno) ERTS_SSI_AUX_WORK_ASYNC_READY); } -static erts_aint32_t +static ERTS_INLINE erts_aint32_t handle_async_ready(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) { @@ -1103,7 +1110,7 @@ handle_async_ready(ErtsAuxWorkData *awdp, | ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN); } -static erts_aint32_t +static ERTS_INLINE erts_aint32_t handle_async_ready_clean(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) { @@ -1111,7 +1118,8 @@ handle_async_ready_clean(ErtsAuxWorkData *awdp, #ifdef ERTS_SMP if (awdp->async_ready.need_thr_prgr - && !erts_thr_progress_has_reached(awdp->async_ready.thr_prgr)) { + && !erts_thr_progress_has_reached_this(thr_prgr_current(awdp), + awdp->async_ready.thr_prgr)) { return aux_work & ~ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN; } @@ -1139,7 +1147,7 @@ handle_async_ready_clean(ErtsAuxWorkData *awdp, #endif -static erts_aint32_t +static ERTS_INLINE erts_aint32_t handle_fix_alloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) { ErtsSchedulerSleepInfo *ssi = awdp->ssi; @@ -1167,16 +1175,18 @@ erts_alloc_notify_delayed_dealloc(int ix) ERTS_SSI_AUX_WORK_DD); } -static erts_aint32_t +static ERTS_INLINE erts_aint32_t handle_delayed_dealloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) { ErtsSchedulerSleepInfo *ssi = awdp->ssi; int need_thr_progress = 0; + ErtsThrPrgrVal wakeup = ERTS_THR_PRGR_INVALID; int more_work = 0; unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_DD); erts_alloc_scheduler_handle_delayed_dealloc((void *) awdp->esdp, &need_thr_progress, + &wakeup, &more_work); if (more_work) { if (set_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_DD) @@ -1188,9 +1198,12 @@ handle_delayed_dealloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) } if (need_thr_progress) { + if (wakeup == ERTS_THR_PRGR_INVALID) + wakeup = erts_thr_progress_later_than(thr_prgr_current(awdp)); + awdp->dd.thr_prgr = wakeup; set_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_DD_THR_PRGR); - awdp->dd.thr_prgr = erts_thr_progress_later(); - erts_thr_progress_wakeup(awdp->esdp, awdp->dd.thr_prgr); + awdp->dd.thr_prgr = wakeup; + erts_thr_progress_wakeup(awdp->esdp, wakeup); } else if (awdp->dd.completed_callback) { awdp->dd.completed_callback(awdp->dd.completed_arg); @@ -1200,14 +1213,16 @@ handle_delayed_dealloc(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) return aux_work & ~ERTS_SSI_AUX_WORK_DD; } -static erts_aint32_t +static ERTS_INLINE erts_aint32_t handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) { ErtsSchedulerSleepInfo *ssi; int need_thr_progress; int more_work; + ErtsThrPrgrVal wakeup = ERTS_THR_PRGR_INVALID; + ErtsThrPrgrVal current = thr_prgr_current(awdp); - if (!erts_thr_progress_has_reached(awdp->dd.thr_prgr)) + if (!erts_thr_progress_has_reached_this(current, awdp->dd.thr_prgr)) return aux_work & ~ERTS_SSI_AUX_WORK_DD_THR_PRGR; ssi = awdp->ssi; @@ -1216,6 +1231,7 @@ handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) erts_alloc_scheduler_handle_delayed_dealloc((void *) awdp->esdp, &need_thr_progress, + &wakeup, &more_work); if (more_work) { set_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_DD); @@ -1225,8 +1241,10 @@ handle_delayed_dealloc_thr_prgr(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) } if (need_thr_progress) { - awdp->dd.thr_prgr = erts_thr_progress_later(); - erts_thr_progress_wakeup(awdp->esdp, awdp->dd.thr_prgr); + if (wakeup == ERTS_THR_PRGR_INVALID) + wakeup = erts_thr_progress_later_than(current); + awdp->dd.thr_prgr = wakeup; + erts_thr_progress_wakeup(awdp->esdp, wakeup); } else { unset_aux_work_flags(ssi, ERTS_SSI_AUX_WORK_DD_THR_PRGR); @@ -1323,7 +1341,7 @@ erts_smp_notify_check_children_needed(void) ERTS_SSI_AUX_WORK_CHECK_CHILDREN); } -static erts_aint32_t +static ERTS_INLINE erts_aint32_t handle_check_children(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) { unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_CHECK_CHILDREN); @@ -1333,9 +1351,9 @@ handle_check_children(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) #endif -#ifdef ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK +#if HAVE_ERTS_MSEG -static erts_aint32_t +static ERTS_INLINE erts_aint32_t handle_mseg_cache_check(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) { unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK); @@ -1345,7 +1363,7 @@ handle_mseg_cache_check(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) #endif -static erts_aint32_t +static ERTS_INLINE erts_aint32_t handle_setup_aux_work_timer(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) { unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_SET_TMO); @@ -1353,69 +1371,92 @@ handle_setup_aux_work_timer(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) return aux_work & ~ERTS_SSI_AUX_WORK_SET_TMO; } -static ERTS_INLINE erts_aint32_t -handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t aux_work) +static erts_aint32_t +handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work) { +#undef HANDLE_AUX_WORK +#define HANDLE_AUX_WORK(FLG, HNDLR) \ + ignore |= FLG; \ + if (aux_work & FLG) { \ + aux_work = HNDLR(awdp, aux_work); \ + ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); \ + if (!(aux_work & ~ignore)) { \ + ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); \ + return aux_work; \ + } \ + } + + erts_aint32_t aux_work = orig_aux_work; + erts_aint32_t ignore = 0; + +#ifdef ERTS_SMP + thr_prgr_current_reset(awdp); +#endif + + ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); + ASSERT(aux_work); + /* * Handlers are *only* allowed to modify flags in return value * and ssi flags that are explicity handled by the handler. * Handlers are, e.g., not allowed to read the ssi flag field and * then unconditionally return that value. + * + * Flag field returned should only contain flags for work that + * can continue immediately. + */ + + /* + * Keep ERTS_SSI_AUX_WORK flags in expected frequency order relative + * eachother. Most frequent first. */ - ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); - if (aux_work & ERTS_SSI_AUX_WORK_SET_TMO) { - aux_work = handle_setup_aux_work_timer(awdp, aux_work); - ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); - } #ifdef ERTS_SMP - if (aux_work & ERTS_SSI_AUX_WORK_MISC_THR_PRGR) { - aux_work = handle_misc_aux_work_thr_prgr(awdp, aux_work); - ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); - } + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_DD, + handle_delayed_dealloc); + /* DD must be before DD_THR_PRGR */ + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_DD_THR_PRGR, + handle_delayed_dealloc_thr_prgr); #endif - if (aux_work & ERTS_SSI_AUX_WORK_MISC) { - aux_work = handle_misc_aux_work(awdp, aux_work); - ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); - } + + HANDLE_AUX_WORK((ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM + | ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC), + handle_fix_alloc); + #if ERTS_USE_ASYNC_READY_Q - if (aux_work & ERTS_SSI_AUX_WORK_ASYNC_READY) { - aux_work = handle_async_ready(awdp, aux_work); - ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); - } - if (aux_work & ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN) { - aux_work = handle_async_ready_clean(awdp, aux_work); - ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); - } + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_ASYNC_READY, + handle_async_ready); + /* ASYNC_READY must be before ASYNC_READY_CLEAN */ + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN, + handle_async_ready_clean); #endif -#ifdef ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN - if (aux_work & ERTS_SSI_AUX_WORK_CHECK_CHILDREN) { - aux_work = handle_check_children(awdp, aux_work); - ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); - } -#endif - if (aux_work & (ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM - | ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC)) { - aux_work = handle_fix_alloc(awdp, aux_work); - ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); - } + #ifdef ERTS_SMP - if (aux_work & ERTS_SSI_AUX_WORK_DD) { - aux_work = handle_delayed_dealloc(awdp, aux_work); - ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); - } - if (aux_work & ERTS_SSI_AUX_WORK_DD_THR_PRGR) { - aux_work = handle_delayed_dealloc_thr_prgr(awdp, aux_work); - ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); - } + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_MISC_THR_PRGR, + handle_misc_aux_work_thr_prgr); #endif -#ifdef ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK - if (aux_work & ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK) { - aux_work = handle_mseg_cache_check(awdp, aux_work); - ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); - } + /* MISC_THR_PRGR must be before MISC */ + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_MISC, + handle_misc_aux_work); + +#ifdef ERTS_SMP_SCHEDULERS_NEED_TO_CHECK_CHILDREN + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_CHECK_CHILDREN, + handle_check_children); +#endif + + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_SET_TMO, + handle_setup_aux_work_timer); + +#if HAVE_ERTS_MSEG + HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK, + handle_mseg_cache_check); #endif + ERTS_DBG_CHK_AUX_WORK_VAL(aux_work); + return aux_work; + +#undef HANDLE_AUX_WORK + } typedef struct { diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 173b7df69d..c23810f15a 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.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 @@ -248,25 +248,22 @@ typedef enum { | ERTS_SSI_FLG_WAITING \ | ERTS_SSI_FLG_SUSPENDED) -#define ERTS_SSI_AUX_WORK_SET_TMO (((erts_aint32_t) 1) << 0) -#define ERTS_SSI_AUX_WORK_CHECK_CHILDREN (((erts_aint32_t) 1) << 1) -#define ERTS_SSI_AUX_WORK_MISC (((erts_aint32_t) 1) << 2) -#ifdef ERTS_SMP -#define ERTS_SSI_AUX_WORK_MISC_THR_PRGR (((erts_aint32_t) 1) << 3) -#endif -#define ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM (((erts_aint32_t) 1) << 4) -#define ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC (((erts_aint32_t) 1) << 5) -#define ERTS_SSI_AUX_WORK_ASYNC_READY (((erts_aint32_t) 1) << 6) -#define ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN (((erts_aint32_t) 1) << 7) -#ifdef ERTS_SMP -#define ERTS_SSI_AUX_WORK_DD (((erts_aint32_t) 1) << 8) -#define ERTS_SSI_AUX_WORK_DD_THR_PRGR (((erts_aint32_t) 1) << 9) -#endif -#define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK (((erts_aint32_t) 1) << 10) +/* + * Keep ERTS_SSI_AUX_WORK flags in expected frequency order relative + * eachother. Most frequent - lowest bit number. + */ -#if !HAVE_ERTS_MSEG -# undef ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK -#endif +#define ERTS_SSI_AUX_WORK_DD (((erts_aint32_t) 1) << 0) +#define ERTS_SSI_AUX_WORK_DD_THR_PRGR (((erts_aint32_t) 1) << 1) +#define ERTS_SSI_AUX_WORK_FIX_ALLOC_DEALLOC (((erts_aint32_t) 1) << 2) +#define ERTS_SSI_AUX_WORK_FIX_ALLOC_LOWER_LIM (((erts_aint32_t) 1) << 3) +#define ERTS_SSI_AUX_WORK_ASYNC_READY (((erts_aint32_t) 1) << 4) +#define ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN (((erts_aint32_t) 1) << 5) +#define ERTS_SSI_AUX_WORK_MISC_THR_PRGR (((erts_aint32_t) 1) << 6) +#define ERTS_SSI_AUX_WORK_MISC (((erts_aint32_t) 1) << 7) +#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) typedef struct ErtsSchedulerSleepInfo_ ErtsSchedulerSleepInfo; @@ -407,6 +404,9 @@ typedef struct { int sched_id; ErtsSchedulerData *esdp; ErtsSchedulerSleepInfo *ssi; +#ifdef ERTS_SMP + ErtsThrPrgrVal current_thr_prgr; +#endif struct { int ix; #ifdef ERTS_SMP diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.c b/erts/emulator/beam/erl_sched_spec_pre_alloc.c index a7ccea7403..bff9d246a3 100644 --- a/erts/emulator/beam/erl_sched_spec_pre_alloc.c +++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-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 @@ -227,7 +227,6 @@ fetch_remote(erts_sspa_chunk_header_t *chdr, int max) ERTS_THR_MEMORY_BARRIER; else { chdr->head.next.unref_end = (erts_sspa_blk_t *) ilast; - ERTS_THR_MEMORY_BARRIER; chdr->head.next.thr_progress = erts_thr_progress_later(); erts_atomic32_set_relb(&chdr->tail.data.um_refc_ix, um_refc_ix); diff --git a/erts/emulator/beam/erl_smp.h b/erts/emulator/beam/erl_smp.h index 63179dfad4..a32e9d9d7c 100644 --- a/erts/emulator/beam/erl_smp.h +++ b/erts/emulator/beam/erl_smp.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2011. All Rights Reserved. + * Copyright Ericsson AB 2005-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 @@ -61,6 +61,11 @@ typedef erts_spinlock_t erts_smp_spinlock_t; typedef erts_rwlock_t erts_smp_rwlock_t; void erts_thr_fatal_error(int, char *); /* implemented in erl_init.c */ +#define ERTS_SMP_MEMORY_BARRIER ERTS_THR_MEMORY_BARRIER +#define ERTS_SMP_WRITE_MEMORY_BARRIER ERTS_THR_WRITE_MEMORY_BARRIER +#define ERTS_SMP_READ_MEMORY_BARRIER ERTS_THR_READ_MEMORY_BARRIER +#define ERTS_SMP_DATA_DEPENDENCY_READ_MEMORY_BARRIER ERTS_THR_DATA_DEPENDENCY_READ_MEMORY_BARRIER + #else /* #ifdef ERTS_SMP */ #define ERTS_SMP_THR_OPTS_DEFAULT_INITER {0} @@ -95,6 +100,11 @@ typedef struct { int gcc_is_buggy; } erts_smp_spinlock_t; typedef struct { int gcc_is_buggy; } erts_smp_rwlock_t; #endif +#define ERTS_SMP_MEMORY_BARRIER +#define ERTS_SMP_WRITE_MEMORY_BARRIER +#define ERTS_SMP_READ_MEMORY_BARRIER +#define ERTS_SMP_DATA_DEPENDENCY_READ_MEMORY_BARRIER + #endif /* #ifdef ERTS_SMP */ ERTS_GLB_INLINE void erts_smp_thr_init(erts_smp_thr_init_data_t *id); @@ -206,13 +216,8 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig); #endif /* #ifdef ERTS_THR_HAVE_SIG_FUNCS */ /* - * Functions implementing atomic operations with with no (nob), - * full (mb), acquire (acqb), release (relb), read (rb), and - * write (wb) memory barriers. - * - * If SMP support has been disabled, they are mapped to functions - * that performs the same operation, but aren't atomic and don't - * imply memory barriers. + * See "Documentation of atomics and memory barriers" at the top + * of erl_threads.h for info on atomics. */ #ifdef ERTS_SMP @@ -239,6 +244,11 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig); #define erts_smp_dw_atomic_read_relb erts_dw_atomic_read_relb #define erts_smp_dw_atomic_cmpxchg_relb erts_dw_atomic_cmpxchg_relb +#define erts_smp_dw_atomic_init_ddrb erts_dw_atomic_init_ddrb +#define erts_smp_dw_atomic_set_ddrb erts_dw_atomic_set_ddrb +#define erts_smp_dw_atomic_read_ddrb erts_dw_atomic_read_ddrb +#define erts_smp_dw_atomic_cmpxchg_ddrb erts_dw_atomic_cmpxchg_ddrb + #define erts_smp_dw_atomic_init_rb erts_dw_atomic_init_rb #define erts_smp_dw_atomic_set_rb erts_dw_atomic_set_rb #define erts_smp_dw_atomic_read_rb erts_dw_atomic_read_rb @@ -307,6 +317,20 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig); #define erts_smp_atomic_xchg_relb erts_atomic_xchg_relb #define erts_smp_atomic_cmpxchg_relb erts_atomic_cmpxchg_relb +#define erts_smp_atomic_init_ddrb erts_atomic_init_ddrb +#define erts_smp_atomic_set_ddrb erts_atomic_set_ddrb +#define erts_smp_atomic_read_ddrb erts_atomic_read_ddrb +#define erts_smp_atomic_inc_read_ddrb erts_atomic_inc_read_ddrb +#define erts_smp_atomic_dec_read_ddrb erts_atomic_dec_read_ddrb +#define erts_smp_atomic_inc_ddrb erts_atomic_inc_ddrb +#define erts_smp_atomic_dec_ddrb erts_atomic_dec_ddrb +#define erts_smp_atomic_add_read_ddrb erts_atomic_add_read_ddrb +#define erts_smp_atomic_add_ddrb erts_atomic_add_ddrb +#define erts_smp_atomic_read_bor_ddrb erts_atomic_read_bor_ddrb +#define erts_smp_atomic_read_band_ddrb erts_atomic_read_band_ddrb +#define erts_smp_atomic_xchg_ddrb erts_atomic_xchg_ddrb +#define erts_smp_atomic_cmpxchg_ddrb erts_atomic_cmpxchg_ddrb + #define erts_smp_atomic_init_rb erts_atomic_init_rb #define erts_smp_atomic_set_rb erts_atomic_set_rb #define erts_smp_atomic_read_rb erts_atomic_read_rb @@ -393,6 +417,20 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig); #define erts_smp_atomic32_xchg_relb erts_atomic32_xchg_relb #define erts_smp_atomic32_cmpxchg_relb erts_atomic32_cmpxchg_relb +#define erts_smp_atomic32_init_ddrb erts_atomic32_init_ddrb +#define erts_smp_atomic32_set_ddrb erts_atomic32_set_ddrb +#define erts_smp_atomic32_read_ddrb erts_atomic32_read_ddrb +#define erts_smp_atomic32_inc_read_ddrb erts_atomic32_inc_read_ddrb +#define erts_smp_atomic32_dec_read_ddrb erts_atomic32_dec_read_ddrb +#define erts_smp_atomic32_inc_ddrb erts_atomic32_inc_ddrb +#define erts_smp_atomic32_dec_ddrb erts_atomic32_dec_ddrb +#define erts_smp_atomic32_add_read_ddrb erts_atomic32_add_read_ddrb +#define erts_smp_atomic32_add_ddrb erts_atomic32_add_ddrb +#define erts_smp_atomic32_read_bor_ddrb erts_atomic32_read_bor_ddrb +#define erts_smp_atomic32_read_band_ddrb erts_atomic32_read_band_ddrb +#define erts_smp_atomic32_xchg_ddrb erts_atomic32_xchg_ddrb +#define erts_smp_atomic32_cmpxchg_ddrb erts_atomic32_cmpxchg_ddrb + #define erts_smp_atomic32_init_rb erts_atomic32_init_rb #define erts_smp_atomic32_set_rb erts_atomic32_set_rb #define erts_smp_atomic32_read_rb erts_atomic32_read_rb @@ -445,6 +483,11 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig); #define erts_smp_dw_atomic_read_relb erts_no_dw_atomic_read #define erts_smp_dw_atomic_cmpxchg_relb erts_no_dw_atomic_cmpxchg +#define erts_smp_dw_atomic_init_ddrb erts_no_dw_atomic_init +#define erts_smp_dw_atomic_set_ddrb erts_no_dw_atomic_set +#define erts_smp_dw_atomic_read_ddrb erts_no_dw_atomic_read +#define erts_smp_dw_atomic_cmpxchg_ddrb erts_no_dw_atomic_cmpxchg + #define erts_smp_dw_atomic_init_rb erts_no_dw_atomic_init #define erts_smp_dw_atomic_set_rb erts_no_dw_atomic_set #define erts_smp_dw_atomic_read_rb erts_no_dw_atomic_read @@ -513,6 +556,20 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig); #define erts_smp_atomic_xchg_relb erts_no_atomic_xchg #define erts_smp_atomic_cmpxchg_relb erts_no_atomic_cmpxchg +#define erts_smp_atomic_init_ddrb erts_no_atomic_set +#define erts_smp_atomic_set_ddrb erts_no_atomic_set +#define erts_smp_atomic_read_ddrb erts_no_atomic_read +#define erts_smp_atomic_inc_read_ddrb erts_no_atomic_inc_read +#define erts_smp_atomic_dec_read_ddrb erts_no_atomic_dec_read +#define erts_smp_atomic_inc_ddrb erts_no_atomic_inc +#define erts_smp_atomic_dec_ddrb erts_no_atomic_dec +#define erts_smp_atomic_add_read_ddrb erts_no_atomic_add_read +#define erts_smp_atomic_add_ddrb erts_no_atomic_add +#define erts_smp_atomic_read_bor_ddrb erts_no_atomic_read_bor +#define erts_smp_atomic_read_band_ddrb erts_no_atomic_read_band +#define erts_smp_atomic_xchg_ddrb erts_no_atomic_xchg +#define erts_smp_atomic_cmpxchg_ddrb erts_no_atomic_cmpxchg + #define erts_smp_atomic_init_rb erts_no_atomic_set #define erts_smp_atomic_set_rb erts_no_atomic_set #define erts_smp_atomic_read_rb erts_no_atomic_read @@ -599,6 +656,20 @@ ERTS_GLB_INLINE void erts_smp_thr_sigwait(const sigset_t *set, int *sig); #define erts_smp_atomic32_xchg_relb erts_no_atomic32_xchg #define erts_smp_atomic32_cmpxchg_relb erts_no_atomic32_cmpxchg +#define erts_smp_atomic32_init_ddrb erts_no_atomic32_set +#define erts_smp_atomic32_set_ddrb erts_no_atomic32_set +#define erts_smp_atomic32_read_ddrb erts_no_atomic32_read +#define erts_smp_atomic32_inc_read_ddrb erts_no_atomic32_inc_read +#define erts_smp_atomic32_dec_read_ddrb erts_no_atomic32_dec_read +#define erts_smp_atomic32_inc_ddrb erts_no_atomic32_inc +#define erts_smp_atomic32_dec_ddrb erts_no_atomic32_dec +#define erts_smp_atomic32_add_read_ddrb erts_no_atomic32_add_read +#define erts_smp_atomic32_add_ddrb erts_no_atomic32_add +#define erts_smp_atomic32_read_bor_ddrb erts_no_atomic32_read_bor +#define erts_smp_atomic32_read_band_ddrb erts_no_atomic32_read_band +#define erts_smp_atomic32_xchg_ddrb erts_no_atomic32_xchg +#define erts_smp_atomic32_cmpxchg_ddrb erts_no_atomic32_cmpxchg + #define erts_smp_atomic32_init_rb erts_no_atomic32_set #define erts_smp_atomic32_set_rb erts_no_atomic32_set #define erts_smp_atomic32_read_rb erts_no_atomic32_read diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c index 75f8209c3b..9ef83746c5 100644 --- a/erts/emulator/beam/erl_thr_progress.c +++ b/erts/emulator/beam/erl_thr_progress.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-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 @@ -33,10 +33,11 @@ * This module keeps track of the progress of a set of managed threads. Only * threads that behave well can be allowed to be managed. A managed thread * should update its thread progress frequently. Currently only scheduler - * threads and the aux_thread are managed threads. We typically do not want - * any async threads as managed threads since they cannot guarantee a - * frequent update of thread progress, since they execute user implemented - * driver code. + * threads, the system-message-dispatcher threads, and the aux-thread are + * managed threads. We typically do not want any async threads as managed + * threads since they cannot guarantee a frequent update of thread progress, + * since they execute user implemented driver code that is assumed to be + * time consuming. * * erts_thr_progress_current() returns the global current thread progress * value of managed threads. I.e., the latest progress value that all @@ -112,8 +113,10 @@ * * On 32-bit systems we therefore need a double word atomic. */ - +#undef read_acqb #define read_acqb erts_thr_prgr_read_acqb__ +#undef read_nob +#define read_nob erts_thr_prgr_read_nob__ #ifdef ARCH_64 @@ -129,12 +132,6 @@ set_nob(ERTS_THR_PRGR_ATOMIC *atmc, ErtsThrPrgrVal val) erts_atomic_set_nob(atmc, val); } -static ERTS_INLINE ErtsThrPrgrVal -read_nob(ERTS_THR_PRGR_ATOMIC *atmc) -{ - return (ErtsThrPrgrVal) erts_atomic_read_nob(atmc); -} - static ERTS_INLINE void init_nob(ERTS_THR_PRGR_ATOMIC *atmc, ErtsThrPrgrVal val) { @@ -143,52 +140,44 @@ init_nob(ERTS_THR_PRGR_ATOMIC *atmc, ErtsThrPrgrVal val) #else -#undef dw_sint_to_val -#define dw_sint_to_val erts_thr_prgr_dw_sint_to_val__ +#undef dw_aint_to_val +#define dw_aint_to_val erts_thr_prgr_dw_aint_to_val__ static void -val_to_dw_sint(ethr_dw_sint_t *dw_sint, ErtsThrPrgrVal val) +val_to_dw_aint(erts_dw_aint_t *dw_aint, ErtsThrPrgrVal val) { #ifdef ETHR_SU_DW_NAINT_T__ - dw_sint->dw_sint = (ETHR_SU_DW_NAINT_T__) val; + dw_aint->dw_sint = (ETHR_SU_DW_NAINT_T__) val; #else - dw_sint->sint[ETHR_DW_SINT_LOW_WORD] - = (ethr_sint_t) (val & 0xffffffff); - dw_sint->sint[ETHR_DW_SINT_HIGH_WORD] - = (ethr_sint_t) ((val >> 32) & 0xffffffff); + dw_aint->sint[ERTS_DW_AINT_LOW_WORD] + = (erts_aint_t) (val & 0xffffffff); + dw_aint->sint[ERTS_DW_AINT_HIGH_WORD] + = (erts_aint_t) ((val >> 32) & 0xffffffff); #endif } static ERTS_INLINE void set_mb(ERTS_THR_PRGR_ATOMIC *atmc, ErtsThrPrgrVal val) { - ethr_dw_sint_t dw_sint; - val_to_dw_sint(&dw_sint, val); - erts_dw_atomic_set_mb(atmc, &dw_sint); + erts_dw_aint_t dw_aint; + val_to_dw_aint(&dw_aint, val); + erts_dw_atomic_set_mb(atmc, &dw_aint); } static ERTS_INLINE void set_nob(ERTS_THR_PRGR_ATOMIC *atmc, ErtsThrPrgrVal val) { - ethr_dw_sint_t dw_sint; - val_to_dw_sint(&dw_sint, val); - erts_dw_atomic_set_nob(atmc, &dw_sint); -} - -static ERTS_INLINE ErtsThrPrgrVal -read_nob(ERTS_THR_PRGR_ATOMIC *atmc) -{ - ethr_dw_sint_t dw_sint; - erts_dw_atomic_read_nob(atmc, &dw_sint); - return erts_thr_prgr_dw_sint_to_val__(&dw_sint); + erts_dw_aint_t dw_aint; + val_to_dw_aint(&dw_aint, val); + erts_dw_atomic_set_nob(atmc, &dw_aint); } static ERTS_INLINE void init_nob(ERTS_THR_PRGR_ATOMIC *atmc, ErtsThrPrgrVal val) { - ethr_dw_sint_t dw_sint; - val_to_dw_sint(&dw_sint, val); - erts_dw_atomic_init_nob(atmc, &dw_sint); + erts_dw_aint_t dw_aint; + val_to_dw_aint(&dw_aint, val); + erts_dw_atomic_init_nob(atmc, &dw_aint); } #endif @@ -1222,9 +1211,9 @@ erts_thr_progress_block(void) } void -erts_thr_progress_fatal_error_block(SWord timeout) +erts_thr_progress_fatal_error_block(SWord timeout, + ErtsThrPrgrData *tmp_tpd_bufp) { - ErtsThrPrgrData tpd_buf; ErtsThrPrgrData *tpd = perhaps_thr_prgr_data(NULL); erts_aint32_t bc; SWord time_left = timeout; @@ -1248,7 +1237,7 @@ erts_thr_progress_fatal_error_block(SWord timeout) * since we never complete an unblock after a fatal error * block. */ - tpd = &tpd_buf; + tpd = tmp_tpd_bufp; init_tmp_thr_prgr_data(tpd); } diff --git a/erts/emulator/beam/erl_thr_progress.h b/erts/emulator/beam/erl_thr_progress.h index 68d14174b9..1bbc49993e 100644 --- a/erts/emulator/beam/erl_thr_progress.h +++ b/erts/emulator/beam/erl_thr_progress.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-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 @@ -44,7 +44,6 @@ #define erts_smp_thr_progress_unblock erts_thr_progress_unblock #define erts_smp_thr_progress_is_blocking erts_thr_progress_is_blocking -void erts_thr_progress_fatal_error_block(SWord timeout); void erts_thr_progress_block(void); void erts_thr_progress_unblock(void); int erts_thr_progress_is_blocking(void); @@ -73,6 +72,10 @@ typedef struct { ErtsThrPrgrVal current; } previous; } ErtsThrPrgrData; + +void erts_thr_progress_fatal_error_block(SWord timeout, + ErtsThrPrgrData *tmp_tpd_bufp); + #endif /* ERTS_SMP */ #endif @@ -86,6 +89,7 @@ typedef struct { #ifdef ERTS_SMP #define ERTS_THR_PRGR_VAL_WAITING (~((ErtsThrPrgrVal) 0)) +#define ERTS_THR_PRGR_INVALID (~((ErtsThrPrgrVal) 0)) extern erts_tsd_key_t erts_thr_prgr_data_key__; @@ -127,14 +131,19 @@ void erts_thr_progress_dbg_print_state(void); #ifdef ARCH_32 #define ERTS_THR_PRGR_ATOMIC erts_dw_atomic_t -ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_dw_sint_to_val__(ethr_dw_sint_t *dw_sint); +ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_dw_aint_to_val__(erts_dw_aint_t *dw_aint); #endif +ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_nob__(ERTS_THR_PRGR_ATOMIC *atmc); ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_acqb__(ERTS_THR_PRGR_ATOMIC *atmc); +ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_mb__(ERTS_THR_PRGR_ATOMIC *atmc); ERTS_GLB_INLINE int erts_thr_progress_is_managed_thread(void); +ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_progress_later_than(ErtsThrPrgrVal val); ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_progress_later(void); ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_progress_current(void); ERTS_GLB_INLINE int erts_thr_progress_has_passed__(ErtsThrPrgrVal val1, ErtsThrPrgrVal val2); +ERTS_GLB_INLINE int erts_thr_progress_has_reached_this(ErtsThrPrgrVal this, ErtsThrPrgrVal val); +ERTS_GLB_INLINE int erts_thr_progress_cmp(ErtsThrPrgrVal val1, ErtsThrPrgrVal val2); ERTS_GLB_INLINE int erts_thr_progress_has_reached(ErtsThrPrgrVal val); #if ERTS_GLB_INLINE_INCL_FUNC_DEF @@ -142,33 +151,61 @@ ERTS_GLB_INLINE int erts_thr_progress_has_reached(ErtsThrPrgrVal val); #ifdef ARCH_64 ERTS_GLB_INLINE ErtsThrPrgrVal +erts_thr_prgr_read_nob__(ERTS_THR_PRGR_ATOMIC *atmc) +{ + return (ErtsThrPrgrVal) erts_atomic_read_nob(atmc); +} + +ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_acqb__(ERTS_THR_PRGR_ATOMIC *atmc) { return (ErtsThrPrgrVal) erts_atomic_read_acqb(atmc); } +ERTS_GLB_INLINE ErtsThrPrgrVal +erts_thr_prgr_read_mb__(ERTS_THR_PRGR_ATOMIC *atmc) +{ + return (ErtsThrPrgrVal) erts_atomic_read_mb(atmc); +} + #else /* ARCH_32 */ ERTS_GLB_INLINE ErtsThrPrgrVal -erts_thr_prgr_dw_sint_to_val__(ethr_dw_sint_t *dw_sint) +erts_thr_prgr_dw_aint_to_val__(erts_dw_aint_t *dw_aint) { #ifdef ETHR_SU_DW_NAINT_T__ - return (ErtsThrPrgrVal) dw_sint->dw_sint; + return (ErtsThrPrgrVal) dw_aint->dw_sint; #else ErtsThrPrgrVal res; - res = (ErtsThrPrgrVal) ((Uint32) dw_sint->sint[ETHR_DW_SINT_HIGH_WORD]); + res = (ErtsThrPrgrVal) ((Uint32) dw_aint->sint[ERTS_DW_AINT_HIGH_WORD]); res <<= 32; - res |= (ErtsThrPrgrVal) ((Uint32) dw_sint->sint[ETHR_DW_SINT_LOW_WORD]); + res |= (ErtsThrPrgrVal) ((Uint32) dw_aint->sint[ERTS_DW_AINT_LOW_WORD]); return res; #endif } ERTS_GLB_INLINE ErtsThrPrgrVal +erts_thr_prgr_read_nob__(ERTS_THR_PRGR_ATOMIC *atmc) +{ + erts_dw_aint_t dw_aint; + erts_dw_atomic_read_nob(atmc, &dw_aint); + return erts_thr_prgr_dw_aint_to_val__(&dw_aint); +} + +ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_acqb__(ERTS_THR_PRGR_ATOMIC *atmc) { - ethr_dw_sint_t dw_sint; - erts_dw_atomic_read_acqb(atmc, &dw_sint); - return erts_thr_prgr_dw_sint_to_val__(&dw_sint); + erts_dw_aint_t dw_aint; + erts_dw_atomic_read_acqb(atmc, &dw_aint); + return erts_thr_prgr_dw_aint_to_val__(&dw_aint); +} + +ERTS_GLB_INLINE ErtsThrPrgrVal +erts_thr_prgr_read_mb__(ERTS_THR_PRGR_ATOMIC *atmc) +{ + erts_dw_aint_t dw_aint; + erts_dw_atomic_read_mb(atmc, &dw_aint); + return erts_thr_prgr_dw_aint_to_val__(&dw_aint); } #endif @@ -181,9 +218,9 @@ erts_thr_progress_is_managed_thread(void) } ERTS_GLB_INLINE ErtsThrPrgrVal -erts_thr_progress_later(void) +erts_thr_progress_later_than(ErtsThrPrgrVal val) { - ErtsThrPrgrVal val = erts_thr_prgr_read_acqb__(&erts_thr_prgr__.current); + ERTS_THR_MEMORY_BARRIER; if (val == (ERTS_THR_PRGR_VAL_WAITING-((ErtsThrPrgrVal)2))) return ((ErtsThrPrgrVal) 0); else if (val == (ERTS_THR_PRGR_VAL_WAITING-((ErtsThrPrgrVal)1))) @@ -193,9 +230,19 @@ erts_thr_progress_later(void) } ERTS_GLB_INLINE ErtsThrPrgrVal +erts_thr_progress_later(void) +{ + ErtsThrPrgrVal val = erts_thr_prgr_read_mb__(&erts_thr_prgr__.current); + return erts_thr_progress_later_than(val); +} + +ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_progress_current(void) { - return erts_thr_prgr_read_acqb__(&erts_thr_prgr__.current); + if (erts_thr_progress_is_managed_thread()) + return erts_thr_prgr_read_nob__(&erts_thr_prgr__.current); + else + return erts_thr_prgr_read_acqb__(&erts_thr_prgr__.current); } ERTS_GLB_INLINE int @@ -217,13 +264,29 @@ erts_thr_progress_has_passed__(ErtsThrPrgrVal val1, ErtsThrPrgrVal val0) } ERTS_GLB_INLINE int -erts_thr_progress_has_reached(ErtsThrPrgrVal val) +erts_thr_progress_has_reached_this(ErtsThrPrgrVal this, ErtsThrPrgrVal val) +{ + if (this == val) + return 1; + return erts_thr_progress_has_passed__(this, val); +} + +ERTS_GLB_INLINE int +erts_thr_progress_cmp(ErtsThrPrgrVal val1, ErtsThrPrgrVal val2) { - ErtsThrPrgrVal current; - current = erts_thr_prgr_read_acqb__(&erts_thr_prgr__.current); - if (current == val) + if (val1 == val2) + return 0; + if (erts_thr_progress_has_passed__(val1, val2)) return 1; - return erts_thr_progress_has_passed__(current, val); + else + return -1; +} + +ERTS_GLB_INLINE int +erts_thr_progress_has_reached(ErtsThrPrgrVal val) +{ + ErtsThrPrgrVal current = erts_thr_progress_current(); + return erts_thr_progress_has_reached_this(current, val); } #endif diff --git a/erts/emulator/beam/erl_thr_queue.c b/erts/emulator/beam/erl_thr_queue.c index efb8c635d7..70949ece76 100644 --- a/erts/emulator/beam/erl_thr_queue.c +++ b/erts/emulator/beam/erl_thr_queue.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-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 @@ -418,10 +418,9 @@ clean(ErtsThrQ_t *q, int max_ops, int do_notify) } if (q->head.unref_end == (ErtsThrQElement_t *) ilast) - ERTS_THR_MEMORY_BARRIER; + ERTS_SMP_MEMORY_BARRIER; else { q->head.next.unref_end = (ErtsThrQElement_t *) ilast; - ERTS_THR_MEMORY_BARRIER; #ifdef ERTS_SMP q->head.next.thr_progress = erts_thr_progress_later(); #endif diff --git a/erts/emulator/beam/erl_threads.h b/erts/emulator/beam/erl_threads.h index 065e7077c0..ee47c98009 100644 --- a/erts/emulator/beam/erl_threads.h +++ b/erts/emulator/beam/erl_threads.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2011. All Rights Reserved. + * Copyright Ericsson AB 2001-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 @@ -25,6 +25,235 @@ #ifndef ERL_THREAD_H__ #define ERL_THREAD_H__ +/* + * --- Documentation of atomics and memory barriers -------------------------- + * + * The following explicit memory barriers exist: + * + * - ERTS_THR_MEMORY_BARRIER + * Full memory barrier. Orders both loads, and stores. No + * load or store is allowed to be reordered over the + * barrier. + * - ERTS_THR_WRITE_MEMORY_BARRIER + * Write barrier. Orders *only* stores. These are not + * allowed to be reordered over the barrier. + * - ERTS_THR_READ_MEMORY_BARRIER + * Read barrier. Orders *only* loads. These are not + * allowed to be reordered over the barrier. + * - ERTS_THR_DATA_DEPENDENCY_READ_MEMORY_BARRIER + * Data dependency read barrier. Orders *only* loads + * according to data dependency across the barrier. + * + * If thread support has been disabled, these barriers will become no-ops. + * + * If the prefix ERTS_THR_ is replaced with ERTS_SMP_, the barriers will + * be enabled only in the SMP enabled runtime system. + * + * --- Atomic operations --- + * + * Atomics operations exist for 32-bit, word size, and double word size + * integers. Function prototypes are listed below. + * + * Each function implementing an atomic operation exist with the following + * implied memory barrier semantics. Not all combinations are useful, but + * all of them exist for simplicity. <B> is suffix in function name: + * + * - <B> - Description + * + * - mb - Full memory barrier. Orders both loads, and + * stores before, and after the atomic operation. + * No load or store is allowed to be reordered + * over the atomic operation. + * - relb - Release barrier. Orders both loads, and + * stores appearing *before* the atomic + * operation. These are not allowed to be + * reordered over the atomic operation. + * - acqb - Acquire barrier. Orders both loads, and stores + * appearing *after* the atomic operation. These + * are not allowed to be reordered over the + * atomic operation. + * - wb - Write barrier. Orders *only* stores. These are + * not allowed to be reordered over the barrier. + * Store in atomic operation is ordered *after* + * the barrier. + * - rb - Read barrier. Orders *only* loads. These are + * not allowed to be reordered over the barrier. + * Load in atomic operation is ordered *before* + * the barrier. + * - ddrb - Data dependency read barrier. Orders *only* + * loads according to data dependency across the + * barrier. Load in atomic operation is ordered + * before the barrier. + * + * If thread support has been disabled, these functions are mapped to + * functions that performs the same operation, but aren't atomic + * and don't imply any memory barriers. + * + * If the atomic operations are prefixed with erts_smp_ instead of only + * erts_ the atomic operations will only be atomic in the SMP enabled + * runtime system, and will be mapped to non-atomic operations without + * memory barriers in the runtime system without SMP support. Atomic + * operations with erts_smp_ prefix should use the atomic types + * erts_smp_atomic32_t, erts_smp_atomic_t, and erts_smp_dw_atomic_t + * instead of erts_atomic32_t, erts_atomic_t, and erts_dw_atomic_t. The + * integer data types erts_aint32_t, erts_aint_t, and erts_dw_atomic_t + * are the same. + * + * --- 32-bit atomic operations --- + * + * The following 32-bit atomic operations exist. <B> should be + * replaced with a supported memory barrier (see above). Note + * that sizeof(erts_atomic32_t) might be larger than 4! + * + * + * Initialize (not necessarily the same as the set operation): + * void erts_atomic32_init_<B>(erts_atomic32_t *atmc, + * erts_aint32_t val); + * + * Set value: + * void erts_atomic32_set_<B>(erts_atomic32_t *atmc, + * erts_aint32_t val); + * + * Read; returns current value: + * erts_aint32_t erts_atomic32_read_<B>(erts_atomic32_t *atmc); + * + * Increment; returns resulting value: + * erts_aint32_t erts_atomic32_inc_read_<B>(erts_atomic32_t *atmc); + * + * Decrement; returns resulting value: + * erts_aint32_t erts_atomic32_dec_read_<B>(erts_atomic32_t *atmc); + * + * Increment: + * void erts_atomic32_inc_<B>(erts_atomic32_t *atmc); + * + * Decrement: + * void erts_atomic32_dec_<B>(erts_atomic32_t *atmc); + * + * Add value; returns resulting value: + * erts_aint32_t erts_atomic32_add_read_<B>(erts_atomic32_t *atmc, + * erts_aint32_t val); + * + * Add value: + * void erts_atomic32_add_<B>(erts_atomic32_t *atmc, + * erts_aint32_t val); + * + * Bitwise-or; returns previous value: + * erts_aint32_t erts_atomic32_read_bor_<B>(erts_atomic32_t *atmc, + * erts_aint32_t val); + * + * Bitwise-and; returns previous value: + * erts_aint32_t erts_atomic32_read_band_<B>(erts_atomic32_t *atmc, + * erts_aint32_t val); + * + * Exchange; returns previous value: + * erts_aint32_t erts_atomic32_xchg_<B>(erts_atomic32_t *atmc, + * erts_aint32_t val); + * + * Compare and exchange; returns previous or current value. If + * returned value equals 'exp' the value was changed to 'new'; + * otherwise not: + * erts_aint32_t erts_atomic32_cmpxchg_<B>(erts_atomic32_t *a, + * erts_aint32_t new, + * erts_aint32_t exp); + * + * --- Word size atomic operations --- + * + * The following word size (same size as sizeof(void *)) atomic + * operations exist. <B> should be replaced with a supported + * memory barrier (see above). Note that sizeof(erts_atomic_t) + * might be larger than sizeof(void *)! + * + * Initialize (not necessarily the same as the set operation): + * void erts_atomic_init_<B>(erts_atomic_t *atmc, + * erts_aint_t val); + * + * Set value; + * void erts_atomic_set_<B>(erts_atomic_t *atmc, + * erts_aint_t val); + * + * Read; returns current value: + * erts_aint_t erts_atomic_read_<B>(erts_atomic_t *atmc); + * + * Increment; returns resulting value: + * erts_aint_t erts_atomic_inc_read_<B>(erts_atomic_t *atmc); + * + * Decrement; returns resulting value: + * erts_aint_t erts_atomic_dec_read_<B>(erts_atomic_t *atmc); + * + * Increment: + * void erts_atomic_inc_<B>(erts_atomic_t *atmc); + * + * Decrement: + * void erts_atomic_dec_<B>(erts_atomic_t *atmc); + * + * Add value; returns resulting value: + * erts_aint_t erts_atomic_add_read_<B>(erts_atomic_t *atmc, + * erts_aint_t val); + * + * Add value: + * void erts_atomic_add_<B>(erts_atomic_t *atmc, + * erts_aint_t val); + * + * Bitwise-or; returns previous value: + * erts_aint_t erts_atomic_read_bor_<B>(erts_atomic_t *atmc, + * erts_aint_t val); + * + * Bitwise-and; returns previous value: + * erts_aint_t erts_atomic_read_band_<B>(erts_atomic_t *atmc, + * erts_aint_t val); + * + * Exchange; returns previous value: + * erts_aint_t erts_atomic_xchg_<B>(erts_atomic_t *atmc, + * erts_aint_t val); + * + * Compare and exchange; returns previous or current value. If + * returned value equals 'exp' the value was changed to 'new'; + * otherwise not: + * erts_aint_t erts_atomic_cmpxchg_<B>(erts_atomic_t *a, + * erts_aint_t new, + * erts_aint_t exp); + * + * --- Double word size atomic operations --- + * + * The following double word atomic operations exist. <B> should be + * replaced with a supported memory barrier (see above). + * + * Note that sizeof(erts_dw_atomic_t) usually is larger than + * 2*sizeof(void *)! + * + * The erts_dw_aint_t data type should be accessed as if it was defined + * like this: + * + * typedef struct { + * erts_aint_t sint[2]; + * } erts_dw_aint_t; + * + * Most significant word is 'sint[ERTS_DW_AINT_HIGH_WORD]' and least + * significant word is 'sint[ERTS_DW_AINT_LOW_WORD]'. + * + * + * Initialize (not necessarily the same as the set operation): + * void erts_dw_atomic_init_<B>(erts_dw_atomic_t *var, + * erts_dw_aint_t *val); + * + * Set; value is written into 'val': + * void erts_dw_atomic_set_<B>(erts_dw_atomic_t *var, + * erts_dw_aint_t *val); + * + * Read; value is written into 'val': + * void erts_dw_atomic_read_<B>(erts_dw_atomic_t *var, + * erts_dw_aint_t *val); + * + * Compare and exchange; returns a value != 0 if exchange was + * made; otherwise 0. 'new_val' contains new value to set. If 'exp_act' + * contains the same value as in memory when the function is called, + * 'new' is written to memory; otherwise, not. If exchange was not + * made, 'exp_act' contains the actual value in memory: + * int erts_dw_atomic_cmpxchg_<B>(erts_dw_atomic_t *var, + * erts_dw_aint_t *new, + * erts_dw_aint_t *exp_act); + */ + #define ERTS_SPIN_BODY ETHR_SPIN_BODY #include "sys.h" @@ -52,6 +281,9 @@ typedef Sint32 erts_no_atomic32_t; #endif #define ERTS_THR_MEMORY_BARRIER ETHR_MEMORY_BARRIER +#define ERTS_THR_WRITE_MEMORY_BARRIER ETHR_WRITE_MEMORY_BARRIER +#define ERTS_THR_READ_MEMORY_BARRIER ETHR_READ_MEMORY_BARRIER +#define ERTS_THR_DATA_DEPENDENCY_READ_MEMORY_BARRIER ETHR_READ_DEPEND_MEMORY_BARRIER #ifdef ERTS_ENABLE_LOCK_COUNT #define erts_mtx_lock(L) erts_mtx_lock_x(L, __FILE__, __LINE__) @@ -113,6 +345,9 @@ typedef ethr_ts_event erts_tse_t; #define erts_aint32_t ethr_sint32_t #define erts_atomic32_t ethr_atomic32_t +#define ERTS_DW_AINT_HIGH_WORD ETHR_DW_SINT_HIGH_WORD +#define ERTS_DW_AINT_LOW_WORD ETHR_DW_SINT_LOW_WORD + /* spinlock */ typedef struct { ethr_spinlock_t slck; @@ -149,6 +384,9 @@ __decl_noreturn void __noreturn erts_thr_fatal_error(int, char *); #else /* #ifdef USE_THREADS */ #define ERTS_THR_MEMORY_BARRIER +#define ERTS_THR_WRITE_MEMORY_BARRIER +#define ERTS_THR_READ_MEMORY_BARRIER +#define ERTS_THR_DATA_DEPENDENCY_READ_MEMORY_BARRIER #define ERTS_THR_OPTS_DEFAULT_INITER 0 typedef int erts_thr_opts_t; @@ -361,18 +599,13 @@ ERTS_GLB_INLINE void erts_thr_sigmask(int how, const sigset_t *set, ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig); #endif /* #ifdef HAVE_ETHR_SIG_FUNCS */ +#ifdef USE_THREADS + /* - * Functions implementing atomic operations with with no (nob), - * full (mb), acquire (acqb), release (relb), read (rb), and - * write (wb) memory barriers. - * - * If thread support has been disabled, they are mapped to - * functions that performs the same operation, but aren't atomic - * and don't imply memory barriers. + * See "Documentation of atomics and memory barriers" at the top + * of this file for info on atomics. */ -#ifdef USE_THREADS - /* Double word size atomics */ #define erts_dw_atomic_init_nob ethr_dw_atomic_init @@ -395,6 +628,11 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig); #define erts_dw_atomic_read_relb ethr_dw_atomic_read_relb #define erts_dw_atomic_cmpxchg_relb ethr_dw_atomic_cmpxchg_relb +#define erts_dw_atomic_init_ddrb ethr_dw_atomic_init_ddrb +#define erts_dw_atomic_set_ddrb ethr_dw_atomic_set_ddrb +#define erts_dw_atomic_read_ddrb ethr_dw_atomic_read_ddrb +#define erts_dw_atomic_cmpxchg_ddrb ethr_dw_atomic_cmpxchg_ddrb + #define erts_dw_atomic_init_rb ethr_dw_atomic_init_rb #define erts_dw_atomic_set_rb ethr_dw_atomic_set_rb #define erts_dw_atomic_read_rb ethr_dw_atomic_read_rb @@ -463,6 +701,20 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig); #define erts_atomic_xchg_relb ethr_atomic_xchg_relb #define erts_atomic_cmpxchg_relb ethr_atomic_cmpxchg_relb +#define erts_atomic_init_ddrb ethr_atomic_init_ddrb +#define erts_atomic_set_ddrb ethr_atomic_set_ddrb +#define erts_atomic_read_ddrb ethr_atomic_read_ddrb +#define erts_atomic_inc_read_ddrb ethr_atomic_inc_read_ddrb +#define erts_atomic_dec_read_ddrb ethr_atomic_dec_read_ddrb +#define erts_atomic_inc_ddrb ethr_atomic_inc_ddrb +#define erts_atomic_dec_ddrb ethr_atomic_dec_ddrb +#define erts_atomic_add_read_ddrb ethr_atomic_add_read_ddrb +#define erts_atomic_add_ddrb ethr_atomic_add_ddrb +#define erts_atomic_read_bor_ddrb ethr_atomic_read_bor_ddrb +#define erts_atomic_read_band_ddrb ethr_atomic_read_band_ddrb +#define erts_atomic_xchg_ddrb ethr_atomic_xchg_ddrb +#define erts_atomic_cmpxchg_ddrb ethr_atomic_cmpxchg_ddrb + #define erts_atomic_init_rb ethr_atomic_init_rb #define erts_atomic_set_rb ethr_atomic_set_rb #define erts_atomic_read_rb ethr_atomic_read_rb @@ -549,6 +801,20 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig); #define erts_atomic32_xchg_relb ethr_atomic32_xchg_relb #define erts_atomic32_cmpxchg_relb ethr_atomic32_cmpxchg_relb +#define erts_atomic32_init_ddrb ethr_atomic32_init_ddrb +#define erts_atomic32_set_ddrb ethr_atomic32_set_ddrb +#define erts_atomic32_read_ddrb ethr_atomic32_read_ddrb +#define erts_atomic32_inc_read_ddrb ethr_atomic32_inc_read_ddrb +#define erts_atomic32_dec_read_ddrb ethr_atomic32_dec_read_ddrb +#define erts_atomic32_inc_ddrb ethr_atomic32_inc_ddrb +#define erts_atomic32_dec_ddrb ethr_atomic32_dec_ddrb +#define erts_atomic32_add_read_ddrb ethr_atomic32_add_read_ddrb +#define erts_atomic32_add_ddrb ethr_atomic32_add_ddrb +#define erts_atomic32_read_bor_ddrb ethr_atomic32_read_bor_ddrb +#define erts_atomic32_read_band_ddrb ethr_atomic32_read_band_ddrb +#define erts_atomic32_xchg_ddrb ethr_atomic32_xchg_ddrb +#define erts_atomic32_cmpxchg_ddrb ethr_atomic32_cmpxchg_ddrb + #define erts_atomic32_init_rb ethr_atomic32_init_rb #define erts_atomic32_set_rb ethr_atomic32_set_rb #define erts_atomic32_read_rb ethr_atomic32_read_rb @@ -601,6 +867,11 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig); #define erts_dw_atomic_read_relb erts_no_dw_atomic_read #define erts_dw_atomic_cmpxchg_relb erts_no_dw_atomic_cmpxchg +#define erts_dw_atomic_init_ddrb erts_no_dw_atomic_init +#define erts_dw_atomic_set_ddrb erts_no_dw_atomic_set +#define erts_dw_atomic_read_ddrb erts_no_dw_atomic_read +#define erts_dw_atomic_cmpxchg_ddrb erts_no_dw_atomic_cmpxchg + #define erts_dw_atomic_init_rb erts_no_dw_atomic_init #define erts_dw_atomic_set_rb erts_no_dw_atomic_set #define erts_dw_atomic_read_rb erts_no_dw_atomic_read @@ -669,6 +940,20 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig); #define erts_atomic_xchg_relb erts_no_atomic_xchg #define erts_atomic_cmpxchg_relb erts_no_atomic_cmpxchg +#define erts_atomic_init_ddrb erts_no_atomic_set +#define erts_atomic_set_ddrb erts_no_atomic_set +#define erts_atomic_read_ddrb erts_no_atomic_read +#define erts_atomic_inc_read_ddrb erts_no_atomic_inc_read +#define erts_atomic_dec_read_ddrb erts_no_atomic_dec_read +#define erts_atomic_inc_ddrb erts_no_atomic_inc +#define erts_atomic_dec_ddrb erts_no_atomic_dec +#define erts_atomic_add_read_ddrb erts_no_atomic_add_read +#define erts_atomic_add_ddrb erts_no_atomic_add +#define erts_atomic_read_bor_ddrb erts_no_atomic_read_bor +#define erts_atomic_read_band_ddrb erts_no_atomic_read_band +#define erts_atomic_xchg_ddrb erts_no_atomic_xchg +#define erts_atomic_cmpxchg_ddrb erts_no_atomic_cmpxchg + #define erts_atomic_init_rb erts_no_atomic_set #define erts_atomic_set_rb erts_no_atomic_set #define erts_atomic_read_rb erts_no_atomic_read @@ -755,6 +1040,20 @@ ERTS_GLB_INLINE void erts_thr_sigwait(const sigset_t *set, int *sig); #define erts_atomic32_xchg_relb erts_no_atomic32_xchg #define erts_atomic32_cmpxchg_relb erts_no_atomic32_cmpxchg +#define erts_atomic32_init_ddrb erts_no_atomic32_set +#define erts_atomic32_set_ddrb erts_no_atomic32_set +#define erts_atomic32_read_ddrb erts_no_atomic32_read +#define erts_atomic32_inc_read_ddrb erts_no_atomic32_inc_read +#define erts_atomic32_dec_read_ddrb erts_no_atomic32_dec_read +#define erts_atomic32_inc_ddrb erts_no_atomic32_inc +#define erts_atomic32_dec_ddrb erts_no_atomic32_dec +#define erts_atomic32_add_read_ddrb erts_no_atomic32_add_read +#define erts_atomic32_add_ddrb erts_no_atomic32_add +#define erts_atomic32_read_bor_ddrb erts_no_atomic32_read_bor +#define erts_atomic32_read_band_ddrb erts_no_atomic32_read_band +#define erts_atomic32_xchg_ddrb erts_no_atomic32_xchg +#define erts_atomic32_cmpxchg_ddrb erts_no_atomic32_cmpxchg + #define erts_atomic32_init_rb erts_no_atomic32_set #define erts_atomic32_set_rb erts_no_atomic32_set #define erts_atomic32_read_rb erts_no_atomic32_read diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 152dbcf085..9d52ed4e98 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3118,6 +3118,9 @@ decoded_size(byte *ep, byte* endp, int internal_tags) case LARGE_BIG_EXT: CHKSIZE(4); n = get_int32(ep); + if (n > BIG_ARITY_MAX*sizeof(ErtsDigit)) { + return -1; + } SKIP2(n,4+1); /* skip, size,sign,digits */ heap_size += 1+1+(n+sizeof(Eterm)-1)/sizeof(Eterm); /* XXX: 1 too much? */ break; diff --git a/erts/include/internal/ethr_atomics.h b/erts/include/internal/ethr_atomics.h index 0f3c26f1df..612894b8c1 100644 --- a/erts/include/internal/ethr_atomics.h +++ b/erts/include/internal/ethr_atomics.h @@ -10,7 +10,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-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 @@ -64,12 +64,31 @@ * Appart from a function implementing the atomic operation * with unspecified memory barrier semantics, there are * functions implementing each operation with the following - * memory barrier semantics: - * - rb (read barrier) - * - wb (write barrier) - * - acqb (acquire barrier) - * - relb (release barrier) - * - mb (full memory barrier) + * implied memory barrier semantics: + * - mb - Full memory barrier. Orders both loads, and + * stores before, and after the atomic operation. + * No load or store is allowed to be reordered + * over the atomic operation. + * - relb - Release barrier. Orders both loads, and + * stores appearing *before* the atomic + * operation. These are not allowed to be + * reordered over the atomic operation. + * - acqb - Acquire barrier. Orders both loads, and stores + * appearing *after* the atomic operation. These + * are not allowed to be reordered over the + * atomic operation. + * - wb - Write barrier. Orders *only* stores. These are + * not allowed to be reordered over the barrier. + * Store in atomic operation is ordered *after* + * the barrier. + * - rb - Read barrier. Orders *only* loads. These are + * not allowed to be reordered over the barrier. + * Load in atomic operation is ordered *before* + * the barrier. + * - ddrb - Data dependency read barrier. Orders *only* + * loads according to data dependency across the + * barrier. Load in atomic operation is ordered + * before the barrier. * * We implement all of these operation/barrier * combinations, regardless of whether they are useful @@ -535,24 +554,28 @@ char **ethr_native_su_dw_atomic_ops(void); #ifdef ETHR_NEED_DW_ATMC_PROTOTYPES__ ethr_sint_t *ethr_dw_atomic_addr(ethr_dw_atomic_t *var); int ethr_dw_atomic_cmpxchg(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_sint_t *old_val); +int ethr_dw_atomic_cmpxchg_ddrb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_sint_t *old_val); int ethr_dw_atomic_cmpxchg_rb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_sint_t *old_val); int ethr_dw_atomic_cmpxchg_wb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_sint_t *old_val); int ethr_dw_atomic_cmpxchg_acqb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_sint_t *old_val); int ethr_dw_atomic_cmpxchg_relb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_sint_t *old_val); int ethr_dw_atomic_cmpxchg_mb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_sint_t *old_val); void ethr_dw_atomic_set(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); +void ethr_dw_atomic_set_ddrb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ethr_dw_atomic_set_rb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ethr_dw_atomic_set_wb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ethr_dw_atomic_set_acqb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ethr_dw_atomic_set_relb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ethr_dw_atomic_set_mb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ethr_dw_atomic_read(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); +void ethr_dw_atomic_read_ddrb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ethr_dw_atomic_read_rb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ethr_dw_atomic_read_wb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ethr_dw_atomic_read_acqb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ethr_dw_atomic_read_relb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ethr_dw_atomic_read_mb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ethr_dw_atomic_init(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); +void ethr_dw_atomic_init_ddrb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ethr_dw_atomic_init_rb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ethr_dw_atomic_init_wb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ethr_dw_atomic_init_acqb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); @@ -561,24 +584,28 @@ void ethr_dw_atomic_init_mb(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); #if defined(ETHR_RTCHK_USE_NATIVE_DW_ATOMIC_IMPL__) ethr_sint_t *ETHR_DW_ATOMIC_FUNC__(addr)(ethr_dw_atomic_t *var); int ETHR_DW_ATOMIC_FUNC__(cmpxchg)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_sint_t *old_val); +int ETHR_DW_ATOMIC_FUNC__(cmpxchg_ddrb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_sint_t *old_val); int ETHR_DW_ATOMIC_FUNC__(cmpxchg_rb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_sint_t *old_val); int ETHR_DW_ATOMIC_FUNC__(cmpxchg_wb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_sint_t *old_val); int ETHR_DW_ATOMIC_FUNC__(cmpxchg_acqb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_sint_t *old_val); int ETHR_DW_ATOMIC_FUNC__(cmpxchg_relb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_sint_t *old_val); int ETHR_DW_ATOMIC_FUNC__(cmpxchg_mb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_sint_t *old_val); void ETHR_DW_ATOMIC_FUNC__(set)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); +void ETHR_DW_ATOMIC_FUNC__(set_ddrb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ETHR_DW_ATOMIC_FUNC__(set_rb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ETHR_DW_ATOMIC_FUNC__(set_wb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ETHR_DW_ATOMIC_FUNC__(set_acqb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ETHR_DW_ATOMIC_FUNC__(set_relb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ETHR_DW_ATOMIC_FUNC__(set_mb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ETHR_DW_ATOMIC_FUNC__(read)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); +void ETHR_DW_ATOMIC_FUNC__(read_ddrb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ETHR_DW_ATOMIC_FUNC__(read_rb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ETHR_DW_ATOMIC_FUNC__(read_wb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ETHR_DW_ATOMIC_FUNC__(read_acqb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ETHR_DW_ATOMIC_FUNC__(read_relb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ETHR_DW_ATOMIC_FUNC__(read_mb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ETHR_DW_ATOMIC_FUNC__(init)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); +void ETHR_DW_ATOMIC_FUNC__(init_ddrb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ETHR_DW_ATOMIC_FUNC__(init_rb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ETHR_DW_ATOMIC_FUNC__(init_wb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); void ETHR_DW_ATOMIC_FUNC__(init_acqb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val); @@ -1491,6 +1518,15 @@ static ETHR_INLINE int ETHR_DW_ATMC_FUNC__(cmpxchg_mb)(ethr_dw_atomic_t *var, et return res; } +static ETHR_INLINE int ETHR_DW_ATMC_FUNC__(cmpxchg_ddrb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_sint_t *old_val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_DW_ATMC_FUNC__(cmpxchg)(var, val, old_val); +#else + return ETHR_DW_ATMC_FUNC__(cmpxchg_rb)(var, val, old_val); +#endif +} + /* --- set() --- */ @@ -1981,6 +2017,15 @@ static ETHR_INLINE void ETHR_DW_ATMC_FUNC__(set_mb)(ethr_dw_atomic_t *var, ethr_ } +static ETHR_INLINE void ETHR_DW_ATMC_FUNC__(set_ddrb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_DW_ATMC_FUNC__(set)(var, val); +#else + ETHR_DW_ATMC_FUNC__(set_rb)(var, val); +#endif +} + /* --- read() --- */ @@ -2309,6 +2354,15 @@ static ETHR_INLINE void ETHR_DW_ATMC_FUNC__(read_mb)(ethr_dw_atomic_t *var, ethr } +static ETHR_INLINE void ETHR_DW_ATMC_FUNC__(read_ddrb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_DW_ATMC_FUNC__(read)(var, val); +#else + ETHR_DW_ATMC_FUNC__(read_rb)(var, val); +#endif +} + /* --- init() --- */ @@ -2607,6 +2661,15 @@ static ETHR_INLINE void ETHR_DW_ATMC_FUNC__(init_mb)(ethr_dw_atomic_t *var, ethr } +static ETHR_INLINE void ETHR_DW_ATMC_FUNC__(init_ddrb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_DW_ATMC_FUNC__(init)(var, val); +#else + ETHR_DW_ATMC_FUNC__(init_rb)(var, val); +#endif +} + #endif /* ETHR_DW_ATMC_INLINE__ */ @@ -2616,78 +2679,91 @@ static ETHR_INLINE void ETHR_DW_ATMC_FUNC__(init_mb)(ethr_dw_atomic_t *var, ethr #ifdef ETHR_NEED_ATMC_PROTOTYPES__ ethr_sint_t *ethr_atomic_addr(ethr_atomic_t *var); ethr_sint_t ethr_atomic_cmpxchg(ethr_atomic_t *var, ethr_sint_t val, ethr_sint_t old_val); +ethr_sint_t ethr_atomic_cmpxchg_ddrb(ethr_atomic_t *var, ethr_sint_t val, ethr_sint_t old_val); ethr_sint_t ethr_atomic_cmpxchg_rb(ethr_atomic_t *var, ethr_sint_t val, ethr_sint_t old_val); ethr_sint_t ethr_atomic_cmpxchg_wb(ethr_atomic_t *var, ethr_sint_t val, ethr_sint_t old_val); ethr_sint_t ethr_atomic_cmpxchg_acqb(ethr_atomic_t *var, ethr_sint_t val, ethr_sint_t old_val); ethr_sint_t ethr_atomic_cmpxchg_relb(ethr_atomic_t *var, ethr_sint_t val, ethr_sint_t old_val); ethr_sint_t ethr_atomic_cmpxchg_mb(ethr_atomic_t *var, ethr_sint_t val, ethr_sint_t old_val); ethr_sint_t ethr_atomic_xchg(ethr_atomic_t *var, ethr_sint_t val); +ethr_sint_t ethr_atomic_xchg_ddrb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_xchg_rb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_xchg_wb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_xchg_acqb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_xchg_relb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_xchg_mb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_set(ethr_atomic_t *var, ethr_sint_t val); +void ethr_atomic_set_ddrb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_set_rb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_set_wb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_set_acqb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_set_relb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_set_mb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_init(ethr_atomic_t *var, ethr_sint_t val); +void ethr_atomic_init_ddrb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_init_rb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_init_wb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_init_acqb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_init_relb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_init_mb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_add_read(ethr_atomic_t *var, ethr_sint_t val); +ethr_sint_t ethr_atomic_add_read_ddrb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_add_read_rb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_add_read_wb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_add_read_acqb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_add_read_relb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_add_read_mb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_read(ethr_atomic_t *var); +ethr_sint_t ethr_atomic_read_ddrb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_read_rb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_read_wb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_read_acqb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_read_relb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_read_mb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_inc_read(ethr_atomic_t *var); +ethr_sint_t ethr_atomic_inc_read_ddrb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_inc_read_rb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_inc_read_wb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_inc_read_acqb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_inc_read_relb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_inc_read_mb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_dec_read(ethr_atomic_t *var); +ethr_sint_t ethr_atomic_dec_read_ddrb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_dec_read_rb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_dec_read_wb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_dec_read_acqb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_dec_read_relb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_dec_read_mb(ethr_atomic_t *var); void ethr_atomic_add(ethr_atomic_t *var, ethr_sint_t val); +void ethr_atomic_add_ddrb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_add_rb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_add_wb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_add_acqb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_add_relb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_add_mb(ethr_atomic_t *var, ethr_sint_t val); void ethr_atomic_inc(ethr_atomic_t *var); +void ethr_atomic_inc_ddrb(ethr_atomic_t *var); void ethr_atomic_inc_rb(ethr_atomic_t *var); void ethr_atomic_inc_wb(ethr_atomic_t *var); void ethr_atomic_inc_acqb(ethr_atomic_t *var); void ethr_atomic_inc_relb(ethr_atomic_t *var); void ethr_atomic_inc_mb(ethr_atomic_t *var); void ethr_atomic_dec(ethr_atomic_t *var); +void ethr_atomic_dec_ddrb(ethr_atomic_t *var); void ethr_atomic_dec_rb(ethr_atomic_t *var); void ethr_atomic_dec_wb(ethr_atomic_t *var); void ethr_atomic_dec_acqb(ethr_atomic_t *var); void ethr_atomic_dec_relb(ethr_atomic_t *var); void ethr_atomic_dec_mb(ethr_atomic_t *var); ethr_sint_t ethr_atomic_read_band(ethr_atomic_t *var, ethr_sint_t val); +ethr_sint_t ethr_atomic_read_band_ddrb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_read_band_rb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_read_band_wb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_read_band_acqb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_read_band_relb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_read_band_mb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_read_bor(ethr_atomic_t *var, ethr_sint_t val); +ethr_sint_t ethr_atomic_read_bor_ddrb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_read_bor_rb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_read_bor_wb(ethr_atomic_t *var, ethr_sint_t val); ethr_sint_t ethr_atomic_read_bor_acqb(ethr_atomic_t *var, ethr_sint_t val); @@ -3551,6 +3627,15 @@ static ETHR_INLINE ethr_sint_t ETHR_ATMC_FUNC__(cmpxchg_mb)(ethr_atomic_t *var, return res; } +static ETHR_INLINE ethr_sint_t ETHR_ATMC_FUNC__(cmpxchg_ddrb)(ethr_atomic_t *var, ethr_sint_t val, ethr_sint_t old_val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_ATMC_FUNC__(cmpxchg)(var, val, old_val); +#else + return ETHR_ATMC_FUNC__(cmpxchg_rb)(var, val, old_val); +#endif +} + /* --- xchg() --- */ @@ -3801,6 +3886,15 @@ static ETHR_INLINE ethr_sint_t ETHR_ATMC_FUNC__(xchg_mb)(ethr_atomic_t *var, eth return res; } +static ETHR_INLINE ethr_sint_t ETHR_ATMC_FUNC__(xchg_ddrb)(ethr_atomic_t *var, ethr_sint_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_ATMC_FUNC__(xchg)(var, val); +#else + return ETHR_ATMC_FUNC__(xchg_rb)(var, val); +#endif +} + /* --- set() --- */ @@ -3943,6 +4037,15 @@ static ETHR_INLINE void ETHR_ATMC_FUNC__(set_mb)(ethr_atomic_t *var, ethr_sint_t #endif } +static ETHR_INLINE void ETHR_ATMC_FUNC__(set_ddrb)(ethr_atomic_t *var, ethr_sint_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_ATMC_FUNC__(set)(var, val); +#else + ETHR_ATMC_FUNC__(set_rb)(var, val); +#endif +} + /* --- init() --- */ @@ -4085,6 +4188,15 @@ static ETHR_INLINE void ETHR_ATMC_FUNC__(init_mb)(ethr_atomic_t *var, ethr_sint_ #endif } +static ETHR_INLINE void ETHR_ATMC_FUNC__(init_ddrb)(ethr_atomic_t *var, ethr_sint_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_ATMC_FUNC__(init)(var, val); +#else + ETHR_ATMC_FUNC__(init_rb)(var, val); +#endif +} + /* --- add_read() --- */ @@ -4335,6 +4447,15 @@ static ETHR_INLINE ethr_sint_t ETHR_ATMC_FUNC__(add_read_mb)(ethr_atomic_t *var, return res; } +static ETHR_INLINE ethr_sint_t ETHR_ATMC_FUNC__(add_read_ddrb)(ethr_atomic_t *var, ethr_sint_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_ATMC_FUNC__(add_read)(var, val); +#else + return ETHR_ATMC_FUNC__(add_read_rb)(var, val); +#endif +} + /* --- read() --- */ @@ -4489,6 +4610,15 @@ static ETHR_INLINE ethr_sint_t ETHR_ATMC_FUNC__(read_mb)(ethr_atomic_t *var) return res; } +static ETHR_INLINE ethr_sint_t ETHR_ATMC_FUNC__(read_ddrb)(ethr_atomic_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_ATMC_FUNC__(read)(var); +#else + return ETHR_ATMC_FUNC__(read_rb)(var); +#endif +} + /* --- inc_read() --- */ @@ -4643,6 +4773,15 @@ static ETHR_INLINE ethr_sint_t ETHR_ATMC_FUNC__(inc_read_mb)(ethr_atomic_t *var) return res; } +static ETHR_INLINE ethr_sint_t ETHR_ATMC_FUNC__(inc_read_ddrb)(ethr_atomic_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_ATMC_FUNC__(inc_read)(var); +#else + return ETHR_ATMC_FUNC__(inc_read_rb)(var); +#endif +} + /* --- dec_read() --- */ @@ -4797,6 +4936,15 @@ static ETHR_INLINE ethr_sint_t ETHR_ATMC_FUNC__(dec_read_mb)(ethr_atomic_t *var) return res; } +static ETHR_INLINE ethr_sint_t ETHR_ATMC_FUNC__(dec_read_ddrb)(ethr_atomic_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_ATMC_FUNC__(dec_read)(var); +#else + return ETHR_ATMC_FUNC__(dec_read_rb)(var); +#endif +} + /* --- add() --- */ @@ -4939,6 +5087,15 @@ static ETHR_INLINE void ETHR_ATMC_FUNC__(add_mb)(ethr_atomic_t *var, ethr_sint_t #endif } +static ETHR_INLINE void ETHR_ATMC_FUNC__(add_ddrb)(ethr_atomic_t *var, ethr_sint_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_ATMC_FUNC__(add)(var, val); +#else + ETHR_ATMC_FUNC__(add_rb)(var, val); +#endif +} + /* --- inc() --- */ @@ -5081,6 +5238,15 @@ static ETHR_INLINE void ETHR_ATMC_FUNC__(inc_mb)(ethr_atomic_t *var) #endif } +static ETHR_INLINE void ETHR_ATMC_FUNC__(inc_ddrb)(ethr_atomic_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_ATMC_FUNC__(inc)(var); +#else + ETHR_ATMC_FUNC__(inc_rb)(var); +#endif +} + /* --- dec() --- */ @@ -5223,6 +5389,15 @@ static ETHR_INLINE void ETHR_ATMC_FUNC__(dec_mb)(ethr_atomic_t *var) #endif } +static ETHR_INLINE void ETHR_ATMC_FUNC__(dec_ddrb)(ethr_atomic_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_ATMC_FUNC__(dec)(var); +#else + ETHR_ATMC_FUNC__(dec_rb)(var); +#endif +} + /* --- read_band() --- */ @@ -5473,6 +5648,15 @@ static ETHR_INLINE ethr_sint_t ETHR_ATMC_FUNC__(read_band_mb)(ethr_atomic_t *var return res; } +static ETHR_INLINE ethr_sint_t ETHR_ATMC_FUNC__(read_band_ddrb)(ethr_atomic_t *var, ethr_sint_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_ATMC_FUNC__(read_band)(var, val); +#else + return ETHR_ATMC_FUNC__(read_band_rb)(var, val); +#endif +} + /* --- read_bor() --- */ @@ -5723,6 +5907,15 @@ static ETHR_INLINE ethr_sint_t ETHR_ATMC_FUNC__(read_bor_mb)(ethr_atomic_t *var, return res; } +static ETHR_INLINE ethr_sint_t ETHR_ATMC_FUNC__(read_bor_ddrb)(ethr_atomic_t *var, ethr_sint_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_ATMC_FUNC__(read_bor)(var, val); +#else + return ETHR_ATMC_FUNC__(read_bor_rb)(var, val); +#endif +} + #endif /* ETHR_ATMC_INLINE__ */ @@ -5732,78 +5925,91 @@ static ETHR_INLINE ethr_sint_t ETHR_ATMC_FUNC__(read_bor_mb)(ethr_atomic_t *var, #ifdef ETHR_NEED_ATMC32_PROTOTYPES__ ethr_sint32_t *ethr_atomic32_addr(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_cmpxchg(ethr_atomic32_t *var, ethr_sint32_t val, ethr_sint32_t old_val); +ethr_sint32_t ethr_atomic32_cmpxchg_ddrb(ethr_atomic32_t *var, ethr_sint32_t val, ethr_sint32_t old_val); ethr_sint32_t ethr_atomic32_cmpxchg_rb(ethr_atomic32_t *var, ethr_sint32_t val, ethr_sint32_t old_val); ethr_sint32_t ethr_atomic32_cmpxchg_wb(ethr_atomic32_t *var, ethr_sint32_t val, ethr_sint32_t old_val); ethr_sint32_t ethr_atomic32_cmpxchg_acqb(ethr_atomic32_t *var, ethr_sint32_t val, ethr_sint32_t old_val); ethr_sint32_t ethr_atomic32_cmpxchg_relb(ethr_atomic32_t *var, ethr_sint32_t val, ethr_sint32_t old_val); ethr_sint32_t ethr_atomic32_cmpxchg_mb(ethr_atomic32_t *var, ethr_sint32_t val, ethr_sint32_t old_val); ethr_sint32_t ethr_atomic32_xchg(ethr_atomic32_t *var, ethr_sint32_t val); +ethr_sint32_t ethr_atomic32_xchg_ddrb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_xchg_rb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_xchg_wb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_xchg_acqb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_xchg_relb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_xchg_mb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_set(ethr_atomic32_t *var, ethr_sint32_t val); +void ethr_atomic32_set_ddrb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_set_rb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_set_wb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_set_acqb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_set_relb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_set_mb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_init(ethr_atomic32_t *var, ethr_sint32_t val); +void ethr_atomic32_init_ddrb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_init_rb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_init_wb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_init_acqb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_init_relb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_init_mb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_add_read(ethr_atomic32_t *var, ethr_sint32_t val); +ethr_sint32_t ethr_atomic32_add_read_ddrb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_add_read_rb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_add_read_wb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_add_read_acqb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_add_read_relb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_add_read_mb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_read(ethr_atomic32_t *var); +ethr_sint32_t ethr_atomic32_read_ddrb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_read_rb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_read_wb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_read_acqb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_read_relb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_read_mb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_inc_read(ethr_atomic32_t *var); +ethr_sint32_t ethr_atomic32_inc_read_ddrb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_inc_read_rb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_inc_read_wb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_inc_read_acqb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_inc_read_relb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_inc_read_mb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_dec_read(ethr_atomic32_t *var); +ethr_sint32_t ethr_atomic32_dec_read_ddrb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_dec_read_rb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_dec_read_wb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_dec_read_acqb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_dec_read_relb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_dec_read_mb(ethr_atomic32_t *var); void ethr_atomic32_add(ethr_atomic32_t *var, ethr_sint32_t val); +void ethr_atomic32_add_ddrb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_add_rb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_add_wb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_add_acqb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_add_relb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_add_mb(ethr_atomic32_t *var, ethr_sint32_t val); void ethr_atomic32_inc(ethr_atomic32_t *var); +void ethr_atomic32_inc_ddrb(ethr_atomic32_t *var); void ethr_atomic32_inc_rb(ethr_atomic32_t *var); void ethr_atomic32_inc_wb(ethr_atomic32_t *var); void ethr_atomic32_inc_acqb(ethr_atomic32_t *var); void ethr_atomic32_inc_relb(ethr_atomic32_t *var); void ethr_atomic32_inc_mb(ethr_atomic32_t *var); void ethr_atomic32_dec(ethr_atomic32_t *var); +void ethr_atomic32_dec_ddrb(ethr_atomic32_t *var); void ethr_atomic32_dec_rb(ethr_atomic32_t *var); void ethr_atomic32_dec_wb(ethr_atomic32_t *var); void ethr_atomic32_dec_acqb(ethr_atomic32_t *var); void ethr_atomic32_dec_relb(ethr_atomic32_t *var); void ethr_atomic32_dec_mb(ethr_atomic32_t *var); ethr_sint32_t ethr_atomic32_read_band(ethr_atomic32_t *var, ethr_sint32_t val); +ethr_sint32_t ethr_atomic32_read_band_ddrb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_read_band_rb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_read_band_wb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_read_band_acqb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_read_band_relb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_read_band_mb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_read_bor(ethr_atomic32_t *var, ethr_sint32_t val); +ethr_sint32_t ethr_atomic32_read_bor_ddrb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_read_bor_rb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_read_bor_wb(ethr_atomic32_t *var, ethr_sint32_t val); ethr_sint32_t ethr_atomic32_read_bor_acqb(ethr_atomic32_t *var, ethr_sint32_t val); @@ -6667,6 +6873,15 @@ static ETHR_INLINE ethr_sint32_t ETHR_ATMC32_FUNC__(cmpxchg_mb)(ethr_atomic32_t return res; } +static ETHR_INLINE ethr_sint32_t ETHR_ATMC32_FUNC__(cmpxchg_ddrb)(ethr_atomic32_t *var, ethr_sint32_t val, ethr_sint32_t old_val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_ATMC32_FUNC__(cmpxchg)(var, val, old_val); +#else + return ETHR_ATMC32_FUNC__(cmpxchg_rb)(var, val, old_val); +#endif +} + /* --- xchg() --- */ @@ -6917,6 +7132,15 @@ static ETHR_INLINE ethr_sint32_t ETHR_ATMC32_FUNC__(xchg_mb)(ethr_atomic32_t *va return res; } +static ETHR_INLINE ethr_sint32_t ETHR_ATMC32_FUNC__(xchg_ddrb)(ethr_atomic32_t *var, ethr_sint32_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_ATMC32_FUNC__(xchg)(var, val); +#else + return ETHR_ATMC32_FUNC__(xchg_rb)(var, val); +#endif +} + /* --- set() --- */ @@ -7059,6 +7283,15 @@ static ETHR_INLINE void ETHR_ATMC32_FUNC__(set_mb)(ethr_atomic32_t *var, ethr_si #endif } +static ETHR_INLINE void ETHR_ATMC32_FUNC__(set_ddrb)(ethr_atomic32_t *var, ethr_sint32_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_ATMC32_FUNC__(set)(var, val); +#else + ETHR_ATMC32_FUNC__(set_rb)(var, val); +#endif +} + /* --- init() --- */ @@ -7201,6 +7434,15 @@ static ETHR_INLINE void ETHR_ATMC32_FUNC__(init_mb)(ethr_atomic32_t *var, ethr_s #endif } +static ETHR_INLINE void ETHR_ATMC32_FUNC__(init_ddrb)(ethr_atomic32_t *var, ethr_sint32_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_ATMC32_FUNC__(init)(var, val); +#else + ETHR_ATMC32_FUNC__(init_rb)(var, val); +#endif +} + /* --- add_read() --- */ @@ -7451,6 +7693,15 @@ static ETHR_INLINE ethr_sint32_t ETHR_ATMC32_FUNC__(add_read_mb)(ethr_atomic32_t return res; } +static ETHR_INLINE ethr_sint32_t ETHR_ATMC32_FUNC__(add_read_ddrb)(ethr_atomic32_t *var, ethr_sint32_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_ATMC32_FUNC__(add_read)(var, val); +#else + return ETHR_ATMC32_FUNC__(add_read_rb)(var, val); +#endif +} + /* --- read() --- */ @@ -7605,6 +7856,15 @@ static ETHR_INLINE ethr_sint32_t ETHR_ATMC32_FUNC__(read_mb)(ethr_atomic32_t *va return res; } +static ETHR_INLINE ethr_sint32_t ETHR_ATMC32_FUNC__(read_ddrb)(ethr_atomic32_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_ATMC32_FUNC__(read)(var); +#else + return ETHR_ATMC32_FUNC__(read_rb)(var); +#endif +} + /* --- inc_read() --- */ @@ -7759,6 +8019,15 @@ static ETHR_INLINE ethr_sint32_t ETHR_ATMC32_FUNC__(inc_read_mb)(ethr_atomic32_t return res; } +static ETHR_INLINE ethr_sint32_t ETHR_ATMC32_FUNC__(inc_read_ddrb)(ethr_atomic32_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_ATMC32_FUNC__(inc_read)(var); +#else + return ETHR_ATMC32_FUNC__(inc_read_rb)(var); +#endif +} + /* --- dec_read() --- */ @@ -7913,6 +8182,15 @@ static ETHR_INLINE ethr_sint32_t ETHR_ATMC32_FUNC__(dec_read_mb)(ethr_atomic32_t return res; } +static ETHR_INLINE ethr_sint32_t ETHR_ATMC32_FUNC__(dec_read_ddrb)(ethr_atomic32_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_ATMC32_FUNC__(dec_read)(var); +#else + return ETHR_ATMC32_FUNC__(dec_read_rb)(var); +#endif +} + /* --- add() --- */ @@ -8055,6 +8333,15 @@ static ETHR_INLINE void ETHR_ATMC32_FUNC__(add_mb)(ethr_atomic32_t *var, ethr_si #endif } +static ETHR_INLINE void ETHR_ATMC32_FUNC__(add_ddrb)(ethr_atomic32_t *var, ethr_sint32_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_ATMC32_FUNC__(add)(var, val); +#else + ETHR_ATMC32_FUNC__(add_rb)(var, val); +#endif +} + /* --- inc() --- */ @@ -8197,6 +8484,15 @@ static ETHR_INLINE void ETHR_ATMC32_FUNC__(inc_mb)(ethr_atomic32_t *var) #endif } +static ETHR_INLINE void ETHR_ATMC32_FUNC__(inc_ddrb)(ethr_atomic32_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_ATMC32_FUNC__(inc)(var); +#else + ETHR_ATMC32_FUNC__(inc_rb)(var); +#endif +} + /* --- dec() --- */ @@ -8339,6 +8635,15 @@ static ETHR_INLINE void ETHR_ATMC32_FUNC__(dec_mb)(ethr_atomic32_t *var) #endif } +static ETHR_INLINE void ETHR_ATMC32_FUNC__(dec_ddrb)(ethr_atomic32_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_ATMC32_FUNC__(dec)(var); +#else + ETHR_ATMC32_FUNC__(dec_rb)(var); +#endif +} + /* --- read_band() --- */ @@ -8589,6 +8894,15 @@ static ETHR_INLINE ethr_sint32_t ETHR_ATMC32_FUNC__(read_band_mb)(ethr_atomic32_ return res; } +static ETHR_INLINE ethr_sint32_t ETHR_ATMC32_FUNC__(read_band_ddrb)(ethr_atomic32_t *var, ethr_sint32_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_ATMC32_FUNC__(read_band)(var, val); +#else + return ETHR_ATMC32_FUNC__(read_band_rb)(var, val); +#endif +} + /* --- read_bor() --- */ @@ -8839,6 +9153,15 @@ static ETHR_INLINE ethr_sint32_t ETHR_ATMC32_FUNC__(read_bor_mb)(ethr_atomic32_t return res; } +static ETHR_INLINE ethr_sint32_t ETHR_ATMC32_FUNC__(read_bor_ddrb)(ethr_atomic32_t *var, ethr_sint32_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_ATMC32_FUNC__(read_bor)(var, val); +#else + return ETHR_ATMC32_FUNC__(read_bor_rb)(var, val); +#endif +} + #endif /* ETHR_ATMC32_INLINE__ */ #endif /* ETHR_ATOMICS_H__ */ diff --git a/erts/lib_src/common/ethr_atomics.c b/erts/lib_src/common/ethr_atomics.c index 5796bdc22e..e4213e1eef 100644 --- a/erts/lib_src/common/ethr_atomics.c +++ b/erts/lib_src/common/ethr_atomics.c @@ -10,7 +10,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2011. All Rights Reserved. + * Copyright Ericsson AB 2011-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 @@ -64,12 +64,31 @@ * Appart from a function implementing the atomic operation * with unspecified memory barrier semantics, there are * functions implementing each operation with the following - * memory barrier semantics: - * - rb (read barrier) - * - wb (write barrier) - * - acqb (acquire barrier) - * - relb (release barrier) - * - mb (full memory barrier) + * implied memory barrier semantics: + * - mb - Full memory barrier. Orders both loads, and + * stores before, and after the atomic operation. + * No load or store is allowed to be reordered + * over the atomic operation. + * - relb - Release barrier. Orders both loads, and + * stores appearing *before* the atomic + * operation. These are not allowed to be + * reordered over the atomic operation. + * - acqb - Acquire barrier. Orders both loads, and stores + * appearing *after* the atomic operation. These + * are not allowed to be reordered over the + * atomic operation. + * - wb - Write barrier. Orders *only* stores. These are + * not allowed to be reordered over the barrier. + * Store in atomic operation is ordered *after* + * the barrier. + * - rb - Read barrier. Orders *only* loads. These are + * not allowed to be reordered over the barrier. + * Load in atomic operation is ordered *before* + * the barrier. + * - ddrb - Data dependency read barrier. Orders *only* + * loads according to data dependency across the + * barrier. Load in atomic operation is ordered + * before the barrier. * * We implement all of these operation/barrier * combinations, regardless of whether they are useful @@ -542,6 +561,15 @@ int ethr_dw_atomic_cmpxchg(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_s } #endif +int ETHR_DW_ATOMIC_FUNC__(cmpxchg_ddrb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_sint_t *old_val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ETHR_DW_ATOMIC_FUNC__(cmpxchg)(var, val, old_val); +#else + return ETHR_DW_ATOMIC_FUNC__(cmpxchg_rb)(var, val, old_val); +#endif +} + int ETHR_DW_ATOMIC_FUNC__(cmpxchg_rb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val, ethr_dw_sint_t *old_val) { int res; @@ -756,6 +784,15 @@ void ethr_dw_atomic_set(ethr_dw_atomic_t *var, ethr_dw_sint_t *val) } #endif +void ETHR_DW_ATOMIC_FUNC__(set_ddrb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_DW_ATOMIC_FUNC__(set)(var, val); +#else + ETHR_DW_ATOMIC_FUNC__(set_rb)(var, val); +#endif +} + void ETHR_DW_ATOMIC_FUNC__(set_rb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val) { ETHR_ASSERT(!ethr_not_inited__); @@ -910,6 +947,15 @@ void ethr_dw_atomic_read(ethr_dw_atomic_t *var, ethr_dw_sint_t *val) } #endif +void ETHR_DW_ATOMIC_FUNC__(read_ddrb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_DW_ATOMIC_FUNC__(read)(var, val); +#else + ETHR_DW_ATOMIC_FUNC__(read_rb)(var, val); +#endif +} + void ETHR_DW_ATOMIC_FUNC__(read_rb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val) { ETHR_ASSERT(!ethr_not_inited__); @@ -1061,6 +1107,15 @@ void ethr_dw_atomic_init(ethr_dw_atomic_t *var, ethr_dw_sint_t *val) } #endif +void ETHR_DW_ATOMIC_FUNC__(init_ddrb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ETHR_DW_ATOMIC_FUNC__(init)(var, val); +#else + ETHR_DW_ATOMIC_FUNC__(init_rb)(var, val); +#endif +} + void ETHR_DW_ATOMIC_FUNC__(init_rb)(ethr_dw_atomic_t *var, ethr_dw_sint_t *val) { ETHR_ASSERT(var); @@ -1221,6 +1276,15 @@ ethr_sint_t ethr_atomic_cmpxchg(ethr_atomic_t *var, ethr_sint_t val, ethr_sint_t return res; } +ethr_sint_t ethr_atomic_cmpxchg_ddrb(ethr_atomic_t *var, ethr_sint_t val, ethr_sint_t old_val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ethr_atomic_cmpxchg(var, val, old_val); +#else + return ethr_atomic_cmpxchg_rb(var, val, old_val); +#endif +} + ethr_sint_t ethr_atomic_cmpxchg_rb(ethr_atomic_t *var, ethr_sint_t val, ethr_sint_t old_val) { ethr_sint_t res; @@ -1332,6 +1396,15 @@ ethr_sint_t ethr_atomic_xchg(ethr_atomic_t *var, ethr_sint_t val) return res; } +ethr_sint_t ethr_atomic_xchg_ddrb(ethr_atomic_t *var, ethr_sint_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ethr_atomic_xchg(var, val); +#else + return ethr_atomic_xchg_rb(var, val); +#endif +} + ethr_sint_t ethr_atomic_xchg_rb(ethr_atomic_t *var, ethr_sint_t val) { ethr_sint_t res; @@ -1437,6 +1510,15 @@ void ethr_atomic_set(ethr_atomic_t *var, ethr_sint_t val) } +void ethr_atomic_set_ddrb(ethr_atomic_t *var, ethr_sint_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ethr_atomic_set(var, val); +#else + ethr_atomic_set_rb(var, val); +#endif +} + void ethr_atomic_set_rb(ethr_atomic_t *var, ethr_sint_t val) { ETHR_ASSERT(!ethr_not_inited__); @@ -1536,6 +1618,15 @@ void ethr_atomic_init(ethr_atomic_t *var, ethr_sint_t val) } +void ethr_atomic_init_ddrb(ethr_atomic_t *var, ethr_sint_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ethr_atomic_init(var, val); +#else + ethr_atomic_init_rb(var, val); +#endif +} + void ethr_atomic_init_rb(ethr_atomic_t *var, ethr_sint_t val) { ETHR_ASSERT(var); @@ -1632,6 +1723,15 @@ ethr_sint_t ethr_atomic_add_read(ethr_atomic_t *var, ethr_sint_t val) return res; } +ethr_sint_t ethr_atomic_add_read_ddrb(ethr_atomic_t *var, ethr_sint_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ethr_atomic_add_read(var, val); +#else + return ethr_atomic_add_read_rb(var, val); +#endif +} + ethr_sint_t ethr_atomic_add_read_rb(ethr_atomic_t *var, ethr_sint_t val) { ethr_sint_t res; @@ -1738,6 +1838,15 @@ ethr_sint_t ethr_atomic_read(ethr_atomic_t *var) return res; } +ethr_sint_t ethr_atomic_read_ddrb(ethr_atomic_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ethr_atomic_read(var); +#else + return ethr_atomic_read_rb(var); +#endif +} + ethr_sint_t ethr_atomic_read_rb(ethr_atomic_t *var) { ethr_sint_t res; @@ -1843,6 +1952,15 @@ ethr_sint_t ethr_atomic_inc_read(ethr_atomic_t *var) return res; } +ethr_sint_t ethr_atomic_inc_read_ddrb(ethr_atomic_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ethr_atomic_inc_read(var); +#else + return ethr_atomic_inc_read_rb(var); +#endif +} + ethr_sint_t ethr_atomic_inc_read_rb(ethr_atomic_t *var) { ethr_sint_t res; @@ -1949,6 +2067,15 @@ ethr_sint_t ethr_atomic_dec_read(ethr_atomic_t *var) return res; } +ethr_sint_t ethr_atomic_dec_read_ddrb(ethr_atomic_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ethr_atomic_dec_read(var); +#else + return ethr_atomic_dec_read_rb(var); +#endif +} + ethr_sint_t ethr_atomic_dec_read_rb(ethr_atomic_t *var) { ethr_sint_t res; @@ -2054,6 +2181,15 @@ void ethr_atomic_add(ethr_atomic_t *var, ethr_sint_t val) } +void ethr_atomic_add_ddrb(ethr_atomic_t *var, ethr_sint_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ethr_atomic_add(var, val); +#else + ethr_atomic_add_rb(var, val); +#endif +} + void ethr_atomic_add_rb(ethr_atomic_t *var, ethr_sint_t val) { ETHR_ASSERT(!ethr_not_inited__); @@ -2154,6 +2290,15 @@ void ethr_atomic_inc(ethr_atomic_t *var) } +void ethr_atomic_inc_ddrb(ethr_atomic_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ethr_atomic_inc(var); +#else + ethr_atomic_inc_rb(var); +#endif +} + void ethr_atomic_inc_rb(ethr_atomic_t *var) { ETHR_ASSERT(!ethr_not_inited__); @@ -2254,6 +2399,15 @@ void ethr_atomic_dec(ethr_atomic_t *var) } +void ethr_atomic_dec_ddrb(ethr_atomic_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ethr_atomic_dec(var); +#else + ethr_atomic_dec_rb(var); +#endif +} + void ethr_atomic_dec_rb(ethr_atomic_t *var) { ETHR_ASSERT(!ethr_not_inited__); @@ -2355,6 +2509,15 @@ ethr_sint_t ethr_atomic_read_band(ethr_atomic_t *var, ethr_sint_t val) return res; } +ethr_sint_t ethr_atomic_read_band_ddrb(ethr_atomic_t *var, ethr_sint_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ethr_atomic_read_band(var, val); +#else + return ethr_atomic_read_band_rb(var, val); +#endif +} + ethr_sint_t ethr_atomic_read_band_rb(ethr_atomic_t *var, ethr_sint_t val) { ethr_sint_t res; @@ -2461,6 +2624,15 @@ ethr_sint_t ethr_atomic_read_bor(ethr_atomic_t *var, ethr_sint_t val) return res; } +ethr_sint_t ethr_atomic_read_bor_ddrb(ethr_atomic_t *var, ethr_sint_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ethr_atomic_read_bor(var, val); +#else + return ethr_atomic_read_bor_rb(var, val); +#endif +} + ethr_sint_t ethr_atomic_read_bor_rb(ethr_atomic_t *var, ethr_sint_t val) { ethr_sint_t res; @@ -2587,6 +2759,15 @@ ethr_sint32_t ethr_atomic32_cmpxchg(ethr_atomic32_t *var, ethr_sint32_t val, eth return res; } +ethr_sint32_t ethr_atomic32_cmpxchg_ddrb(ethr_atomic32_t *var, ethr_sint32_t val, ethr_sint32_t old_val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ethr_atomic32_cmpxchg(var, val, old_val); +#else + return ethr_atomic32_cmpxchg_rb(var, val, old_val); +#endif +} + ethr_sint32_t ethr_atomic32_cmpxchg_rb(ethr_atomic32_t *var, ethr_sint32_t val, ethr_sint32_t old_val) { ethr_sint32_t res; @@ -2675,6 +2856,15 @@ ethr_sint32_t ethr_atomic32_xchg(ethr_atomic32_t *var, ethr_sint32_t val) return res; } +ethr_sint32_t ethr_atomic32_xchg_ddrb(ethr_atomic32_t *var, ethr_sint32_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ethr_atomic32_xchg(var, val); +#else + return ethr_atomic32_xchg_rb(var, val); +#endif +} + ethr_sint32_t ethr_atomic32_xchg_rb(ethr_atomic32_t *var, ethr_sint32_t val) { ethr_sint32_t res; @@ -2762,6 +2952,15 @@ void ethr_atomic32_set(ethr_atomic32_t *var, ethr_sint32_t val) } +void ethr_atomic32_set_ddrb(ethr_atomic32_t *var, ethr_sint32_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ethr_atomic32_set(var, val); +#else + ethr_atomic32_set_rb(var, val); +#endif +} + void ethr_atomic32_set_rb(ethr_atomic32_t *var, ethr_sint32_t val) { ETHR_ASSERT(!ethr_not_inited__); @@ -2843,6 +3042,15 @@ void ethr_atomic32_init(ethr_atomic32_t *var, ethr_sint32_t val) } +void ethr_atomic32_init_ddrb(ethr_atomic32_t *var, ethr_sint32_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ethr_atomic32_init(var, val); +#else + ethr_atomic32_init_rb(var, val); +#endif +} + void ethr_atomic32_init_rb(ethr_atomic32_t *var, ethr_sint32_t val) { ETHR_ASSERT(var); @@ -2921,6 +3129,15 @@ ethr_sint32_t ethr_atomic32_add_read(ethr_atomic32_t *var, ethr_sint32_t val) return res; } +ethr_sint32_t ethr_atomic32_add_read_ddrb(ethr_atomic32_t *var, ethr_sint32_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ethr_atomic32_add_read(var, val); +#else + return ethr_atomic32_add_read_rb(var, val); +#endif +} + ethr_sint32_t ethr_atomic32_add_read_rb(ethr_atomic32_t *var, ethr_sint32_t val) { ethr_sint32_t res; @@ -3009,6 +3226,15 @@ ethr_sint32_t ethr_atomic32_read(ethr_atomic32_t *var) return res; } +ethr_sint32_t ethr_atomic32_read_ddrb(ethr_atomic32_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ethr_atomic32_read(var); +#else + return ethr_atomic32_read_rb(var); +#endif +} + ethr_sint32_t ethr_atomic32_read_rb(ethr_atomic32_t *var) { ethr_sint32_t res; @@ -3097,6 +3323,15 @@ ethr_sint32_t ethr_atomic32_inc_read(ethr_atomic32_t *var) return res; } +ethr_sint32_t ethr_atomic32_inc_read_ddrb(ethr_atomic32_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ethr_atomic32_inc_read(var); +#else + return ethr_atomic32_inc_read_rb(var); +#endif +} + ethr_sint32_t ethr_atomic32_inc_read_rb(ethr_atomic32_t *var) { ethr_sint32_t res; @@ -3185,6 +3420,15 @@ ethr_sint32_t ethr_atomic32_dec_read(ethr_atomic32_t *var) return res; } +ethr_sint32_t ethr_atomic32_dec_read_ddrb(ethr_atomic32_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ethr_atomic32_dec_read(var); +#else + return ethr_atomic32_dec_read_rb(var); +#endif +} + ethr_sint32_t ethr_atomic32_dec_read_rb(ethr_atomic32_t *var) { ethr_sint32_t res; @@ -3272,6 +3516,15 @@ void ethr_atomic32_add(ethr_atomic32_t *var, ethr_sint32_t val) } +void ethr_atomic32_add_ddrb(ethr_atomic32_t *var, ethr_sint32_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ethr_atomic32_add(var, val); +#else + ethr_atomic32_add_rb(var, val); +#endif +} + void ethr_atomic32_add_rb(ethr_atomic32_t *var, ethr_sint32_t val) { ETHR_ASSERT(!ethr_not_inited__); @@ -3354,6 +3607,15 @@ void ethr_atomic32_inc(ethr_atomic32_t *var) } +void ethr_atomic32_inc_ddrb(ethr_atomic32_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ethr_atomic32_inc(var); +#else + ethr_atomic32_inc_rb(var); +#endif +} + void ethr_atomic32_inc_rb(ethr_atomic32_t *var) { ETHR_ASSERT(!ethr_not_inited__); @@ -3436,6 +3698,15 @@ void ethr_atomic32_dec(ethr_atomic32_t *var) } +void ethr_atomic32_dec_ddrb(ethr_atomic32_t *var) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + ethr_atomic32_dec(var); +#else + ethr_atomic32_dec_rb(var); +#endif +} + void ethr_atomic32_dec_rb(ethr_atomic32_t *var) { ETHR_ASSERT(!ethr_not_inited__); @@ -3519,6 +3790,15 @@ ethr_sint32_t ethr_atomic32_read_band(ethr_atomic32_t *var, ethr_sint32_t val) return res; } +ethr_sint32_t ethr_atomic32_read_band_ddrb(ethr_atomic32_t *var, ethr_sint32_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ethr_atomic32_read_band(var, val); +#else + return ethr_atomic32_read_band_rb(var, val); +#endif +} + ethr_sint32_t ethr_atomic32_read_band_rb(ethr_atomic32_t *var, ethr_sint32_t val) { ethr_sint32_t res; @@ -3607,6 +3887,15 @@ ethr_sint32_t ethr_atomic32_read_bor(ethr_atomic32_t *var, ethr_sint32_t val) return res; } +ethr_sint32_t ethr_atomic32_read_bor_ddrb(ethr_atomic32_t *var, ethr_sint32_t val) +{ +#ifdef ETHR_ORDERED_READ_DEPEND + return ethr_atomic32_read_bor(var, val); +#else + return ethr_atomic32_read_bor_rb(var, val); +#endif +} + ethr_sint32_t ethr_atomic32_read_bor_rb(ethr_atomic32_t *var, ethr_sint32_t val) { ethr_sint32_t res; diff --git a/erts/lib_src/utils/make_atomics_api b/erts/lib_src/utils/make_atomics_api index f4e71c7618..d8b1a56100 100755 --- a/erts/lib_src/utils/make_atomics_api +++ b/erts/lib_src/utils/make_atomics_api @@ -4,7 +4,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-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 @@ -50,7 +50,9 @@ -define(DW_RTCHK_MACRO, "ETHR_RTCHK_USE_NATIVE_DW_ATOMIC_IMPL__"). %% Barrier versions we implement --define(BARRIERS, [none, rb, wb, acqb, relb, mb]). +-define(BARRIERS, [none, ddrb, rb, wb, acqb, relb, mb]). +-define(NON_NATIVE_BARRIERS, [ddrb]). +-define(NATIVE_BARRIERS, (?BARRIERS -- ?NON_NATIVE_BARRIERS)). -define(ATOMIC_SIZES, ["dword", "word", "32"]). @@ -381,7 +383,6 @@ do_func_header(#atomic_context{dw = true, cmpxchg, Inline, Func) -> [Inline, "int ", Func, "(", AtomicT, " *", Arg1, ", ", AintT, " *", Arg2, ", ", AintT, " *", Arg3, ")"]. - xbarriers(_Op, none, _NB) -> {"", ""}; @@ -455,7 +456,7 @@ try_barrier_order_first(mb) -> try_barrier_order(B) -> First = try_barrier_order_first(B), - First ++ (?BARRIERS -- First). + First ++ (?NATIVE_BARRIERS -- First). native_barrier_op(#atomic_context{'NATMC' = NATMC} = AC, If, ExtraDecl, Op, B, NB, TypeCasts) -> NOpStr = opstr(native(Op)), @@ -571,12 +572,12 @@ do_cmpxchg_fallback_define(#atomic_context{'NATMC' = NATMC, NoneTryBarrierOrder = try_barrier_order(none), %% First a sanity check [" -#if (", NotDefCMPXCHG(hd(?BARRIERS)) , +#if (", NotDefCMPXCHG(hd(?NATIVE_BARRIERS)) , lists:map(fun (B) -> [" \\ && ", NotDefCMPXCHG(B)] end, - tl(?BARRIERS)), ") + tl(?NATIVE_BARRIERS)), ") # error \"No native cmpxchg() op available\" #endif @@ -744,7 +745,7 @@ translate_have_defs(#atomic_context{dw = DW, 'NATMC' = NATMC}) -> ] end] end, - ?BARRIERS) + ?NATIVE_BARRIERS) end, case DW of true -> ?DW_ATOMIC_OPS; @@ -801,6 +802,46 @@ rtchk_fallback_call(Return, #atomic_context{dw = DW, false -> [RetVar, " ="] end, [?DW_FUNC_MACRO, "(", opstr(Op), op_barrier_ext(B), ")"], Arg1, Arg2, Arg3, ""). +non_native_barrier(B) -> + lists:member(B, ?NON_NATIVE_BARRIERS). + +non_native_barrier_impl(AC, Type, Macro, Op, B) -> + [" +", func_header(AC, Type, Macro, Op, B), " +{", + case B of + ddrb -> + [" +#ifdef ETHR_ORDERED_READ_DEPEND + ", func_call(AC, Type, Macro, Op, none, true), " +#else + ", func_call(AC, Type, Macro, Op, rb, true), " +#endif +" + ] + end, + "} +" + ]. + +func_call(#atomic_context{'ATMC' = ATMC} = AC, inline_implementation, _Macro, Op, B, RetStatement) -> + func_call(AC, Op, ["ETHR_", ATMC, "_FUNC__(", opstr(Op), op_barrier_ext(B), ")"], RetStatement); +func_call(#atomic_context{atomic = Atomic} = AC, implementation, false, Op, B, RetStatement) -> + func_call(AC, Op, [Atomic, "_", opstr(Op), op_barrier_ext(B)], RetStatement); +func_call(AC, implementation, Macro, Op, B, RetStatement) -> + func_call(AC, Op, [Macro, "(", opstr(Op), op_barrier_ext(B), ")"], RetStatement). + +func_call(#atomic_context{dw = DW, arg1 = Arg1, arg2 = Arg2, arg3 = Arg3} = AC, Op, Func, true) -> + op_call(Op, DW, case is_return_op(AC, Op) of + true -> "return"; + false -> "" + end, Func, Arg1, Arg2, Arg3, ""); +func_call(#atomic_context{dw = DW, arg1 = Arg1, arg2 = Arg2, arg3 = Arg3, ret_var = RetVar} = AC, Op, Func, false) -> + op_call(Op, DW, case is_return_op(AC, Op) of + true -> [RetVar, " = "]; + false -> "" + end, Func, Arg1, Arg2, Arg3, ""). + make_implementations(#atomic_context{dw = DW, ret_type = RetType, ret_var = RetVar, @@ -858,73 +899,78 @@ make_implementations(#atomic_context{dw = DW, ", lists:map(fun (B) -> - TryBarriers = try_barrier_order(B), - [" + case non_native_barrier(B) of + true -> + non_native_barrier_impl(AC, inline_implementation, false, Op, B); + false -> + TryBarriers = try_barrier_order(B), + [" ", func_header(AC, inline_implementation, false, Op, B), " { ", - case is_return_op(AC, Op) of - true -> - [" ", RetType, " ", RetVar, ";\n"]; - _ -> "" - end, - case DW of - true -> - [RtchkBegin, - "\n", - su_dw_native_barrier_op(AC, "#if", Op, B, hd(TryBarriers)), - lists:map(fun (NB) -> - su_dw_native_barrier_op(AC, "#elif", Op, B, NB) - end, - tl(TryBarriers)), - lists:map(fun (NB) -> - dw_native_barrier_op(AC, "#elif", "", Op, B, NB) - end, - TryBarriers), - case simple_fallback(AC, Op, B) of - "" -> - %% No simple fallback available; - %% use cmpxchg() fallbacks... - [cmpxchg_fallbacks(AC#atomic_context{'NATMC' = ["SU_", NATMC]}, true, Op, B), - cmpxchg_fallbacks(AC, false, Op, B), - "#else + case is_return_op(AC, Op) of + true -> + [" ", RetType, " ", RetVar, ";\n"]; + _ -> "" + end, + case DW of + true -> + [RtchkBegin, + "\n", + su_dw_native_barrier_op(AC, "#if", Op, B, hd(TryBarriers)), + lists:map(fun (NB) -> + su_dw_native_barrier_op(AC, "#elif", Op, B, NB) + end, + tl(TryBarriers)), + lists:map(fun (NB) -> + dw_native_barrier_op(AC, "#elif", "", Op, B, NB) + end, + TryBarriers), + case simple_fallback(AC, Op, B) of + "" -> + %% No simple fallback available; + %% use cmpxchg() fallbacks... + [cmpxchg_fallbacks(AC#atomic_context{'NATMC' = ["SU_", NATMC]}, true, Op, B), + cmpxchg_fallbacks(AC, false, Op, B), + "#else #error \"Missing implementation of ", Atomic, "_", opstr(Op), op_barrier_ext(B), "()!\" #endif " - ]; - SimpleFallback -> - ["#else\n", SimpleFallback, "#endif\n"] - end, - RtchkEnd(false, Op, B), "\n"]; - false -> - [native_barrier_op(AC, "#if", "", Op, B, hd(TryBarriers), true), - lists:map(fun (NB) -> - native_barrier_op(AC, "#elif", "", Op, B, NB, true) - end, - tl(TryBarriers)), - case simple_fallback(AC, Op, B) of - "" -> - %% No simple fallback available; - %% use cmpxchg() fallbacks... - [cmpxchg_fallbacks(AC, false, Op, B), - "#else + ]; + SimpleFallback -> + ["#else\n", SimpleFallback, "#endif\n"] + end, + RtchkEnd(false, Op, B), "\n"]; + false -> + [native_barrier_op(AC, "#if", "", Op, B, hd(TryBarriers), true), + lists:map(fun (NB) -> + native_barrier_op(AC, "#elif", "", Op, B, NB, true) + end, + tl(TryBarriers)), + case simple_fallback(AC, Op, B) of + "" -> + %% No simple fallback available; + %% use cmpxchg() fallbacks... + [cmpxchg_fallbacks(AC, false, Op, B), + "#else #error \"Missing implementation of ", Atomic, "_", opstr(Op), op_barrier_ext(B), "()!\" #endif " - ]; - SimpleFallback -> - ["#else\n", SimpleFallback, "#endif\n"] - end] - end, - case is_return_op(AC, Op) of - true -> - [" return ", RetVar, ";\n"]; - false -> - "" - end, - "}\n"] + ]; + SimpleFallback -> + ["#else\n", SimpleFallback, "#endif\n"] + end] + end, + case is_return_op(AC, Op) of + true -> + [" return ", RetVar, ";\n"]; + false -> + "" + end, + "}\n"] + end end, - ?BARRIERS)] + ?NATIVE_BARRIERS ++ ?NON_NATIVE_BARRIERS)] %% non-native needs to be after native... end, case DW of true -> ?DW_ATOMIC_OPS; @@ -1159,33 +1205,38 @@ int ethr_have_native_dw_atomic(void) ", lists:map(fun (B) -> - ["\n", - func_header(AC, implementation, - case DW of - true -> ?DW_FUNC_MACRO; - false -> false - end, Op, B), - "\n{\n", - case is_return_op(AC, Op) of - true -> [" ", RetType, " ", RetVar, ";\n"]; - false -> "" - end, - case Op of - init -> ""; - _ -> [" ETHR_ASSERT(!ethr_not_inited__);\n"] - end, - [" ETHR_ASSERT(", Arg1, ");\n"], - make_native_impl_op(AC, Op, B), - make_amc_fallback_op(AC#atomic_context{arg1 = FallbackVar}, Op, B), - make_locked_fallback_op(AC#atomic_context{arg1 = FallbackVar}, Op, B), - case is_return_op(AC, Op) of - true -> [" return ", RetVar, ";" - ]; - false -> - "" - end, - "\n}\n", - make_symbol_to_fallback_impl(AC, Op, B)] + Macro = case DW of + true -> ?DW_FUNC_MACRO; + false -> false + end, + case non_native_barrier(B) of + true -> + non_native_barrier_impl(AC, implementation, Macro, Op, B); + false -> + ["\n", + func_header(AC, implementation, Macro, Op, B), + "\n{\n", + case is_return_op(AC, Op) of + true -> [" ", RetType, " ", RetVar, ";\n"]; + false -> "" + end, + case Op of + init -> ""; + _ -> [" ETHR_ASSERT(!ethr_not_inited__);\n"] + end, + [" ETHR_ASSERT(", Arg1, ");\n"], + make_native_impl_op(AC, Op, B), + make_amc_fallback_op(AC#atomic_context{arg1 = FallbackVar}, Op, B), + make_locked_fallback_op(AC#atomic_context{arg1 = FallbackVar}, Op, B), + case is_return_op(AC, Op) of + true -> [" return ", RetVar, ";" + ]; + false -> + "" + end, + "\n}\n", + make_symbol_to_fallback_impl(AC, Op, B)] + end end, ?BARRIERS)] end, @@ -1233,7 +1284,7 @@ static char *native_", DW, "atomic", Bits, "_ops[] = {", #endif" ] end, - ?BARRIERS) + ?NATIVE_BARRIERS) end, case NBits of "dw" -> ?DW_ATOMIC_OPS; @@ -1390,25 +1441,51 @@ comments() -> * Appart from a function implementing the atomic operation * with unspecified memory barrier semantics, there are * functions implementing each operation with the following - * memory barrier semantics: -", + * implied memory barrier semantics:", lists:map(fun (none) -> ""; - (rb) -> - [" * - rb (read barrier)\n"]; - (wb) -> - [" * - wb (write barrier)\n"]; + (mb) -> + [" + * - mb - Full memory barrier. Orders both loads, and + * stores before, and after the atomic operation. + * No load or store is allowed to be reordered + * over the atomic operation."]; (acqb) -> - [" * - acqb (acquire barrier)\n"]; + [" + * - acqb - Acquire barrier. Orders both loads, and stores + * appearing *after* the atomic operation. These + * are not allowed to be reordered over the + * atomic operation."]; (relb) -> - [" * - relb (release barrier)\n"]; - (mb) -> - [" * - mb (full memory barrier)\n"]; + [" + * - relb - Release barrier. Orders both loads, and + * stores appearing *before* the atomic + * operation. These are not allowed to be + * reordered over the atomic operation."]; + (rb) -> + [" + * - rb - Read barrier. Orders *only* loads. These are + * not allowed to be reordered over the barrier. + * Load in atomic operation is ordered *before* + * the barrier. "]; + (ddrb) -> + [" + * - ddrb - Data dependency read barrier. Orders *only* + * loads according to data dependency across the + * barrier. Load in atomic operation is ordered + * before the barrier."]; + (wb) -> + [" + * - wb - Write barrier. Orders *only* stores. These are + * not allowed to be reordered over the barrier. + * Store in atomic operation is ordered *after* + * the barrier."]; (B) -> [" * - ", a2l(B), "\n"] end, - ?BARRIERS), - " * + lists:reverse(?BARRIERS)), + " + * * We implement all of these operation/barrier * combinations, regardless of whether they are useful * or not (some of them are useless). diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl index 7103d2390f..62bdc74cc8 100644 --- a/lib/compiler/src/beam_disasm.erl +++ b/lib/compiler/src/beam_disasm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2011. All Rights Reserved. +%% Copyright Ericsson AB 2000-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 @@ -182,10 +182,14 @@ process_chunks(F) -> Literals = beam_disasm_literals(LiteralBin), Code = beam_disasm_code(CodeBin, Atoms, mk_imports(ImportsList), StrBin, Lambdas, Literals, Module), - Attributes = optional_chunk(F, attributes), + Attributes = + case optional_chunk(F, attributes) of + none -> []; + Atts when is_list(Atts) -> Atts + end, CompInfo = case optional_chunk(F, "CInf") of - none -> none; + none -> []; CompInfoBin when is_binary(CompInfoBin) -> binary_to_term(CompInfoBin) end, @@ -198,7 +202,7 @@ process_chunks(F) -> end. %%----------------------------------------------------------------------- -%% Retrieve an optional chunk or none if the chunk doesn't exist. +%% Retrieve an optional chunk or return 'none' if the chunk doesn't exist. %%----------------------------------------------------------------------- optional_chunk(F, ChunkTag) -> diff --git a/lib/compiler/test/beam_disasm_SUITE.erl b/lib/compiler/test/beam_disasm_SUITE.erl index 44574ae64a..62afc80ca6 100644 --- a/lib/compiler/test/beam_disasm_SUITE.erl +++ b/lib/compiler/test/beam_disasm_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-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 @@ -60,6 +60,6 @@ stripped(Config) when is_list(Config) -> ?line true = is_list(Attr), ?line true = is_list(CompileInfo), ?line {ok, {tmp, _}} = beam_lib:strip(BeamName), - ?line {beam_file, tmp, _, none, none, [_|_]} = + ?line {beam_file, tmp, _, [], [], [_|_]} = beam_disasm:file(BeamName), ok. diff --git a/lib/hipe/main/hipe.app.src b/lib/hipe/main/hipe.app.src index e5d6d1e540..c05aec4c4a 100644 --- a/lib/hipe/main/hipe.app.src +++ b/lib/hipe/main/hipe.app.src @@ -74,7 +74,6 @@ hipe_arm_specific, hipe_bb, hipe_beam_to_icode, - hipe_ceach, hipe_coalescing_regalloc, hipe_consttab, hipe_data_pp, diff --git a/lib/hipe/main/hipe.erl b/lib/hipe/main/hipe.erl index 309d847374..1be679a13f 100644 --- a/lib/hipe/main/hipe.erl +++ b/lib/hipe/main/hipe.erl @@ -574,12 +574,8 @@ file(File, Options) when is_atom(File) -> disasm(File) -> case beam_disasm:file(File) of #beam_file{labeled_exports = LabeledExports, - compile_info = CompInfo0, + compile_info = CompInfo, code = BeamCode} -> - CompInfo = case CompInfo0 of - none -> []; - _ -> CompInfo0 - end, CompOpts = proplists:get_value(options, CompInfo, []), HCompOpts = case lists:keyfind(hipe, 1, CompOpts) of {hipe, L} when is_list(L) -> L; diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile index 7be97abf66..1734ae4df4 100644 --- a/lib/ssh/src/Makefile +++ b/lib/ssh/src/Makefile @@ -45,11 +45,11 @@ MODULES= \ ssh_sup \ sshc_sup \ sshd_sup \ + ssh_connection_sup \ ssh_channel \ ssh_connection \ ssh_connection_handler \ ssh_connection_manager \ - ssh_connection_controler \ ssh_shell \ ssh_system_sup \ ssh_subsystem_sup \ diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src index 7a58dbe54f..316c09eb06 100644 --- a/lib/ssh/src/ssh.app.src +++ b/lib/ssh/src/ssh.app.src @@ -15,7 +15,7 @@ ssh_connection, ssh_connection_handler, ssh_connection_manager, - ssh_connection_controler, + ssh_connection_sup, ssh_shell, sshc_sup, sshd_sup, diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 5751f2eaa0..39c7fe329e 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -72,17 +72,25 @@ stop() -> connect(Host, Port, Options) -> connect(Host, Port, Options, infinity). connect(Host, Port, Options, Timeout) -> - {SocketOpts, Opts} = handle_options(Options), - DisableIpv6 = proplists:get_value(ip_v6_disabled, Opts, false), - Inet = inetopt(DisableIpv6), + case handle_options(Options) of + {error, _Reason} = Error -> + Error; + {SocketOptions, SshOptions} -> + DisableIpv6 = proplists:get_value(ip_v6_disabled, SshOptions, false), + Inet = inetopt(DisableIpv6), + do_connect(Host, Port, [Inet | SocketOptions], + [{host, Host} | SshOptions], Timeout, DisableIpv6) + end. + +do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) -> try sshc_sup:start_child([[{address, Host}, {port, Port}, - {role, client}, - {channel_pid, self()}, - {socket_opts, [Inet | SocketOpts]}, - {ssh_opts, [{host, Host}| Opts]}]]) of + {role, client}, + {channel_pid, self()}, + {socket_opts, SocketOptions}, + {ssh_opts, SshOptions}]]) of {ok, ConnectionSup} -> {ok, Manager} = - ssh_connection_controler:connection_manager(ConnectionSup), + ssh_connection_sup:connection_manager(ConnectionSup), MRef = erlang:monitor(process, Manager), receive {Manager, is_connected} -> @@ -95,7 +103,8 @@ connect(Host, Port, Options, Timeout) -> %% match the Manager in this case {_, not_connected, {error, econnrefused}} when DisableIpv6 == false -> do_demonitor(MRef, Manager), - connect(Host, Port, [{ip_v6_disabled, true} | Options], Timeout); + do_connect(Host, Port, proplists:delete(inet6, SocketOptions), + SshOptions, Timeout, true); {_, not_connected, {error, Reason}} -> do_demonitor(MRef, Manager), {error, Reason}; @@ -192,7 +201,7 @@ daemon(HostAddr, Port, Options0) -> {HostAddr, inet6, [{ip, HostAddr} | Options1]} end, - start_daemon(Host, Port, [{role, server} | Options], Inet). + start_daemon(Host, Port, Options, Inet). %%-------------------------------------------------------------------- %% Function: stop_listener(SysRef) -> ok @@ -258,7 +267,14 @@ shell(Host, Port, Options) -> %%% Internal functions %%-------------------------------------------------------------------- start_daemon(Host, Port, Options, Inet) -> - {SocketOpts, Opts} = handle_options(Options), + case handle_options(Options) of + {error, _Reason} = Error -> + Error; + {SocketOptions, SshOptions}-> + do_start_daemon(Host, Port,[{role, server} |SshOptions] , [Inet | SocketOptions]) + end. + +do_start_daemon(Host, Port, Options, SocketOptions) -> case ssh_system_sup:system_supervisor(Host, Port) of undefined -> %% TODO: It would proably make more sense to call the @@ -266,8 +282,8 @@ start_daemon(Host, Port, Options, Inet) -> %% monent. The name is a legacy name! try sshd_sup:start_child([{address, Host}, {port, Port}, {role, server}, - {socket_opts, [Inet | SocketOpts]}, - {ssh_opts, Opts}]) of + {socket_opts, SocketOptions}, + {ssh_opts, Options}]) of {ok, SysSup} -> {ok, SysSup}; {error, {already_started, _}} -> @@ -286,68 +302,148 @@ start_daemon(Host, Port, Options, Inet) -> end. handle_options(Opts) -> - handle_options(proplists:unfold(Opts), [], []). -handle_options([], SockOpts, Opts) -> - {SockOpts, Opts}; -%% TODO: Could do some type checks here on plain ssh-opts -handle_options([{system_dir, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{user_dir, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{user_dir_fun, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{silently_accept_hosts, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{user_interaction, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{public_key_alg, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{connect_timeout, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{user, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{dsa_pass_phrase, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{rsa_pass_phrase, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{password, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{user_passwords, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{pwdfun, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{user_auth, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{key_cb, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{role, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{channel, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{compression, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{allow_user_interaction, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{infofun, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{connectfun, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{disconnectfun , _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{failfun, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{ip_v6_disabled, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]); -handle_options([{ip, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, [Opt |SockOpts], Opts); -handle_options([{ifaddr, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, [Opt |SockOpts], Opts); -handle_options([{fd, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, [Opt | SockOpts], Opts); -handle_options([{nodelay, _} = Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, [Opt | SockOpts], Opts); -handle_options([Opt | Rest], SockOpts, Opts) -> - handle_options(Rest, SockOpts, [Opt | Opts]). + try handle_option(proplists:unfold(Opts), [], []) of + {_,_} = Options -> + Options + catch + throw:Error -> + Error + end. + +handle_option([], SocketOptions, SshOptions) -> + {SocketOptions, SshOptions}; +handle_option([{system_dir, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{user_dir, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{user_dir_fun, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{silently_accept_hosts, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{user_interaction, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{public_key_alg, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{connect_timeout, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{user, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{dsa_pass_phrase, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{rsa_pass_phrase, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{password, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{user_passwords, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{pwdfun, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{user_auth, _} = Opt | Rest],SocketOptions, SshOptions ) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{key_cb, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{role, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{compression, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{allow_user_interaction, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{infofun, _} = Opt | Rest],SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{connectfun, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{disconnectfun, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{failfun, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{ip_v6_disabled, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{transport, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{subsystems, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{ssh_cli, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{shell, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions). + +handle_ssh_option({system_dir, Value} = Opt) when is_list(Value) -> + Opt; +handle_ssh_option({user_dir, Value} = Opt) when is_list(Value) -> + Opt; +handle_ssh_option({user_dir_fun, Value} = Opt) when is_function(Value) -> + Opt; +handle_ssh_option({silently_accept_hosts, Value} = Opt) when Value == true; Value == false -> + Opt; +handle_ssh_option({user_interaction, Value} = Opt) when Value == true; Value == false -> + Opt; +handle_ssh_option({public_key_alg, Value} = Opt) when Value == ssh_rsa; Value == ssh_dsa -> + Opt; +handle_ssh_option({connect_timeout, Value} = Opt) when is_integer(Value); Value == infinity -> + Opt; +handle_ssh_option({user, Value} = Opt) when is_list(Value) -> + Opt; +handle_ssh_option({dsa_pass_phrase, Value} = Opt) when is_list(Value) -> + Opt; +handle_ssh_option({rsa_pass_phrase, Value} = Opt) when is_list(Value) -> + Opt; +handle_ssh_option({password, Value} = Opt) when is_list(Value) -> + Opt; +handle_ssh_option({user_passwords, Value} = Opt) when is_list(Value)-> + Opt; +handle_ssh_option({pwdfun, Value} = Opt) when is_function(Value) -> + Opt; +handle_ssh_option({user_auth, Value} = Opt) when is_function(Value) -> + Opt; +handle_ssh_option({key_cb, Value} = Opt) when is_atom(Value) -> + Opt; +handle_ssh_option({compression, Value} = Opt) when is_atom(Value) -> + Opt; +handle_ssh_option({allow_user_interaction, Value} = Opt) when Value == true; + Value == false -> + Opt; +handle_ssh_option({infofun, Value} = Opt) when is_function(Value) -> + Opt; +handle_ssh_option({connectfun, Value} = Opt) when is_function(Value) -> + Opt; +handle_ssh_option({disconnectfun , Value} = Opt) when is_function(Value) -> + Opt; +handle_ssh_option({failfun, Value} = Opt) when is_function(Value) -> + Opt; +handle_ssh_option({ip_v6_disabled, Value} = Opt) when is_function(Value) -> + Opt; +handle_ssh_option({transport, {Protocol, Cb, ClosTag}} = Opt) when is_atom(Protocol), + is_atom(Cb), + is_atom(ClosTag) -> + Opt; +handle_ssh_option({subsystems, Value} = Opt) when is_list(Value) -> + Opt; +handle_ssh_option({ssh_cli, {Cb, _}}= Opt) when is_atom(Cb) -> + Opt; +handle_ssh_option({shell, {Module, Function, _}} = Opt) when is_atom(Module), + is_atom(Function) -> + Opt; +handle_ssh_option({shell, Value} = Opt) when is_function(Value) -> + Opt; +handle_ssh_option(Opt) -> + throw({error, {eoptions, Opt}}). + +handle_inet_option({active, _} = Opt) -> + throw({error, {{eoptions, Opt}, "Ssh has built in flow control, " + "and activ is handled internaly user is not allowd" + "to specify this option"}}); +handle_inet_option({inet, _} = Opt) -> + throw({error, {{eoptions, Opt},"Is set internaly use ip_v6_disabled to" + " enforce iv4 in the server, client will fallback to ipv4 if" + " it can not use ipv6"}}); +handle_inet_option({reuseaddr, _} = Opt) -> + throw({error, {{eoptions, Opt},"Is set internaly user is not allowd" + "to specify this option"}}); +%% Option verified by inet +handle_inet_option(Opt) -> + Opt. %% Has IPv6 been disabled? inetopt(true) -> diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl index 59fbd24cf5..d023656c32 100644 --- a/lib/ssh/src/ssh_acceptor.erl +++ b/lib/ssh/src/ssh_acceptor.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -86,11 +86,11 @@ handle_connection(Callback, Address, Port, Options, Socket) -> {ok, SubSysSup} = ssh_system_sup:start_subsystem(SystemSup, Options), ConnectionSup = ssh_system_sup:connection_supervisor(SystemSup), {ok, Pid} = - ssh_connection_controler:start_manager_child(ConnectionSup, - [server, Socket, Options, SubSysSup]), + ssh_connection_sup:start_manager_child(ConnectionSup, + [server, Socket, Options]), Callback:controlling_process(Socket, Pid), SshOpts = proplists:get_value(ssh_opts, Options), - Pid ! {start_connection, server, [Address, Port, Socket, SshOpts]}. + Pid ! {start_connection, server, [Address, Port, Socket, SshOpts, SubSysSup]}. handle_error(timeout) -> ok; diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 62d684f4dc..1a4517c689 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -340,7 +340,7 @@ verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) -> ?UINT32(SigLen), Sig:SigLen/binary>> = AlgSig, ssh_transport:verify(PlainText, sha, Sig, Key); false -> - {error, key_unacceptable} + false end. build_sig_data(SessionId, User, Service, KeyBlob, Alg) -> diff --git a/lib/ssh/src/ssh_connect.hrl b/lib/ssh/src/ssh_connect.hrl index e06c9ea211..932b0642f1 100644 --- a/lib/ssh/src/ssh_connect.hrl +++ b/lib/ssh/src/ssh_connect.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -253,7 +253,6 @@ -record(connection, { requests = [], %% [{ChannelId, Pid}...] awaiting reply on request, channel_cache, - channel_pids = [], port_bindings, channel_id_seed, cli_spec, @@ -261,5 +260,6 @@ port, options, exec, - sub_system_supervisor + sub_system_supervisor, + connection_supervisor }). diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl index cb02d7b824..46f0c7e688 100644 --- a/lib/ssh/src/ssh_connection.erl +++ b/lib/ssh/src/ssh_connection.erl @@ -744,8 +744,8 @@ handle_msg(#ssh_msg_global_request{name = _Type, %%% This transport message will also be handled at the connection level handle_msg(#ssh_msg_disconnect{code = Code, - description = Description, - language = _Lang }, + description = Description, + language = _Lang }, #connection{channel_cache = Cache} = Connection0, _, _) -> {Connection, Replies} = ssh_channel:cache_foldl(fun(Channel, {Connection1, Acc}) -> @@ -779,7 +779,7 @@ handle_cli_msg(#connection{channel_cache = Cache} = Connection0, handle_cli_msg(Connection0, _, Channel, Reply0) -> {Reply, Connection} = reply_msg(Channel, Connection0, Reply0), {{replies, [Reply]}, Connection}. - + channel_eof_msg(ChannelId) -> #ssh_msg_channel_eof{recipient_channel = ChannelId}. diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index 9079089d5d..5b3d1b8a1b 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -41,7 +41,7 @@ -export([hello/2, kexinit/2, key_exchange/2, new_keys/2, userauth/2, connected/2]). --export([init/1, state_name/3, handle_event/3, +-export([init/1, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). %% spawn export @@ -106,22 +106,28 @@ peer_address(ConnectionHandler) -> %% initialize. %%-------------------------------------------------------------------- init([Role, Manager, Socket, SshOpts]) -> + process_flag(trap_exit, true), {NumVsn, StrVsn} = ssh_transport:versions(Role, SshOpts), ssh_bits:install_messages(ssh_transport:transport_messages(NumVsn)), {Protocol, Callback, CloseTag} = proplists:get_value(transport, SshOpts, {tcp, gen_tcp, tcp_closed}), - Ssh = init_ssh(Role, NumVsn, StrVsn, SshOpts, Socket), - {ok, hello, #state{ssh_params = - Ssh#ssh{send_sequence = 0, recv_sequence = 0}, - socket = Socket, - decoded_data_buffer = <<>>, - encoded_data_buffer = <<>>, - transport_protocol = Protocol, - transport_cb = Callback, - transport_close_tag = CloseTag, - manager = Manager, - opts = SshOpts - }}. + try init_ssh(Role, NumVsn, StrVsn, SshOpts, Socket) of + Ssh -> + {ok, hello, #state{ssh_params = + Ssh#ssh{send_sequence = 0, recv_sequence = 0}, + socket = Socket, + decoded_data_buffer = <<>>, + encoded_data_buffer = <<>>, + transport_protocol = Protocol, + transport_cb = Callback, + transport_close_tag = CloseTag, + manager = Manager, + opts = SshOpts + }} + catch + exit:Reason -> + {stop, {shutdown, Reason}} + end. %%-------------------------------------------------------------------- %% Function: %% state_name(Event, State) -> {next_state, NextStateName, NextState}| @@ -179,7 +185,12 @@ kexinit({#ssh_msg_kexinit{} = Kex, Payload}, next_packet(State#state{ssh_params = Ssh})} catch #ssh_msg_disconnect{} = DisconnectMsg -> - handle_disconnect(DisconnectMsg, State) + handle_disconnect(DisconnectMsg, State); + _:Error -> + Desc = log_error(Error), + handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + description = Desc, + language = "en"}, State) end. key_exchange(#ssh_msg_kexdh_init{} = Msg, @@ -192,7 +203,12 @@ key_exchange(#ssh_msg_kexdh_init{} = Msg, {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})} catch #ssh_msg_disconnect{} = DisconnectMsg -> - handle_disconnect(DisconnectMsg, State) + handle_disconnect(DisconnectMsg, State); + _:Error -> + Desc = log_error(Error), + handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + description = Desc, + language = "en"}, State) end; key_exchange(#ssh_msg_kexdh_reply{} = Msg, @@ -203,7 +219,12 @@ key_exchange(#ssh_msg_kexdh_reply{} = Msg, {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})} catch #ssh_msg_disconnect{} = DisconnectMsg -> - handle_disconnect(DisconnectMsg, State) + handle_disconnect(DisconnectMsg, State); + _:Error -> + Desc = log_error(Error), + handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + description = Desc, + language = "en"}, State) end; key_exchange(#ssh_msg_kex_dh_gex_group{} = Msg, @@ -216,7 +237,12 @@ key_exchange(#ssh_msg_kex_dh_gex_group{} = Msg, {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})} catch #ssh_msg_disconnect{} = DisconnectMsg -> - handle_disconnect(DisconnectMsg, State) + handle_disconnect(DisconnectMsg, State); + _:Error -> + Desc = log_error(Error), + handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + description = Desc, + language = "en"}, State) end; key_exchange(#ssh_msg_kex_dh_gex_request{} = Msg, @@ -227,7 +253,12 @@ key_exchange(#ssh_msg_kex_dh_gex_request{} = Msg, {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})} catch #ssh_msg_disconnect{} = DisconnectMsg -> - handle_disconnect(DisconnectMsg, State) + handle_disconnect(DisconnectMsg, State); + _:Error -> + Desc = log_error(Error), + handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + description = Desc, + language = "en"}, State) end; key_exchange(#ssh_msg_kex_dh_gex_reply{} = Msg, #state{ssh_params = #ssh{role = client} = Ssh0} = State) -> @@ -237,7 +268,12 @@ key_exchange(#ssh_msg_kex_dh_gex_reply{} = Msg, {next_state, new_keys, next_packet(State#state{ssh_params = Ssh})} catch #ssh_msg_disconnect{} = DisconnectMsg -> - handle_disconnect(DisconnectMsg, State) + handle_disconnect(DisconnectMsg, State); + _:Error -> + Desc = log_error(Error), + handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + description = Desc, + language = "en"}, State) end. new_keys(#ssh_msg_newkeys{} = Msg, #state{ssh_params = Ssh0} = State0) -> @@ -248,8 +284,12 @@ new_keys(#ssh_msg_newkeys{} = Msg, #state{ssh_params = Ssh0} = State0) -> {next_state, NextStateName, next_packet(State)} catch #ssh_msg_disconnect{} = DisconnectMsg -> - handle_disconnect(DisconnectMsg, State0), - {stop, normal, State0} + handle_disconnect(DisconnectMsg, State0); + _:Error -> + Desc = log_error(Error), + handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + description = Desc, + language = "en"}, State0) end. userauth(#ssh_msg_service_request{name = "ssh-userauth"} = Msg, @@ -262,7 +302,12 @@ userauth(#ssh_msg_service_request{name = "ssh-userauth"} = Msg, {next_state, userauth, next_packet(State#state{ssh_params = Ssh})} catch #ssh_msg_disconnect{} = DisconnectMsg -> - handle_disconnect(DisconnectMsg, State) + handle_disconnect(DisconnectMsg, State); + _:Error -> + Desc = log_error(Error), + handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE, + description = Desc, + language = "en"}, State) end; userauth(#ssh_msg_service_accept{name = "ssh-userauth"}, @@ -284,7 +329,12 @@ userauth(#ssh_msg_userauth_request{service = "ssh-connection", {next_state, userauth, next_packet(State#state{ssh_params = Ssh})} catch #ssh_msg_disconnect{} = DisconnectMsg -> - handle_disconnect(DisconnectMsg, State) + handle_disconnect(DisconnectMsg, State); + _:Error -> + Desc = log_error(Error), + handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE, + description = Desc, + language = "en"}, State) end; userauth(#ssh_msg_userauth_request{service = "ssh-connection", @@ -307,7 +357,12 @@ userauth(#ssh_msg_userauth_request{service = "ssh-connection", {next_state, userauth, next_packet(State#state{ssh_params = Ssh})} catch #ssh_msg_disconnect{} = DisconnectMsg -> - handle_disconnect(DisconnectMsg, State) + handle_disconnect(DisconnectMsg, State); + _:Error -> + Desc = log_error(Error), + handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE, + description = Desc, + language = "en"}, State) end; userauth(#ssh_msg_userauth_info_request{} = Msg, @@ -319,7 +374,12 @@ userauth(#ssh_msg_userauth_info_request{} = Msg, {next_state, userauth, next_packet(State#state{ssh_params = Ssh})} catch #ssh_msg_disconnect{} = DisconnectMsg -> - handle_disconnect(DisconnectMsg, State) + handle_disconnect(DisconnectMsg, State); + _:Error -> + Desc = log_error(Error), + handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE, + description = Desc, + language = "en"}, State) end; userauth(#ssh_msg_userauth_info_response{} = Msg, @@ -330,7 +390,12 @@ userauth(#ssh_msg_userauth_info_response{} = Msg, {next_state, userauth, next_packet(State#state{ssh_params = Ssh})} catch #ssh_msg_disconnect{} = DisconnectMsg -> - handle_disconnect(DisconnectMsg, State) + handle_disconnect(DisconnectMsg, State); + _:Error -> + Desc = log_error(Error), + handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE, + description = Desc, + language = "en"}, State) end; userauth(#ssh_msg_userauth_success{}, #state{ssh_params = #ssh{role = client}, @@ -361,24 +426,11 @@ userauth(#ssh_msg_userauth_failure{authentications = Methodes}, %% The prefered authentication method failed try next method userauth(#ssh_msg_userauth_failure{}, - #state{ssh_params = #ssh{role = client} = Ssh0, - manager = Pid} = State) -> + #state{ssh_params = #ssh{role = client} = Ssh0} = State) -> case ssh_auth:userauth_request_msg(Ssh0) of - {disconnect, Event, {Msg, _}} -> - try - send_msg(Msg, State), - ssh_connection_manager:event(Pid, Event) - catch - exit:{noproc, _Reason} -> - Report = io_lib:format("Connection Manager terminated: ~p~n", - [Pid]), - error_logger:info_report(Report); - exit:Exit -> - Report = io_lib:format("Connection Manager returned:~n~p~n~p~n", - [Msg, Exit]), - error_logger:info_report(Report) - end, - {stop, normal, State}; + {disconnect, DisconnectMsg,{Msg, Ssh}} -> + send_msg(Msg, State), + handle_disconnect(DisconnectMsg, State#state{ssh_params = Ssh}); {Msg, Ssh} -> send_msg(Msg, State), {next_state, userauth, next_packet(State#state{ssh_params = Ssh})} @@ -398,25 +450,6 @@ connected({#ssh_msg_kexinit{}, _Payload} = Event, State) -> kexinit(Event, State#state{renegotiate = true}). %%-------------------------------------------------------------------- -%% Function: -%% state_name(Event, From, State) -> {next_state, NextStateName, NextState} | -%% {next_state, NextStateName, -%% NextState, Timeout} | -%% {reply, Reply, NextStateName, NextState}| -%% {reply, Reply, NextStateName, -%% NextState, Timeout} | -%% {stop, Reason, NewState}| -%% {stop, Reason, Reply, NewState} -%% Description: There should be one instance of this function for each -%% possible state name. Whenever a gen_fsm receives an event sent using -%% gen_fsm:sync_send_event/2,3, the instance of this function with the same -%% name as the current state name StateName is called to handle the event. -%%-------------------------------------------------------------------- -state_name(_Event, _From, State) -> - Reply = ok, - {reply, Reply, state_name, State}. - -%%-------------------------------------------------------------------- %% Function: %% handle_event(Event, StateName, State) -> {next_state, NextStateName, %% NextState} | @@ -566,10 +599,18 @@ handle_info({Protocol, Socket, Data}, Statename, Statename, State); handle_info({CloseTag, _Socket}, _StateName, - #state{transport_close_tag = CloseTag, %%manager = Pid, + #state{transport_close_tag = CloseTag, ssh_params = #ssh{role = _Role, opts = _Opts}} = State) -> - %%ok = ssh_connection_manager:delivered(Pid), - {stop, normal, State}; + DisconnectMsg = + #ssh_msg_disconnect{code = ?SSH_DISCONNECT_CONNECTION_LOST, + description = "Connection Lost", + language = "en"}, + {stop, {shutdown, DisconnectMsg}, State}; + +%%% So that terminate will be run when supervisor is shutdown +handle_info({'EXIT', _Sup, Reason}, _StateName, State) -> + {stop, Reason, State}; + handle_info(UnexpectedMessage, StateName, #state{ssh_params = SshParams} = State) -> Msg = lists:flatten(io_lib:format( "Unexpected message '~p' received in state '~p'\n" @@ -581,7 +622,6 @@ handle_info(UnexpectedMessage, StateName, #state{ssh_params = SshParams} = State error_logger:info_report(Msg), {next_state, StateName, State}. - %%-------------------------------------------------------------------- %% Function: terminate(Reason, StateName, State) -> void() %% Description:This function is called by a gen_fsm when it is about @@ -596,22 +636,31 @@ terminate(normal, _, #state{transport_cb = Transport, (catch Transport:close(Socket)), ok; -terminate(shutdown, _, State) -> +%% Terminated as manager terminated +terminate(shutdown, StateName, #state{ssh_params = Ssh0} = State) -> DisconnectMsg = #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION, - description = "Application disconnect", + description = "Application shutdown", language = "en"}, - handle_disconnect(DisconnectMsg, State); + {SshPacket, Ssh} = ssh_transport:ssh_packet(DisconnectMsg, Ssh0), + send_msg(SshPacket, State), + terminate(normal, StateName, State#state{ssh_params = Ssh}); -terminate(Reason, _, State) -> - Desc = io_lib:format("Erlang ssh connection handler failed with reason: " - "~p , please report this to [email protected] \n", - [Reason]), +terminate({shutdown, #ssh_msg_disconnect{} = Msg}, StateName, #state{ssh_params = Ssh0, manager = Pid} = State) -> + {SshPacket, Ssh} = ssh_transport:ssh_packet(Msg, Ssh0), + send_msg(SshPacket, State), + ssh_connection_manager:event(Pid, Msg), + terminate(normal, StateName, State#state{ssh_params = Ssh}); +terminate(Reason, StateName, #state{ssh_params = Ssh0, manager = Pid} = State) -> + log_error(Reason), DisconnectMsg = - #ssh_msg_disconnect{code = ?SSH_DISCONNECT_CONNECTION_LOST, - description = Desc, + #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION, + description = "Internal error", language = "en"}, - handle_disconnect(DisconnectMsg, State). + {SshPacket, Ssh} = ssh_transport:ssh_packet(DisconnectMsg, Ssh0), + ssh_connection_manager:event(Pid, DisconnectMsg), + send_msg(SshPacket, State), + terminate(normal, StateName, State#state{ssh_params = Ssh}). %%-------------------------------------------------------------------- %% Function: @@ -757,11 +806,8 @@ generate_event(<<?BYTE(Byte), _/binary>> = Msg, StateName, next_packet(State), {next_state, StateName, State} catch - exit:{noproc, _Reason} -> - Report = io_lib:format("~p Connection Handler terminated: ~p~n", - [self(), Pid]), - error_logger:info_report(Report), - {stop, normal, State0} + exit:{noproc, Reason} -> + {stop, {shutdown, Reason}, State0} end; generate_event(Msg, StateName, State0, EncData) -> Event = ssh_bits:decode(Msg), @@ -786,13 +832,18 @@ generate_event_new_state(#state{ssh_params = next_packet(#state{decoded_data_buffer = <<>>, encoded_data_buffer = Buff, + ssh_params = #ssh{decrypt_block_size = BlockSize}, socket = Socket, - transport_protocol = Protocol} = - State) when Buff =/= <<>> andalso size(Buff) >= 8 -> - %% More data from the next packet has been received - %% Fake a socket-recive message so that the data will be processed - inet:setopts(Socket, [{active, once}]), - self() ! {Protocol, Socket, <<>>}, + transport_protocol = Protocol} = State) when Buff =/= <<>> -> + case size(Buff) >= erlang:max(8, BlockSize) of + true -> + %% Enough data from the next packet has been received to + %% decode the length indicator, fake a socket-recive + %% message so that the data will be processed + self() ! {Protocol, Socket, <<>>}; + false -> + inet:setopts(Socket, [{active, once}]) + end, State; next_packet(#state{socket = Socket} = State) -> @@ -853,24 +904,8 @@ handle_ssh_packet(Length, StateName, #state{decoded_data_buffer = DecData0, handle_disconnect(DisconnectMsg, State0) end. -handle_disconnect(#ssh_msg_disconnect{} = Msg, - #state{ssh_params = Ssh0, manager = Pid} = State) -> - {SshPacket, Ssh} = ssh_transport:ssh_packet(Msg, Ssh0), - try - send_msg(SshPacket, State), - ssh_connection_manager:event(Pid, Msg) - catch - exit:{noproc, _Reason} -> - Report = io_lib:format("~p Connection Manager terminated: ~p~n", - [self(), Pid]), - error_logger:info_report(Report); - exit:Exit -> - Report = io_lib:format("Connection Manager returned:~n~p~n~p~n", - [Msg, Exit]), - error_logger:info_report(Report) - end, - (catch ssh_userreg:delete_user(Pid)), - {stop, normal, State#state{ssh_params = Ssh}}. +handle_disconnect(#ssh_msg_disconnect{} = Msg, State) -> + {stop, {shutdown, Msg}, State}. counterpart_versions(NumVsn, StrVsn, #ssh{role = server} = Ssh) -> Ssh#ssh{c_vsn = NumVsn , c_version = StrVsn}; @@ -928,3 +963,11 @@ ssh_info([peer | Rest], #ssh{peer = Peer} = SshParams, Acc) -> ssh_info([ _ | Rest], SshParams, Acc) -> ssh_info(Rest, SshParams, Acc). + +log_error(Reason) -> + Report = io_lib:format("Erlang ssh connection handler failed with reason: " + "~p ~n, Stacktace: ~p ~n" + "please report this to [email protected] \n", + [Reason, erlang:get_stacktrace()]), + error_logger:error_report(Report), + "Internal error". diff --git a/lib/ssh/src/ssh_connection_manager.erl b/lib/ssh/src/ssh_connection_manager.erl index f729276e65..406a042d72 100644 --- a/lib/ssh/src/ssh_connection_manager.erl +++ b/lib/ssh/src/ssh_connection_manager.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -182,17 +182,15 @@ send_eof(ConnectionManager, ChannelId) -> %% {stop, Reason} %% Description: Initiates the server %%-------------------------------------------------------------------- -init([server, _Socket, Opts, SubSysSup]) -> +init([server, _Socket, Opts]) -> process_flag(trap_exit, true), ssh_bits:install_messages(ssh_connection:messages()), - Cache = ssh_channel:cache_create(), + Cache = ssh_channel:cache_create(), {ok, #state{role = server, connection_state = #connection{channel_cache = Cache, channel_id_seed = 0, port_bindings = [], - requests = [], - channel_pids = [], - sub_system_supervisor = SubSysSup}, + requests = []}, opts = Opts, connected = false}}; @@ -207,15 +205,14 @@ init([client, Opts]) -> Options = proplists:get_value(ssh_opts, Opts), ChannelPid = proplists:get_value(channel_pid, Opts), self() ! - {start_connection, client, [Parent, Address, Port, - ChannelPid, SocketOpts, Options]}, + {start_connection, client, [Parent, Address, Port, SocketOpts, Options]}, {ok, #state{role = client, client = ChannelPid, connection_state = #connection{channel_cache = Cache, channel_id_seed = 0, port_bindings = [], - requests = [], - channel_pids = []}, + connection_supervisor = Parent, + requests = []}, opts = Opts, connected = false}}. @@ -269,29 +266,25 @@ handle_call({ssh_msg, Pid, Msg}, From, when Role == client andalso (not IsConnected) -> lists:foreach(fun send_msg/1, Replies), ClientPid ! {self(), not_connected, Reason}, - {stop, normal, State#state{connection = Connection}}; + {stop, {shutdown, normal}, State#state{connection = Connection}}; {disconnect, Reason, {{replies, Replies}, Connection}} -> lists:foreach(fun send_msg/1, Replies), SSHOpts = proplists:get_value(ssh_opts, Opts), disconnect_fun(Reason, SSHOpts), - {stop, normal, State#state{connection_state = Connection}} + {stop, {shutdown, normal}, State#state{connection_state = Connection}} catch - exit:{noproc, Reason} -> - Report = io_lib:format("Connection probably terminated:~n~p~n~p~n~p~n", - [ConnectionMsg, Reason, erlang:get_stacktrace()]), - error_logger:info_report(Report), - {noreply, State}; - error:Error -> - Report = io_lib:format("Connection message returned:~n~p~n~p~n~p~n", - [ConnectionMsg, Error, erlang:get_stacktrace()]), - error_logger:info_report(Report), - {noreply, State}; - exit:Exit -> - Report = io_lib:format("Connection message returned:~n~p~n~p~n~p~n", - [ConnectionMsg, Exit, erlang:get_stacktrace()]), - error_logger:info_report(Report), - {noreply, State} - end; + _:Reason -> + {disconnect, Reason, {{replies, Replies}, Connection}} = + ssh_connection:handle_msg( + #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION, + description = "Internal error", + language = "en"}, Connection0, undefined, + Role), + lists:foreach(fun send_msg/1, Replies), + SSHOpts = proplists:get_value(ssh_opts, Opts), + disconnect_fun(Reason, SSHOpts), + {stop, {shutdown, Reason}, State#state{connection_state = Connection}} + end; handle_call({global_request, Pid, _, _, _} = Request, From, #state{connection_state = @@ -341,7 +334,8 @@ handle_call({info, ChannelPid}, _From, handle_call({open, ChannelPid, Type, InitialWindowSize, MaxPacketSize, Data}, From, #state{connection = Pid, connection_state = - #connection{channel_cache = Cache}} = State0) -> + #connection{channel_cache = Cache}} = State0) -> + erlang:monitor(process, ChannelPid), {ChannelId, State1} = new_channel_id(State0), Msg = ssh_connection:channel_open_msg(Type, ChannelId, InitialWindowSize, @@ -404,18 +398,18 @@ handle_call({close, ChannelId}, _, {reply, ok, State} end; -handle_call(stop, _, #state{role = _client, - client = _ChannelPid, - connection = Pid} = State) -> - DisconnectMsg = - #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION, - description = "Application disconnect", - language = "en"}, - (catch gen_fsm:send_all_state_event(Pid, DisconnectMsg)), -% ssh_connection_handler:send(Pid, DisconnectMsg), - {stop, normal, ok, State}; -handle_call(stop, _, State) -> - {stop, normal, ok, State}; +handle_call(stop, _, #state{connection_state = Connection0, + role = Role, + opts = Opts} = State) -> + {disconnect, Reason, {{replies, Replies}, Connection}} = + ssh_connection:handle_msg(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION, + description = "User closed down connection", + language = "en"}, Connection0, undefined, + Role), + lists:foreach(fun send_msg/1, Replies), + SSHOpts = proplists:get_value(ssh_opts, Opts), + disconnect_fun(Reason, SSHOpts), + {stop, normal, ok, State#state{connection_state = Connection}}; %% API violation make it the violaters problem %% by ignoring it. The violating process will get @@ -493,31 +487,33 @@ handle_cast({failure, ChannelId}, #state{connection = Pid} = State) -> %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- handle_info({start_connection, server, - [Address, Port, Socket, Options]}, + [Address, Port, Socket, Options, SubSysSup]}, #state{connection_state = CState} = State) -> {ok, Connection} = ssh_transport:accept(Address, Port, Socket, Options), Shell = proplists:get_value(shell, Options), Exec = proplists:get_value(exec, Options), CliSpec = proplists:get_value(ssh_cli, Options, {ssh_cli, [Shell]}), + ssh_connection_handler:send_event(Connection, socket_control), {noreply, State#state{connection = Connection, connection_state = CState#connection{address = Address, port = Port, cli_spec = CliSpec, options = Options, - exec = Exec}}}; + exec = Exec, + sub_system_supervisor = SubSysSup + }}}; handle_info({start_connection, client, - [Parent, Address, Port, ChannelPid, SocketOpts, Options]}, - State) -> + [Parent, Address, Port, SocketOpts, Options]}, + #state{client = Pid} = State) -> case (catch ssh_transport:connect(Parent, Address, Port, SocketOpts, Options)) of {ok, Connection} -> - erlang:monitor(process, ChannelPid), {noreply, State#state{connection = Connection}}; Reason -> - ChannelPid ! {self(), not_connected, Reason}, - {stop, normal, State} + Pid ! {self(), not_connected, Reason}, + {stop, {shutdown, normal}, State} end; handle_info({ssh_cm, _Sender, Msg}, State0) -> @@ -537,20 +533,13 @@ handle_info(ssh_connected, #state{role = client, client = Pid} handle_info(ssh_connected, #state{role = server} = State) -> {noreply, State#state{connected = true}}; -handle_info({'DOWN', _Ref, process, ChannelPid, normal}, State0) -> - handle_down(handle_channel_down(ChannelPid, State0)); - -handle_info({'DOWN', _Ref, process, ChannelPid, shutdown}, State0) -> - handle_down(handle_channel_down(ChannelPid, State0)); - -handle_info({'DOWN', _Ref, process, ChannelPid, Reason}, State0) -> - Report = io_lib:format("Pid ~p DOWN ~p\n", [ChannelPid, Reason]), - error_logger:error_report(Report), - handle_down(handle_channel_down(ChannelPid, State0)); +%%% Handle that ssh channels user process goes down +handle_info({'DOWN', _Ref, process, ChannelPid, _Reason}, State) -> + handle_down(handle_channel_down(ChannelPid, State)); -handle_info({'EXIT', _, _}, State) -> - %% Handled in 'DOWN' - {noreply, State}. +%%% So that terminate will be run when supervisor is shutdown +handle_info({'EXIT', _Sup, Reason}, State) -> + {stop, Reason, State}. %%-------------------------------------------------------------------- %% Function: terminate(Reason, State) -> void() @@ -559,20 +548,19 @@ handle_info({'EXIT', _, _}, State) -> %% cleaning up. When it returns, the gen_server terminates with Reason. %% The return value is ignored. %%-------------------------------------------------------------------- -terminate(Reason, #state{connection_state = - #connection{requests = Requests, - sub_system_supervisor = SubSysSup}, +terminate(_Reason, #state{role = client, + connection_state = + #connection{connection_supervisor = Supervisor}}) -> + sshc_sup:stop_child(Supervisor); + +terminate(_Reason, #state{role = server, + connection_state = + #connection{sub_system_supervisor = SubSysSup}, opts = Opts}) -> - SSHOpts = proplists:get_value(ssh_opts, Opts), - disconnect_fun(Reason, SSHOpts), - (catch lists:foreach(fun({_, From}) -> - gen_server:reply(From, {error, connection_closed}) - end, Requests)), Address = proplists:get_value(address, Opts), Port = proplists:get_value(port, Opts), SystemSup = ssh_system_sup:system_supervisor(Address, Port), - ssh_system_sup:stop_subsystem(SystemSup, SubSysSup), - ok. + ssh_system_sup:stop_subsystem(SystemSup, SubSysSup). %%-------------------------------------------------------------------- %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} @@ -619,16 +607,7 @@ decode_ssh_msg(Msg) -> send_msg(Msg) -> - case catch do_send_msg(Msg) of - {'EXIT', Reason}-> - Report = io_lib:format("Connection Manager fail to send:~n~p~n" - "Reason why it failed was:~n~p~n", - [Msg, Reason]), - error_logger:info_report(Report); - _ -> - ok - end. - + catch do_send_msg(Msg). do_send_msg({channel_data, Pid, Data}) -> Pid ! {ssh_cm, self(), Data}; do_send_msg({channel_requst_reply, From, Data}) -> @@ -676,8 +655,8 @@ handle_down({{replies, Replies}, State}) -> {noreply, State}. handle_channel_down(ChannelPid, #state{connection_state = - #connection{channel_cache = Cache}} = - State) -> + #connection{channel_cache = Cache}} = + State) -> ssh_channel:cache_foldl( fun(Channel, Acc) when Channel#channel.user == ChannelPid -> ssh_channel:cache_delete(Cache, diff --git a/lib/ssh/src/ssh_connection_sup.erl b/lib/ssh/src/ssh_connection_sup.erl new file mode 100644 index 0000000000..e3544af1c6 --- /dev/null +++ b/lib/ssh/src/ssh_connection_sup.erl @@ -0,0 +1,113 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-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 +%% 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% +%% +%% +%%---------------------------------------------------------------------- +%% Purpose: Ssh connection supervisor. +%%---------------------------------------------------------------------- + +-module(ssh_connection_sup). + +-behaviour(supervisor). + +-export([start_link/1, start_handler_child/2, start_manager_child/2, + connection_manager/1]). + +%% Supervisor callback +-export([init/1]). + +%%%========================================================================= +%%% API +%%%========================================================================= +start_link(Args) -> + supervisor:start_link(?MODULE, [Args]). + +%% Will be called from the manager child process +start_handler_child(Sup, Args) -> + [Spec] = child_specs(handler, Args), + supervisor:start_child(Sup, Spec). + +%% Will be called from the acceptor process +start_manager_child(Sup, Args) -> + [Spec] = child_specs(manager, Args), + supervisor:start_child(Sup, Spec). + +connection_manager(SupPid) -> + Children = supervisor:which_children(SupPid), + {ok, ssh_connection_manager(Children)}. + +%%%========================================================================= +%%% Supervisor callback +%%%========================================================================= +init([Args]) -> + RestartStrategy = one_for_all, + MaxR = 0, + MaxT = 3600, + Children = child_specs(Args), + {ok, {{RestartStrategy, MaxR, MaxT}, Children}}. + +%%%========================================================================= +%%% Internal functions +%%%========================================================================= +child_specs(Opts) -> + case proplists:get_value(role, Opts) of + client -> + child_specs(manager, [client | Opts]); + server -> + %% Children started by acceptor process + [] + end. + +% The manager process starts the handler process +child_specs(manager, Opts) -> + [manager_spec(Opts)]; +child_specs(handler, Opts) -> + [handler_spec(Opts)]. + +manager_spec([server = Role, Socket, Opts]) -> + Name = make_ref(), + StartFunc = {ssh_connection_manager, start_link, [[Role, Socket, Opts]]}, + Restart = temporary, + Shutdown = 3600, + Modules = [ssh_connection_manager], + Type = worker, + {Name, StartFunc, Restart, Shutdown, Type, Modules}; + +manager_spec([client = Role | Opts]) -> + Name = make_ref(), + StartFunc = {ssh_connection_manager, start_link, [[Role, Opts]]}, + Restart = temporary, + Shutdown = 3600, + Modules = [ssh_connection_manager], + Type = worker, + {Name, StartFunc, Restart, Shutdown, Type, Modules}. + +handler_spec([Role, Socket, Opts]) -> + Name = make_ref(), + StartFunc = {ssh_connection_handler, + start_link, [Role, self(), Socket, Opts]}, + Restart = temporary, + Shutdown = 3600, + Modules = [ssh_connection_handler], + Type = worker, + {Name, StartFunc, Restart, Shutdown, Type, Modules}. + +ssh_connection_manager([{_, Child, _, [ssh_connection_manager]} | _]) -> + Child; +ssh_connection_manager([_ | Rest]) -> + ssh_connection_manager(Rest). diff --git a/lib/ssh/src/ssh_subsystem_sup.erl b/lib/ssh/src/ssh_subsystem_sup.erl index d71b6bbc56..cd6defd535 100644 --- a/lib/ssh/src/ssh_subsystem_sup.erl +++ b/lib/ssh/src/ssh_subsystem_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -70,13 +70,12 @@ ssh_connectinon_child_spec(Opts) -> Address = proplists:get_value(address, Opts), Port = proplists:get_value(port, Opts), Role = proplists:get_value(role, Opts), - Name = id(Role, ssh_connection_controler, Address, Port), - StartFunc = {ssh_connection_controler, start_link, [Opts]}, + Name = id(Role, ssh_connection_sup, Address, Port), + StartFunc = {ssh_connection_sup, start_link, [Opts]}, Restart = transient, -% Restart = permanent, Shutdown = 5000, - Modules = [ssh_connection_controler], - Type = worker, + Modules = [ssh_connection_sup], + Type = supervisor, {Name, StartFunc, Restart, Shutdown, Type, Modules}. ssh_channel_child_spec(Opts) -> @@ -86,7 +85,6 @@ ssh_channel_child_spec(Opts) -> Name = id(Role, ssh_channel_sup, Address, Port), StartFunc = {ssh_channel_sup, start_link, [Opts]}, Restart = transient, -% Restart = permanent, Shutdown = infinity, Modules = [ssh_channel_sup], Type = supervisor, @@ -95,7 +93,7 @@ ssh_channel_child_spec(Opts) -> id(Role, Sup, Address, Port) -> {Role, Sup, Address, Port}. -ssh_connection_sup([{_, Child, _, [ssh_connection_controler]} | _]) -> +ssh_connection_sup([{_, Child, _, [ssh_connection_sup]} | _]) -> Child; ssh_connection_sup([_ | Rest]) -> ssh_connection_sup(Rest). diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl index 920baaadef..23480f5fe3 100644 --- a/lib/ssh/src/ssh_system_sup.erl +++ b/lib/ssh/src/ssh_system_sup.erl @@ -146,7 +146,7 @@ ssh_acceptor_child_spec(ServerOpts) -> ssh_subsystem_child_spec(ServerOpts) -> Name = make_ref(), StartFunc = {ssh_subsystem_sup, start_link, [ServerOpts]}, - Restart = temporary, + Restart = transient, Shutdown = infinity, Modules = [ssh_subsystem_sup], Type = supervisor, diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index 6140c87f6e..1f912c9bdf 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -142,7 +142,7 @@ connect(ConnectionSup, Address, Port, SocketOpts, Opts) -> case do_connect(Callback, Address, Port, SocketOpts, Timeout) of {ok, Socket} -> {ok, Pid} = - ssh_connection_controler:start_handler_child(ConnectionSup, + ssh_connection_sup:start_handler_child(ConnectionSup, [client, Socket, [{address, Address}, {port, Port} | @@ -174,12 +174,11 @@ accept(Address, Port, Socket, Options) -> ssh_system_sup:connection_supervisor( ssh_system_sup:system_supervisor(Address, Port)), {ok, Pid} = - ssh_connection_controler:start_handler_child(ConnectionSup, + ssh_connection_sup:start_handler_child(ConnectionSup, [server, Socket, [{address, Address}, {port, Port} | Options]]), Callback:controlling_process(Socket, Pid), - ssh_connection_handler:send_event(Pid, socket_control), {ok, Pid}. format_version({Major,Minor}) -> diff --git a/lib/ssh/src/sshc_sup.erl b/lib/ssh/src/sshc_sup.erl index 7c29c669e4..1d2779de23 100644 --- a/lib/ssh/src/sshc_sup.erl +++ b/lib/ssh/src/sshc_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -26,7 +26,7 @@ -behaviour(supervisor). --export([start_link/1, start_child/1]). +-export([start_link/1, start_child/1, stop_child/1]). %% Supervisor callback -export([init/1]). @@ -40,12 +40,19 @@ start_link(Args) -> start_child(Args) -> supervisor:start_child(?MODULE, Args). +stop_child(Client) -> + spawn(fun() -> + ClientSup = whereis(?MODULE), + supervisor:terminate_child(ClientSup, Client) + end), + ok. + %%%========================================================================= %%% Supervisor callback %%%========================================================================= init(Args) -> RestartStrategy = simple_one_for_one, - MaxR = 10, + MaxR = 0, MaxT = 3600, {ok, {{RestartStrategy, MaxR, MaxT}, [child_spec(Args)]}}. @@ -54,12 +61,9 @@ init(Args) -> %%%========================================================================= child_spec(_) -> Name = undefined, % As simple_one_for_one is used. - StartFunc = {ssh_connection_controler, start_link, []}, - Restart = temporary, -% Shutdown = infinity, - Shutdown = 5000, - Modules = [ssh_connection_controler], -% Type = supervisor, - Type = worker, + StartFunc = {ssh_connection_sup, start_link, []}, + Restart = temporary, + Shutdown = infinity, + Modules = [ssh_connection_sup], + Type = supervisor, {Name, StartFunc, Restart, Shutdown, Type, Modules}. - diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index 9c13180159..012367a6df 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -88,7 +88,7 @@ end_per_testcase(TestCase, Config) when TestCase == server_password_option; UserDir = filename:join(?config(priv_dir, Config), nopubkey), ssh_test_lib:del_dirs(UserDir), end_per_testcase(Config); -end_per_testcase(TestCase, Config) -> +end_per_testcase(_TestCase, Config) -> end_per_testcase(Config). end_per_testcase(_Config) -> ssh:stop(), @@ -108,14 +108,16 @@ all() -> {group, rsa_key}, {group, dsa_pass_key}, {group, rsa_pass_key}, + {group, internal_error}, daemon_already_started, server_password_option, server_userpassword_option]. groups() -> [{dsa_key, [], [exec, exec_compressed, shell, known_hosts]}, - {rsa_key, [], [exec, exec_compressed, shell, known_hosts]}, + {rsa_key, [], [exec, exec_compressed, shell, known_hosts]}, {dsa_pass_key, [], [pass_phrase]}, - {rsa_pass_key, [], [pass_phrase]} + {rsa_pass_key, [], [pass_phrase]}, + {internal_error, [], [internal_error]} ]. init_per_group(dsa_key, Config) -> @@ -138,6 +140,12 @@ init_per_group(dsa_pass_key, Config) -> PrivDir = ?config(priv_dir, Config), ssh_test_lib:setup_dsa_pass_pharse(DataDir, PrivDir, "Password"), [{pass_phrase, {dsa_pass_phrase, "Password"}}| Config]; +init_per_group(internal_error, Config) -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + ssh_test_lib:setup_dsa(DataDir, PrivDir), + file:delete(filename:join(PrivDir, "system/ssh_host_dsa_key")), + Config; init_per_group(_, Config) -> Config. @@ -157,6 +165,11 @@ end_per_group(rsa_pass_key, Config) -> PrivDir = ?config(priv_dir, Config), ssh_test_lib:clean_rsa(PrivDir), Config; +end_per_group(internal_error, Config) -> + PrivDir = ?config(priv_dir, Config), + ssh_test_lib:clean_dsa(PrivDir), + Config; + end_per_group(_, Config) -> Config. @@ -268,9 +281,14 @@ shell(Config) when is_list(Config) -> IO = ssh_test_lib:start_io_server(), Shell = ssh_test_lib:start_shell(Port, IO, UserDir), receive + {'EXIT', _, _} -> + test_server:fail(no_ssh_connection); ErlShellStart -> - test_server:format("Erlang shell start: ~p~n", [ErlShellStart]) - end, + test_server:format("Erlang shell start: ~p~n", [ErlShellStart]), + do_shell(IO, Shell) + end. + +do_shell(IO, Shell) -> receive ErlPrompt0 -> test_server:format("Erlang prompt: ~p~n", [ErlPrompt0]) @@ -361,12 +379,15 @@ server_password_option(Config) when is_list(Config) -> {password, "morot"}, {user_interaction, false}, {user_dir, UserDir}]), + + Reason = "Unable to connect using the available authentication methods", + {error, Reason} = - ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, - {user, "vego"}, - {password, "foo"}, - {user_interaction, false}, - {user_dir, UserDir}]), + ssh:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "vego"}, + {password, "foo"}, + {user_interaction, false}, + {user_dir, UserDir}]), test_server:format("Test of wrong password: Error msg: ~p ~n", [Reason]), @@ -396,25 +417,20 @@ server_userpassword_option(Config) when is_list(Config) -> {user_dir, UserDir}]), ssh:close(ConnectionRef), - {error, Reason0} = - ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, - {user, "foo"}, - {password, "morot"}, - {user_interaction, false}, - {user_dir, UserDir}]), - - test_server:format("Test of user foo that does not exist. " - "Error msg: ~p ~n", [Reason0]), + Reason = "Unable to connect using the available authentication methods", - {error, Reason1} = - ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, - {user, "vego"}, - {password, "foo"}, - {user_interaction, false}, - {user_dir, UserDir}]), - test_server:format("Test of wrong Password. " - "Error msg: ~p ~n", [Reason1]), - + {error, Reason} = + ssh:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "foo"}, + {password, "morot"}, + {user_interaction, false}, + {user_dir, UserDir}]), + {error, Reason} = + ssh:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "vego"}, + {password, "foo"}, + {user_interaction, false}, + {user_dir, UserDir}]), ssh:stop_daemon(Pid). %%-------------------------------------------------------------------- @@ -445,6 +461,7 @@ known_hosts(Config) when is_list(Config) -> [Host, _Ip] = string:tokens(HostAndIp, ","), "ssh-" ++ _ = Alg, ssh:stop_daemon(Pid). +%%-------------------------------------------------------------------- pass_phrase(doc) -> ["Test that we can use keyes protected by pass phrases"]; @@ -470,5 +487,27 @@ pass_phrase(Config) when is_list(Config) -> ssh:stop_daemon(Pid). %%-------------------------------------------------------------------- + +internal_error(doc) -> + ["Test that client does not hang if disconnects due to internal error"]; + +internal_error(suite) -> + []; + +internal_error(Config) when is_list(Config) -> + process_flag(trap_exit, true), + SystemDir = filename:join(?config(priv_dir, Config), system), + UserDir = ?config(priv_dir, Config), + + {_Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, UserDir}, + {failfun, fun ssh_test_lib:failfun/2}]), + {error,"Internal error"} = + ssh:connect(Host, Port, [{silently_accept_hosts, true}, + {user_dir, UserDir}, + {user_interaction, false}]). + + +%%-------------------------------------------------------------------- %% Internal functions %%-------------------------------------------------------------------- diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl index 6e4480ee9d..de946d4c4c 100644 --- a/lib/ssh/test/ssh_sftpd_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_SUITE.erl @@ -30,7 +30,6 @@ -include_lib("kernel/include/file.hrl"). --define(SFPD_PORT, 9999). -define(USER, "Alladin"). -define(PASSWD, "Sesame"). -define(XFER_PACKET_SIZE, 32768). @@ -102,13 +101,15 @@ init_per_testcase(TestCase, Config) -> ClientUserDir = filename:join(PrivDir, nopubkey), SystemDir = filename:join(?config(priv_dir, Config), system), + Port = ssh_test_lib:inet_port(node()), + {ok, Sftpd} = - ssh_sftpd:listen(?SFPD_PORT, [{system_dir, SystemDir}, + ssh_sftpd:listen(Port, [{system_dir, SystemDir}, {user_dir, PrivDir}, {user_passwords,[{?USER, ?PASSWD}]}, {pwdfun, fun(_,_) -> true end}]), - Cm = ssh_test_lib:connect(?SFPD_PORT, + Cm = ssh_test_lib:connect(Port, [{user_dir, ClientUserDir}, {user, ?USER}, {password, ?PASSWD}, {user_interaction, false}, diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl index 26bbdf5c5c..609663c87a 100644 --- a/lib/ssh/test/ssh_test_lib.erl +++ b/lib/ssh/test/ssh_test_lib.erl @@ -43,12 +43,8 @@ connect(Host, Options) -> connect(any, Port, Options) -> connect(hostname(), Port, Options); connect(Host, Port, Options) -> - case ssh:connect(Host, Port, Options) of - {ok, ConnectionRef} -> - ConnectionRef; - Error -> - Error - end. + {ok, ConnectionRef} = ssh:connect(Host, Port, Options), + ConnectionRef. daemon(Options) -> daemon(any, inet_port(), Options). @@ -338,3 +334,13 @@ del_dirs(Dir) -> _ -> ok end. + +inet_port(Node) -> + {Port, Socket} = do_inet_port(Node), + rpc:call(Node, gen_tcp, close, [Socket]), + Port. + +do_inet_port(Node) -> + {ok, Socket} = rpc:call(Node, gen_tcp, listen, [0, [{reuseaddr, true}]]), + {ok, Port} = rpc:call(Node, inet, port, [Socket]), + {Port, Socket}. diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl index dfe526564d..c337617ee4 100644 --- a/lib/ssh/test/ssh_to_openssh_SUITE.erl +++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl @@ -445,7 +445,7 @@ erlang_client_openssh_server_password(Config) when is_list(Config) -> %% to make sure we don't public-key-auth UserDir = ?config(data_dir, Config), {error, Reason0} = - ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true}, + ssh:connect(any, ?SSH_DEFAULT_PORT, [{silently_accept_hosts, true}, {user, "foo"}, {password, "morot"}, {user_interaction, false}, @@ -459,12 +459,12 @@ erlang_client_openssh_server_password(Config) when is_list(Config) -> case length(string:tokens(User, " ")) of 1 -> {error, Reason1} = - ssh_test_lib:connect(?SSH_DEFAULT_PORT, - [{silently_accept_hosts, true}, - {user, User}, - {password, "foo"}, - {user_interaction, false}, - {user_dir, UserDir}]), + ssh:connect(any, ?SSH_DEFAULT_PORT, + [{silently_accept_hosts, true}, + {user, User}, + {password, "foo"}, + {user_interaction, false}, + {user_dir, UserDir}]), test_server:format("Test of wrong Pasword. " "Error msg: ~p~n", [Reason1]); _ -> diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml index 79a0c8ad89..ef81b06500 100644 --- a/lib/stdlib/doc/src/gen_event.xml +++ b/lib/stdlib/doc/src/gen_event.xml @@ -120,8 +120,10 @@ gen_event:stop -----> Module:terminate/2 <name>start_link(EventMgrName) -> Result</name> <fsummary>Create a generic event manager process in a supervision tree.</fsummary> <type> - <v>EventMgrName = {local,Name} | {global,Name}</v> + <v>EventMgrName = {local,Name} | {global,GlobalName} + | {via,Module,ViaName}</v> <v> Name = atom()</v> + <v> GlobalName = ViaName = term()</v> <v>Result = {ok,Pid} | {error,{already_started,Pid}}</v> <v> Pid = pid()</v> </type> @@ -132,10 +134,17 @@ gen_event:stop -----> Module:terminate/2 the event manager is linked to the supervisor.</p> <p>If <c>EventMgrName={local,Name}</c>, the event manager is registered locally as <c>Name</c> using <c>register/2</c>. - If <c>EventMgrName={global,Name}</c>, the event manager is - registered globally as <c>Name</c> using + If <c>EventMgrName={global,GlobalName}</c>, the event manager is + registered globally as <c>GlobalName</c> using <c>global:register_name/2</c>. If no name is provided, - the event manager is not registered.</p> + the event manager is not registered. + If <c>EventMgrName={via,Module,ViaName}</c>, the event manager will + register with the registry represented by <c>Module</c>. + The <c>Module</c> callback should export the functions + <c>register_name/2</c>, <c>unregister_name/1</c>, + <c>whereis_name/1</c> and <c>send/2</c>, which should behave like the + corresponding functions in <c>global</c>. Thus, + <c>{via,global,GlobalName}</c> is a valid reference.</p> <p>If the event manager is successfully created the function returns <c>{ok,Pid}</c>, where <c>Pid</c> is the pid of the event manager. If there already exists a process with @@ -149,8 +158,10 @@ gen_event:stop -----> Module:terminate/2 <name>start(EventMgrName) -> Result</name> <fsummary>Create a stand-alone event manager process.</fsummary> <type> - <v>EventMgrName = {local,Name} | {global,Name}</v> + <v>EventMgrName = {local,Name} | {global,GlobalName} + | {via,Module,ViaName}</v> <v> Name = atom()</v> + <v> GlobalName = ViaName = term()</v> <v>Result = {ok,Pid} | {error,{already_started,Pid}}</v> <v> Pid = pid()</v> </type> @@ -166,8 +177,10 @@ gen_event:stop -----> Module:terminate/2 <name>add_handler(EventMgrRef, Handler, Args) -> Result</name> <fsummary>Add an event handler to a generic event manager.</fsummary> <type> - <v>EventMgr = Name | {Name,Node} | {global,Name} | pid()</v> + <v>EventMgr = Name | {Name,Node} | {global,GlobalName} + | {via,Module,ViaName} | pid()</v> <v> Name = Node = atom()</v> + <v> GlobalName = ViaName = term()</v> <v>Handler = Module | {Module,Id}</v> <v> Module = atom()</v> <v> Id = term()</v> @@ -185,8 +198,10 @@ gen_event:stop -----> Module:terminate/2 <item><c>Name</c>, if the event manager is locally registered,</item> <item><c>{Name,Node}</c>, if the event manager is locally registered at another node, or</item> - <item><c>{global,Name}</c>, if the event manager is globally + <item><c>{global,GlobalName}</c>, if the event manager is globally registered.</item> + <item><c>{via,Module,ViaName}</c>, if the event manager is registered + through an alternative process registry.</item> </list> <p><c>Handler</c> is the name of the callback module <c>Module</c> or a tuple <c>{Module,Id}</c>, where <c>Id</c> is any term. @@ -208,8 +223,10 @@ gen_event:stop -----> Module:terminate/2 <name>add_sup_handler(EventMgrRef, Handler, Args) -> Result</name> <fsummary>Add a supervised event handler to a generic event manager.</fsummary> <type> - <v>EventMgr = Name | {Name,Node} | {global,Name} | pid()</v> + <v>EventMgr = Name | {Name,Node} | {global,GlobalName} + | {via,Module,ViaName} | pid()</v> <v> Name = Node = atom()</v> + <v> GlobalName = ViaName = term()</v> <v>Handler = Module | {Module,Id}</v> <v> Module = atom()</v> <v> Id = term()</v> @@ -253,8 +270,10 @@ gen_event:stop -----> Module:terminate/2 <name>sync_notify(EventMgrRef, Event) -> ok</name> <fsummary>Notify an event manager about an event.</fsummary> <type> - <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v> + <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName} + | {via,Module,ViaName} | pid()</v> <v> Name = Node = atom()</v> + <v> GlobalName = ViaName = term()</v> <v>Event = term()</v> </type> <desc> @@ -278,8 +297,10 @@ gen_event:stop -----> Module:terminate/2 <name>call(EventMgrRef, Handler, Request, Timeout) -> Result</name> <fsummary>Make a synchronous call to a generic event manager.</fsummary> <type> - <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v> + <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName} + | {via,Module,ViaName} | pid()</v> <v> Name = Node = atom()</v> + <v> GlobalName = ViaName = term()</v> <v>Handler = Module | {Module,Id}</v> <v> Module = atom()</v> <v> Id = term()</v> @@ -318,8 +339,10 @@ gen_event:stop -----> Module:terminate/2 <name>delete_handler(EventMgrRef, Handler, Args) -> Result</name> <fsummary>Delete an event handler from a generic event manager.</fsummary> <type> - <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v> + <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName} + | {via,Module,ViaName} | pid()</v> <v> Name = Node = atom()</v> + <v> GlobalName = ViaName = term()</v> <v>Handler = Module | {Module,Id}</v> <v> Module = atom()</v> <v> Id = term()</v> @@ -346,8 +369,10 @@ gen_event:stop -----> Module:terminate/2 <name>swap_handler(EventMgrRef, {Handler1,Args1}, {Handler2,Args2}) -> Result</name> <fsummary>Replace an event handler in a generic event manager.</fsummary> <type> - <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v> + <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName} + | {via,Module,ViaName} | pid()</v> <v> Name = Node = atom()</v> + <v> GlobalName = ViaName = term()</v> <v>Handler1 = Handler2 = Module | {Module,Id}</v> <v> Module = atom()</v> <v> Id = term()</v> @@ -390,8 +415,10 @@ gen_event:stop -----> Module:terminate/2 <name>swap_sup_handler(EventMgrRef, {Handler1,Args1}, {Handler2,Args2}) -> Result</name> <fsummary>Replace an event handler in a generic event manager.</fsummary> <type> - <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v> + <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName} + | {via,Module,ViaName} | pid()</v> <v> Name = Node = atom()</v> + <v> GlobalName = ViaName = term()</v> <v>Handler1 = Handler 2 = Module | {Module,Id}</v> <v> Module = atom()</v> <v> Id = term()</v> @@ -412,8 +439,10 @@ gen_event:stop -----> Module:terminate/2 <name>which_handlers(EventMgrRef) -> [Handler]</name> <fsummary>Return all event handlers installed in a generic event manager.</fsummary> <type> - <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v> + <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName} + | {via,Module,ViaName} | pid()</v> <v> Name = Node = atom()</v> + <v> GlobalName = ViaName = term()</v> <v>Handler = Module | {Module,Id}</v> <v> Module = atom()</v> <v> Id = term()</v> @@ -429,8 +458,10 @@ gen_event:stop -----> Module:terminate/2 <name>stop(EventMgrRef) -> ok</name> <fsummary>Terminate a generic event manager.</fsummary> <type> - <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v> + <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName} + | {via,Module,ViaName} | pid()</v> <v>Name = Node = atom()</v> + <v>GlobalName = ViaName = term()</v> </type> <desc> <p>Terminates the event manager <c>EventMgrRef</c>. Before diff --git a/lib/stdlib/doc/src/gen_fsm.xml b/lib/stdlib/doc/src/gen_fsm.xml index 421eeb4fd3..73c1911f1e 100644 --- a/lib/stdlib/doc/src/gen_fsm.xml +++ b/lib/stdlib/doc/src/gen_fsm.xml @@ -84,9 +84,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4 <name>start_link(FsmName, Module, Args, Options) -> Result</name> <fsummary>Create a gen_fsm process in a supervision tree.</fsummary> <type> - <v>FsmName = {local,Name} | {global,GlobalName}</v> + <v>FsmName = {local,Name} | {global,GlobalName} + | {via,Module,ViaName}</v> <v> Name = atom()</v> - <v> GlobalName = term()</v> + <v> GlobalName = ViaName = term()</v> <v>Module = atom()</v> <v>Args = term()</v> <v>Options = [Option]</v> @@ -113,8 +114,16 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4 locally as <c>Name</c> using <c>register/2</c>. If <c>FsmName={global,GlobalName}</c>, the gen_fsm is registered globally as <c>GlobalName</c> using - <c>global:register_name/2</c>. If no name is provided, - the gen_fsm is not registered.</p> + <c>global:register_name/2</c>. + If <c>EventMgrName={via,Module,ViaName}</c>, the event manager will + register with the registry represented by <c>Module</c>. + The <c>Module</c> callback should export the functions + <c>register_name/2</c>, <c>unregister_name/1</c>, + <c>whereis_name/1</c> and <c>send/2</c>, which should behave like the + corresponding functions in <c>global</c>. Thus, + <c>{via,global,GlobalName}</c> is a valid reference.</p> + <p>If no name is provided, + the gen_fsm is not registered.</p> <p><c>Module</c> is the name of the callback module.</p> <p><c>Args</c> is an arbitrary term which is passed as the argument to <c>Module:init/1</c>.</p> @@ -154,9 +163,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4 <name>start(FsmName, Module, Args, Options) -> Result</name> <fsummary>Create a stand-alone gen_fsm process.</fsummary> <type> - <v>FsmName = {local,Name} | {global,GlobalName}</v> + <v>FsmName = {local,Name} | {global,GlobalName} + | {via,Module,ViaName}</v> <v> Name = atom()</v> - <v> GlobalName = term()</v> + <v> GlobalName = ViaName = term()</v> <v>Module = atom()</v> <v>Args = term()</v> <v>Options = [Option]</v> @@ -180,9 +190,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4 <name>send_event(FsmRef, Event) -> ok</name> <fsummary>Send an event asynchronously to a generic FSM.</fsummary> <type> - <v>FsmRef = Name | {Name,Node} | {global,GlobalName} | pid()</v> + <v>FsmRef = Name | {Name,Node} | {global,GlobalName} + | {via,Module,ViaName} | pid()</v> <v> Name = Node = atom()</v> - <v> GlobalName = term()</v> + <v> GlobalName = ViaName = term()</v> <v>Event = term()</v> </type> <desc> @@ -196,9 +207,11 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4 <item>the pid,</item> <item><c>Name</c>, if the gen_fsm is locally registered,</item> <item><c>{Name,Node}</c>, if the gen_fsm is locally - registered at another node, or</item> - <item><c>{global,GlobalName}</c>, if the gen_fsm is globally - registered.</item> + registered at another node, or</item> + <item><c>{global,GlobalName}</c>, if the gen_fsm is globally + registered.</item> + <item><c>{via,Module,ViaName}</c>, if the event manager is registered + through an alternative process registry.</item> </list> <p><c>Event</c> is an arbitrary term which is passed as one of the arguments to <c>Module:StateName/2</c>.</p> @@ -208,9 +221,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4 <name>send_all_state_event(FsmRef, Event) -> ok</name> <fsummary>Send an event asynchronously to a generic FSM.</fsummary> <type> - <v>FsmRef = Name | {Name,Node} | {global,GlobalName} | pid()</v> + <v>FsmRef = Name | {Name,Node} | {global,GlobalName} + | {via,Module,ViaName} | pid()</v> <v> Name = Node = atom()</v> - <v> GlobalName = term()</v> + <v> GlobalName = ViaName = term()</v> <v>Event = term()</v> </type> <desc> @@ -232,9 +246,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4 <name>sync_send_event(FsmRef, Event, Timeout) -> Reply</name> <fsummary>Send an event synchronously to a generic FSM.</fsummary> <type> - <v>FsmRef = Name | {Name,Node} | {global,GlobalName} | pid()</v> + <v>FsmRef = Name | {Name,Node} | {global,GlobalName} + | {via,Module,ViaName} | pid()</v> <v> Name = Node = atom()</v> - <v> GlobalName = term()</v> + <v> GlobalName = ViaName = term()</v> <v>Event = term()</v> <v>Timeout = int()>0 | infinity</v> <v>Reply = term()</v> @@ -264,9 +279,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4 <name>sync_send_all_state_event(FsmRef, Event, Timeout) -> Reply</name> <fsummary>Send an event synchronously to a generic FSM.</fsummary> <type> - <v>FsmRef = Name | {Name,Node} | {global,GlobalName} | pid()</v> + <v>FsmRef = Name | {Name,Node} | {global,GlobalName} + | {via,Module,ViaName} | pid()</v> <v> Name = Node = atom()</v> - <v> GlobalName = term()</v> + <v> GlobalName = ViaName = term()</v> <v>Event = term()</v> <v>Timeout = int()>0 | infinity</v> <v>Reply = term()</v> @@ -388,9 +404,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4 <v> | {log_to_file,FileName} | {install,{Func,FuncState}}</v> <v>StateName = atom()</v> <v>StateData = term()</v> - <v>FsmName = {local,Name} | {global,GlobalName}</v> + <v>FsmName = {local,Name} | {global,GlobalName} + | {via,Module,ViaName}</v> <v> Name = atom()</v> - <v> GlobalName = term()</v> + <v> GlobalName = ViaName = term()</v> <v>Timeout = int() | infinity</v> </type> <desc> diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml index edeb7dff91..9edff1b9cf 100644 --- a/lib/stdlib/doc/src/gen_server.xml +++ b/lib/stdlib/doc/src/gen_server.xml @@ -83,9 +83,10 @@ gen_server:abcast -----> Module:handle_cast/2 <name>start_link(ServerName, Module, Args, Options) -> Result</name> <fsummary>Create a gen_server process in a supervision tree.</fsummary> <type> - <v>ServerName = {local,Name} | {global,GlobalName}</v> + <v>ServerName = {local,Name} | {global,GlobalName} + | {via,Module,ViaName}</v> <v> Name = atom()</v> - <v> GlobalName = term()</v> + <v> GlobalName = ViaName = term()</v> <v>Module = atom()</v> <v>Args = term()</v> <v>Options = [Option]</v> @@ -111,14 +112,22 @@ gen_server:abcast -----> Module:handle_cast/2 If <c>ServerName={global,GlobalName}</c> the gen_server is registered globally as <c>GlobalName</c> using <c>global:register_name/2</c>. If no name is provided, - the gen_server is not registered.</p> + the gen_server is not registered. + If <c>EventMgrName={via,Module,ViaName}</c>, the event manager will + register with the registry represented by <c>Module</c>. + The <c>Module</c> callback should export the functions + <c>register_name/2</c>, <c>unregister_name/1</c>, + <c>whereis_name/1</c> and <c>send/2</c>, which should behave like the + corresponding functions in <c>global</c>. Thus, + <c>{via,global,GlobalName}</c> is a valid reference.</p> <p><c>Module</c> is the name of the callback module.</p> <p><c>Args</c> is an arbitrary term which is passed as the argument to <c>Module:init/1</c>.</p> <p>If the option <c>{timeout,Time}</c> is present, the gen_server is allowed to spend <c>Time</c> milliseconds initializing or it will be terminated and the start function - will return <c>{error,timeout}</c>.</p> + will return <c>{error,timeout}</c>. + </p> <p>If the option <c>{debug,Dbgs}</c> is present, the corresponding <c>sys</c> function will be called for each item in <c>Dbgs</c>. See @@ -151,9 +160,10 @@ gen_server:abcast -----> Module:handle_cast/2 <name>start(ServerName, Module, Args, Options) -> Result</name> <fsummary>Create a stand-alone gen_server process.</fsummary> <type> - <v>ServerName = {local,Name} | {global,GlobalName}</v> + <v>ServerName = {local,Name} | {global,GlobalName} + | {via,Module,ViaName}</v> <v> Name = atom()</v> - <v> GlobalName = term()</v> + <v> GlobalName = ViaName = term()</v> <v>Module = atom()</v> <v>Args = term()</v> <v>Options = [Option]</v> @@ -178,9 +188,10 @@ gen_server:abcast -----> Module:handle_cast/2 <name>call(ServerRef, Request, Timeout) -> Reply</name> <fsummary>Make a synchronous call to a generic server.</fsummary> <type> - <v>ServerRef = Name | {Name,Node} | {global,GlobalName} | pid()</v> + <v>ServerRef = Name | {Name,Node} | {global,GlobalName} + | {via,Module,ViaName} | pid()</v> <v> Node = atom()</v> - <v> GlobalName = term()</v> + <v> GlobalName = ViaName = term()</v> <v>Request = term()</v> <v>Timeout = int()>0 | infinity</v> <v>Reply = term()</v> @@ -198,6 +209,8 @@ gen_server:abcast -----> Module:handle_cast/2 registered at another node, or</item> <item><c>{global,GlobalName}</c>, if the gen_server is globally registered.</item> + <item><c>{via,Module,ViaName}</c>, if the gen_server is + registered through an alternative process registry.</item> </list> <p><c>Request</c> is an arbitrary term which is passed as one of the arguments to <c>Module:handle_call/3</c>.</p> @@ -281,9 +294,10 @@ gen_server:abcast -----> Module:handle_cast/2 <name>cast(ServerRef, Request) -> ok</name> <fsummary>Send an asynchronous request to a generic server.</fsummary> <type> - <v>ServerRef = Name | {Name,Node} | {global,GlobalName} | pid()</v> + <v>ServerRef = Name | {Name,Node} | {global,GlobalName} + | {via,Module,ViaName} | pid()</v> <v> Node = atom()</v> - <v> GlobalName = term()</v> + <v> GlobalName = ViaName = term()</v> <v>Request = term()</v> </type> <desc> @@ -355,9 +369,10 @@ gen_server:abcast -----> Module:handle_cast/2 <v> Dbg = trace | log | statistics</v> <v> | {log_to_file,FileName} | {install,{Func,FuncState}}</v> <v>State = term()</v> - <v>ServerName = {local,Name} | {global,GlobalName}</v> + <v>ServerName = {local,Name} | {global,GlobalName} + | {via,Module,ViaName}</v> <v> Name = atom()</v> - <v> GlobalName = term()</v> + <v> GlobalName = ViaName = term()</v> <v>Timeout = int() | infinity</v> </type> <desc> diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml index 33a7f5bb6a..d1e62230bc 100644 --- a/lib/stdlib/doc/src/supervisor.xml +++ b/lib/stdlib/doc/src/supervisor.xml @@ -264,8 +264,14 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} <p>If <c><anno>SupName</anno>={local,Name}</c> the supervisor is registered locally as <c>Name</c> using <c>register/2</c>. If <c><anno>SupName</anno>={global,Name}</c> the supervisor is registered - globally as <c>Name</c> using <c>global:register_name/2</c>. - If no name is provided, the supervisor is not registered.</p> + globally as <c>Name</c> using <c>global:register_name/2</c>. If + <c><anno>SupName</anno>={via,Module,Name}</c> the supervisor + is registered as <c>Name</c> using the registry represented by + <c>Module</c>. The <c>Module</c> callback should export the functions + <c>register_name/2</c>, <c>unregister_name/1</c> and <c>send/2</c>, + which should behave like the corresponding functions in <c>global</c>. + Thus, <c>{via,global,Name}</c> is a valid reference.</p> + <p>If no name is provided, the supervisor is not registered.</p> <p><c><anno>Module</anno></c> is the name of the callback module.</p> <p><c><anno>Args</anno></c> is an arbitrary term which is passed as the argument to <c><anno>Module</anno>:init/1</c>.</p> @@ -308,6 +314,8 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} registered at another node, or</item> <item><c>{global,Name}</c>, if the supervisor is globally registered.</item> + <item><c>{via,Module,Name}</c>, if the supervisor is registered + through an alternative process registry.</item> </list> <p><c><anno>ChildSpec</anno></c> should be a valid child specification (unless the supervisor is a <c>simple_one_for_one</c> diff --git a/lib/stdlib/doc/src/supervisor_bridge.xml b/lib/stdlib/doc/src/supervisor_bridge.xml index c1a5e7947f..f6712d6c1d 100644 --- a/lib/stdlib/doc/src/supervisor_bridge.xml +++ b/lib/stdlib/doc/src/supervisor_bridge.xml @@ -63,6 +63,13 @@ If <c><anno>SupBridgeName</anno>={global,<anno>Name</anno>}</c> the supervisor_bridge is registered globally as <c><anno>Name</anno></c> using <c>global:register_name/2</c>. + If <c><anno>SupBridgeName</anno>={via,<anno>Module</anno>,<anno>Name</anno>}</c> the supervisor_bridge is + registered as <c><anno>Name</anno></c> using a registry represented + by <anno>Module</anno>. The <c>Module</c> callback should export + the functions <c>register_name/2</c>, <c>unregister_name/1</c> + and <c>send/2</c>, which should behave like the + corresponding functions in <c>global</c>. Thus, + <c>{via,global,GlobalName}</c> is a valid reference. If no name is provided, the supervisor_bridge is not registered. If there already exists a process with the specified <c><anno>SupBridgeName</anno></c> the function returns diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl index 5d803091b6..42555aedd7 100644 --- a/lib/stdlib/src/gen.erl +++ b/lib/stdlib/src/gen.erl @@ -36,7 +36,7 @@ %%----------------------------------------------------------------- -type linkage() :: 'link' | 'nolink'. --type emgr_name() :: {'local', atom()} | {'global', term()}. +-type emgr_name() :: {'local', atom()} | {'global', term()} | {via, atom(), term()}. -type start_ret() :: {'ok', pid()} | 'ignore' | {'error', term()}. @@ -53,7 +53,7 @@ %% start(GenMod, LinkP, Name, Mod, Args, Options) %% GenMod = atom(), callback module implementing the 'real' fsm %% LinkP = link | nolink -%% Name = {local, atom()} | {global, term()} +%% Name = {local, atom()} | {global, term()} | {via, atom(), term()} %% Args = term(), init arguments (to Mod:init/1) %% Options = [{timeout, Timeout} | {debug, [Flag]} | {spawn_opt, OptionList}] %% Flag = trace | log | {logfile, File} | statistics | debug @@ -158,9 +158,12 @@ call(Name, Label, Request, Timeout) exit(noproc) end; %% Global by name -call({global, _Name}=Process, Label, Request, Timeout) - when Timeout =:= infinity; - is_integer(Timeout), Timeout >= 0 -> +call(Process, Label, Request, Timeout) + when ((tuple_size(Process) == 2 andalso element(1, Process) == global) + orelse + (tuple_size(Process) == 3 andalso element(1, Process) == via)) + andalso + (Timeout =:= infinity orelse (is_integer(Timeout) andalso Timeout >= 0)) -> case where(Process) of Pid when is_pid(Pid) -> Node = node(Pid), @@ -274,6 +277,7 @@ reply({To, Tag}, Reply) -> %%% Misc. functions. %%%----------------------------------------------------------------- where({global, Name}) -> global:whereis_name(Name); +where({via, Module, Name}) -> Module:whereis_name(Name); where({local, Name}) -> whereis(Name). name_register({local, Name} = LN) -> @@ -287,8 +291,16 @@ name_register({global, Name} = GN) -> case global:register_name(Name, self()) of yes -> true; no -> {false, where(GN)} + end; +name_register({via, Module, Name} = GN) -> + case Module:register_name(Name, self()) of + yes -> + true; + no -> + {false, where(GN)} end. + timeout(Options) -> case opt(timeout, Options) of {ok, Time} -> diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl index 3317b30e5c..ca05df20c0 100644 --- a/lib/stdlib/src/gen_event.erl +++ b/lib/stdlib/src/gen_event.erl @@ -107,8 +107,10 @@ -type add_handler_ret() :: ok | term() | {'EXIT',term()}. -type del_handler_ret() :: ok | term() | {'EXIT',term()}. --type emgr_name() :: {'local', atom()} | {'global', atom()}. --type emgr_ref() :: atom() | {atom(), atom()} | {'global', atom()} | pid(). +-type emgr_name() :: {'local', atom()} | {'global', atom()} + | {'via', atom(), term()}. +-type emgr_ref() :: atom() | {atom(), atom()} | {'global', atom()} + | {'via', atom(), term()} | pid(). -type start_ret() :: {'ok', pid()} | {'error', term()}. %%--------------------------------------------------------------------------- @@ -143,6 +145,7 @@ init_it(Starter, Parent, Name0, _, _, Options) -> name({local,Name}) -> Name; name({global,Name}) -> Name; +name({via,_, Name}) -> Name; name(Pid) when is_pid(Pid) -> Pid. -spec add_handler(emgr_ref(), handler(), term()) -> term(). @@ -209,6 +212,9 @@ call1(M, Handler, Query, Timeout) -> send({global, Name}, Cmd) -> catch global:send(Name, Cmd), ok; +send({via, Mod, Name}, Cmd) -> + catch Mod:send(Name, Cmd), + ok; send(M, Cmd) -> M ! Cmd, ok. diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl index 80866c0806..51dc0bcee2 100644 --- a/lib/stdlib/src/gen_fsm.erl +++ b/lib/stdlib/src/gen_fsm.erl @@ -165,7 +165,7 @@ %%% start(Name, Mod, Args, Options) %%% start_link(Mod, Args, Options) %%% start_link(Name, Mod, Args, Options) where: -%%% Name ::= {local, atom()} | {global, atom()} +%%% Name ::= {local, atom()} | {global, atom()} | {via, atom(), term()} %%% Mod ::= atom(), callback module implementing the 'real' fsm %%% Args ::= term(), init arguments (to Mod:init/1) %%% Options ::= [{debug, [Flag]}] @@ -191,6 +191,9 @@ start_link(Name, Mod, Args, Options) -> send_event({global, Name}, Event) -> catch global:send(Name, {'$gen_event', Event}), ok; +send_event({via, Mod, Name}, Event) -> + catch Mod:send(Name, {'$gen_event', Event}), + ok; send_event(Name, Event) -> Name ! {'$gen_event', Event}, ok. @@ -214,6 +217,9 @@ sync_send_event(Name, Event, Timeout) -> send_all_state_event({global, Name}, Event) -> catch global:send(Name, {'$gen_all_state_event', Event}), ok; +send_all_state_event({via, Mod, Name}, Event) -> + catch Mod:send(Name, {'$gen_all_state_event', Event}), + ok; send_all_state_event(Name, Event) -> Name ! {'$gen_all_state_event', Event}, ok. @@ -273,7 +279,10 @@ cancel_timer(Ref) -> enter_loop(Mod, Options, StateName, StateData) -> enter_loop(Mod, Options, StateName, StateData, self(), infinity). -enter_loop(Mod, Options, StateName, StateData, ServerName = {_,_}) -> +enter_loop(Mod, Options, StateName, StateData, {Scope,_} = ServerName) + when Scope == local; Scope == global -> + enter_loop(Mod, Options, StateName, StateData, ServerName,infinity); +enter_loop(Mod, Options, StateName, StateData, {via,_,_} = ServerName) -> enter_loop(Mod, Options, StateName, StateData, ServerName,infinity); enter_loop(Mod, Options, StateName, StateData, Timeout) -> enter_loop(Mod, Options, StateName, StateData, self(), Timeout). @@ -303,6 +312,15 @@ get_proc_name({global, Name}) -> Name; _Pid -> exit(process_not_registered_globally) + end; +get_proc_name({via, Mod, Name}) -> + case Mod:whereis_name(Name) of + undefined -> + exit({process_not_registered_via, Mod}); + Pid when Pid =:= self() -> + Name; + _Pid -> + exit({process_not_registered_via, Mod}) end. get_parent() -> @@ -367,12 +385,15 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) -> name({local,Name}) -> Name; name({global,Name}) -> Name; +name({via,_, Name}) -> Name; name(Pid) when is_pid(Pid) -> Pid. unregister_name({local,Name}) -> _ = (catch unregister(Name)); unregister_name({global,Name}) -> _ = global:unregister_name(Name); +unregister_name({via, Mod, Name}) -> + _ = Mod:unregister_name(Name); unregister_name(Pid) when is_pid(Pid) -> Pid. diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl index f720ec15f5..b384e8baf7 100644 --- a/lib/stdlib/src/gen_server.erl +++ b/lib/stdlib/src/gen_server.erl @@ -142,7 +142,7 @@ %%% start(Name, Mod, Args, Options) %%% start_link(Mod, Args, Options) %%% start_link(Name, Mod, Args, Options) where: -%%% Name ::= {local, atom()} | {global, atom()} +%%% Name ::= {local, atom()} | {global, atom()} | {via, atom(), term()} %%% Mod ::= atom(), callback module implementing the 'real' server %%% Args ::= term(), init arguments (to Mod:init/1) %%% Options ::= [{timeout, Timeout} | {debug, [Flag]}] @@ -194,6 +194,9 @@ call(Name, Request, Timeout) -> cast({global,Name}, Request) -> catch global:send(Name, cast_msg(Request)), ok; +cast({via, Mod, Name}, Request) -> + catch Mod:send(Name, cast_msg(Request)), + ok; cast({Name,Node}=Dest, Request) when is_atom(Name), is_atom(Node) -> do_cast(Dest, Request); cast(Dest, Request) when is_atom(Dest) -> @@ -266,7 +269,11 @@ multi_call(Nodes, Name, Req, Timeout) enter_loop(Mod, Options, State) -> enter_loop(Mod, Options, State, self(), infinity). -enter_loop(Mod, Options, State, ServerName = {_, _}) -> +enter_loop(Mod, Options, State, ServerName = {Scope, _}) + when Scope == local; Scope == local -> + enter_loop(Mod, Options, State, ServerName, infinity); + +enter_loop(Mod, Options, State, ServerName = {via, _, _}) -> enter_loop(Mod, Options, State, ServerName, infinity); enter_loop(Mod, Options, State, Timeout) -> @@ -327,12 +334,15 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) -> name({local,Name}) -> Name; name({global,Name}) -> Name; +name({via,_, Name}) -> Name; name(Pid) when is_pid(Pid) -> Pid. unregister_name({local,Name}) -> _ = (catch unregister(Name)); unregister_name({global,Name}) -> _ = global:unregister_name(Name); +unregister_name({via, Mod, Name}) -> + _ = Mod:unregister_name(Name); unregister_name(Pid) when is_pid(Pid) -> Pid. @@ -827,6 +837,15 @@ get_proc_name({global, Name}) -> Name; _Pid -> exit(process_not_registered_globally) + end; +get_proc_name({via, Mod, Name}) -> + case Mod:whereis_name(Name) of + undefined -> + exit({process_not_registered_via, Mod}); + Pid when Pid =:= self() -> + Name; + _Pid -> + exit({process_not_registered_via, Mod}) end. get_parent() -> diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile index aa6a660c34..b36265302c 100644 --- a/lib/stdlib/test/Makefile +++ b/lib/stdlib/test/Makefile @@ -20,6 +20,7 @@ MODULES= \ digraph_utils_SUITE \ dummy1_h \ dummy_h \ + dummy_via \ edlin_expand_SUITE \ epp_SUITE \ erl_eval_helper \ diff --git a/lib/stdlib/test/dummy_via.erl b/lib/stdlib/test/dummy_via.erl new file mode 100644 index 0000000000..e405811cbe --- /dev/null +++ b/lib/stdlib/test/dummy_via.erl @@ -0,0 +1,94 @@ +-module(dummy_via). +-export([reset/0, + register_name/2, + whereis_name/1, + unregister_name/1, + send/2]). + + +reset() -> + P = whereis(?MODULE), + catch unlink(P), + Ref = erlang:monitor(process, P), + catch exit(P, kill), + receive {'DOWN',Ref,_,_,_} -> ok end, + Me = self(), + Pid = spawn_link(fun() -> + register(?MODULE, self()), + Me ! {self(), started}, + loop([]) + end), + receive + {Pid, started} -> + Pid + after 10000 -> + exit(timeout) + end. + +register_name(Name, Pid) when is_pid(Pid) -> + call({register_name, Name, Pid}). + +unregister_name(Name) -> + call({unregister_name, Name}). + +whereis_name(Name) -> + call({whereis_name, Name}). + +send(Name, Msg) -> + case whereis_name(Name) of + undefined -> + exit({badarg, {Name, Msg}}); + Pid when is_pid(Pid) -> + Pid ! Msg, + Pid + end. + +call(Req) -> + MRef = erlang:monitor(process, ?MODULE), + ?MODULE ! {self(), MRef, Req}, + receive + {'DOWN', MRef, _, _, _} -> + erlang:error(badarg); + {MRef, badarg} -> + erlang:error(badarg); + {MRef, Reply} -> + Reply + after 5000 -> + erlang:error(timeout) + end. + +loop(Reg) -> + receive + {'DOWN', _, _, P, _} when is_pid(P) -> + loop([X || {_,Pid,_} = X <- Reg, Pid =/= P]); + {From, Ref, Request} when is_pid(From), is_reference(Ref) -> + {Reply, NewReg} = handle_request(Request, Reg), + From ! {Ref, Reply}, + loop(NewReg) + end. + +handle_request({register_name, Name, Pid}, Reg) when is_pid(Pid) -> + case lists:keyfind(Name, 1, Reg) of + false -> + Ref = erlang:monitor(process, Pid), + {yes, [{Name, Pid, Ref}|Reg]}; + _ -> + {no, Reg} + end; +handle_request({whereis_name, Name}, Reg) -> + case lists:keyfind(Name, 1, Reg) of + {_, Pid, _} -> + {Pid, Reg}; + false -> + {undefined, Reg} + end; +handle_request({unregister_name, Name}, Reg) -> + case lists:keyfind(Name, 1, Reg) of + {_, _, Ref} -> + catch erlang:demonitor(Ref); + _ -> + ok + end, + {ok, lists:keydelete(Name, 1, Reg)}; +handle_request(_, Reg) -> + {badarg, Reg}. diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 101828fdef..59532b65a0 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -72,6 +72,7 @@ exit_many_many_tables_owner/1]). -export([write_concurrency/1, heir/1, give_away/1, setopts/1]). -export([bad_table/1, types/1]). +-export([otp_9932/1]). -export([otp_9423/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -145,6 +146,7 @@ all() -> exit_many_large_table_owner, exit_many_tables_owner, exit_many_many_tables_owner, write_concurrency, heir, give_away, setopts, bad_table, types, + otp_9932, otp_9423]. groups() -> @@ -5434,6 +5436,22 @@ types_do(Opts) -> ?line verify_etsmem(EtsMem). +%% OTP-9932: Memory overwrite when inserting large integers in compressed bag. +%% Will crash with segv on 64-bit opt if not fixed. +otp_9932(Config) when is_list(Config) -> + T = ets:new(xxx, [bag, compressed]), + Fun = fun(N) -> + Key = {1316110174588445 bsl N,1316110174588583 bsl N}, + S = {Key, Key}, + true = ets:insert(T, S), + [S] = ets:lookup(T, Key), + true = ets:insert(T, S), + [S] = ets:lookup(T, Key) + end, + lists:foreach(Fun, lists:seq(0, 16)), + ets:delete(T). + + otp_9423(doc) -> ["vm-deadlock caused by race between ets:delete and others on write_concurrency table"]; otp_9423(Config) when is_list(Config) -> InitF = fun(_) -> {0,0} end, diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl index b3a7edc140..5c51e12e35 100644 --- a/lib/stdlib/test/gen_event_SUITE.erl +++ b/lib/stdlib/test/gen_event_SUITE.erl @@ -62,6 +62,8 @@ start(suite) -> []; start(Config) when is_list(Config) -> OldFl = process_flag(trap_exit, true), + ?line dummy_via:reset(), + ?line {ok, Pid0} = gen_event:start(), %anonymous ?line [] = gen_event:which_handlers(Pid0), ?line ok = gen_event:stop(Pid0), @@ -85,6 +87,11 @@ start(Config) when is_list(Config) -> ?line [] = gen_event:which_handlers(Pid4), ?line ok = gen_event:stop({global, my_dummy_name}), + ?line {ok, Pid5} = gen_event:start_link({via, dummy_via, my_dummy_name}), + ?line [] = gen_event:which_handlers({via, dummy_via, my_dummy_name}), + ?line [] = gen_event:which_handlers(Pid5), + ?line ok = gen_event:stop({via, dummy_via, my_dummy_name}), + ?line {ok, _} = gen_event:start_link({local, my_dummy_name}), ?line {error, {already_started, _}} = gen_event:start_link({local, my_dummy_name}), @@ -92,15 +99,28 @@ start(Config) when is_list(Config) -> gen_event:start({local, my_dummy_name}), ?line ok = gen_event:stop(my_dummy_name), - ?line {ok, Pid5} = gen_event:start_link({global, my_dummy_name}), + ?line {ok, Pid6} = gen_event:start_link({global, my_dummy_name}), ?line {error, {already_started, _}} = gen_event:start_link({global, my_dummy_name}), ?line {error, {already_started, _}} = gen_event:start({global, my_dummy_name}), - exit(Pid5, shutdown), + exit(Pid6, shutdown), + receive + {'EXIT', Pid6, shutdown} -> ok + after 10000 -> + ?t:fail(exit_gen_event) + end, + + ?line {ok, Pid7} = gen_event:start_link({via, dummy_via, my_dummy_name}), + ?line {error, {already_started, _}} = + gen_event:start_link({via, dummy_via, my_dummy_name}), + ?line {error, {already_started, _}} = + gen_event:start({via, dummy_via, my_dummy_name}), + + exit(Pid7, shutdown), receive - {'EXIT', Pid5, shutdown} -> ok + {'EXIT', Pid7, shutdown} -> ok after 10000 -> ?t:fail(exit_gen_event) end, diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl index d60629d841..bdb4ea65b5 100644 --- a/lib/stdlib/test/gen_fsm_SUITE.erl +++ b/lib/stdlib/test/gen_fsm_SUITE.erl @@ -21,11 +21,11 @@ -include_lib("test_server/include/test_server.hrl"). %% Test cases --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). --export([ start1/1, start2/1, start3/1, start4/1 , start5/1, start6/1, - start7/1, start8/1, start9/1, start10/1, start11/1]). +-export([start1/1, start2/1, start3/1, start4/1, start5/1, start6/1, + start7/1, start8/1, start9/1, start10/1, start11/1, start12/1]). -export([ abnormal1/1, abnormal2/1]). @@ -56,14 +56,14 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. -all() -> +all() -> [{group, start}, {group, abnormal}, shutdown, {group, sys}, hibernate, enter_loop]. -groups() -> +groups() -> [{start, [], [start1, start2, start3, start4, start5, start6, start7, - start8, start9, start10, start11]}, + start8, start9, start10, start11, start12]}, {abnormal, [], [abnormal1, abnormal2]}, {sys, [], [sys1, call_format_status, error_format_status]}]. @@ -258,6 +258,25 @@ start11(Config) when is_list(Config) -> test_server:messages_get(), ok. +%% Via register linked +start12(Config) when is_list(Config) -> + ?line dummy_via:reset(), + ?line {ok, Pid} = + gen_fsm:start_link({via, dummy_via, my_fsm}, gen_fsm_SUITE, [], []), + ?line {error, {already_started, Pid}} = + gen_fsm:start_link({via, dummy_via, my_fsm}, gen_fsm_SUITE, [], []), + ?line {error, {already_started, Pid}} = + gen_fsm:start({via, dummy_via, my_fsm}, gen_fsm_SUITE, [], []), + + ?line ok = do_func_test(Pid), + ?line ok = do_sync_func_test(Pid), + ?line ok = do_func_test({via, dummy_via, my_fsm}), + ?line ok = do_sync_func_test({via, dummy_via, my_fsm}), + ?line stop_it({via, dummy_via, my_fsm}), + + test_server:messages_get(), + ok. + %% Check that time outs in calls work abnormal1(suite) -> []; @@ -362,7 +381,25 @@ call_format_status(Config) when is_list(Config) -> ?line Status4 = sys:get_status(GlobalName2), ?line {status, Pid4, _Mod, [_PDict4, running, _, _, Data4]} = Status4, ?line [format_status_called | _] = lists:reverse(Data4), - ?line stop_it(Pid4). + ?line stop_it(Pid4), + + %% check that format_status can handle a name being a term other than a + %% pid or atom + ?line dummy_via:reset(), + ViaName1 = {via, dummy_via, "CallFormatStatus"}, + ?line {ok, Pid5} = gen_fsm:start(ViaName1, gen_fsm_SUITE, [], []), + ?line Status5 = sys:get_status(ViaName1), + ?line {status, Pid5, _Mod, [_PDict5, running, _, _, Data5]} = Status5, + ?line [format_status_called | _] = lists:reverse(Data5), + ?line stop_it(Pid5), + ViaName2 = {via, dummy_via, {name, "term"}}, + ?line {ok, Pid6} = gen_fsm:start(ViaName2, gen_fsm_SUITE, [], []), + ?line Status6 = sys:get_status(ViaName2), + ?line {status, Pid6, _Mod, [_PDict6, running, _, _, Data6]} = Status6, + ?line [format_status_called | _] = lists:reverse(Data6), + ?line stop_it(Pid6). + + error_format_status(Config) when is_list(Config) -> ?line error_logger_forwarder:register(), @@ -520,6 +557,8 @@ enter_loop(doc) -> enter_loop(Config) when is_list(Config) -> OldFlag = process_flag(trap_exit, true), + ?line dummy_via:reset(), + %% Locally registered process + {local, Name} ?line {ok, Pid1a} = proc_lib:start_link(?MODULE, enter_loop, [local, local]), @@ -623,10 +662,22 @@ enter_loop(Config) when is_list(Config) -> {'EXIT', Pid6b, process_not_registered_globally} -> ok after 1000 -> - ?line test_server:fail(gen_server_started) + ?line test_server:fail(gen_fsm_started) end, global:unregister_name(armitage), + dummy_via:register_name(armitage, self()), + ?line {ok, Pid6c} = + proc_lib:start_link(?MODULE, enter_loop, [anon, via]), + receive + {'EXIT', Pid6c, {process_not_registered_via, dummy_via}} -> + ok + after 1000 -> + ?line test_server:fail({gen_fsm_started, process_info(self(), + messages)}) + end, + dummy_via:unregister_name(armitage), + process_flag(trap_exit, OldFlag), ok. @@ -635,6 +686,7 @@ enter_loop(Reg1, Reg2) -> case Reg1 of local -> register(armitage, self()); global -> global:register_name(armitage, self()); + via -> dummy_via:register_name(armitage, self()); anon -> ignore end, proc_lib:init_ack({ok, self()}), @@ -643,6 +695,9 @@ enter_loop(Reg1, Reg2) -> gen_fsm:enter_loop(?MODULE, [], state0, [], {local,armitage}); global -> gen_fsm:enter_loop(?MODULE, [], state0, [], {global,armitage}); + via -> + gen_fsm:enter_loop(?MODULE, [], state0, [], + {via, dummy_via, armitage}); anon -> gen_fsm:enter_loop(?MODULE, [], state0, []) end. diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl index 7fb8d54f2d..cdf15ba017 100644 --- a/lib/stdlib/test/gen_server_SUITE.erl +++ b/lib/stdlib/test/gen_server_SUITE.erl @@ -36,7 +36,7 @@ ]). % spawn export --export([spec_init_local/2, spec_init_global/2, +-export([spec_init_local/2, spec_init_global/2, spec_init_via/2, spec_init_default_timeout/2, spec_init_anonymous/1, spec_init_anonymous_default_timeout/1, spec_init_not_proc_lib/1, cast_fast_messup/0]). @@ -199,6 +199,35 @@ start(Config) when is_list(Config) -> test_server:fail(not_stopped) end, + %% via register + ?line dummy_via:reset(), + ?line {ok, Pid6} = + gen_server:start({via, dummy_via, my_test_name}, + gen_server_SUITE, [], []), + ?line ok = gen_server:call({via, dummy_via, my_test_name}, started_p), + ?line {error, {already_started, Pid6}} = + gen_server:start({via, dummy_via, my_test_name}, + gen_server_SUITE, [], []), + ?line ok = gen_server:call({via, dummy_via, my_test_name}, stop), + test_server:sleep(1), + ?line {'EXIT', {noproc,_}} = (catch gen_server:call(Pid6, started_p, 10)), + + %% via register linked + ?line dummy_via:reset(), + ?line {ok, Pid7} = + gen_server:start_link({via, dummy_via, my_test_name}, + gen_server_SUITE, [], []), + ?line ok = gen_server:call({via, dummy_via, my_test_name}, started_p), + ?line {error, {already_started, Pid7}} = + gen_server:start({via, dummy_via, my_test_name}, + gen_server_SUITE, [], []), + ?line ok = gen_server:call({via, dummy_via, my_test_name}, stop), + ?line receive + {'EXIT', Pid7, stopped} -> + ok + after 5000 -> + test_server:fail(not_stopped) + end, test_server:messages_get(), %% Must wait for all error messages before going to next test. @@ -853,6 +882,8 @@ otp_5854(doc) -> otp_5854(Config) when is_list(Config) -> OldFlag = process_flag(trap_exit, true), + ?line dummy_via:reset(), + %% Make sure gen_server:enter_loop does not accept {local,Name} %% when it's another process than the calling one which is %% registered under that name @@ -881,6 +912,18 @@ otp_5854(Config) when is_list(Config) -> end, global:unregister_name(armitage), + %% (same for {via, Mod, Name}) + dummy_via:register_name(armitage, self()), + ?line {ok, Pid3} = + start_link(spec_init_via, [{not_ok, armitage}, []]), + receive + {'EXIT', Pid3, {process_not_registered_via, dummy_via}} -> + ok + after 1000 -> + ?line test_server:fail(gen_server_started) + end, + dummy_via:unregister_name(armitage), + process_flag(trap_exit, OldFlag), ok. @@ -1060,7 +1103,22 @@ spec_init_global({not_ok, Name}, Options) -> %% Supervised init can occur here ... gen_server:enter_loop(?MODULE, Options, {}, {global, Name}, infinity). -spec_init_default_timeout({ok, Name}, Options) -> +spec_init_via({ok, Name}, Options) -> + process_flag(trap_exit, true), + dummy_via:register_name(Name, self()), + proc_lib:init_ack({ok, self()}), + %% Supervised init can occur here ... + gen_server:enter_loop(?MODULE, Options, {}, + {via, dummy_via, Name}, infinity); + +spec_init_via({not_ok, Name}, Options) -> + process_flag(trap_exit, true), + proc_lib:init_ack({ok, self()}), + %% Supervised init can occur here ... + gen_server:enter_loop(?MODULE, Options, {}, + {via, dummy_via, Name}, infinity). + +spec_init_default_timeout({ok, Name}, Options) -> process_flag(trap_exit, true), register(Name, self()), proc_lib:init_ack({ok, self()}), diff --git a/system/doc/design_principles/spec_proc.xml b/system/doc/design_principles/spec_proc.xml index 1bc2d32461..c5472c96a6 100644 --- a/system/doc/design_principles/spec_proc.xml +++ b/system/doc/design_principles/spec_proc.xml @@ -108,7 +108,7 @@ ok <list type="bulleted"> <item>be started in a way that makes the process fit into a supervision tree,</item> - <item>support the <c>sys</c><seealso marker="#debug">debug facilities</seealso>, and</item> + <item>support the <c>sys</c> <seealso marker="#debug">debug facilities</seealso>, and</item> <item>take care of <seealso marker="#msg">system messages</seealso>.</item> </list> <p>System messages are messages with special meaning, used in diff --git a/system/doc/reference_manual/modules.xml b/system/doc/reference_manual/modules.xml index 0dbc0ab56b..377b2853ca 100644 --- a/system/doc/reference_manual/modules.xml +++ b/system/doc/reference_manual/modules.xml @@ -114,6 +114,12 @@ fact(0) -> % | <p>If this attribute is not specified, the version defaults to the MD5 checksum of the module.</p> </item> + <tag><c>-on_load(Function).</c></tag> + <item> + <p>Names a function that should be run automatically when a + module a loaded. See <seealso marker="code_loading#on_load"> + code loading</seealso> for more information.</p> + </item> </taglist> </section> @@ -180,7 +186,7 @@ fact(0) -> % | <p>Read more in <seealso marker="typespec">Types and Function specifications</seealso>. </p> <p> - The desciption is based on + The description is based on <url href="http://www.erlang.org/eeps/eep-0008.html">EEP8 - Types and function specifications</url> which will not be further updated. |