diff options
Diffstat (limited to 'erts/emulator')
82 files changed, 6555 insertions, 3695 deletions
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 21351df656..a9f3bb8e89 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -633,6 +633,15 @@ GENERATE += $(TTF_DIR)/driver_tab.c # This list must be consistent with PRE_LOADED_MODULES in # erts/preloaded/src/Makefile. +ifeq ($(USE_ESOCK), yes) +ESOCK_PRELOAD_BEAM = \ + $(ERL_TOP)/erts/preloaded/ebin/socket.beam \ + $(ERL_TOP)/erts/preloaded/ebin/net.beam +else +ESOCK_PRELOAD_BEAM = \ + $(ERL_TOP)/erts/preloaded/ebin/net.beam +endif + PRELOAD_BEAM = $(ERL_TOP)/erts/preloaded/ebin/erts_code_purger.beam \ $(ERL_TOP)/erts/preloaded/ebin/erl_init.beam \ $(ERL_TOP)/erts/preloaded/ebin/init.beam \ @@ -641,8 +650,7 @@ PRELOAD_BEAM = $(ERL_TOP)/erts/preloaded/ebin/erts_code_purger.beam \ $(ERL_TOP)/erts/preloaded/ebin/prim_inet.beam \ $(ERL_TOP)/erts/preloaded/ebin/prim_file.beam \ $(ERL_TOP)/erts/preloaded/ebin/zlib.beam \ - $(ERL_TOP)/erts/preloaded/ebin/socket.beam \ - $(ERL_TOP)/erts/preloaded/ebin/net.beam \ + $(ESOCK_PRELOAD_BEAM) \ $(ERL_TOP)/erts/preloaded/ebin/prim_zip.beam \ $(ERL_TOP)/erts/preloaded/ebin/erl_prim_loader.beam \ $(ERL_TOP)/erts/preloaded/ebin/erlang.beam \ @@ -835,6 +843,15 @@ EMU_OBJS = \ $(OBJDIR)/beam_catches.o $(OBJDIR)/code_ix.o \ $(OBJDIR)/beam_ranges.o + +ifeq ($(USE_ESOCK), yes) + +# WE ARE USING ESOCK + +ESOCK_NIF_OBJS = \ + $(OBJDIR)/socket_nif.o \ + $(OBJDIR)/net_nif.o + ifneq ($(TARGET), win32) # These are *currently* only needed for non-win32, # since the nif-functions for socket and net are basically @@ -847,6 +864,16 @@ else ESOCK_RUN_OBJS = endif +else + +# WE ARE *NOT* USING ESOCK + +ESOCK_NIF_OBJS = +ESOCK_RUN_OBJS = + +endif + + RUN_OBJS += \ $(OBJDIR)/erl_alloc.o $(OBJDIR)/erl_mtrace.o \ $(OBJDIR)/erl_alloc_util.o $(OBJDIR)/erl_goodfit_alloc.o \ @@ -894,7 +921,7 @@ RUN_OBJS += \ $(OBJDIR)/erl_ptab.o $(OBJDIR)/erl_map.o \ $(OBJDIR)/erl_msacc.o $(OBJDIR)/erl_lock_flags.o \ $(OBJDIR)/erl_io_queue.o $(OBJDIR)/erl_db_catree.o \ - $(ESOCK_RUN_OBJS) + $(ESOCK_RUN_OBJS) $(OBJDIR)/erl_flxctr.o LTTNG_OBJS = $(OBJDIR)/erlang_lttng.o @@ -903,8 +930,7 @@ NIF_OBJS = \ $(OBJDIR)/prim_buffer_nif.o \ $(OBJDIR)/prim_file_nif.o \ $(OBJDIR)/zlib_nif.o \ - $(OBJDIR)/socket_nif.o \ - $(OBJDIR)/net_nif.o + $(ESOCK_NIF_OBJS) ifeq ($(TARGET),win32) DRV_OBJS = \ diff --git a/erts/emulator/beam/arith_instrs.tab b/erts/emulator/beam/arith_instrs.tab index 5f23b2c168..f14b376419 100644 --- a/erts/emulator/beam/arith_instrs.tab +++ b/erts/emulator/beam/arith_instrs.tab @@ -51,11 +51,50 @@ plus.fetch(Op1, Op2) { plus.execute(Fail, Dst) { if (ERTS_LIKELY(is_both_small(PlusOp1, PlusOp2))) { +#ifdef HAVE_OVERFLOW_CHECK_BUILTINS + Sint lhs_tagged, rhs_untagged, res; + + /* The value part of immediate integers start right after the tag and + * occupy the rest of the word, so if you squint a bit they look like + * fixed-point integers; as long as you mask the tag away you will get + * correct results from addition/subtraction since they share the same + * notion of zero. It's fairly easy to see that the following holds + * when (a + b) is in range: + * + * (a >> s) + (b >> s) == ((a & ~m) + (b & ~m)) >> s + * + * Where 's' is the tag size and 'm' is the tag mask. + * + * The left-hand side is our fallback in the #else clause and is the + * fastest way to do this safely in plain C. The actual addition will + * never overflow since `Sint` has a much greater range than our + * smalls, so we can use the IS_SSMALL macro to see if the result is + * within range. + * + * What we're doing below is an extension of the right-hand side. By + * treating `a` and `b` as fixed-point integers, all additions whose + * result is out of range will also overflow `Sint` and we can use the + * compiler's overflow intrinsics to check for this condition. + * + * In addition, since the tag lives in the lowest bits we can further + * optimize this by only stripping the tag from either side. The higher + * bits can't influence the tag bits since we bail on overflow, so the + * tag bits from the tagged side will simply appear in the result. */ + lhs_tagged = PlusOp1; + rhs_untagged = PlusOp2 & ~_TAG_IMMED1_MASK; + + if (ERTS_LIKELY(!__builtin_add_overflow(lhs_tagged, rhs_untagged, &res))) { + ASSERT(is_small(res)); + $Dst = res; + $NEXT0(); + } +#else Sint i = signed_val(PlusOp1) + signed_val(PlusOp2); if (ERTS_LIKELY(IS_SSMALL(i))) { $Dst = make_small(i); $NEXT0(); } +#endif } $OUTLINED_ARITH_2($Fail, mixed_plus, BIF_splus_2, PlusOp1, PlusOp2, $Dst); } @@ -73,11 +112,26 @@ minus.fetch(Op1, Op2) { minus.execute(Fail, Dst) { if (ERTS_LIKELY(is_both_small(MinusOp1, MinusOp2))) { +#ifdef HAVE_OVERFLOW_CHECK_BUILTINS + Sint lhs_tagged, rhs_untagged, res; + + /* See plus.execute */ + lhs_tagged = MinusOp1; + rhs_untagged = MinusOp2 & ~_TAG_IMMED1_MASK; + + if (ERTS_LIKELY(!__builtin_sub_overflow(lhs_tagged, rhs_untagged, &res))) { + ASSERT(is_small(res)); + $Dst = res; + $NEXT0(); + } +#else Sint i = signed_val(MinusOp1) - signed_val(MinusOp2); + if (ERTS_LIKELY(IS_SSMALL(i))) { $Dst = make_small(i); $NEXT0(); } +#endif } $OUTLINED_ARITH_2($Fail, mixed_minus, BIF_sminus_2, MinusOp1, MinusOp2, $Dst); } @@ -97,12 +151,27 @@ increment.execute(IncrementVal, Dst) { Eterm result; if (ERTS_LIKELY(is_small(increment_reg_val))) { +#ifdef HAVE_OVERFLOW_CHECK_BUILTINS + Sint lhs_tagged, rhs_untagged, res; + + /* See plus.execute */ + lhs_tagged = increment_reg_val; + rhs_untagged = (Sint)increment_val << _TAG_IMMED1_SIZE; + + if (ERTS_LIKELY(!__builtin_add_overflow(lhs_tagged, rhs_untagged, &res))) { + ASSERT(is_small(res)); + $Dst = res; + $NEXT0(); + } +#else Sint i = signed_val(increment_reg_val) + increment_val; if (ERTS_LIKELY(IS_SSMALL(i))) { $Dst = make_small(i); $NEXT0(); } +#endif } + result = erts_mixed_plus(c_p, increment_reg_val, make_small(increment_val)); ERTS_HOLE_CHECK(c_p); if (ERTS_LIKELY(is_value(result))) { @@ -118,11 +187,15 @@ i_times(Fail, Op1, Op2, Dst) { Eterm op2 = $Op2; #ifdef HAVE_OVERFLOW_CHECK_BUILTINS if (ERTS_LIKELY(is_both_small(op1, op2))) { - Sint a = signed_val(op1); - Sint b = signed_val(op2); - Sint res; - if (ERTS_LIKELY(!__builtin_mul_overflow(a, b, &res) && IS_SSMALL(res))) { - $Dst = make_small(res); + /* See plus.execute */ + Sint lhs_untagged, rhs_actual, res; + + lhs_untagged = op1 & ~_TAG_IMMED1_MASK; + rhs_actual = signed_val(op2); + + if (ERTS_LIKELY(!__builtin_mul_overflow(lhs_untagged, rhs_actual, &res))) { + ASSERT(!(res & _TAG_IMMED1_MASK)); + $Dst = res | _TAG_IMMED1_SMALL; $NEXT0(); } } diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index 762c5da9be..4d52435139 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -353,6 +353,22 @@ erts_debug_disassemble_1(BIF_ALIST_1) return TUPLE3(hp, addr, bin, mfa); } +BIF_RETTYPE +erts_debug_interpreter_size_0(BIF_ALIST_0) +{ + int i; + BeamInstr low, high; + + low = high = (BeamInstr) process_main; + for (i = 0; i < NUM_SPECIFIC_OPS; i++) { + BeamInstr a = BeamOpCodeAddr(i); + if (a > high) { + high = a; + } + } + return erts_make_integer(high - low, BIF_P); +} + void dbg_bt(Process* p, Eterm* sp) { diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index ea01ce597d..bae64afb97 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -375,44 +375,33 @@ do { \ /* * process_main() is already huge, so we want to avoid inlining - * into it. Especially functions that are seldom used. + * seldom used functions into it. */ -#ifdef __GNUC__ -# define NOINLINE __attribute__((__noinline__)) -#else -# define NOINLINE -#endif - - -/* - * The following functions are called directly by process_main(). - * Don't inline them. - */ -static void init_emulator_finish(void) NOINLINE; -static ErtsCodeMFA *ubif2mfa(void* uf) NOINLINE; +static void init_emulator_finish(void) ERTS_NOINLINE; +static ErtsCodeMFA *ubif2mfa(void* uf) ERTS_NOINLINE; static BeamInstr* handle_error(Process* c_p, BeamInstr* pc, - Eterm* reg, ErtsCodeMFA* bif_mfa) NOINLINE; + Eterm* reg, ErtsCodeMFA* bif_mfa) ERTS_NOINLINE; static BeamInstr* call_error_handler(Process* p, ErtsCodeMFA* mfa, - Eterm* reg, Eterm func) NOINLINE; + Eterm* reg, Eterm func) ERTS_NOINLINE; static BeamInstr* fixed_apply(Process* p, Eterm* reg, Uint arity, - BeamInstr *I, Uint offs) NOINLINE; + BeamInstr *I, Uint offs) ERTS_NOINLINE; static BeamInstr* apply(Process* p, Eterm* reg, - BeamInstr *I, Uint offs) NOINLINE; + BeamInstr *I, Uint offs) ERTS_NOINLINE; static BeamInstr* call_fun(Process* p, int arity, - Eterm* reg, Eterm args) NOINLINE; + Eterm* reg, Eterm args) ERTS_NOINLINE; static BeamInstr* apply_fun(Process* p, Eterm fun, - Eterm args, Eterm* reg) NOINLINE; + Eterm args, Eterm* reg) ERTS_NOINLINE; static Eterm new_fun(Process* p, Eterm* reg, - ErlFunEntry* fe, int num_free) NOINLINE; + ErlFunEntry* fe, int num_free) ERTS_NOINLINE; static int is_function2(Eterm Term, Uint arity); static Eterm erts_gc_new_map(Process* p, Eterm* reg, Uint live, - Uint n, BeamInstr* ptr) NOINLINE; + Uint n, BeamInstr* ptr) ERTS_NOINLINE; static Eterm erts_gc_new_small_map_lit(Process* p, Eterm* reg, Eterm keys_literal, - Uint live, BeamInstr* ptr) NOINLINE; + Uint live, BeamInstr* ptr) ERTS_NOINLINE; static Eterm erts_gc_update_map_assoc(Process* p, Eterm* reg, Uint live, - Uint n, BeamInstr* new_p) NOINLINE; + Uint n, BeamInstr* new_p) ERTS_NOINLINE; static Eterm erts_gc_update_map_exact(Process* p, Eterm* reg, Uint live, - Uint n, Eterm* new_p) NOINLINE; + Uint n, Eterm* new_p) ERTS_NOINLINE; static Eterm get_map_element(Eterm map, Eterm key); static Eterm get_map_element_hash(Eterm map, Eterm key, Uint32 hx); @@ -3283,7 +3272,7 @@ erts_current_reductions(Process *c_p, Process *p) } else { reds_left = c_p->fcalls; } - return REDS_IN(c_p) - reds_left; + return REDS_IN(c_p) - reds_left - erts_proc_sched_data(p)->virtual_reds; } int diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index d0e2d9afc2..b81056c774 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -1915,7 +1915,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm return_term, Eterm *refp, erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); erts_dsprintf(dsbufp, "Discarding message %T from %T to %T in an old " - "incarnation (%u) of this node (%u)\n", + "incarnation (%d) of this node (%d)\n", msg, p->common.id, to, @@ -1959,7 +1959,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm return_term, Eterm *refp, erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); erts_dsprintf(dsbufp, "Discarding message %T from %T to %T in an old " - "incarnation (%u) of this node (%u)\n", + "incarnation (%d) of this node (%d)\n", msg, p->common.id, to, diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 34a0be4f2d..db9c258cb7 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -437,6 +437,7 @@ bif erts_debug:set_internal_state/2 bif erts_debug:display/1 bif erts_debug:dist_ext_to_term/2 bif erts_debug:instructions/0 +bif erts_debug:interpreter_size/0 bif erts_debug:dirty_cpu/2 bif erts_debug:dirty_io/2 bif erts_debug:dirty/3 diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 7666f23a4f..522f50287a 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -2176,24 +2176,6 @@ term_to_Uint64(Eterm term, Uint64 *up) #endif } -int -term_to_Uint32(Eterm term, Uint32 *up) -{ -#if ERTS_SIZEOF_ETERM == 4 - return term_to_Uint(term,up); -#else - if (is_small(term)) { - Sint i = signed_val(term); - if (i >= 0) { - *up = (Uint32) i; - return 1; - } - } - *up = BADARG; - return 0; -#endif -} - int term_to_Sint(Eterm term, Sint *sp) { diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index 3fed076419..ad19cce395 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -168,8 +168,6 @@ Eterm erts_uint64_array_to_big(Uint **, int, int, Uint64 *); int term_to_Uint64(Eterm, Uint64*); int term_to_Sint64(Eterm, Sint64*); #endif -int term_to_Uint32(Eterm, Uint32*); - Uint32 big_to_uint32(Eterm b); int term_equals_2pow32(Eterm); diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 06d8c7cda8..80e871aaf6 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -394,8 +394,12 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p, ErtsProcLocks orig_lock erts_print(to, to_arg, "OldBinVHeap: %b64u\n", BIN_OLD_VHEAP(p)); erts_print(to, to_arg, "BinVHeap unused: %b64u\n", BIN_VHEAP_SZ(p) - p->off_heap.overhead); - erts_print(to, to_arg, "OldBinVHeap unused: %b64u\n", - BIN_OLD_VHEAP_SZ(p) - BIN_OLD_VHEAP(p)); + if (BIN_OLD_VHEAP_SZ(p) >= BIN_OLD_VHEAP(p)) { + erts_print(to, to_arg, "OldBinVHeap unused: %b64u\n", + BIN_OLD_VHEAP_SZ(p) - BIN_OLD_VHEAP(p)); + } else { + erts_print(to, to_arg, "OldBinVHeap unused: overflow\n"); + } erts_print(to, to_arg, "Memory: %beu\n", erts_process_memory(p, !0)); if (garbing) { diff --git a/erts/emulator/beam/bs_instrs.tab b/erts/emulator/beam/bs_instrs.tab index 9cad2b03c5..bd1ad91e45 100644 --- a/erts/emulator/beam/bs_instrs.tab +++ b/erts/emulator/beam/bs_instrs.tab @@ -125,7 +125,7 @@ BS_GET_UNCHECKED_FIELD_SIZE(Bits, Unit, Fail, Dst) { TEST_BIN_VHEAP(VNh, Nh, Live) { Uint need = $Nh; if (E - HTOP < need || MSO(c_p).overhead + $VNh >= BIN_VHEAP_SZ(c_p)) { - SWAPOUT; + $GC_SWAPOUT(); PROCESS_MAIN_CHK_LOCKS(c_p); FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, $Live, FCALLS); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 8bbe6450eb..ff19ef018e 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -55,7 +55,6 @@ */ #if 0 #define ERTS_DIST_MSG_DBG -FILE *dbg_file; #endif #if 0 /* Enable this to print the dist debug messages to a file instead */ @@ -67,6 +66,7 @@ FILE *dbg_file; #endif #if defined(ERTS_DIST_MSG_DBG) || defined(ERTS_RAW_DIST_MSG_DBG) +FILE *dbg_file; static void bw(byte *buf, ErlDrvSizeT sz) { bin_write(ERTS_PRINT_FILE, dbg_file, buf, sz); @@ -743,7 +743,7 @@ void init_dist(void) sprintf(buff, ERTS_DIST_MSG_DBG_FILE, getpid()); dbg_file = fopen(buff,"w+"); } -#elif defined (ERTS_DIST_MSG_DBG) +#elif defined(ERTS_DIST_MSG_DBG) || defined(ERTS_RAW_DIST_MSG_DBG) dbg_file = stderr; #endif @@ -775,19 +775,25 @@ void init_dist(void) static ERTS_INLINE ErtsDistOutputBuf * alloc_dist_obuf(Uint size, Uint headers) { - int i; + Uint obuf_size = sizeof(ErtsDistOutputBuf)*(headers); ErtsDistOutputBuf *obuf; - Uint obuf_size = sizeof(ErtsDistOutputBuf)*(headers) + - sizeof(byte)*size; - Binary *bin = erts_bin_drv_alloc(obuf_size); - obuf = (ErtsDistOutputBuf *) &bin->orig_bytes[size]; + Binary *bin; + byte *extp; + int i; + + bin = erts_bin_drv_alloc(obuf_size + size); erts_refc_add(&bin->intern.refc, headers - 1, 1); + + obuf = (ErtsDistOutputBuf *)&bin->orig_bytes[0]; + extp = (byte *)&bin->orig_bytes[obuf_size]; + for (i = 0; i < headers; i++) { obuf[i].bin = bin; - obuf[i].extp = (byte *)&bin->orig_bytes[0]; + obuf[i].extp = extp; #ifdef DEBUG obuf[i].dbg_pattern = ERTS_DIST_OUTPUT_BUF_DBG_PATTERN; - obuf[i].alloc_endp = obuf->extp + size; + obuf[i].ext_startp = extp; + obuf[i].alloc_endp = &extp[size]; ASSERT(bin == ErtsDistOutputBuf2Binary(obuf)); #endif } @@ -1360,7 +1366,7 @@ erts_dist_seq_tree_foreach_delete_yielding(DistSeqNode **root, limit); if (res > 0) { if (ysp != &ys) - erts_free(ERTS_ALC_T_ML_YIELD_STATE, ysp); + erts_free(ERTS_ALC_T_SEQ_YIELD_STATE, ysp); *vyspp = NULL; } else { @@ -1838,7 +1844,7 @@ int erts_net_message(Port *prt, if (locks) erts_proc_unlock(rp, locks); - } else if (ede_hfrag) { + } else if (ede_hfrag != NULL) { erts_free_dist_ext_copy(erts_get_dist_ext(ede_hfrag)); free_message_buffer(ede_hfrag); } @@ -1880,16 +1886,18 @@ int erts_net_message(Port *prt, goto invalid_message; } rp = erts_proc_lookup(to); + if (rp) { ErtsProcLocks locks = 0; erts_queue_dist_message(rp, locks, edep, ede_hfrag, token, am_Empty); if (locks) erts_proc_unlock(rp, locks); - } else if (ede_hfrag) { + } else if (ede_hfrag != NULL) { erts_free_dist_ext_copy(erts_get_dist_ext(ede_hfrag)); free_message_buffer(ede_hfrag); } + break; } @@ -1930,15 +1938,19 @@ int erts_net_message(Port *prt, goto invalid_message; } - if (!erts_proc_lookup(watcher)) break; /* Process not alive */ - - if (reason == THE_NON_VALUE) { + if (!erts_proc_lookup(watcher)) { + if (ede_hfrag != NULL) { + erts_free_dist_ext_copy(erts_get_dist_ext(ede_hfrag)); + free_message_buffer(ede_hfrag); + } + break; /* Process not alive */ + } #ifdef ERTS_DIST_MSG_DBG + if (reason == THE_NON_VALUE) { dist_msg_dbg(edep, "MSG", buf, orig_len); -#endif - } +#endif erts_proc_sig_send_dist_monitor_down( dep, ref, watched, watcher, edep, ede_hfrag, reason); @@ -1987,13 +1999,19 @@ int erts_net_message(Port *prt, goto invalid_message; } - if (!erts_proc_lookup(to)) break; /* Process not alive */ + if (!erts_proc_lookup(to)) { + if (ede_hfrag != NULL) { + erts_free_dist_ext_copy(erts_get_dist_ext(ede_hfrag)); + free_message_buffer(ede_hfrag); + } + break; /* Process not alive */ + } - if (reason == THE_NON_VALUE) { #ifdef ERTS_DIST_MSG_DBG + if (reason == THE_NON_VALUE) { dist_msg_dbg(edep, "MSG", buf, orig_len); -#endif } +#endif erts_proc_sig_send_dist_link_exit(dep, from, to, edep, ede_hfrag, @@ -2042,13 +2060,19 @@ int erts_net_message(Port *prt, goto invalid_message; } - if (!erts_proc_lookup(to)) break; /* Process not alive */ + if (!erts_proc_lookup(to)) { + if (ede_hfrag != NULL) { + erts_free_dist_ext_copy(erts_get_dist_ext(ede_hfrag)); + free_message_buffer(ede_hfrag); + } + break; /* Process not alive */ + } - if (reason == THE_NON_VALUE) { #ifdef ERTS_DIST_MSG_DBG + if (reason == THE_NON_VALUE) { dist_msg_dbg(edep, "MSG", buf, orig_len); -#endif } +#endif erts_proc_sig_send_dist_exit(dep, from, to, edep, ede_hfrag, reason, token); break; @@ -2301,8 +2325,18 @@ erts_dsig_send(ErtsDSigSendContext *ctx) ctx->data_size = ctx->max_finalize_prepend; erts_reset_atom_cache_map(ctx->acmp); - erts_encode_dist_ext_size(ctx->ctl, ctx->flags, ctx->acmp, &ctx->data_size); + switch (erts_encode_dist_ext_size(ctx->ctl, ctx->flags, + ctx->acmp, &ctx->data_size)) { + case ERTS_EXT_SZ_OK: + break; + case ERTS_EXT_SZ_SYSTEM_LIMIT: + retval = ERTS_DSIG_SEND_TOO_LRG; + goto done; + case ERTS_EXT_SZ_YIELD: + ERTS_INTERNAL_ERROR("Unexpected yield result"); + break; + } if (is_non_value(ctx->msg)) { ctx->phase = ERTS_DSIG_SEND_PHASE_ALLOC; break; @@ -2312,17 +2346,31 @@ erts_dsig_send(ErtsDSigSendContext *ctx) ctx->u.sc.level = 0; ctx->phase = ERTS_DSIG_SEND_PHASE_MSG_SIZE; - case ERTS_DSIG_SEND_PHASE_MSG_SIZE: - if (!ctx->no_trap) { - if (erts_encode_dist_ext_size_int(ctx->msg, ctx, &ctx->data_size)) { - retval = ERTS_DSIG_SEND_CONTINUE; - goto done; - } - } else { - erts_encode_dist_ext_size(ctx->msg, ctx->flags, ctx->acmp, &ctx->data_size); + case ERTS_DSIG_SEND_PHASE_MSG_SIZE: { + ErtsExtSzRes sz_res; + sz_res = (!ctx->no_trap + ? erts_encode_dist_ext_size_ctx(ctx->msg, + ctx, + &ctx->data_size) + : erts_encode_dist_ext_size(ctx->msg, + ctx->flags, + ctx->acmp, + &ctx->data_size)); + switch (sz_res) { + case ERTS_EXT_SZ_OK: + break; + case ERTS_EXT_SZ_SYSTEM_LIMIT: + retval = ERTS_DSIG_SEND_TOO_LRG; + goto done; + case ERTS_EXT_SZ_YIELD: + if (ctx->no_trap) + ERTS_INTERNAL_ERROR("Unexpected yield result"); + retval = ERTS_DSIG_SEND_CONTINUE; + goto done; } ctx->phase = ERTS_DSIG_SEND_PHASE_ALLOC; + } case ERTS_DSIG_SEND_PHASE_ALLOC: erts_finalize_atom_cache_map(ctx->acmp, ctx->flags); @@ -2341,7 +2389,8 @@ erts_dsig_send(ErtsDSigSendContext *ctx) (ctx->fragments-1) * ERTS_DIST_FRAGMENT_HEADER_SIZE, ctx->fragments); ctx->obuf->ext_start = &ctx->obuf->extp[0]; - ctx->obuf->ext_endp = &ctx->obuf->extp[0] + ctx->max_finalize_prepend + ctx->dhdr_ext_size; + ctx->obuf->ext_endp = &ctx->obuf->extp[0] + ctx->max_finalize_prepend + + ctx->dhdr_ext_size; /* Encode internal version of dist header */ ctx->obuf->extp = erts_encode_ext_dist_header_setup( @@ -2380,8 +2429,8 @@ erts_dsig_send(ErtsDSigSendContext *ctx) case ERTS_DSIG_SEND_PHASE_FIN: { ASSERT(ctx->obuf->extp < ctx->obuf->ext_endp); - ASSERT(((byte*)&ctx->obuf->bin->orig_bytes[0]) <= ctx->obuf->extp - ctx->max_finalize_prepend); - ASSERT(ctx->obuf->ext_endp <= ((byte*)ctx->obuf->bin->orig_bytes) + ctx->data_size + ctx->dhdr_ext_size); + ASSERT(ctx->obuf->ext_startp <= ctx->obuf->extp - ctx->max_finalize_prepend); + ASSERT(ctx->obuf->ext_endp <= (byte*)ctx->obuf->ext_startp + ctx->data_size + ctx->dhdr_ext_size); ctx->data_size = ctx->obuf->ext_endp - ctx->obuf->extp; @@ -3424,6 +3473,8 @@ dist_ctrl_get_data_1(BIF_ALIST_1) obufsize -= size_obuf(obuf); if (reds < 0) { erts_de_runlock(dep); + if (obufsize) + erts_atomic_add_nob(&dep->qsize, (erts_aint_t) -obufsize); ERTS_BIF_YIELD1(bif_export[BIF_dist_ctrl_get_data_1], BIF_P, BIF_ARG_1); } @@ -3457,6 +3508,7 @@ dist_ctrl_get_data_1(BIF_ALIST_1) pb->bytes = (byte*) obuf->extp; pb->flags = 0; res = make_binary(pb); + hp += PROC_BIN_SIZE; } else { hp = HAlloc(BIF_P, PROC_BIN_SIZE * 2 + 4 + hsz); pb = (ProcBin *) (char *) hp; @@ -3748,10 +3800,12 @@ int distribution_info(fmtfn_t to, void *arg) /* Called by break handler */ BIF_RETTYPE setnode_2(BIF_ALIST_2) { Process *net_kernel; - Uint32 creation; + Uint creation; /* valid creation ? */ - if(!term_to_Uint32(BIF_ARG_2, &creation)) + if(!term_to_Uint(BIF_ARG_2, &creation)) + goto error; + if(creation > 3) goto error; /* valid node name ? */ @@ -3795,7 +3849,7 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2) erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); erts_thr_progress_block(); inc_no_nodes(); - erts_set_this_node(BIF_ARG_1, creation); + erts_set_this_node(BIF_ARG_1, (Uint32) creation); erts_is_alive = 1; send_nodes_mon_msgs(NULL, am_nodeup, BIF_ARG_1, am_visible, NIL); erts_thr_progress_unblock(); diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h index f953a2ab8c..067028634b 100644 --- a/erts/emulator/beam/dist.h +++ b/erts/emulator/beam/dist.h @@ -54,12 +54,11 @@ #define DFLAG_DIST_MANDATORY (DFLAG_EXTENDED_REFERENCES \ | DFLAG_EXTENDED_PIDS_PORTS \ | DFLAG_UTF8_ATOMS \ - | DFLAG_NEW_FUN_TAGS \ - | DFLAG_BIG_CREATION) + | DFLAG_NEW_FUN_TAGS) /* * Additional optimistic flags when encoding toward pending connection. - * If remote node (erl_interface) does not support these then we may need + * If remote node (erl_interface) does not supporting these then we may need * to transcode messages enqueued before connection setup was finished. */ #define DFLAG_DIST_HOPEFULLY (DFLAG_EXPORT_PTR_TAG \ @@ -76,6 +75,7 @@ | DFLAG_SMALL_ATOM_TAGS \ | DFLAG_UTF8_ATOMS \ | DFLAG_MAP_TAG \ + | DFLAG_BIG_CREATION \ | DFLAG_SEND_SENDER \ | DFLAG_BIG_SEQTRACE_LABELS \ | DFLAG_EXIT_PAYLOAD \ diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index e6169ebeaa..b9f0334172 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -3904,7 +3904,7 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func) { Uint sz; Uint found_type; - UWord pre_pattern; + UWord pre_pattern, expected_pattern; UWord post_pattern; UWord *ui_ptr; #ifdef HARD_DEBUG @@ -3914,6 +3914,8 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func) if (!ptr) return NULL; + expected_pattern = MK_PATTERN(n); + ui_ptr = (UWord *) ptr; pre_pattern = *(--ui_ptr); *size = sz = *(--ui_ptr); @@ -3922,7 +3924,13 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func) #endif found_type = GET_TYPE_OF_PATTERN(pre_pattern); - if (pre_pattern != MK_PATTERN(n)) { + + if (found_type != n) { + erts_exit(ERTS_ABORT_EXIT, "ERROR: Miss matching allocator types" + " used in alloc and free\n"); + } + + if (pre_pattern != expected_pattern) { if ((FIXED_FENCE_PATTERN_MASK & pre_pattern) != FIXED_FENCE_PATTERN) erts_exit(ERTS_ABORT_EXIT, "ERROR: Fence at beginning of memory block (p=0x%u) " @@ -3932,8 +3940,7 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func) sys_memcpy((void *) &post_pattern, (void *) (((char *)ptr)+sz), sizeof(UWord)); - if (post_pattern != MK_PATTERN(n) - || pre_pattern != post_pattern) { + if (post_pattern != expected_pattern || pre_pattern != post_pattern) { char fbuf[10]; char obuf[10]; char *ftype; diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index e7329daa2d..92e5069c71 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -282,6 +282,7 @@ type ENVIRONMENT SYSTEM SYSTEM environment type PERSISTENT_TERM LONG_LIVED CODE persisten_term type PERSISTENT_LOCK_Q SHORT_LIVED SYSTEM persistent_lock_q +type PERSISTENT_TERM_TMP SHORT_LIVED SYSTEM persistent_term_tmp_table # # Types used for special emulators diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 8d4464969a..25ac3bc5af 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -6567,6 +6567,14 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) __FILE__, __LINE__); } + /* The various fields packed into the header word must not overlap */ + ERTS_CT_ASSERT(!(MBC_ABLK_OFFSET_MASK & MBC_ABLK_SZ_MASK)); + ERTS_CT_ASSERT(!(MBC_ABLK_OFFSET_MASK & BLK_FLG_MASK)); + ERTS_CT_ASSERT(!(MBC_ABLK_SZ_MASK & BLK_FLG_MASK)); + ERTS_CT_ASSERT(!(MBC_FBLK_SZ_MASK & BLK_FLG_MASK)); + ERTS_CT_ASSERT(!(SBC_BLK_SZ_MASK & BLK_FLG_MASK)); + ERTS_CT_ASSERT(!(CRR_SZ_MASK & CRR_FLG_MASK)); + if (!initialized) goto error; diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index ea1afe8f58..b46b311c59 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -334,8 +334,11 @@ void erts_alcu_sched_spec_data_init(struct ErtsSchedulerData_ *esdp); #endif #if MBC_ABLK_OFFSET_BITS -# define MBC_ABLK_OFFSET_SHIFT (sizeof(UWord)*8 - MBC_ABLK_OFFSET_BITS) -# define MBC_ABLK_OFFSET_MASK ((~((UWord)0) << MBC_ABLK_OFFSET_SHIFT) & ~BLK_FLG_MASK) +/* The shift is reduced by 1 since the highest bit is used for a flag. */ +# define MBC_ABLK_OFFSET_SHIFT (sizeof(UWord)*8 - 1 - MBC_ABLK_OFFSET_BITS) +# define MBC_ABLK_OFFSET_MASK \ + (((UWORD_CONSTANT(1) << MBC_ABLK_OFFSET_BITS) - UWORD_CONSTANT(1)) \ + << MBC_ABLK_OFFSET_SHIFT) # define MBC_ABLK_SZ_MASK (~MBC_ABLK_OFFSET_MASK & ~BLK_FLG_MASK) #else # define MBC_ABLK_SZ_MASK (~BLK_FLG_MASK) diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index a7424bbcb8..2704b99aa4 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -768,7 +768,7 @@ static ErtsProcessInfoArgs pi_args[] = { {am_memory, 0, ERTS_PI_FLAG_NEED_MSGQ_LEN|ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN}, {am_garbage_collection, 3+2 + 3+2 + 3+2 + 3+2 + 3+2 + ERTS_MAX_HEAP_SIZE_MAP_SZ, 0, ERTS_PROC_LOCK_MAIN}, {am_group_leader, 0, 0, ERTS_PROC_LOCK_MAIN}, - {am_reductions, 0, 0, ERTS_PROC_LOCK_MAIN}, + {am_reductions, 0, ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN}, {am_priority, 0, 0, 0}, {am_trace, 0, 0, ERTS_PROC_LOCK_MAIN}, {am_binary, 0, ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN}, @@ -2579,6 +2579,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) /* Need to be the only thread running... */ erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + BIF_P->scheduler_data->current_process = NULL; erts_thr_progress_block(); if (BIF_ARG_1 == am_info) @@ -2592,6 +2593,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) erts_thr_progress_unblock(); erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + BIF_P->scheduler_data->current_process = BIF_P; ASSERT(dsbufp && dsbufp->str); res = new_binary(BIF_P, (byte *) dsbufp->str, dsbufp->str_len); @@ -2797,10 +2799,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) } else if (BIF_ARG_1 == am_threads) { return am_true; } else if (BIF_ARG_1 == am_creation) { - Uint hsz = 0; - erts_bld_uint(NULL, &hsz, erts_this_node->creation); - hp = hsz ? HAlloc(BIF_P, hsz) : NULL; - BIF_RET(erts_bld_uint(&hp, NULL, erts_this_node->creation)); + return make_small(erts_this_node->creation); } else if (BIF_ARG_1 == am_break_ignored) { extern int ignore_break; if (ignore_break) @@ -3026,6 +3025,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(erts_nif_taints(BIF_P)); } else if (ERTS_IS_ATOM_STR("reader_groups_map", BIF_ARG_1)) { BIF_RET(erts_get_reader_groups_map(BIF_P)); + } else if (ERTS_IS_ATOM_STR("decentralized_counter_groups_map", BIF_ARG_1)) { + BIF_RET(erts_get_decentralized_counter_groups_map(BIF_P)); } else if (ERTS_IS_ATOM_STR("dist_buf_busy_limit", BIF_ARG_1)) { Uint hsz = 0; @@ -4235,7 +4236,10 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) Uint dflags = (TERM_TO_BINARY_DFLAGS & ~DFLAG_EXPORT_PTR_TAG & ~DFLAG_BIT_BINARIES); - BIF_RET(erts_term_to_binary(BIF_P, tp[2], 0, dflags)); + Eterm res = erts_term_to_binary(BIF_P, tp[2], 0, dflags); + if (is_value(res)) + BIF_RET(res); + BIF_ERROR(BIF_P, SYSTEM_LIMIT); } else if (ERTS_IS_ATOM_STR("dist_ctrl", tp[1])) { Eterm res = am_undefined; @@ -4692,6 +4696,14 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) BIF_RET(am_ok); } } + else if (ERTS_IS_ATOM_STR("ets_debug_random_split_join", BIF_ARG_1)) { + if (is_tuple(BIF_ARG_2)) { + Eterm* tpl = tuple_val(BIF_ARG_2); + + if (erts_ets_debug_random_split_join(tpl[1], tpl[2] == am_true)) + BIF_RET(am_ok); + } + } else if (ERTS_IS_ATOM_STR("mbuf", BIF_ARG_1)) { Uint sz = size_object(BIF_ARG_2); ErlHeapFragment* frag = new_message_buffer(sz); diff --git a/erts/emulator/beam/erl_bif_persistent.c b/erts/emulator/beam/erl_bif_persistent.c index 5a78a043ce..f38e0cc5cb 100644 --- a/erts/emulator/beam/erl_bif_persistent.c +++ b/erts/emulator/beam/erl_bif_persistent.c @@ -37,15 +37,6 @@ #include "erl_binary.h" /* - * The limit for the number of persistent terms before - * a warning is issued. - */ - -#define WARNING_LIMIT 20000 -#define XSTR(s) STR(s) -#define STR(s) #s - -/* * Parameters for the hash table. */ #define INITIAL_SIZE 8 @@ -73,14 +64,69 @@ typedef struct trap_data { Uint memory; /* Used by info/0 to count used memory */ } TrapData; +typedef enum { + ERTS_PERSISTENT_TERM_CPY_PLACE_START, + ERTS_PERSISTENT_TERM_CPY_PLACE_1, + ERTS_PERSISTENT_TERM_CPY_PLACE_2, + ERTS_PERSISTENT_TERM_CPY_PLACE_3 +} ErtsPersistentTermCpyTableLocation; + +typedef enum { + ERTS_PERSISTENT_TERM_CPY_NO_REHASH = 0, + ERTS_PERSISTENT_TERM_CPY_REHASH = 1, + ERTS_PERSISTENT_TERM_CPY_TEMP = 2 +} ErtsPersistentTermCpyTableType; + +typedef struct { + HashTable* old_table; /* in param */ + Uint new_size; /* in param */ + ErtsPersistentTermCpyTableType copy_type; /* in param */ + Uint max_iterations; /* in param */ + ErtsPersistentTermCpyTableLocation location; /* in/out param */ + Uint iterations_done; /* in/out param */ + Uint total_iterations_done; /* in/out param */ + HashTable* new_table; /* out param */ +} ErtsPersistentTermCpyTableCtx; + +typedef enum { + PUT2_TRAP_LOCATION_NEW_KEY, + PUT2_TRAP_LOCATION_REPLACE_VALUE +} ErtsPersistentTermPut2TrapLocation; + +typedef struct { + ErtsPersistentTermPut2TrapLocation trap_location; + Eterm key; + Eterm term; + Uint entry_index; + HashTable* hash_table; + Eterm heap[3]; + Eterm tuple; + ErtsPersistentTermCpyTableCtx cpy_ctx; +} ErtsPersistentTermPut2Context; + +typedef enum { + ERASE1_TRAP_LOCATION_TMP_COPY, + ERASE1_TRAP_LOCATION_FINAL_COPY +} ErtsPersistentTermErase1TrapLocation; + +typedef struct { + ErtsPersistentTermErase1TrapLocation trap_location; + Eterm key; + HashTable* old_table; + HashTable* new_table; + Uint entry_index; + Eterm old_term; + HashTable* tmp_table; + ErtsPersistentTermCpyTableCtx cpy_ctx; +} ErtsPersistentTermErase1Context; + /* * Declarations of local functions. */ static HashTable* create_initial_table(void); static Uint lookup(HashTable* hash_table, Eterm key); -static HashTable* copy_table(HashTable* old_table, Uint new_size, int rehash); -static HashTable* tmp_table_copy(HashTable* old_table); +static HashTable* copy_table(ErtsPersistentTermCpyTableCtx* ctx); static int try_seize_update_permission(Process* c_p); static void release_update_permission(int release_updater); static void table_updater(void* table); @@ -127,7 +173,6 @@ static Process* updater_process = NULL; /* Protected by update_table_permission_mtx */ static ErtsThrPrgrLaterOp thr_prog_op; -static int issued_warning = 0; /* * Queue of hash tables to be deleted. @@ -139,7 +184,7 @@ static HashTable** delete_queue_tail = &delete_queue_head; /* * The following variables are only used during crash dumping. They - * are intialized by erts_init_persistent_dumping(). + * are initialized by erts_init_persistent_dumping(). */ ErtsLiteralArea** erts_persistent_areas; @@ -188,101 +233,181 @@ void erts_init_bif_persistent_term(void) &persistent_term_info_trap); } +/* + * Macro used for trapping in persistent_term_put_2 and + * persistent_term_erase_1 + */ +#define TRAPPING_COPY_TABLE(TABLE_DEST, OLD_TABLE, NEW_SIZE, COPY_TYPE, LOC_NAME, TRAP_CODE) \ + do { \ + ctx->cpy_ctx = (ErtsPersistentTermCpyTableCtx){ \ + .old_table = OLD_TABLE, \ + .new_size = NEW_SIZE, \ + .copy_type = COPY_TYPE, \ + .location = ERTS_PERSISTENT_TERM_CPY_PLACE_START \ + }; \ + L_ ## LOC_NAME: \ + ctx->cpy_ctx.max_iterations = MAX(1, max_iterations); \ + TABLE_DEST = copy_table(&ctx->cpy_ctx); \ + iterations_until_trap -= ctx->cpy_ctx.total_iterations_done; \ + if (TABLE_DEST == NULL) { \ + ctx->trap_location = LOC_NAME; \ + erts_set_gc_state(BIF_P, 0); \ + BUMP_ALL_REDS(BIF_P); \ + TRAP_CODE; \ + } \ + } while (0) + +static int persistent_term_put_2_ctx_bin_dtor(Binary *context_bin) +{ + ErtsPersistentTermPut2Context* ctx = ERTS_MAGIC_BIN_DATA(context_bin); + if (ctx->cpy_ctx.new_table != NULL) { + erts_free(ERTS_ALC_T_PERSISTENT_TERM, ctx->cpy_ctx.new_table); + release_update_permission(0); + } + return 1; +} +/* + * A linear congruential generator that is used in the debug emulator + * to trap after a random number of iterations in + * persistent_term_put_2 and persistent_term_erase_1. + * + * https://en.wikipedia.org/wiki/Linear_congruential_generator + */ +#define GET_SMALL_RANDOM_INT(SEED) \ + (1103515245 * (SEED) + 12345) % 227 + BIF_RETTYPE persistent_term_put_2(BIF_ALIST_2) { - Eterm key; - Eterm term; - Eterm heap[3]; - Eterm tuple; - HashTable* hash_table; - Uint term_size; - Uint lit_area_size; - ErlOffHeap code_off_heap; - ErtsLiteralArea* literal_area; - erts_shcopy_t info; - Eterm* ptr; - Uint entry_index; + static const Uint ITERATIONS_PER_RED = 32; + ErtsPersistentTermPut2Context* ctx; + Eterm state_mref = THE_NON_VALUE; + long iterations_until_trap; + long max_iterations; +#define PUT_TRAP_CODE \ + BIF_TRAP2(bif_export[BIF_persistent_term_put_2], BIF_P, state_mref, BIF_ARG_2) +#define TRAPPING_COPY_TABLE_PUT(TABLE_DEST, OLD_TABLE, NEW_SIZE, COPY_TYPE, LOC_NAME) \ + TRAPPING_COPY_TABLE(TABLE_DEST, OLD_TABLE, NEW_SIZE, COPY_TYPE, LOC_NAME, PUT_TRAP_CODE) + +#ifdef DEBUG + (void)ITERATIONS_PER_RED; + iterations_until_trap = max_iterations = + GET_SMALL_RANDOM_INT(ERTS_BIF_REDS_LEFT(BIF_P) + (Uint)&ctx); +#else + iterations_until_trap = max_iterations = + ITERATIONS_PER_RED * ERTS_BIF_REDS_LEFT(BIF_P); +#endif + if (is_internal_magic_ref(BIF_ARG_1) && + (ERTS_MAGIC_BIN_DESTRUCTOR(erts_magic_ref2bin(BIF_ARG_1)) == + persistent_term_put_2_ctx_bin_dtor)) { + /* Restore state after a trap */ + Binary* state_bin; + state_mref = BIF_ARG_1; + state_bin = erts_magic_ref2bin(state_mref); + ctx = ERTS_MAGIC_BIN_DATA(state_bin); + ASSERT(BIF_P->flags & F_DISABLE_GC); + erts_set_gc_state(BIF_P, 1); + switch (ctx->trap_location) { + case PUT2_TRAP_LOCATION_NEW_KEY: + goto L_PUT2_TRAP_LOCATION_NEW_KEY; + case PUT2_TRAP_LOCATION_REPLACE_VALUE: + goto L_PUT2_TRAP_LOCATION_REPLACE_VALUE; + } + } else { + /* Save state in magic bin in case trapping is necessary */ + Eterm* hp; + Binary* state_bin = erts_create_magic_binary(sizeof(ErtsPersistentTermPut2Context), + persistent_term_put_2_ctx_bin_dtor); + hp = HAlloc(BIF_P, ERTS_MAGIC_REF_THING_SIZE); + state_mref = erts_mk_magic_ref(&hp, &MSO(BIF_P), state_bin); + ctx = ERTS_MAGIC_BIN_DATA(state_bin); + /* + * IMPORTANT: The following field is used to detect if + * persistent_term_put_2_ctx_bin_dtor needs to free memory + */ + ctx->cpy_ctx.new_table = NULL; + } + if (!try_seize_update_permission(BIF_P)) { ERTS_BIF_YIELD2(bif_export[BIF_persistent_term_put_2], BIF_P, BIF_ARG_1, BIF_ARG_2); } + ctx->hash_table = (HashTable *) erts_atomic_read_nob(&the_hash_table); - hash_table = (HashTable *) erts_atomic_read_nob(&the_hash_table); + ctx->key = BIF_ARG_1; + ctx->term = BIF_ARG_2; - key = BIF_ARG_1; - term = BIF_ARG_2; + ctx->entry_index = lookup(ctx->hash_table, ctx->key); - entry_index = lookup(hash_table, key); - - heap[0] = make_arityval(2); - heap[1] = key; - heap[2] = term; - tuple = make_tuple(heap); + ctx->heap[0] = make_arityval(2); + ctx->heap[1] = ctx->key; + ctx->heap[2] = ctx->term; + ctx->tuple = make_tuple(ctx->heap); - if (is_nil(hash_table->term[entry_index])) { - Uint size = hash_table->allocated; - if (MUST_GROW(hash_table)) { - size *= 2; + if (is_nil(ctx->hash_table->term[ctx->entry_index])) { + Uint new_size = ctx->hash_table->allocated; + if (MUST_GROW(ctx->hash_table)) { + new_size *= 2; } - hash_table = copy_table(hash_table, size, 0); - entry_index = lookup(hash_table, key); - hash_table->num_entries++; + TRAPPING_COPY_TABLE_PUT(ctx->hash_table, + ctx->hash_table, + new_size, + ERTS_PERSISTENT_TERM_CPY_NO_REHASH, + PUT2_TRAP_LOCATION_NEW_KEY); + ctx->entry_index = lookup(ctx->hash_table, ctx->key); + ctx->hash_table->num_entries++; } else { - Eterm tuple = hash_table->term[entry_index]; + Eterm tuple = ctx->hash_table->term[ctx->entry_index]; Eterm old_term; ASSERT(is_tuple_arity(tuple, 2)); old_term = boxed_val(tuple)[2]; - if (EQ(term, old_term)) { + if (EQ(ctx->term, old_term)) { /* Same value. No need to update anything. */ release_update_permission(0); BIF_RET(am_ok); } else { /* Mark the old term for deletion. */ - mark_for_deletion(hash_table, entry_index); - hash_table = copy_table(hash_table, hash_table->allocated, 0); + mark_for_deletion(ctx->hash_table, ctx->entry_index); + TRAPPING_COPY_TABLE_PUT(ctx->hash_table, + ctx->hash_table, + ctx->hash_table->allocated, + ERTS_PERSISTENT_TERM_CPY_NO_REHASH, + PUT2_TRAP_LOCATION_REPLACE_VALUE); } } - /* - * Preserve internal sharing in the term by using the - * sharing-preserving functions. However, literals must - * be copied in case the module holding them are unloaded. - */ - INITIALIZE_SHCOPY(info); - info.copy_literals = 1; - term_size = copy_shared_calculate(tuple, &info); - ERTS_INIT_OFF_HEAP(&code_off_heap); - lit_area_size = ERTS_LITERAL_AREA_ALLOC_SIZE(term_size); - literal_area = erts_alloc(ERTS_ALC_T_LITERAL, lit_area_size); - ptr = &literal_area->start[0]; - literal_area->end = ptr + term_size; - tuple = copy_shared_perform(tuple, term_size, &info, &ptr, &code_off_heap); - ASSERT(tuple_val(tuple) == literal_area->start); - literal_area->off_heap = code_off_heap.first; - DESTROY_SHCOPY(info); - erts_set_literal_tag(&tuple, literal_area->start, term_size); - hash_table->term[entry_index] = tuple; - - erts_schedule_thr_prgr_later_op(table_updater, hash_table, &thr_prog_op); - suspend_updater(BIF_P); - - /* - * Issue a warning once if the warning limit has been exceeded. - */ - - if (hash_table->num_entries > WARNING_LIMIT && issued_warning == 0) { - static char w[] = - "More than " XSTR(WARNING_LIMIT) " persistent terms " - "have been created.\n" - "It is recommended to avoid creating an excessive number of\n" - "persistent terms, as creation and deletion of persistent terms\n" - "will be slower as the number of persistent terms increases.\n"; - issued_warning = 1; - erts_send_warning_to_logger_str(BIF_P->group_leader, w); + { + Uint term_size; + Uint lit_area_size; + ErlOffHeap code_off_heap; + ErtsLiteralArea* literal_area; + erts_shcopy_t info; + Eterm* ptr; + /* + * Preserve internal sharing in the term by using the + * sharing-preserving functions. However, literals must + * be copied in case the module holding them are unloaded. + */ + INITIALIZE_SHCOPY(info); + info.copy_literals = 1; + term_size = copy_shared_calculate(ctx->tuple, &info); + ERTS_INIT_OFF_HEAP(&code_off_heap); + lit_area_size = ERTS_LITERAL_AREA_ALLOC_SIZE(term_size); + literal_area = erts_alloc(ERTS_ALC_T_LITERAL, lit_area_size); + ptr = &literal_area->start[0]; + literal_area->end = ptr + term_size; + ctx->tuple = copy_shared_perform(ctx->tuple, term_size, &info, &ptr, &code_off_heap); + ASSERT(tuple_val(ctx->tuple) == literal_area->start); + literal_area->off_heap = code_off_heap.first; + DESTROY_SHCOPY(info); + erts_set_literal_tag(&ctx->tuple, literal_area->start, term_size); + ctx->hash_table->term[ctx->entry_index] = ctx->tuple; + + erts_schedule_thr_prgr_later_op(table_updater, ctx->hash_table, &thr_prog_op); + suspend_updater(BIF_P); } - + BUMP_REDS(BIF_P, (max_iterations - iterations_until_trap) / ITERATIONS_PER_RED); ERTS_BIF_YIELD_RETURN(BIF_P, am_ok); } @@ -349,26 +474,84 @@ BIF_RETTYPE persistent_term_get_2(BIF_ALIST_2) BIF_RET(result); } -BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1) +static int persistent_term_erase_1_ctx_bin_dtor(Binary *context_bin) { - Eterm key = BIF_ARG_1; - HashTable* old_table; - HashTable* new_table; - Uint entry_index; - Eterm old_term; + ErtsPersistentTermErase1Context* ctx = ERTS_MAGIC_BIN_DATA(context_bin); + if (ctx->cpy_ctx.new_table != NULL) { + if (ctx->cpy_ctx.copy_type == ERTS_PERSISTENT_TERM_CPY_TEMP) { + erts_free(ERTS_ALC_T_PERSISTENT_TERM_TMP, ctx->cpy_ctx.new_table); + } else { + erts_free(ERTS_ALC_T_PERSISTENT_TERM, ctx->cpy_ctx.new_table); + } + if (ctx->tmp_table != NULL) { + erts_free(ERTS_ALC_T_PERSISTENT_TERM_TMP, ctx->tmp_table); + } + release_update_permission(0); + } + return 1; +} +BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1) +{ + static const Uint ITERATIONS_PER_RED = 32; + ErtsPersistentTermErase1Context* ctx; + Eterm state_mref = THE_NON_VALUE; + long iterations_until_trap; + long max_iterations; +#ifdef DEBUG + (void)ITERATIONS_PER_RED; + iterations_until_trap = max_iterations = + GET_SMALL_RANDOM_INT(ERTS_BIF_REDS_LEFT(BIF_P) + (Uint)&ctx); +#else + iterations_until_trap = max_iterations = + ITERATIONS_PER_RED * ERTS_BIF_REDS_LEFT(BIF_P); +#endif +#define ERASE_TRAP_CODE \ + BIF_TRAP1(bif_export[BIF_persistent_term_erase_1], BIF_P, state_mref); +#define TRAPPING_COPY_TABLE_ERASE(TABLE_DEST, OLD_TABLE, NEW_SIZE, REHASH, LOC_NAME) \ + TRAPPING_COPY_TABLE(TABLE_DEST, OLD_TABLE, NEW_SIZE, REHASH, LOC_NAME, ERASE_TRAP_CODE) + if (is_internal_magic_ref(BIF_ARG_1) && + (ERTS_MAGIC_BIN_DESTRUCTOR(erts_magic_ref2bin(BIF_ARG_1)) == + persistent_term_erase_1_ctx_bin_dtor)) { + /* Restore the state after a trap */ + Binary* state_bin; + state_mref = BIF_ARG_1; + state_bin = erts_magic_ref2bin(state_mref); + ctx = ERTS_MAGIC_BIN_DATA(state_bin); + ASSERT(BIF_P->flags & F_DISABLE_GC); + erts_set_gc_state(BIF_P, 1); + switch (ctx->trap_location) { + case ERASE1_TRAP_LOCATION_TMP_COPY: + goto L_ERASE1_TRAP_LOCATION_TMP_COPY; + case ERASE1_TRAP_LOCATION_FINAL_COPY: + goto L_ERASE1_TRAP_LOCATION_FINAL_COPY; + } + } else { + /* Save state in magic bin in case trapping is necessary */ + Eterm* hp; + Binary* state_bin = erts_create_magic_binary(sizeof(ErtsPersistentTermErase1Context), + persistent_term_erase_1_ctx_bin_dtor); + hp = HAlloc(BIF_P, ERTS_MAGIC_REF_THING_SIZE); + state_mref = erts_mk_magic_ref(&hp, &MSO(BIF_P), state_bin); + ctx = ERTS_MAGIC_BIN_DATA(state_bin); + /* + * IMPORTANT: The following two fields are used to detect if + * persistent_term_erase_1_ctx_bin_dtor needs to free memory + */ + ctx->cpy_ctx.new_table = NULL; + ctx->tmp_table = NULL; + } if (!try_seize_update_permission(BIF_P)) { ERTS_BIF_YIELD1(bif_export[BIF_persistent_term_erase_1], BIF_P, BIF_ARG_1); } - old_table = (HashTable *) erts_atomic_read_nob(&the_hash_table); - entry_index = lookup(old_table, key); - old_term = old_table->term[entry_index]; - if (is_boxed(old_term)) { + ctx->key = BIF_ARG_1; + ctx->old_table = (HashTable *) erts_atomic_read_nob(&the_hash_table); + ctx->entry_index = lookup(ctx->old_table, ctx->key); + ctx->old_term = ctx->old_table->term[ctx->entry_index]; + if (is_boxed(ctx->old_term)) { Uint new_size; - HashTable* tmp_table; - /* * Since we don't use any delete markers, we must rehash * the table when deleting terms to ensure that all terms @@ -378,8 +561,12 @@ BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1) * temporary table copy of the same size as the old one. */ - ASSERT(is_tuple_arity(old_term, 2)); - tmp_table = tmp_table_copy(old_table); + ASSERT(is_tuple_arity(ctx->old_term, 2)); + TRAPPING_COPY_TABLE_ERASE(ctx->tmp_table, + ctx->old_table, + ctx->old_table->allocated, + ERTS_PERSISTENT_TERM_CPY_TEMP, + ERASE1_TRAP_LOCATION_TMP_COPY); /* * Delete the term from the temporary table. Then copy the @@ -387,18 +574,28 @@ BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1) * while copying. */ - tmp_table->term[entry_index] = NIL; - tmp_table->num_entries--; - new_size = tmp_table->allocated; - if (MUST_SHRINK(tmp_table)) { + ctx->tmp_table->term[ctx->entry_index] = NIL; + ctx->tmp_table->num_entries--; + new_size = ctx->tmp_table->allocated; + if (MUST_SHRINK(ctx->tmp_table)) { new_size /= 2; } - new_table = copy_table(tmp_table, new_size, 1); - erts_free(ERTS_ALC_T_TMP, tmp_table); + TRAPPING_COPY_TABLE_ERASE(ctx->new_table, + ctx->tmp_table, + new_size, + ERTS_PERSISTENT_TERM_CPY_REHASH, + ERASE1_TRAP_LOCATION_FINAL_COPY); + erts_free(ERTS_ALC_T_PERSISTENT_TERM_TMP, ctx->tmp_table); + /* + * IMPORTANT: Memory management depends on that ctx->tmp_table + * is set to NULL on the line below + */ + ctx->tmp_table = NULL; - mark_for_deletion(old_table, entry_index); - erts_schedule_thr_prgr_later_op(table_updater, new_table, &thr_prog_op); + mark_for_deletion(ctx->old_table, ctx->entry_index); + erts_schedule_thr_prgr_later_op(table_updater, ctx->new_table, &thr_prog_op); suspend_updater(BIF_P); + BUMP_REDS(BIF_P, (max_iterations - iterations_until_trap) / ITERATIONS_PER_RED); ERTS_BIF_YIELD_RETURN(BIF_P, am_true); } @@ -406,7 +603,7 @@ BIF_RETTYPE persistent_term_erase_1(BIF_ALIST_1) * Key is not present. Nothing to do. */ - ASSERT(is_nil(old_term)); + ASSERT(is_nil(ctx->old_term)); release_update_permission(0); BIF_RET(am_false); } @@ -740,65 +937,104 @@ lookup(HashTable* hash_table, Eterm key) } static HashTable* -tmp_table_copy(HashTable* old_table) +copy_table(ErtsPersistentTermCpyTableCtx* ctx) { - Uint size = old_table->allocated; - HashTable* tmp_table; + Uint old_size = ctx->old_table->allocated; Uint i; - - tmp_table = (HashTable *) erts_alloc(ERTS_ALC_T_TMP, - sizeof(HashTable) + - sizeof(Eterm) * (size-1)); - *tmp_table = *old_table; - for (i = 0; i < size; i++) { - tmp_table->term[i] = old_table->term[i]; + ErtsAlcType_t alloc_type; + ctx->total_iterations_done = 0; + switch(ctx->location) { + case ERTS_PERSISTENT_TERM_CPY_PLACE_1: goto L_copy_table_place_1; + case ERTS_PERSISTENT_TERM_CPY_PLACE_2: goto L_copy_table_place_2; + case ERTS_PERSISTENT_TERM_CPY_PLACE_3: goto L_copy_table_place_3; + case ERTS_PERSISTENT_TERM_CPY_PLACE_START: + ctx->iterations_done = 0; } - return tmp_table; -} - -static HashTable* -copy_table(HashTable* old_table, Uint new_size, int rehash) -{ - HashTable* new_table; - Uint old_size = old_table->allocated; - Uint i; - - new_table = (HashTable *) erts_alloc(ERTS_ALC_T_PERSISTENT_TERM, - sizeof(HashTable) + - sizeof(Eterm) * (new_size-1)); - if (old_table->allocated == new_size && !rehash) { + if (ctx->copy_type == ERTS_PERSISTENT_TERM_CPY_TEMP) { + alloc_type = ERTS_ALC_T_PERSISTENT_TERM_TMP; + } else { + alloc_type = ERTS_ALC_T_PERSISTENT_TERM; + } + ctx->new_table = (HashTable *) erts_alloc(alloc_type, + sizeof(HashTable) + + sizeof(Eterm) * (ctx->new_size-1)); + if (ctx->old_table->allocated == ctx->new_size && + (ctx->copy_type == ERTS_PERSISTENT_TERM_CPY_NO_REHASH || + ctx->copy_type == ERTS_PERSISTENT_TERM_CPY_TEMP)) { /* * Same size and no key deleted. Make an exact copy of the table. */ - *new_table = *old_table; - for (i = 0; i < new_size; i++) { - new_table->term[i] = old_table->term[i]; + *ctx->new_table = *ctx->old_table; + L_copy_table_place_1: + for (i = ctx->iterations_done; + i < MIN(ctx->iterations_done + ctx->max_iterations, + ctx->new_size); + i++) { + ctx->new_table->term[i] = ctx->old_table->term[i]; } + ctx->total_iterations_done = (i - ctx->iterations_done); + if (i < ctx->new_size) { + ctx->iterations_done = i; + ctx->location = ERTS_PERSISTENT_TERM_CPY_PLACE_1; + return NULL; + } + ctx->iterations_done = 0; } else { /* * The size of the table has changed or an element has been * deleted. Must rehash, by inserting all old terms into the * new (empty) table. */ - new_table->allocated = new_size; - new_table->num_entries = old_table->num_entries; - new_table->mask = new_size - 1; - for (i = 0; i < new_size; i++) { - new_table->term[i] = NIL; + ctx->new_table->allocated = ctx->new_size; + ctx->new_table->num_entries = ctx->old_table->num_entries; + ctx->new_table->mask = ctx->new_size - 1; + L_copy_table_place_2: + for (i = ctx->iterations_done; + i < MIN(ctx->iterations_done + ctx->max_iterations, + ctx->new_size); + i++) { + ctx->new_table->term[i] = NIL; + } + ctx->total_iterations_done = (i - ctx->iterations_done); + ctx->max_iterations -= ctx->total_iterations_done; + if (i < ctx->new_size) { + ctx->iterations_done = i; + ctx->location = ERTS_PERSISTENT_TERM_CPY_PLACE_2; + return NULL; } - for (i = 0; i < old_size; i++) { - if (is_tuple(old_table->term[i])) { - Eterm key = tuple_val(old_table->term[i])[1]; - Uint entry_index = lookup(new_table, key); - ASSERT(is_nil(new_table->term[entry_index])); - new_table->term[entry_index] = old_table->term[i]; + ctx->iterations_done = 0; + L_copy_table_place_3: + for (i = ctx->iterations_done; + i < MIN(ctx->iterations_done + ctx->max_iterations, + old_size); + i++) { + if (is_tuple(ctx->old_table->term[i])) { + Eterm key = tuple_val(ctx->old_table->term[i])[1]; + Uint entry_index = lookup(ctx->new_table, key); + ASSERT(is_nil(ctx->new_table->term[entry_index])); + ctx->new_table->term[entry_index] = ctx->old_table->term[i]; } } + ctx->total_iterations_done += (i - ctx->iterations_done); + if (i < old_size) { + ctx->iterations_done = i; + ctx->location = ERTS_PERSISTENT_TERM_CPY_PLACE_3; + return NULL; + } + ctx->iterations_done = 0; + } + ctx->new_table->first_to_delete = 0; + ctx->new_table->num_to_delete = 0; + erts_atomic_init_nob(&ctx->new_table->refc, (erts_aint_t)1); + { + HashTable* new_table = ctx->new_table; + /* + * IMPORTANT: Memory management depends on that ctx->new_table is + * set to NULL on the line below + */ + ctx->new_table = NULL; + return new_table; } - new_table->first_to_delete = 0; - new_table->num_to_delete = 0; - erts_atomic_init_nob(&new_table->refc, (erts_aint_t)1); - return new_table; } static void diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c index 6f8d2f8c35..6a4f43297e 100644 --- a/erts/emulator/beam/erl_cpu_topology.c +++ b/erts/emulator/beam/erl_cpu_topology.c @@ -34,6 +34,7 @@ #include "error.h" #include "bif.h" #include "erl_cpu_topology.h" +#include "erl_flxctr.h" #define ERTS_MAX_READER_GROUPS 64 @@ -58,6 +59,7 @@ static erts_cpu_info_t *cpuinfo; static int max_main_threads; static int reader_groups; +static int decentralized_counter_groups; static ErtsCpuBindData *scheduler2cpu_map; static erts_rwmtx_t cpuinfo_rwmtx; @@ -127,6 +129,8 @@ static erts_cpu_groups_map_t *cpu_groups_maps; static erts_cpu_groups_map_t *reader_groups_map; +static erts_cpu_groups_map_t *decentralized_counter_groups_map; + #define ERTS_TOPOLOGY_CG ERTS_TOPOLOGY_MAX_DEPTH #define ERTS_MAX_CPU_TOPOLOGY_ID ((int) 0xffff) @@ -138,6 +142,7 @@ static void cpu_bind_order_sort(erts_cpu_topology_t *cpudata, static void write_schedulers_bind_change(erts_cpu_topology_t *cpudata, int size); static void reader_groups_callback(int, ErtsSchedulerData *, int, void *); +static void flxctr_groups_callback(int, ErtsSchedulerData *, int, void *); static erts_cpu_groups_map_t *add_cpu_groups(int groups, erts_cpu_groups_callback_t callback, void *arg); @@ -1646,7 +1651,8 @@ erts_get_logical_processors(int *conf, int *onln, int *avail) } void -erts_pre_early_init_cpu_topology(int *max_rg_p, +erts_pre_early_init_cpu_topology(int *max_dcg_p, + int *max_rg_p, int *conf_p, int *onln_p, int *avail_p) @@ -1654,6 +1660,7 @@ erts_pre_early_init_cpu_topology(int *max_rg_p, cpu_groups_maps = NULL; no_cpu_groups_callbacks = 0; *max_rg_p = ERTS_MAX_READER_GROUPS; + *max_dcg_p = ERTS_MAX_FLXCTR_GROUPS; cpuinfo = erts_cpu_info_create(); get_logical_processors(conf_p, onln_p, avail_p); } @@ -1662,7 +1669,9 @@ void erts_early_init_cpu_topology(int no_schedulers, int *max_main_threads_p, int max_reader_groups, - int *reader_groups_p) + int *reader_groups_p, + int max_decentralized_counter_groups, + int *decentralized_counter_groups_p) { user_cpudata = NULL; user_cpudata_size = 0; @@ -1687,6 +1696,12 @@ erts_early_init_cpu_topology(int no_schedulers, max_main_threads = no_schedulers; *max_main_threads_p = max_main_threads; + decentralized_counter_groups = max_main_threads; + if (decentralized_counter_groups <= 1 || max_decentralized_counter_groups <= 1) + decentralized_counter_groups = 1; + if (decentralized_counter_groups > max_decentralized_counter_groups) + decentralized_counter_groups = max_decentralized_counter_groups; + *decentralized_counter_groups_p = decentralized_counter_groups; reader_groups = max_main_threads; if (reader_groups <= 1 || max_reader_groups <= 1) reader_groups = 0; @@ -1718,6 +1733,9 @@ erts_init_cpu_topology(void) reader_groups_map = add_cpu_groups(reader_groups, reader_groups_callback, NULL); + decentralized_counter_groups_map = add_cpu_groups(decentralized_counter_groups, + flxctr_groups_callback, + NULL); if (cpu_bind_order == ERTS_CPU_BIND_NONE) erts_rwmtx_rwunlock(&cpuinfo_rwmtx); @@ -1789,6 +1807,15 @@ reader_groups_callback(int suspending, erts_rwmtx_set_reader_group(suspending ? 0 : group+1); } +void +flxctr_groups_callback(int suspending, + ErtsSchedulerData *esdp, + int group, + void *unused) +{ + erts_flxctr_set_slot(suspending ? 0 : group+1); +} + static Eterm get_cpu_groups_map(Process *c_p, erts_cpu_groups_map_t *map, int offset); @@ -1821,6 +1848,16 @@ erts_get_reader_groups_map(Process *c_p) return res; } +Eterm +erts_get_decentralized_counter_groups_map(Process *c_p) +{ + Eterm res; + erts_rwmtx_rlock(&cpuinfo_rwmtx); + res = get_cpu_groups_map(c_p, decentralized_counter_groups_map, 1); + erts_rwmtx_runlock(&cpuinfo_rwmtx); + return res; +} + /* * CPU groups */ diff --git a/erts/emulator/beam/erl_cpu_topology.h b/erts/emulator/beam/erl_cpu_topology.h index 88bcad79ab..4a428d7972 100644 --- a/erts/emulator/beam/erl_cpu_topology.h +++ b/erts/emulator/beam/erl_cpu_topology.h @@ -27,14 +27,19 @@ #ifndef ERL_CPU_TOPOLOGY_H__ #define ERL_CPU_TOPOLOGY_H__ -void erts_pre_early_init_cpu_topology(int *max_rg_p, - int *conf_p, - int *onln_p, - int *avail_p); -void erts_early_init_cpu_topology(int no_schedulers, - int *max_main_threads_p, - int max_reader_groups, - int *reader_groups_p); +void +erts_pre_early_init_cpu_topology(int *max_dcg_p, + int *max_rg_p, + int *conf_p, + int *onln_p, + int *avail_p); +void +erts_early_init_cpu_topology(int no_schedulers, + int *max_main_threads_p, + int max_reader_groups, + int *reader_groups_p, + int max_decentralized_counter_groups, + int *decentralized_counter_groups_p); void erts_init_cpu_topology(void); @@ -70,6 +75,7 @@ Eterm erts_bind_schedulers(Process *c_p, Eterm how); Eterm erts_get_schedulers_binds(Process *c_p); Eterm erts_get_reader_groups_map(Process *c_p); +Eterm erts_get_decentralized_counter_groups_map(Process *c_p); Eterm erts_set_cpu_topology(Process *c_p, Eterm term); Eterm erts_get_cpu_topology_term(Process *c_p, Eterm which); diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 0a50af4d1a..d24f30f126 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -42,6 +42,7 @@ #include "bif.h" #include "big.h" #include "erl_binary.h" +#include "bif.h" erts_atomic_t erts_ets_misc_mem_size; @@ -64,6 +65,11 @@ do { \ } \ }while(0) +#define DB_GET_APPROX_NITEMS(DB) \ + erts_flxctr_read_approx(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID) +#define DB_GET_APPROX_MEM_CONSUMED(DB) \ + erts_flxctr_read_approx(&(DB)->common.counters, ERTS_DB_TABLE_MEM_COUNTER_ID) + static BIF_RETTYPE db_bif_fail(Process* p, Uint freason, Uint bif_ix, Export* bif_exp) { @@ -398,8 +404,9 @@ static void free_dbtable(void *vtb) { DbTable *tb = (DbTable *) vtb; - - ASSERT(erts_atomic_read_nob(&tb->common.memory_size) == sizeof(DbTable)); + ASSERT(erts_flxctr_is_snapshot_ongoing(&tb->common.counters) || + sizeof(DbTable) == erts_flxctr_read_approx(&tb->common.counters, + ERTS_DB_TABLE_MEM_COUNTER_ID)); erts_rwmtx_destroy(&tb->common.rwlock); erts_mtx_destroy(&tb->common.fixlock); @@ -408,7 +415,8 @@ free_dbtable(void *vtb) if (tb->common.btid) erts_bin_release(tb->common.btid); - erts_db_free(ERTS_ALC_T_DB_TABLE, tb, (void *) tb, sizeof(DbTable)); + erts_flxctr_destroy(&tb->common.counters, ERTS_ALC_T_DB_TABLE); + erts_free(ERTS_ALC_T_DB_TABLE, tb); } static void schedule_free_dbtable(DbTable* tb) @@ -1731,12 +1739,16 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2) */ { DbTable init_tb; - - erts_atomic_init_nob(&init_tb.common.memory_size, 0); + erts_flxctr_init(&init_tb.common.counters, 0, 2, ERTS_ALC_T_DB_TABLE); tb = (DbTable*) erts_db_alloc(ERTS_ALC_T_DB_TABLE, &init_tb, sizeof(DbTable)); - erts_atomic_init_nob(&tb->common.memory_size, - erts_atomic_read_nob(&init_tb.common.memory_size)); + erts_flxctr_init(&tb->common.counters, + status & DB_CA_ORDERED_SET, + 2, + ERTS_ALC_T_DB_TABLE); + erts_flxctr_add(&tb->common.counters, + ERTS_DB_TABLE_MEM_COUNTER_ID, + DB_GET_APPROX_MEM_CONSUMED(&init_tb)); } tb->common.meth = meth; @@ -1750,8 +1762,6 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2) tb->common.owner = BIF_P->common.id; set_heir(BIF_P, tb, heir, heir_data); - erts_atomic_init_nob(&tb->common.nitems, 0); - tb->common.fixing_procs = NULL; tb->common.compress = is_compressed; #ifdef ETS_DBG_FORCE_TRAP @@ -2128,19 +2138,18 @@ BIF_RETTYPE ets_internal_delete_all_2(BIF_ALIST_2) { SWord initial_reds = ERTS_BIF_REDS_LEFT(BIF_P); SWord reds = initial_reds; - Eterm nitems; + Eterm nitems_holder = THE_NON_VALUE; DbTable* tb; - CHECK_TABLES(); DB_BIF_GET_TABLE(tb, DB_WRITE, LCK_WRITE, BIF_ets_internal_delete_all_2); if (BIF_ARG_2 == am_undefined) { - nitems = erts_make_integer(erts_atomic_read_nob(&tb->common.nitems), - BIF_P); - - reds = tb->common.meth->db_delete_all_objects(BIF_P, tb, reds); - + reds = tb->common.meth->db_delete_all_objects(BIF_P, + tb, + reds, + &nitems_holder); + ASSERT(nitems_holder != THE_NON_VALUE); ASSERT(!(tb->common.status & DB_BUSY)); if (reds < 0) { @@ -2159,7 +2168,7 @@ BIF_RETTYPE ets_internal_delete_all_2(BIF_ALIST_2) db_unlock(tb, LCK_WRITE); BUMP_ALL_REDS(BIF_P); BIF_TRAP2(bif_export[BIF_ets_internal_delete_all_2], BIF_P, - BIF_ARG_1, nitems); + BIF_ARG_1, nitems_holder); } else { /* Done, no trapping needed */ @@ -2169,15 +2178,19 @@ BIF_RETTYPE ets_internal_delete_all_2(BIF_ALIST_2) } else { /* - * The table lookup succeeded and second argument is nitems + * The table lookup succeeded and second argument is nitems_holder * and not 'undefined', which means we have trapped at least once * and are now done. */ - nitems = BIF_ARG_2; + nitems_holder = BIF_ARG_2; } - db_unlock(tb, LCK_WRITE); + { + Eterm nitems = + tb->common.meth->db_delete_all_objects_get_nitems_from_holder(BIF_P, + nitems_holder); BIF_RET(nitems); + } } static void delete_all_objects_continue(Process* p, DbTable* tb) @@ -2190,7 +2203,7 @@ static void delete_all_objects_continue(Process* p, DbTable* tb) if ((tb->common.status & (DB_DELETE|DB_BUSY)) != DB_BUSY) return; - reds = tb->common.meth->db_delete_all_objects(p, tb, reds); + reds = tb->common.meth->db_delete_all_objects(p, tb, reds, NULL); if (reds < 0) { BUMP_ALL_REDS(p); @@ -3277,13 +3290,29 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1) int i; Eterm* hp; Uint freason; + Sint size = -1; + Sint memory = -1; + Eterm table; + int is_ctrs_read_result_set = 0; /*Process* rp = NULL;*/ /* If/when we implement lockless private tables: Eterm owner; */ - - if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_INFO, LCK_READ, &freason)) == NULL) { - if (freason == BADARG && (is_atom(BIF_ARG_1) || is_ref(BIF_ARG_1))) + if(is_tuple(BIF_ARG_1) && + is_tuple_arity(BIF_ARG_1, 2) && + erts_flxctr_is_snapshot_result(tuple_val(BIF_ARG_1)[1])) { + Eterm counter_read_result = tuple_val(BIF_ARG_1)[1]; + table = tuple_val(BIF_ARG_1)[2]; + size = erts_flxctr_get_snapshot_result_after_trap(counter_read_result, + ERTS_DB_TABLE_NITEMS_COUNTER_ID); + memory = erts_flxctr_get_snapshot_result_after_trap(counter_read_result, + ERTS_DB_TABLE_MEM_COUNTER_ID); + is_ctrs_read_result_set = 1; + } else { + table = BIF_ARG_1; + } + if ((tb = db_get_table(BIF_P, table, DB_INFO, LCK_READ, &freason)) == NULL) { + if (freason == BADARG && (is_atom(table) || is_ref(table))) BIF_RET(am_undefined); else return db_bif_fail(BIF_P, freason, BIF_ets_info_1, NULL); @@ -3314,9 +3343,35 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1) BIF_ERROR(BIF_P, BADARG); } }*/ + + if (!is_ctrs_read_result_set) { + ErtsFlxCtrSnapshotResult res = + erts_flxctr_snapshot(&tb->common.counters, ERTS_ALC_T_DB_TABLE, BIF_P); + if (ERTS_FLXCTR_GET_RESULT_AFTER_TRAP == res.type) { + Eterm tuple; + db_unlock(tb, LCK_READ); + hp = HAlloc(BIF_P, 3); + tuple = TUPLE2(hp, res.trap_resume_state, table); + BIF_TRAP1(bif_export[BIF_ets_info_1], BIF_P, tuple); + } else if (res.type == ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP) { + db_unlock(tb, LCK_READ); + BIF_TRAP1(bif_export[BIF_ets_info_1], BIF_P, table); + } else { + size = res.result[ERTS_DB_TABLE_NITEMS_COUNTER_ID]; + memory = res.result[ERTS_DB_TABLE_MEM_COUNTER_ID]; + is_ctrs_read_result_set = 1; + } + } for (i = 0; i < sizeof(fields)/sizeof(Eterm); i++) { - results[i] = table_info(BIF_P, tb, fields[i]); - ASSERT(is_value(results[i])); + if (is_ctrs_read_result_set && am_size == fields[i]) { + results[i] = erts_make_integer(size, BIF_P); + } else if (is_ctrs_read_result_set && am_memory == fields[i]) { + Sint words = (Sint) ((memory + sizeof(Sint) - 1) / sizeof(Sint)); + results[i] = erts_make_integer(words, BIF_P); + } else { + results[i] = table_info(BIF_P, tb, fields[i]); + ASSERT(is_value(results[i])); + } } db_unlock(tb, LCK_READ); @@ -3344,14 +3399,43 @@ BIF_RETTYPE ets_info_2(BIF_ALIST_2) DbTable* tb; Eterm ret = THE_NON_VALUE; Uint freason; - + if (erts_flxctr_is_snapshot_result(BIF_ARG_1)) { + Sint res; + if (am_memory == BIF_ARG_2) { + res = erts_flxctr_get_snapshot_result_after_trap(BIF_ARG_1, + ERTS_DB_TABLE_MEM_COUNTER_ID); + res = (Sint) ((res + sizeof(Sint) - 1) / sizeof(Sint)); + } else { + res = erts_flxctr_get_snapshot_result_after_trap(BIF_ARG_1, + ERTS_DB_TABLE_NITEMS_COUNTER_ID); + } + BIF_RET(erts_make_integer(res, BIF_P)); + } if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_INFO, LCK_READ, &freason)) == NULL) { if (freason == BADARG && (is_atom(BIF_ARG_1) || is_ref(BIF_ARG_1))) BIF_RET(am_undefined); else return db_bif_fail(BIF_P, freason, BIF_ets_info_2, NULL); } - ret = table_info(BIF_P, tb, BIF_ARG_2); + if (BIF_ARG_2 == am_size || BIF_ARG_2 == am_memory) { + ErtsFlxCtrSnapshotResult res = + erts_flxctr_snapshot(&tb->common.counters, ERTS_ALC_T_DB_TABLE, BIF_P); + if (ERTS_FLXCTR_GET_RESULT_AFTER_TRAP == res.type) { + db_unlock(tb, LCK_READ); + BIF_TRAP2(bif_export[BIF_ets_info_2], BIF_P, res.trap_resume_state, BIF_ARG_2); + } else if (res.type == ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP) { + db_unlock(tb, LCK_READ); + BIF_TRAP2(bif_export[BIF_ets_info_2], BIF_P, BIF_ARG_1, BIF_ARG_2); + } else if (BIF_ARG_2 == am_size) { + ret = erts_make_integer(res.result[ERTS_DB_TABLE_NITEMS_COUNTER_ID], BIF_P); + } else { /* BIF_ARG_2 == am_memory */ + Sint r = res.result[ERTS_DB_TABLE_MEM_COUNTER_ID]; + r = (Sint) ((r + sizeof(Sint) - 1) / sizeof(Sint)); + ret = erts_make_integer(r, BIF_P); + } + } else { + ret = table_info(BIF_P, tb, BIF_ARG_2); + } db_unlock(tb, LCK_READ); if (is_non_value(ret)) { BIF_ERROR(BIF_P, BADARG); @@ -4121,7 +4205,8 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What) int use_monotonic; if (What == am_size) { - ret = make_small(erts_atomic_read_nob(&tb->common.nitems)); + Uint size = (Uint) (DB_GET_APPROX_NITEMS(tb)); + ret = erts_make_integer(size, p); } else if (What == am_type) { if (tb->common.status & DB_SET) { ret = am_set; @@ -4136,7 +4221,7 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What) ret = am_bag; } } else if (What == am_memory) { - Uint words = (Uint) ((erts_atomic_read_nob(&tb->common.memory_size) + Uint words = (Uint) ((DB_GET_APPROX_MEM_CONSUMED(tb) + sizeof(Uint) - 1) / sizeof(Uint)); @@ -4294,9 +4379,9 @@ static void print_table(fmtfn_t to, void *to_arg, int show, DbTable* tb) tb->common.meth->db_print(to, to_arg, show, tb); - erts_print(to, to_arg, "Objects: %d\n", (int)erts_atomic_read_nob(&tb->common.nitems)); + erts_print(to, to_arg, "Objects: %d\n", (int)DB_GET_APPROX_NITEMS(tb)); erts_print(to, to_arg, "Words: %bpu\n", - (Uint) ((erts_atomic_read_nob(&tb->common.memory_size) + (Uint) ((DB_GET_APPROX_MEM_CONSUMED(tb) + sizeof(Uint) - 1) / sizeof(Uint))); @@ -4488,3 +4573,16 @@ int erts_ets_force_split(Eterm tid, int on) db_unlock(tb, LCK_WRITE); return 1; } + +int erts_ets_debug_random_split_join(Eterm tid, int on) +{ + DbTable* tb = tid2tab(tid); + if (!tb || !IS_CATREE_TABLE(tb->common.type)) + return 0; + + db_lock(tb, LCK_WRITE); + if (!(tb->common.status & DB_DELETE)) + db_catree_debug_random_split_join(&tb->catree, on); + db_unlock(tb, LCK_WRITE); + return 1; +} diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h index dc77fbb60c..b3dc1b9ba3 100644 --- a/erts/emulator/beam/erl_db.h +++ b/erts/emulator/beam/erl_db.h @@ -131,6 +131,7 @@ extern erts_atomic_t erts_ets_misc_mem_size; Eterm erts_ets_colliding_names(Process*, Eterm name, Uint cnt); int erts_ets_force_split(Eterm tid, int on); +int erts_ets_debug_random_split_join(Eterm tid, int on); Uint erts_db_get_max_tabs(void); Eterm erts_db_make_tid(Process *c_p, DbTableCommon *tb); @@ -160,7 +161,9 @@ do { \ erts_aint_t sz__ = (((erts_aint_t) (ALLOC_SZ)) \ - ((erts_aint_t) (FREE_SZ))); \ ASSERT((TAB)); \ - erts_atomic_add_nob(&(TAB)->common.memory_size, sz__); \ + erts_flxctr_add(&(TAB)->common.counters, \ + ERTS_DB_TABLE_MEM_COUNTER_ID, \ + sz__); \ } while (0) #define ERTS_ETS_MISC_MEM_ADD(SZ) \ @@ -305,10 +308,10 @@ erts_db_free(ErtsAlcType_t type, DbTable *tab, void *ptr, Uint size) ASSERT(ptr != 0); ASSERT(size == ERTS_ALC_DBG_BLK_SZ(ptr)); ERTS_DB_ALC_MEM_UPDATE_(tab, size, 0); - - ASSERT(((void *) tab) != ptr - || erts_atomic_read_nob(&tab->common.memory_size) == 0); - + ASSERT(((void *) tab) != ptr || + tab->common.counters.is_decentralized || + 0 == erts_flxctr_read_centralized(&tab->common.counters, + ERTS_DB_TABLE_MEM_COUNTER_ID)); erts_free(type, ptr); } diff --git a/erts/emulator/beam/erl_db_catree.c b/erts/emulator/beam/erl_db_catree.c index 0402c6b7b4..e0d5e44f58 100644 --- a/erts/emulator/beam/erl_db_catree.c +++ b/erts/emulator/beam/erl_db_catree.c @@ -149,7 +149,12 @@ static SWord db_free_table_continue_catree(DbTable *tbl, SWord); static void db_foreach_offheap_catree(DbTable *, void (*)(ErlOffHeap *, void *), void *); -static SWord db_delete_all_objects_catree(Process* p, DbTable* tbl, SWord reds); +static SWord db_delete_all_objects_catree(Process* p, + DbTable* tbl, + SWord reds, + Eterm* nitems_holder_wb); +static Eterm db_delete_all_objects_get_nitems_from_holder_catree(Process* p, + Eterm nitems_holder); static int db_lookup_dbterm_catree(Process *, DbTable *, Eterm key, Eterm obj, DbUpdateHandle*); @@ -191,6 +196,7 @@ DbTableMethod db_catree = db_select_replace_continue_catree, db_take_catree, db_delete_all_objects_catree, + db_delete_all_objects_get_nitems_from_holder_catree, db_free_table_catree, db_free_table_continue_catree, db_print_catree, @@ -641,7 +647,8 @@ static int dbg_fastrand(void) static void dbg_provoke_random_splitjoin(DbTableCATree* tb, DbTableCATreeNode* base_node) { - if (tb->common.status & DB_CATREE_FORCE_SPLIT) + if (tb->common.status & DB_CATREE_FORCE_SPLIT || + !(tb->common.status & DB_CATREE_DEBUG_RANDOM_SPLIT_JOIN)) return; switch (dbg_fastrand() % 8) { @@ -1357,6 +1364,8 @@ static SWord do_free_base_node_cont(DbTableCATree *tb, SWord num_left) PUSH_NODE(&tb->free_stack_elems, root); root = p; } else { + DEC_NITEMS((DbTable*)tb); + tb->nr_of_deleted_items++; free_term((DbTable*)tb, root); if (--num_left >= 0) { break; @@ -1397,6 +1406,10 @@ int db_create_catree(Process *p, DbTable *tbl) root = create_base_node(tb, NULL); tb->deletion = 0; tb->base_nodes_to_free_list = NULL; + tb->nr_of_deleted_items = 0; +#ifdef DEBUG + tbl->common.status |= DB_CATREE_DEBUG_RANDOM_SPLIT_JOIN; +#endif erts_atomic_init_relb(&(tb->root), (erts_aint_t)root); return DB_ERROR_NONE; } @@ -2050,6 +2063,7 @@ static SWord db_free_table_continue_catree(DbTable *tbl, SWord reds) PUSH_NODE(&tb->free_stack_rnodes, GET_ROOT(tb)); tb->is_routing_nodes_freed = 0; tb->base_nodes_to_free_list = NULL; + tb->nr_of_deleted_items = 0; } if ( ! tb->is_routing_nodes_freed ) { reds = do_free_routing_nodes_catree_cont(tb, reds); @@ -2079,13 +2093,57 @@ static SWord db_free_table_continue_catree(DbTable *tbl, SWord reds) return 1; } -static SWord db_delete_all_objects_catree(Process* p, DbTable* tbl, SWord reds) +static +int db_catree_nr_of_items_deleted_wb_dtor(Binary *context_bin) { + (void)context_bin; + return 1; +} + +typedef struct { + Uint nr_of_deleted_items; +} DbCATreeNrOfItemsDeletedWb; + +static Eterm +create_and_install_num_of_deleted_items_wb_bin(Process *p, DbTableCATree *tb) +{ + Binary* bin = + erts_create_magic_binary(sizeof(DbCATreeNrOfItemsDeletedWb), + db_catree_nr_of_items_deleted_wb_dtor); + DbCATreeNrOfItemsDeletedWb* data = ERTS_MAGIC_BIN_DATA(bin); + Eterm* hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE); + Eterm mref = erts_mk_magic_ref(&hp, &MSO(p), bin); + data->nr_of_deleted_items = 0; + tb->nr_of_deleted_items_wb = bin; + erts_refc_inctest(&bin->intern.refc, 2); + return mref; +} + +static Eterm db_delete_all_objects_get_nitems_from_holder_catree(Process* p, + Eterm mref) +{ + Binary* bin = erts_magic_ref2bin(mref); + DbCATreeNrOfItemsDeletedWb* data = ERTS_MAGIC_BIN_DATA(bin); + return erts_make_integer(data->nr_of_deleted_items, p); +} + +static SWord db_delete_all_objects_catree(Process* p, + DbTable* tbl, + SWord reds, + Eterm* nitems_holder_wb) { + DbTableCATree *tb = &tbl->catree; + DbCATreeNrOfItemsDeletedWb* data; + if (!tb->deletion) { + *nitems_holder_wb = + create_and_install_num_of_deleted_items_wb_bin(p, tb); + } reds = db_free_table_continue_catree(tbl, reds); if (reds < 0) return reds; + data = ERTS_MAGIC_BIN_DATA(tb->nr_of_deleted_items_wb); + data->nr_of_deleted_items = tb->nr_of_deleted_items; + erts_bin_release(tb->nr_of_deleted_items_wb); db_create_catree(p, tbl); - erts_atomic_set_nob(&tbl->catree.common.nitems, 0); return reds; } @@ -2203,6 +2261,14 @@ void db_catree_force_split(DbTableCATree* tb, int on) tb->common.status &= ~DB_CATREE_FORCE_SPLIT; } +void db_catree_debug_random_split_join(DbTableCATree* tb, int on) +{ + if (on) + tb->common.status |= DB_CATREE_DEBUG_RANDOM_SPLIT_JOIN; + else + tb->common.status &= ~DB_CATREE_DEBUG_RANDOM_SPLIT_JOIN; +} + void db_calc_stats_catree(DbTableCATree* tb, DbCATreeStats* stats) { DbTableCATreeNode* stack[ERL_DB_CATREE_MAX_ROUTE_NODE_LAYER_HEIGHT]; diff --git a/erts/emulator/beam/erl_db_catree.h b/erts/emulator/beam/erl_db_catree.h index 418837be8e..cf3498dabb 100644 --- a/erts/emulator/beam/erl_db_catree.h +++ b/erts/emulator/beam/erl_db_catree.h @@ -87,6 +87,10 @@ typedef struct db_table_catree { CATreeNodeStack free_stack_rnodes; DbTableCATreeNode *base_nodes_to_free_list; int is_routing_nodes_freed; + /* The fields below are used by delete_all_objects and + select_delete(DeleteAll)*/ + Uint nr_of_deleted_items; + Binary* nr_of_deleted_items_wb; } DbTableCATree; typedef struct { @@ -104,7 +108,6 @@ void db_initialize_catree(void); int db_create_catree(Process *p, DbTable *tbl); - TreeDbTerm** catree_find_root(Eterm key, CATreeRootIterator*); TreeDbTerm** catree_find_next_from_pb_key_root(Eterm key, CATreeRootIterator*); @@ -121,6 +124,7 @@ void erts_lcnt_enable_db_catree_lock_count(DbTableCATree *tb, int enable); #endif void db_catree_force_split(DbTableCATree*, int on); +void db_catree_debug_random_split_join(DbTableCATree*, int on); typedef struct { Uint route_nodes; diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index f225730029..ceaccf7e44 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -85,6 +85,14 @@ #include "erl_db_hash.h" +#define ADD_NITEMS(DB, TO_ADD) \ + erts_flxctr_add(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID, TO_ADD) +#define INC_NITEMS(DB) \ + erts_flxctr_inc_read_centralized(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID) +#define DEC_NITEMS(DB) \ + erts_flxctr_dec_read_centralized(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID) +#define RESET_NITEMS(DB) \ + erts_flxctr_reset(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID) /* * The following symbols can be manipulated to "tune" the linear hash array */ @@ -121,7 +129,9 @@ : ((struct segment**) erts_atomic_read_nob(&(tb)->segtab))) #endif #define NACTIVE(tb) ((int)erts_atomic_read_nob(&(tb)->nactive)) -#define NITEMS(tb) ((int)erts_atomic_read_nob(&(tb)->common.nitems)) +#define NITEMS(tb) \ + ((Sint)erts_flxctr_read_centralized(&(tb)->common.counters, \ + ERTS_DB_TABLE_NITEMS_COUNTER_ID)) #define SLOT_IX_TO_SEG_IX(i) (((i)+(EXT_SEGSZ-FIRST_SEGSZ)) >> EXT_SEGSZ_EXP) @@ -444,7 +454,12 @@ static void db_foreach_offheap_hash(DbTable *, void (*)(ErlOffHeap *, void *), void *); -static SWord db_delete_all_objects_hash(Process* p, DbTable* tbl, SWord reds); +static SWord db_delete_all_objects_hash(Process* p, + DbTable* tbl, + SWord reds, + Eterm* nitems_holder_wb); +static Eterm db_delete_all_objects_get_nitems_from_holder_hash(Process* p, + Eterm nitems_holder); #ifdef HARDDEBUG static void db_check_table_hash(DbTableHash *tb); #endif @@ -548,6 +563,7 @@ DbTableMethod db_hash = db_select_replace_continue_hash, db_take_hash, db_delete_all_objects_hash, + db_delete_all_objects_get_nitems_from_holder_hash, db_free_empty_table_hash, db_free_table_continue_hash, db_print_hash, @@ -806,7 +822,7 @@ int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail) if (tb->common.status & DB_SET) { HashDbTerm* bnext = b->next; if (is_pseudo_deleted(b)) { - erts_atomic_inc_nob(&tb->common.nitems); + INC_NITEMS(tb); b->pseudo_deleted = 0; } else if (key_clash_fail) { @@ -835,7 +851,7 @@ int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail) do { if (db_eq(&tb->common,obj,&q->dbterm)) { if (is_pseudo_deleted(q)) { - erts_atomic_inc_nob(&tb->common.nitems); + INC_NITEMS(tb); q->pseudo_deleted = 0; ASSERT(q->hvalue == hval); if (q != b) { /* must move to preserve key insertion order */ @@ -858,7 +874,7 @@ Lnew: q->pseudo_deleted = 0; q->next = b; *bp = q; - nitems = erts_atomic_inc_read_nob(&tb->common.nitems); + nitems = INC_NITEMS(tb); WUNLOCK_HASH(lck); { int nactive = NACTIVE(tb); @@ -1056,7 +1072,7 @@ int db_erase_hash(DbTable *tbl, Eterm key, Eterm *ret) } WUNLOCK_HASH(lck); if (nitems_diff) { - erts_atomic_add_nob(&tb->common.nitems, nitems_diff); + ADD_NITEMS(tb, nitems_diff); try_shrink(tb); } free_term_list(tb, free_us); @@ -1117,7 +1133,7 @@ static int db_erase_object_hash(DbTable *tbl, Eterm object, Eterm *ret) } WUNLOCK_HASH(lck); if (nitems_diff) { - erts_atomic_add_nob(&tb->common.nitems, nitems_diff); + ADD_NITEMS(tb, nitems_diff); try_shrink(tb); } free_term_list(tb, free_us); @@ -2023,7 +2039,7 @@ static int select_delete_on_match_res(traverse_context_t* ctx_base, Sint slot_ix del->next = ctx->free_us; ctx->free_us = del; } - erts_atomic_dec_nob(&ctx->base.tb->common.nitems); + DEC_NITEMS(ctx->base.tb); return 1; } @@ -2300,7 +2316,7 @@ static int db_take_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret) } WUNLOCK_HASH(lck); if (nitems_diff) { - erts_atomic_add_nob(&tb->common.nitems, nitems_diff); + ADD_NITEMS(tb, nitems_diff); try_shrink(tb); } free_term_list(tb, free_us); @@ -2360,7 +2376,7 @@ static SWord db_mark_all_deleted_hash(DbTable *tbl, SWord reds) fixdel->slot = NACTIVE(tb) - 1; fixdel->all = 1; fixdel->trap = 0; - erts_atomic_set_nob(&tb->common.nitems, 0); + RESET_NITEMS(tb); return loops < 0 ? 0 : loops / LOOPS_PER_REDUCTION; } @@ -2468,7 +2484,8 @@ static SWord db_free_table_continue_hash(DbTable *tbl, SWord reds) (void*)tb->locks, sizeof(DbTableHashFineLocks)); tb->locks = NULL; } - ASSERT(erts_atomic_read_nob(&tb->common.memory_size) == sizeof(DbTable)); + ASSERT(sizeof(DbTable) == erts_flxctr_read_approx(&tb->common.counters, + ERTS_DB_TABLE_MEM_COUNTER_ID)); return reds; /* Done */ } @@ -3080,7 +3097,7 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj, ASSERT(q->hvalue == hval); q->pseudo_deleted = 0; *bp = b = q; - erts_atomic_inc_nob(&tb->common.nitems); + INC_NITEMS(tb); } HRelease(p, hend, htop); @@ -3123,7 +3140,7 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle) } WUNLOCK_HASH(lck); - erts_atomic_dec_nob(&tb->common.nitems); + DEC_NITEMS(tb); try_shrink(tb); } else { if (handle->flags & DB_MUST_RESIZE) { @@ -3132,7 +3149,7 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle) } if (handle->flags & DB_INC_TRY_GROW) { int nactive; - int nitems = erts_atomic_inc_read_nob(&tb->common.nitems); + int nitems = INC_NITEMS(tb); WUNLOCK_HASH(lck); nactive = NACTIVE(tb); @@ -3153,8 +3170,17 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle) return; } -static SWord db_delete_all_objects_hash(Process* p, DbTable* tbl, SWord reds) +static SWord db_delete_all_objects_hash(Process* p, + DbTable* tbl, + SWord reds, + Eterm* nitems_holder_wb) { + if (nitems_holder_wb != NULL) { + Uint nr_of_items = + erts_flxctr_read_centralized(&tbl->common.counters, + ERTS_DB_TABLE_NITEMS_COUNTER_ID); + *nitems_holder_wb = erts_make_integer(nr_of_items, p); + } if (IS_FIXED(tbl)) { reds = db_mark_all_deleted_hash(tbl, reds); } else { @@ -3163,11 +3189,16 @@ static SWord db_delete_all_objects_hash(Process* p, DbTable* tbl, SWord reds) return reds; db_create_hash(p, tbl); - erts_atomic_set_nob(&tbl->hash.common.nitems, 0); + RESET_NITEMS(tbl); } return reds; } +static Eterm db_delete_all_objects_get_nitems_from_holder_hash(Process* p, + Eterm nitems_holder){ + return nitems_holder; +} + void db_foreach_offheap_hash(DbTable *tbl, void (*func)(ErlOffHeap *, void *), void * arg) diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index f9ba04f399..492ea81b63 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -51,9 +51,20 @@ #include "erl_db_tree_util.h" #define GETKEY_WITH_POS(Keypos, Tplp) (*((Tplp) + Keypos)) -#define NITEMS(tb) ((int)erts_atomic_read_nob(&(tb)->common.nitems)) -#define TREE_MAX_ELEMENTS 0xFFFFFFFFUL +#define NITEMS_CENTRALIZED(tb) \ + ((Sint)erts_flxctr_read_centralized(&(tb)->common.counters, \ + ERTS_DB_TABLE_NITEMS_COUNTER_ID)) +#define ADD_NITEMS(DB, TO_ADD) \ + erts_flxctr_add(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID, TO_ADD) +#define INC_NITEMS(DB) \ + erts_flxctr_inc(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID) +#define INC_NITEMS_CENTRALIZED(DB) \ + erts_flxctr_inc_read_centralized(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID) +#define RESET_NITEMS(DB) \ + erts_flxctr_reset(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID) +#define IS_CENTRALIZED_CTR(tb) (!(tb)->common.counters.is_decentralized) +#define APPROX_MEM_CONSUMED(tb) erts_flxctr_read_approx(&(tb)->common.counters, ERTS_DB_TABLE_MEM_COUNTER_ID) #define TOPN_NODE(Dtt, Pos) \ (((Pos) < Dtt->pos) ? \ @@ -296,7 +307,7 @@ int tree_balance_right(TreeDbTerm **this); static int delsub(TreeDbTerm **this); static TreeDbTerm *slot_search(Process *p, TreeDbTerm *root, Sint slot, DbTable *tb, DbTableTree *stack_container, - CATreeRootIterator *iter); + CATreeRootIterator *iter, int* is_EOT); static TreeDbTerm *find_node(DbTableCommon *tb, TreeDbTerm *root, Eterm key, DbTableTree *stack_container); static TreeDbTerm **find_node2(DbTableCommon *tb, TreeDbTerm **root, Eterm key); @@ -433,8 +444,12 @@ static void db_foreach_offheap_tree(DbTable *, void (*)(ErlOffHeap *, void *), void *); -static SWord db_delete_all_objects_tree(Process* p, DbTable* tbl, SWord reds); - +static SWord db_delete_all_objects_tree(Process* p, + DbTable* tbl, + SWord reds, + Eterm* nitems_holder_wb); +static Eterm db_delete_all_objects_get_nitems_from_holder_tree(Process* p, + Eterm nitems_holder); #ifdef HARDDEBUG static void db_check_table_tree(DbTable *tbl); #endif @@ -478,6 +493,7 @@ DbTableMethod db_tree = db_select_replace_continue_tree, db_take_tree, db_delete_all_objects_tree, + db_delete_all_objects_get_nitems_from_holder_tree, db_free_empty_table_tree, db_free_table_continue_tree, db_print_tree, @@ -595,7 +611,8 @@ int db_last_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root, } if (stack) { PUSH_NODE(stack, this); - stack->slot = NITEMS(tbl); + /* Always centralized counters when static stack is used */ + stack->slot = NITEMS_CENTRALIZED(tbl); release_stack(tbl,stack_container,stack); } *ret = db_copy_key(p, tbl, &this->dbterm); @@ -661,10 +678,7 @@ int db_put_tree_common(DbTableCommon *tb, TreeDbTerm **root, Eterm obj, for (;;) if (!*this) { /* Found our place */ state = 1; - if (erts_atomic_inc_read_nob(&tb->nitems) >= TREE_MAX_ELEMENTS) { - erts_atomic_dec_nob(&tb->nitems); - return DB_ERROR_SYSRES; - } + INC_NITEMS(((DbTable*)tb)); *this = new_dbterm(tb, obj); (*this)->balance = 0; (*this)->left = (*this)->right = NULL; @@ -888,7 +902,7 @@ int db_slot_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root, TreeDbTerm *st; Eterm *hp, *hend; Eterm copy; - + int is_EOT = 0; /* * The notion of a "slot" is not natural in a tree, but we try to * simulate it by giving the n'th node in the tree instead. @@ -899,10 +913,10 @@ int db_slot_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root, if (is_not_small(slot_term) || ((slot = signed_val(slot_term)) < 0) || - (slot > NITEMS(tbl))) + (IS_CENTRALIZED_CTR(tbl) && slot > NITEMS_CENTRALIZED(tbl))) return DB_ERROR_BADPARAM; - if (slot == NITEMS(tbl)) { + if (IS_CENTRALIZED_CTR(tbl) && slot == NITEMS_CENTRALIZED(tbl)) { *ret = am_EOT; return DB_ERROR_NONE; } @@ -912,7 +926,11 @@ int db_slot_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root, * are counted from 1 and up. */ ++slot; - st = slot_search(p, root, slot, tbl, stack_container, iter); + st = slot_search(p, root, slot, tbl, stack_container, iter, &is_EOT); + if (is_EOT) { + *ret = am_EOT; + return DB_ERROR_NONE; + } if (st == NULL) { *ret = am_false; return DB_ERROR_UNSPEC; @@ -2244,7 +2262,8 @@ void db_print_tree_common(fmtfn_t to, void *to_arg, erts_print(to, to_arg, "\n" "------------------------------------------------\n"); #else - erts_print(to, to_arg, "Ordered set (AVL tree), Elements: %d\n", NITEMS(tbl)); + erts_print(to, to_arg, "Ordered set (AVL tree), Elements: %d\n", + erts_flxctr_read_approx(&tbl->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID)); #endif } @@ -2281,24 +2300,41 @@ static SWord db_free_table_continue_tree(DbTable *tbl, SWord reds) (DbTable *) tb, (void *) tb->static_stack.array, sizeof(TreeDbTerm *) * STACK_NEED); - ASSERT((erts_atomic_read_nob(&tb->common.memory_size) - == sizeof(DbTable)) || - (erts_atomic_read_nob(&tb->common.memory_size) - == (sizeof(DbTable) + sizeof(DbFixation)))); + ASSERT(erts_flxctr_is_snapshot_ongoing(&tb->common.counters) || + ((APPROX_MEM_CONSUMED(tb) + == sizeof(DbTable)) || + (APPROX_MEM_CONSUMED(tb) + == (sizeof(DbTable) + sizeof(DbFixation))))); } return reds; } -static SWord db_delete_all_objects_tree(Process* p, DbTable* tbl, SWord reds) +static SWord db_delete_all_objects_tree(Process* p, + DbTable* tbl, + SWord reds, + Eterm* nitems_holder_wb) { + if (nitems_holder_wb != NULL) { + Uint nr_of_items = + erts_flxctr_read_centralized(&tbl->common.counters, + ERTS_DB_TABLE_NITEMS_COUNTER_ID); + *nitems_holder_wb = erts_make_integer(nr_of_items, p); + } reds = db_free_table_continue_tree(tbl, reds); if (reds < 0) return reds; db_create_tree(p, tbl); - erts_atomic_set_nob(&tbl->tree.common.nitems, 0); + RESET_NITEMS(tbl); return reds; } +static Eterm db_delete_all_objects_get_nitems_from_holder_tree(Process* p, + Eterm holder) +{ + (void)p; + return holder; +} + static void do_db_tree_foreach_offheap(TreeDbTerm *, void (*)(ErlOffHeap *, void *), void *); @@ -2383,7 +2419,7 @@ static TreeDbTerm *linkout_tree(DbTableCommon *tb, TreeDbTerm **root, tstack[tpos++] = this; state = delsub(this); } - erts_atomic_dec_nob(&tb->nitems); + DEC_NITEMS(((DbTable*)tb)); break; } } @@ -2450,7 +2486,7 @@ static TreeDbTerm *linkout_object_tree(DbTableCommon *tb, TreeDbTerm **root, tstack[tpos++] = this; state = delsub(this); } - erts_atomic_dec_nob(&tb->nitems); + DEC_NITEMS(((DbTable*)tb)); break; } } @@ -2745,7 +2781,8 @@ static int delsub(TreeDbTerm **this) static TreeDbTerm *slot_search(Process *p, TreeDbTerm *root, Sint slot, DbTable *tb, DbTableTree *stack_container, - CATreeRootIterator *iter) + CATreeRootIterator *iter, + int* is_EOT) { TreeDbTerm *this; TreeDbTerm *tmp; @@ -2837,8 +2874,12 @@ static TreeDbTerm *slot_search(Process *p, TreeDbTerm *root, break; next_root: - if (!iter) + if (!iter) { + if (stack->slot == (slot-1)) { + *is_EOT = 1; + } break; /* EOT */ + } ASSERT(slot > stack->slot); if (lastobj) { @@ -2846,8 +2887,12 @@ next_root: lastobj = NULL; } pp = catree_find_next_root(iter, &lastkey); - if (!pp) + if (!pp) { + if (stack->slot == (slot-1)) { + *is_EOT = 1; + } break; /* EOT */ + } root = *pp; stack->pos = 0; find_next(&tb->common, root, stack, lastkey); diff --git a/erts/emulator/beam/erl_db_tree_util.h b/erts/emulator/beam/erl_db_tree_util.h index 02df74678d..ba4a8f79e5 100644 --- a/erts/emulator/beam/erl_db_tree_util.h +++ b/erts/emulator/beam/erl_db_tree_util.h @@ -25,6 +25,8 @@ ** Internal functions and macros used by both the CA tree and the AVL tree */ + +#if defined(ARCH_32) /* ** A stack of this size is enough for an AVL tree with more than ** 0xFFFFFFFF elements. May be subject to change if @@ -34,8 +36,19 @@ ** Where n denotes the number of nodes, h(n) the height of the tree ** with n nodes and log is the binary logarithm. */ - #define STACK_NEED 50 +#elif defined(ARCH_64) +/* +** A stack of this size is enough for an AVL tree with more than +** 2^61 elements. +** The Maximal height of an AVL tree is calculated as above. +*/ +#define STACK_NEED 90 +#else +#error "Unsported architecture" +#endif + + #define PUSH_NODE(Dtt, Tdt) \ ((Dtt)->array[(Dtt)->pos++] = Tdt) @@ -50,6 +63,9 @@ #define EMPTY_NODE(Dtt) (TOP_NODE(Dtt) == NULL) +#define DEC_NITEMS(DB) \ + erts_flxctr_dec(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID) + static ERTS_INLINE void free_term(DbTable *tb, TreeDbTerm* p) { db_free_term(tb, p, offsetof(TreeDbTerm, dbterm)); diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index e3d3c0e804..02d4dd6c9a 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -21,6 +21,7 @@ #ifndef _DB_UTIL_H #define _DB_UTIL_H +#include "erl_flxctr.h" #include "global.h" #include "erl_message.h" #include "erl_bif_unique.h" @@ -207,8 +208,12 @@ typedef struct db_table_method enum DbIterSafety*); int (*db_take)(Process *, DbTable *, Eterm, Eterm *); - SWord (*db_delete_all_objects)(Process* p, DbTable* db, SWord reds); - + SWord (*db_delete_all_objects)(Process* p, + DbTable* db, + SWord reds, + Eterm* nitems_holder_wb); + Eterm (*db_delete_all_objects_get_nitems_from_holder)(Process* p, + Eterm nitems_holder); int (*db_free_empty_table)(DbTable* db); SWord (*db_free_table_continue)(DbTable* db, SWord reds); @@ -257,6 +262,9 @@ typedef struct { DbTable *prev; } DbTableList; +#define ERTS_DB_TABLE_NITEMS_COUNTER_ID 0 +#define ERTS_DB_TABLE_MEM_COUNTER_ID 1 + /* * This structure contains data for all different types of database * tables. Note that these fields must match the same fields @@ -281,8 +289,11 @@ typedef struct db_table_common { Eterm the_name; /* an atom */ Binary *btid; DbTableMethod* meth; /* table methods */ - erts_atomic_t nitems; /* Total number of items in table */ - erts_atomic_t memory_size;/* Total memory size. NOTE: in bytes! */ + /* The ErtsFlxCtr below contains: + * - Total number of items in table + * - Total memory size (NOTE: in bytes!) */ + ErtsFlxCtr counters; + char extra_for_flxctr[ERTS_FLXCTR_NR_OF_EXTRA_BYTES(2)]; struct { /* Last fixation time */ ErtsMonotonicTime monotonic; ErtsMonotonicTime offset; @@ -315,6 +326,7 @@ typedef struct db_table_common { #define DB_BUSY (1 << 12) #define DB_CATREE_FORCE_SPLIT (1 << 31) /* erts_debug */ +#define DB_CATREE_DEBUG_RANDOM_SPLIT_JOIN (1 << 30) /* erts_debug */ #define IS_HASH_TABLE(Status) (!!((Status) & \ (DB_BAG | DB_SET | DB_DUPLICATE_BAG))) diff --git a/erts/emulator/beam/erl_flxctr.c b/erts/emulator/beam/erl_flxctr.c new file mode 100644 index 0000000000..35f4a21508 --- /dev/null +++ b/erts/emulator/beam/erl_flxctr.c @@ -0,0 +1,370 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2019. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +/* + * Author: Kjell Winblad + */ + +#include "erl_flxctr.h" + +static int reader_groups_array_size = 0; +#define ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS (reader_groups_array_size) + +static int erts_flxctr_read_ctx_bin_dtor(Binary *context_bin); +static int erts_flxctr_wait_dtor(Binary *context_bin); + +typedef struct { + ErtsThrPrgrLaterOp later_op; + Process* process; + ErtsFlxCtrDecentralizedCtrArray* array; + ErtsFlxCtrDecentralizedCtrArray* next_array; + ErtsAlcType_t alloc_type; + int nr_of_counters; + Sint result[ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE]; +} DecentralizedReadSnapshotInfo; + +typedef enum { + ERTS_FLXCTR_SNAPSHOT_NOT_ONGOING = 0, + ERTS_FLXCTR_SNAPSHOT_ONGOING = 1, + ERTS_FLXCTR_SNAPSHOT_ONGOING_TP_THREAD_DO_FREE = 2 +} erts_flxctr_snapshot_status; + +static void +thr_prg_wake_up_and_count(void* bin_p) +{ + Binary* bin = bin_p; + DecentralizedReadSnapshotInfo* info = ERTS_MAGIC_BIN_DATA(bin); + Process* p = info->process; + ErtsFlxCtrDecentralizedCtrArray* array = info->array; + ErtsFlxCtrDecentralizedCtrArray* next = info->next_array; + int i, sched; + /* Reset result array */ + for (i = 0; i < info->nr_of_counters; i++) { + info->result[i] = 0; + } + /* Read result from snapshot */ + for (sched = 0; sched < ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS; sched++) { + for (i = 0; i < info->nr_of_counters; i++) { + info->result[i] = info->result[i] + + erts_atomic_read_nob(&array->array[sched].counters[i]); + } + } + /* Update the next decentralized counter array */ + for (i = 0; i < info->nr_of_counters; i++) { + erts_atomic_add_nob(&next->array[0].counters[i], info->result[i]); + } + /* Announce that the snapshot is done */ + { + Sint expected = ERTS_FLXCTR_SNAPSHOT_ONGOING; + if (expected != erts_atomic_cmpxchg_mb(&next->snapshot_status, + ERTS_FLXCTR_SNAPSHOT_NOT_ONGOING, + expected)) { + /* The CAS failed which means that this thread need to free the next array. */ + erts_free(info->alloc_type, next->block_start); + } + } + /* Resume the process that requested the snapshot */ + erts_proc_lock(p, ERTS_PROC_LOCK_STATUS); + if (!ERTS_PROC_IS_EXITING(p)) { + erts_resume(p, ERTS_PROC_LOCK_STATUS); + } + /* Free the memory that is no longer needed */ + erts_free(info->alloc_type, array->block_start); + erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + erts_proc_dec_refc(p); + erts_bin_release(bin); +} + +typedef struct { + ErtsThrPrgrLaterOp later_op; + Process* process; +} ErtsFlxCtrWakeUpLaterInfo; + +static void +thr_prg_wake_up_later(void* bin_p) +{ + Binary* bin = bin_p; + ErtsFlxCtrWakeUpLaterInfo* info = ERTS_MAGIC_BIN_DATA(bin); + Process* p = info->process; + /* Resume the requesting process */ + erts_proc_lock(p, ERTS_PROC_LOCK_STATUS); + if (!ERTS_PROC_IS_EXITING(p)) { + erts_resume(p, ERTS_PROC_LOCK_STATUS); + } + erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + /* Free data */ + erts_proc_dec_refc(p); + erts_bin_release(bin); +} + +static +int erts_flxctr_read_ctx_bin_dtor(Binary *context_bin) { + (void)context_bin; + return 1; +} + +static +int erts_flxctr_wait_dtor(Binary *context_bin) { + (void)context_bin; + return 1; +} + +static void suspend_until_thr_prg(Process* p) +{ + Binary* state_bin; + ErtsFlxCtrWakeUpLaterInfo* info; + state_bin = erts_create_magic_binary(sizeof(ErtsFlxCtrWakeUpLaterInfo), + erts_flxctr_wait_dtor); + info = ERTS_MAGIC_BIN_DATA(state_bin); + info->process = p; + erts_refc_inctest(&state_bin->intern.refc, 1); + erts_suspend(p, ERTS_PROC_LOCK_MAIN, NULL); + erts_proc_inc_refc(p); + ERTS_VBUMP_ALL_REDS(p); + erts_schedule_thr_prgr_later_op(thr_prg_wake_up_later, state_bin, &info->later_op); +} + + +static ErtsFlxCtrDecentralizedCtrArray* +create_decentralized_ctr_array(ErtsAlcType_t alloc_type, Uint nr_of_counters) { + /* Allocate an ErtsFlxCtrDecentralizedCtrArray and make sure that + the array field is located at the start of a cache line */ + char* bytes = + erts_alloc(alloc_type, + sizeof(ErtsFlxCtrDecentralizedCtrArray) + + (sizeof(ErtsFlxCtrDecentralizedCtrArrayElem) * + ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS) + + ERTS_CACHE_LINE_SIZE); + void* block_start = bytes; + int bytes_to_next_cacheline_border; + ErtsFlxCtrDecentralizedCtrArray* array; + int i, sched; + bytes = &bytes[offsetof(ErtsFlxCtrDecentralizedCtrArray, array)]; + bytes_to_next_cacheline_border = + ERTS_CACHE_LINE_SIZE - (((Uint)bytes) % ERTS_CACHE_LINE_SIZE); + array = (ErtsFlxCtrDecentralizedCtrArray*) + (&bytes[bytes_to_next_cacheline_border - + (int)offsetof(ErtsFlxCtrDecentralizedCtrArray, array)]); + ASSERT(((Uint)array->array) % ERTS_CACHE_LINE_SIZE == 0); + ASSERT(((Uint)array - (Uint)block_start) <= ERTS_CACHE_LINE_SIZE); + /* Initialize fields */ + erts_atomic_init_nob(&array->snapshot_status, ERTS_FLXCTR_SNAPSHOT_ONGOING); + for (sched = 0; sched < ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS; sched++) { + for (i = 0; i < nr_of_counters; i++) { + erts_atomic_init_nob(&array->array[sched].counters[i], 0); + } + } + array->block_start = block_start; + return array; +} + +void erts_flxctr_setup(int decentralized_counter_groups) +{ + reader_groups_array_size = decentralized_counter_groups+1; +} + +void erts_flxctr_init(ErtsFlxCtr* c, + int is_decentralized, + Uint nr_of_counters, + ErtsAlcType_t alloc_type) +{ + ASSERT(nr_of_counters <= ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE); + c->is_decentralized = is_decentralized; + c->nr_of_counters = nr_of_counters; + if (c->is_decentralized) { + ErtsFlxCtrDecentralizedCtrArray* array = + create_decentralized_ctr_array(alloc_type, nr_of_counters); + erts_atomic_set_nob(&array->snapshot_status, + ERTS_FLXCTR_SNAPSHOT_NOT_ONGOING); + erts_atomic_init_nob(&c->u.counters_ptr, (Sint)array); + ASSERT(((Uint)array->array) % ERTS_CACHE_LINE_SIZE == 0); + } else { + int i; + for (i = 0; i < nr_of_counters; i++) { + erts_atomic_init_nob(&c->u.counters[i], 0); + } + } +} + +void erts_flxctr_destroy(ErtsFlxCtr* c, ErtsAlcType_t type) +{ + if (c->is_decentralized) { + if (erts_flxctr_is_snapshot_ongoing(c)) { + ErtsFlxCtrDecentralizedCtrArray* array = + ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c); + /* Try to delegate the resposibilty of freeing to + thr_prg_wake_up_and_count */ + Sint expected = ERTS_FLXCTR_SNAPSHOT_ONGOING; + if (expected != + erts_atomic_cmpxchg_mb(&array->snapshot_status, + ERTS_FLXCTR_SNAPSHOT_ONGOING_TP_THREAD_DO_FREE, + expected)) { + /* The delegation was unsuccessful which means that no + snapshot is ongoing anymore and the freeing needs + to be done here */ + ERTS_ASSERT(!erts_flxctr_is_snapshot_ongoing(c)); + erts_free(type, array->block_start); + } + } else { + erts_free(type, ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c)->block_start); + } + } +} + +ErtsFlxCtrSnapshotResult +erts_flxctr_snapshot(ErtsFlxCtr* c, + ErtsAlcType_t alloc_type, + Process* p) +{ + if (c->is_decentralized) { + ErtsFlxCtrDecentralizedCtrArray* array = ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c); + if (erts_flxctr_is_snapshot_ongoing(c)) { + /* Let the caller try again later */ + ErtsFlxCtrSnapshotResult res = + {.type = ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP}; + suspend_until_thr_prg(p); + return res; + } else { + Eterm* hp; + Binary* state_bin; + Eterm state_mref; + DecentralizedReadSnapshotInfo* info; + ErtsFlxCtrDecentralizedCtrArray* new_array = + create_decentralized_ctr_array(alloc_type, c->nr_of_counters); + int success = + ((Sint)array) == erts_atomic_cmpxchg_mb(&c->u.counters_ptr, + (Sint)new_array, + (Sint)array); + if (!success) { + /* Let the caller try again later */ + ErtsFlxCtrSnapshotResult res = + {.type = ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP}; + suspend_until_thr_prg(p); + erts_free(alloc_type, new_array->block_start); + return res; + } + /* Create binary with info about the operation that can be + sent to the caller and to a thread progress function */ + state_bin = + erts_create_magic_binary(sizeof(DecentralizedReadSnapshotInfo), + erts_flxctr_read_ctx_bin_dtor); + hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE); + state_mref = erts_mk_magic_ref(&hp, &MSO(p), state_bin); + info = ERTS_MAGIC_BIN_DATA(state_bin); + info->alloc_type = alloc_type; + info->array = array; + info->next_array = new_array; + info->process = p; + info->nr_of_counters = c->nr_of_counters; + erts_proc_inc_refc(p); + erts_refc_inctest(&state_bin->intern.refc, 2); + erts_suspend(p, ERTS_PROC_LOCK_MAIN, NULL); + ERTS_VBUMP_ALL_REDS(p); + erts_schedule_thr_prgr_later_op(thr_prg_wake_up_and_count, + state_bin, + &info->later_op); + { + ErtsFlxCtrSnapshotResult res = { + .type = ERTS_FLXCTR_GET_RESULT_AFTER_TRAP, + .trap_resume_state = state_mref}; + return res; + } + } + } else { + ErtsFlxCtrSnapshotResult res; + int i; + res.type = ERTS_FLXCTR_DONE; + for (i = 0; i < c->nr_of_counters; i++){ + res.result[i] = erts_flxctr_read_centralized(c, i); + } + return res; + } +} + + +Sint erts_flxctr_get_snapshot_result_after_trap(Eterm result_holder, + Uint counter_nr) +{ + Binary* bin = erts_magic_ref2bin(result_holder); + DecentralizedReadSnapshotInfo* data = ERTS_MAGIC_BIN_DATA(bin);; + return data->result[counter_nr]; +} + +int erts_flxctr_is_snapshot_result(Eterm term) +{ + if (is_internal_magic_ref(term)) { + Binary* bin = erts_magic_ref2bin(term); + return ERTS_MAGIC_BIN_DESTRUCTOR(bin) == erts_flxctr_read_ctx_bin_dtor; + } else return 0; +} + +Sint erts_flxctr_read_approx(ErtsFlxCtr* c, + Uint counter_nr) +{ + if (c->is_decentralized) { + ErtsFlxCtrDecentralizedCtrArray* counter = ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c); + Sint sum = 0; + int sched; + for (sched = 0; sched < ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS; sched++) { + sum = sum + erts_atomic_read_nob(&counter->array[sched].counters[counter_nr]); + } + return sum; + } else { + return erts_flxctr_read_centralized(c, counter_nr); + } +} + +int erts_flxctr_is_snapshot_ongoing(ErtsFlxCtr* c) +{ + return c->is_decentralized && + (ERTS_FLXCTR_SNAPSHOT_NOT_ONGOING != + erts_atomic_read_acqb(&ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c)->snapshot_status)); +} + +int erts_flxctr_suspend_until_thr_prg_if_snapshot_ongoing(ErtsFlxCtr* c, Process* p) +{ + if (erts_flxctr_is_snapshot_ongoing(c)) { + suspend_until_thr_prg(p); + return 1; + } else { + return 0; + } +} + +void erts_flxctr_reset(ErtsFlxCtr* c, + Uint counter_nr) +{ + if (c->is_decentralized) { + int sched; + ErtsFlxCtrDecentralizedCtrArray* counter = + ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c); + for (sched = 0; sched < ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS; sched++) { + erts_atomic_set_nob(&counter->array[sched].counters[counter_nr], 0); + } + } else { + erts_atomic_set_nob(&c->u.counters[counter_nr], 0); + } +} + + +void erts_flxctr_set_slot(int group) { + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + esdp->flxctr_slot_no = group; +} diff --git a/erts/emulator/beam/erl_flxctr.h b/erts/emulator/beam/erl_flxctr.h new file mode 100644 index 0000000000..5cab02b9eb --- /dev/null +++ b/erts/emulator/beam/erl_flxctr.h @@ -0,0 +1,406 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2019. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +/** + * @file erl_flxctr.h + * + * @brief This file contains the API of a flexible counter. The + * counter can be configured during its initialization to be + * centralized or decentralized. The centralized configuration makes + * it possible to read the counter value extremely efficiently, but + * updates of the counter value can easily cause contention. The + * decentralized configuration has the reverse trade-off (i.e., + * updates are efficient and scalable but reading the counter value is + * slow and may cause contention). + * + * @author Kjell Winblad + */ + +#ifndef ERL_FLXCTR_H__ +#define ERL_FLXCTR_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "sys.h" +#include "erl_vm.h" +#include "global.h" +#include "error.h" +#include "bif.h" +#include "big.h" +#include "erl_binary.h" +#include "bif.h" +#include <stddef.h> + +/* Public Interface */ + +#define ERTS_MAX_FLXCTR_GROUPS 256 +#define ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE (ERTS_CACHE_LINE_SIZE / sizeof(erts_atomic_t)) + +typedef struct { + int nr_of_counters; + int is_decentralized; + union { + erts_atomic_t counters_ptr; + erts_atomic_t counters[1]; + } u; +} ErtsFlxCtr; + +#define ERTS_FLXCTR_NR_OF_EXTRA_BYTES(NR_OF_COUNTERS) \ + ((NR_OF_COUNTERS-1) * sizeof(erts_atomic_t)) + +/* Called by early_init */ +void erts_flxctr_setup(int decentralized_counter_groups); + +/** + * @brief Initializes an ErtsFlxCtr. The macro + * ERTS_FLXCTR_NR_OF_EXTRA_BYTES should be used to determine how much + * extra space that needs to be allocated directly after the + * ErtsFlxCtr when is_decentralized is set to zero. Each ErtsFlxCtr + * instance may contain up to ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE + * counters. These counters are numbered from zero to + * (ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE-1). Most of the functions in + * this module take a parameter named counter_nr that controls which + * of the ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE counters in the given + * ErtsFlxCtr that should be operated on. + * + * @param c The counter to initialize + * @param is_decentralized Non-zero value to make c decentralized + * @param nr_of_counters The number of counters included in c + * (max ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE) + * @param alloc_type + */ +void erts_flxctr_init(ErtsFlxCtr* c, + int is_decentralized, + Uint nr_of_counters, + ErtsAlcType_t alloc_type); + +/** + * @brief Destroys an initialized counter. + * + * @param c The counter that should be destroyed + * @param alloc_type The allocation type (needs to be the same as the + * one passed to erts_flxctr_init when c was + * initialized) + */ +void erts_flxctr_destroy(ErtsFlxCtr* c, ErtsAlcType_t alloc_type); + +/** + * @brief Adds to_add to the counter with counter_nr in c + * + * @param c the ErtsFlxCtr to operate on + * @param counter_nr The number of the counter in c to modify + * @param to_add The amount that should be added to the specified counter + */ +ERTS_GLB_INLINE +void erts_flxctr_add(ErtsFlxCtr* c, + Uint counter_nr, + int to_add); + +/** + * @brief Increases the specified counter by 1 + * + * @param c The ErtsFlxCtr instance to operate on + * @param counter_nr The number of the counter within c to operate on + */ +ERTS_GLB_INLINE +void erts_flxctr_inc(ErtsFlxCtr* c, + Uint counter_nr); + +/** + * @brief Decreases the specified counter by 1 + */ +ERTS_GLB_INLINE +void erts_flxctr_dec(ErtsFlxCtr* c, + Uint counter_nr); + +/** + * @brief This function tries to return the current value of the + * specified counter but may return an incorrect result if the counter + * is decentralized and other threads are accessing the counter + * concurrently. + * + * @param c The ErtsFlxCtr instance to operate on + * @param counter_nr The number of the counter within c to operate on + * + * @return A snapshot of the specifed counter if c is centralized or a + * possibly incorrect estimate of the counter value if c is + * decentralized + */ +Sint erts_flxctr_read_approx(ErtsFlxCtr* c, + Uint counter_nr); + +/** + * @brief This function can only be used together with an ErtsFlxCtr + * that is configured to be centralized. The function increments the + * specified counter by 1 and returns the value of the counter after + * the increment. + */ +ERTS_GLB_INLINE +Sint erts_flxctr_inc_read_centralized(ErtsFlxCtr* c, + Uint counter_nr); + +/** + * @brief This function can only be used together with a ErtsFlxCtr + * that is configured to be centralized. The function decrements the + * specified counter by 1 and returns the value of the counter after + * the operation. + */ +ERTS_GLB_INLINE +Sint erts_flxctr_dec_read_centralized(ErtsFlxCtr* c, + Uint counter_nr); + +/** + * @brief This function can only be used together with an ErtsFlxCtr + * that is configured to be centralized. The function returns the + * current value of the specified counter. + */ +ERTS_GLB_INLINE +Sint erts_flxctr_read_centralized(ErtsFlxCtr* c, + Uint counter_nr); + + +typedef enum { + ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP, + ERTS_FLXCTR_DONE, + ERTS_FLXCTR_GET_RESULT_AFTER_TRAP +} ErtsFlxctrSnapshotResultType; + +typedef struct { + ErtsFlxctrSnapshotResultType type; + Eterm trap_resume_state; + Sint result[ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE]; +} ErtsFlxCtrSnapshotResult; + +/** + * @brief This function initiates an atomic snapshot of an ErtsFlxCtr + * to read out the values of one or more of the counters that are + * stored in the given ErtsFlxCtr. The caller needs to perform + * different actions after the return of this function depending on + * the value of the type field in the returned struct: + * + * - The caller needs to trap and try again after the trap if the + * return value has the type ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP. + * + * - The caller can get the result directly from the result field of + * the returned struct if the return value has the type + * ERTS_FLXCTR_DONE. The value at index i in the result field + * correspond to counter number i. + * + * - Finally, if the return value has the type + * ERTS_FLXCTR_GET_RESULT_AFTER_TRAP, then the caller needs to save + * the value of the field trap_resume_state from the returned struct + * and trap. After the trap, the values of the counters can be + * obtained by using the function + * erts_flxctr_get_snapshot_result_after_trap. Note that the + * function erts_flxctr_is_snapshot_result can be used to check if a + * value is obtained from the trap_resume_state field in the + * returned struct (this can be useful when the calling function + * wakes up again after the trap). + * + * The snapshot operation that is initiated by this function should be + * considered to be ongoing from the issuing of this function until a + * struct with the type field set to ERTS_FLXCTR_DONE has been + * returned from the function or until the caller of this function has + * woken up after trapping. + * + * @param c The ErtsFlxCtr that the snapshot shall be taken from + * @param alloc_type The allocation type (needs to be the same as the + * type passed to erts_flxctr_init when c was + * initialized) + * @param p The Erlang process that is doing the call + * + * @return See the description above + * + */ +ErtsFlxCtrSnapshotResult +erts_flxctr_snapshot(ErtsFlxCtr* c, + ErtsAlcType_t alloc_type, + Process* p); + +/** + * @brief Checks if the parameter term is a snapshot result (i.e., + * something obtained from the trap_resume_state field of an + * ErtsFlxCtrSnapshotResult struct that has been returned from + * erts_flxctr_snapshot). + * + * @param term The term to check + * + * @return A nonzero value iff the term is a snapshot result + */ +int erts_flxctr_is_snapshot_result(Eterm term); + +/** + * @brief Returns the result of a snapshot for a counter given a + * snapshot result returned by a call to erts_flxctr_snapshot (i.e., + * the value stored in the trap_resume_state field of a struct + * returned by erts_flxctr_snapshot). The caller needs to trap between + * the return of erts_flxctr_snapshot and the call to this function. + */ +Sint erts_flxctr_get_snapshot_result_after_trap(Eterm trap_resume_state, + Uint counter_nr); + +/** + * @brief Resets the specified counter to 0. This function is unsafe + * to call while a snapshot operation may be active (initiated with + * the erts_flxctr_snapshot function). + */ +void erts_flxctr_reset(ErtsFlxCtr* c, + Uint counter_nr); + +/** + * @brief Checks if a snapshot operation is active (snapshots are + * initiated with the erts_flxctr_snapshot function). + * + * @return nonzero value iff a snapshot was active at some point + * between the invocation and return of the function + */ +int erts_flxctr_is_snapshot_ongoing(ErtsFlxCtr* c); + +/** + * @brief This function checks if a snapshot operation is ongoing + * (snapshots are initiated with the erts_flxctr_snapshot function) + * and suspend the given process until thread progress has happened if + * it detected an ongoing snapshot operation. The caller needs to trap + * if a non-zero value is returned. + * + * @param c The ErtsFlxCtr to check + * @param p The calling process + * + * @return nonzero value if the given process has got suspended + */ +int erts_flxctr_suspend_until_thr_prg_if_snapshot_ongoing(ErtsFlxCtr* c, Process* p); + +/* End: Public Interface */ + +/* Internal Declarations */ + +#define ERTS_FLXCTR_GET_CTR_ARRAY_PTR(C) \ + ((ErtsFlxCtrDecentralizedCtrArray*) erts_atomic_read_acqb(&(C)->u.counters_ptr)) +#define ERTS_FLXCTR_GET_CTR_PTR(C, SCHEDULER_ID, COUNTER_ID) \ + &(ERTS_FLXCTR_GET_CTR_ARRAY_PTR(C))->array[SCHEDULER_ID].counters[COUNTER_ID] + + +typedef union { + erts_atomic_t counters[ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE]; + char pad[ERTS_CACHE_LINE_SIZE]; +} ErtsFlxCtrDecentralizedCtrArrayElem; + +typedef struct ErtsFlxCtrDecentralizedCtrArray { + void* block_start; + erts_atomic_t snapshot_status; + ErtsFlxCtrDecentralizedCtrArrayElem array[]; +} ErtsFlxCtrDecentralizedCtrArray; + +void erts_flxctr_set_slot(int group); + +ERTS_GLB_INLINE +int erts_flxctr_get_slot_index(void); + +/* End: Internal Declarations */ + + +/* Implementation of inlined functions */ + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_GLB_INLINE +int erts_flxctr_get_slot_index(void) +{ + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + ASSERT(esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp)); + ASSERT(esdp->flxctr_slot_no > 0); + return esdp->flxctr_slot_no; +} + +ERTS_GLB_INLINE +void erts_flxctr_add(ErtsFlxCtr* c, + Uint counter_nr, + int to_add) +{ + ASSERT(counter_nr < c->nr_of_counters); + if (c->is_decentralized) { + erts_atomic_add_nob(ERTS_FLXCTR_GET_CTR_PTR(c, + erts_flxctr_get_slot_index(), + counter_nr), + to_add); + } else { + erts_atomic_add_nob(&c->u.counters[counter_nr], to_add); + } +} + +ERTS_GLB_INLINE +void erts_flxctr_inc(ErtsFlxCtr* c, + Uint counter_nr) +{ + ASSERT(counter_nr < c->nr_of_counters); + if (c->is_decentralized) { + erts_atomic_inc_nob(ERTS_FLXCTR_GET_CTR_PTR(c, + erts_flxctr_get_slot_index(), + counter_nr)); + } else { + erts_atomic_inc_read_nob(&c->u.counters[counter_nr]); + } +} + +ERTS_GLB_INLINE +void erts_flxctr_dec(ErtsFlxCtr* c, + Uint counter_nr) +{ + ASSERT(counter_nr < c->nr_of_counters); + if (c->is_decentralized) { + erts_atomic_dec_nob(ERTS_FLXCTR_GET_CTR_PTR(c, + erts_flxctr_get_slot_index(), + counter_nr)); + } else { + erts_atomic_dec_nob(&c->u.counters[counter_nr]); + } +} + +ERTS_GLB_INLINE +Sint erts_flxctr_inc_read_centralized(ErtsFlxCtr* c, + Uint counter_nr) +{ + ASSERT(counter_nr < c->nr_of_counters); + ASSERT(!c->is_decentralized); + return erts_atomic_inc_read_nob(&c->u.counters[counter_nr]); +} + +ERTS_GLB_INLINE +Sint erts_flxctr_dec_read_centralized(ErtsFlxCtr* c, + Uint counter_nr) +{ + ASSERT(counter_nr < c->nr_of_counters); + ASSERT(!c->is_decentralized); + return erts_atomic_dec_read_nob(&c->u.counters[counter_nr]); +} + +ERTS_GLB_INLINE +Sint erts_flxctr_read_centralized(ErtsFlxCtr* c, + Uint counter_nr) +{ + ASSERT(counter_nr < c->nr_of_counters); + ASSERT(!c->is_decentralized); + return erts_atomic_read_nob(&((erts_atomic_t*)(c->u.counters))[counter_nr]); +} + +#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ + +#endif /* ERL_FLXCTR_H__ */ diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 12750b9aa6..547e4064a2 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -593,6 +593,7 @@ void erts_usage(void) erts_fprintf(stderr, " no_time_warp|single_time_warp|multi_time_warp\n"); erts_fprintf(stderr, "-d don't write a crash dump for internally detected errors\n"); erts_fprintf(stderr, " (halt(String) will still produce a crash dump)\n"); + erts_fprintf(stderr, "-dcg set the limit for the number of decentralized counter groups\n"); erts_fprintf(stderr, "-fn[u|a|l] Control how filenames are interpreted\n"); erts_fprintf(stderr, "-hms size set minimum heap size in words (default %d)\n", H_DEFAULT_SIZE); @@ -785,6 +786,8 @@ early_init(int *argc, char **argv) /* int dirty_io_scheds; int max_reader_groups; int reader_groups; + int max_decentralized_counter_groups; + int decentralized_counter_groups; char envbuf[21]; /* enough for any 64-bit integer */ size_t envbufsz; @@ -804,7 +807,8 @@ early_init(int *argc, char **argv) /* erts_initialized = 0; - erts_pre_early_init_cpu_topology(&max_reader_groups, + erts_pre_early_init_cpu_topology(&max_decentralized_counter_groups, + &max_reader_groups, &ncpu, &ncpuonln, &ncpuavail); @@ -865,6 +869,24 @@ early_init(int *argc, char **argv) /* } if (argv[i][0] == '-') { switch (argv[i][1]) { + case 'd': { + char *sub_param = argv[i]+2; + if (has_prefix("cg", sub_param)) { + char *arg = get_arg(sub_param+2, argv[i+1], &i); + if (sscanf(arg, "%d", &max_decentralized_counter_groups) != 1) { + erts_fprintf(stderr, + "bad decentralized counter groups limit: %s\n", arg); + erts_usage(); + } + if (max_decentralized_counter_groups < 0) { + erts_fprintf(stderr, + "bad decentralized counter groups limit: %d\n", + max_decentralized_counter_groups); + erts_usage(); + } + } + break; + } case 'r': { char *sub_param = argv[i]+2; if (has_prefix("g", sub_param)) { @@ -1186,8 +1208,10 @@ early_init(int *argc, char **argv) /* erts_early_init_cpu_topology(no_schedulers, &max_main_threads, max_reader_groups, - &reader_groups); - + &reader_groups, + max_decentralized_counter_groups, + &decentralized_counter_groups); + erts_flxctr_setup(decentralized_counter_groups); { erts_thr_late_init_data_t elid = ERTS_THR_LATE_INIT_DATA_DEF_INITER; elid.mem.std.alloc = ethr_std_alloc; diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 2a0fb9e2aa..6645341512 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -1590,6 +1590,9 @@ void erts_factory_undo(ErtsHeapFactory* factory) factory->message->hfrag.next = factory->heap_frags; else factory->message->data.heap_frag = factory->heap_frags; + /* Set the message to NIL in order for this message not to be + treated as a distributed message by the cleanup_messages logic */ + factory->message->m[0] = NIL; erts_cleanup_messages(factory->message); break; case FACTORY_TMP: diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index deaf35c2a1..1fbe362330 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1344,11 +1344,18 @@ unsigned char* enif_make_new_binary(ErlNifEnv* env, size_t size, int enif_term_to_binary(ErlNifEnv *dst_env, ERL_NIF_TERM term, ErlNifBinary *bin) { - Sint size; + Uint size; byte *bp; Binary* refbin; - size = erts_encode_ext_size(term); + switch (erts_encode_ext_size(term, &size)) { + case ERTS_EXT_SZ_SYSTEM_LIMIT: + return 0; /* system limit */ + case ERTS_EXT_SZ_YIELD: + ERTS_INTERNAL_ERROR("Unexpected yield"); + case ERTS_EXT_SZ_OK: + break; + } if (!enif_alloc_binary(size, bin)) return 0; diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 49dea8919b..4eb6c3e214 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -805,8 +805,7 @@ static int node_table_cmp(void *venp1, void *venp2) { return ((((ErlNode *) venp1)->sysname == ((ErlNode *) venp2)->sysname) && - ((((ErlNode *) venp1)->creation == ((ErlNode *) venp2)->creation) || - (((ErlNode *) venp1)->creation == 0 || ((ErlNode *) venp2)->creation == 0)) + ((((ErlNode *) venp1)->creation == ((ErlNode *) venp2)->creation)) ? 0 : 1); } @@ -977,7 +976,7 @@ static void print_node(void *venp, void *vpndp) if(pndp->sysname == NIL) { erts_print(pndp->to, pndp->to_arg, "Name: %T ", enp->sysname); } - erts_print(pndp->to, pndp->to_arg, " %u", enp->creation); + erts_print(pndp->to, pndp->to_arg, " %d", enp->creation); #ifdef DEBUG erts_print(pndp->to, pndp->to_arg, " (refc=%ld)", erts_refc_read(&enp->refc, 0)); @@ -1020,7 +1019,7 @@ void erts_print_node_info(fmtfn_t to, /* ----------------------------------------------------------------------- */ void -erts_set_this_node(Eterm sysname, Uint32 creation) +erts_set_this_node(Eterm sysname, Uint creation) { ERTS_LC_ASSERT(erts_thr_progress_is_blocking()); ASSERT(2 <= de_refc_read(erts_this_dist_entry, 2)); diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h index c434926142..aa8af12555 100644 --- a/erts/emulator/beam/erl_node_tables.h +++ b/erts/emulator/beam/erl_node_tables.h @@ -95,6 +95,7 @@ enum dist_entry_state { struct ErtsDistOutputBuf_ { #ifdef DEBUG Uint dbg_pattern; + byte *ext_startp; byte *alloc_endp; #endif ErtsDistOutputBuf *next; @@ -258,7 +259,7 @@ void erts_set_dist_entry_pending(DistEntry *); void erts_set_dist_entry_connected(DistEntry *, Eterm, Uint); ErlNode *erts_find_or_insert_node(Eterm, Uint32, Eterm); void erts_schedule_delete_node(ErlNode *); -void erts_set_this_node(Eterm, Uint32); +void erts_set_this_node(Eterm, Uint); Uint erts_node_table_size(void); void erts_init_node_tables(int); void erts_node_table_info(fmtfn_t, void *); diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c index 4e9f177e51..f58a606d57 100644 --- a/erts/emulator/beam/erl_proc_sig_queue.c +++ b/erts/emulator/beam/erl_proc_sig_queue.c @@ -1019,6 +1019,8 @@ send_gen_exit_signal(Process *c_p, Eterm from_tag, ref_sz = size_object(ref); hsz += ref_sz; + reason_sz = 0; /* Set to silence gcc warning */ + /* The reason was part of the control message, just use copy it into the xsigd */ if (is_value(reason)) { diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 9e662632b4..1f6adb98ef 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -12095,6 +12095,7 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds) Eterm watched; Uint watcher_sz, ref_sz; ErtsHeapFactory factory; + Sint reds_consumed = 0; ASSERT(erts_monitor_is_target(mon) && mon->type == ERTS_MON_TYPE_DIST_PROC); @@ -12139,16 +12140,18 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds) watched, ref, reason); + reds_consumed = reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR); switch (code) { case ERTS_DSIG_SEND_CONTINUE: + case ERTS_DSIG_SEND_YIELD: erts_set_gc_state(c_p, 0); ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx); - /* fall-through */ - case ERTS_DSIG_SEND_YIELD: + reds_consumed = reds; /* force yield */ break; case ERTS_DSIG_SEND_OK: break; case ERTS_DSIG_SEND_TOO_LRG: + erts_kill_dist_connection(dep, dist->connection_id); erts_set_gc_state(c_p, 1); break; default: @@ -12164,7 +12167,7 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds) erts_monitor_release(mon); else erts_monitor_release_both(mdp); - return reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR); + return reds_consumed; } int @@ -12351,6 +12354,7 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds) ErtsLink *dlnk; ErtsLinkData *ldp = NULL; ErtsHeapFactory factory; + Sint reds_consumed = 0; ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC); dlnk = erts_link_to_other(lnk, &ldp); @@ -12387,16 +12391,18 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds) item, reason, SEQ_TRACE_TOKEN(c_p)); + reds_consumed = reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR); switch (code) { + case ERTS_DSIG_SEND_YIELD: case ERTS_DSIG_SEND_CONTINUE: erts_set_gc_state(c_p, 0); ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx); - /* fall-through */ - case ERTS_DSIG_SEND_YIELD: + reds_consumed = reds; /* force yield */ break; case ERTS_DSIG_SEND_OK: break; case ERTS_DSIG_SEND_TOO_LRG: + erts_kill_dist_connection(dep, dist->connection_id); erts_set_gc_state(c_p, 1); break; default: @@ -12412,7 +12418,7 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds) erts_link_release_both(ldp); else if (lnk) erts_link_release(lnk); - return reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR); + return reds_consumed; } int @@ -12893,7 +12899,9 @@ restart: switch (result) { case ERTS_DSIG_SEND_OK: + break; case ERTS_DSIG_SEND_TOO_LRG: /*SEND_SYSTEM_LIMIT*/ + erts_kill_dist_connection(ctx->dep, ctx->connection_id); break; case ERTS_DSIG_SEND_YIELD: /*SEND_YIELD_RETURN*/ case ERTS_DSIG_SEND_CONTINUE: { /*SEND_YIELD_CONTINUE*/ diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 711b73417d..6118c671ee 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -641,6 +641,7 @@ struct ErtsSchedulerData_ { ErtsSchedType type; Uint no; /* Scheduler number for normal schedulers */ Uint dirty_no; /* Scheduler number for dirty schedulers */ + int flxctr_slot_no; /* slot nr when a flxctr is used */ struct enif_environment_t *current_nif; Process *dirty_shadow_process; Port *current_port; @@ -1847,6 +1848,7 @@ int erts_resume_processes(ErtsProcList *); void erts_deep_process_dump(fmtfn_t, void *); Eterm erts_get_reader_groups_map(Process *c_p); +Eterm erts_get_decentralized_counter_groups_map(Process *c_p); Eterm erts_debug_reader_groups_map(Process *c_p, int groups); Uint erts_debug_nbalance(void); diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index ae7084b7f4..c85a7df5ec 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -635,9 +635,11 @@ write_sys_msg_to_port(Eterm unused_to, Eterm message) { byte *buffer; byte *ptr; - unsigned size; + Uint size; + + if (erts_encode_ext_size(message, &size) != ERTS_EXT_SZ_OK) + erts_exit(ERTS_ERROR_EXIT, "Internal error: System limit\n"); - size = erts_encode_ext_size(message); buffer = (byte *) erts_alloc(ERTS_ALC_T_TMP, size); ptr = buffer; diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 471c1c3938..ec67ab2aed 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -51,17 +51,18 @@ #define MAX_STRING_LEN 0xffff -/* - * MAX value for the creation field in pid, port and reference - * for the old PID_EXT, PORT_EXT, REFERENCE_EXT and NEW_REFERENCE_EXT. - * Older nodes (OTP 19-21) will send us these so we must be able to decode them. - * - * From OTP 22 DFLAG_BIG_CREATION is mandatory so this node will always - * encode with new big 32-bit creations using NEW_PID_EXT, NEW_PORT_EXT - * and NEWER_REFERENCE_EXT. +/* MAX value for the creation field in pid, port and reference + for the local node and for the current external format. + + Larger creation values than this are allowed in external pid, port and refs + encoded with NEW_PID_EXT, NEW_PORT_EXT and NEWER_REFERENCE_EXT. + The point here is to prepare for future upgrade to 32-bit creation. + OTP-19 (erts-8.0) can handle big creation values from other (newer) nodes, + but do not use big creation values for the local node yet, + as we still may have to communicate with older nodes. */ -#define ERTS_MAX_TINY_CREATION (3) -#define is_tiny_creation(Cre) ((unsigned)(Cre) <= ERTS_MAX_TINY_CREATION) +#define ERTS_MAX_LOCAL_CREATION (3) +#define is_valid_creation(Cre) ((unsigned)(Cre) <= ERTS_MAX_LOCAL_CREATION) #undef ERTS_DEBUG_USE_DIST_SEP #ifdef DEBUG @@ -111,13 +112,13 @@ static byte* dec_pid(ErtsDistExternal *, ErtsHeapFactory*, byte*, Eterm*, byte t static Sint decoded_size(byte *ep, byte* endp, int internal_tags, struct B2TContext_t*); static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1); -static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint flags, - Binary *context_b); +static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int level, + Uint flags, Binary *context_b); static Uint encode_size_struct2(ErtsAtomCacheMap *, Eterm, unsigned); struct TTBSizeContext_; -static int encode_size_struct_int(struct TTBSizeContext_*, ErtsAtomCacheMap *acmp, Eterm obj, - unsigned dflags, Sint *reds, Uint *res); +static ErtsExtSzRes encode_size_struct_int(struct TTBSizeContext_*, ErtsAtomCacheMap *acmp, + Eterm obj, unsigned dflags, Sint *reds, Uint *res); static Export binary_to_term_trap_export; static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1); @@ -602,49 +603,50 @@ done: return reds < 0 ? 0 : reds; } -int erts_encode_dist_ext_size(Eterm term, Uint32 flags, ErtsAtomCacheMap *acmp, - Uint* szp) +ErtsExtSzRes +erts_encode_dist_ext_size(Eterm term, Uint32 flags, ErtsAtomCacheMap *acmp, Uint* szp) { Uint sz; - if (encode_size_struct_int(NULL, acmp, term, flags, NULL, &sz)) { - return -1; - } else { + ErtsExtSzRes res = encode_size_struct_int(NULL, acmp, term, flags, NULL, &sz); + if (res == ERTS_EXT_SZ_OK) { #ifndef ERTS_DEBUG_USE_DIST_SEP if (!(flags & (DFLAG_DIST_HDR_ATOM_CACHE | DFLAG_NO_MAGIC))) #endif sz++ /* VERSION_MAGIC */; *szp += sz; - return 0; } + return res; } -int erts_encode_dist_ext_size_int(Eterm term, ErtsDSigSendContext *ctx, Uint* szp) +ErtsExtSzRes +erts_encode_dist_ext_size_ctx(Eterm term, ErtsDSigSendContext *ctx, Uint* szp) { Uint sz; - if (encode_size_struct_int(&ctx->u.sc, ctx->acmp, term, ctx->flags, &ctx->reds, &sz)) { - return -1; - } else { + ErtsExtSzRes res = encode_size_struct_int(&ctx->u.sc, ctx->acmp, term, + ctx->flags, &ctx->reds, &sz); + if (res == ERTS_EXT_SZ_OK) { #ifndef ERTS_DEBUG_USE_DIST_SEP if (!(ctx->flags & (DFLAG_DIST_HDR_ATOM_CACHE | DFLAG_NO_MAGIC))) #endif sz++ /* VERSION_MAGIC */; *szp += sz; - return 0; } + return res; } -Uint erts_encode_ext_size(Eterm term) +ErtsExtSzRes erts_encode_ext_size_2(Eterm term, unsigned dflags, Uint *szp) { - return encode_size_struct2(NULL, term, TERM_TO_BINARY_DFLAGS) - + 1 /* VERSION_MAGIC */; + ErtsExtSzRes res = encode_size_struct_int(NULL, NULL, term, dflags, + NULL, szp); + (*szp)++ /* VERSION_MAGIC */; + return res; } -Uint erts_encode_ext_size_2(Eterm term, unsigned dflags) +ErtsExtSzRes erts_encode_ext_size(Eterm term, Uint *szp) { - return encode_size_struct2(NULL, term, dflags) - + 1 /* VERSION_MAGIC */; + return erts_encode_ext_size_2(term, TERM_TO_BINARY_DFLAGS, szp); } Uint erts_encode_ext_size_ets(Eterm term) @@ -699,6 +701,7 @@ dist_ext_size(ErtsDistExternal *edep) } else { sz -= sizeof(ErtsAtomTranslationTable); } + ASSERT(sz % 4 == 0); return sz; } @@ -706,8 +709,9 @@ Uint erts_dist_ext_size(ErtsDistExternal *edep) { Uint sz = dist_ext_size(edep); + sz += 4; /* may need to pad to 8-byte-align ErtsDistExternalData */ sz += edep->data[0].frag_id * sizeof(ErtsDistExternalData); - return sz + ERTS_EXTRA_DATA_ALIGN_SZ(sz); + return sz; } Uint @@ -749,6 +753,8 @@ erts_make_dist_ext_copy(ErtsDistExternal *edep, ErtsDistExternal *new_edep) erts_ref_dist_entry(new_edep->dep); ep += dist_ext_sz; + ep += (UWord)ep & 4; /* 8-byte alignment for ErtsDistExternalData */ + ASSERT((UWord)ep % 8 == 0); new_edep->data = (ErtsDistExternalData*)ep; sys_memzero(new_edep->data, sizeof(ErtsDistExternalData) * edep->data->frag_id); @@ -1248,9 +1254,22 @@ static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1) { Eterm *tp = tuple_val(BIF_ARG_1); Eterm Term = tp[1]; - Eterm bt = tp[2]; + Eterm Opts = tp[2]; + Eterm bt = tp[3]; Binary *bin = erts_magic_ref2bin(bt); - Eterm res = erts_term_to_binary_int(BIF_P, Term, 0, 0,bin); + Eterm res = erts_term_to_binary_int(BIF_P, Term, Opts, 0, 0,bin); + if (is_non_value(res)) { + if (erts_set_gc_state(BIF_P, 1) + || MSO(BIF_P).overhead > BIN_VHEAP_SZ(BIF_P)) { + ERTS_VBUMP_ALL_REDS(BIF_P); + } + if (Opts == am_undefined) + ERTS_BIF_ERROR_TRAPPED1(BIF_P, SYSTEM_LIMIT, + bif_export[BIF_term_to_binary_1], Term); + else + ERTS_BIF_ERROR_TRAPPED2(BIF_P, SYSTEM_LIMIT, + bif_export[BIF_term_to_binary_2], Term, Opts); + } if (is_tuple(res)) { ASSERT(BIF_P->flags & F_DISABLE_GC); BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res); @@ -1267,7 +1286,12 @@ HIPE_WRAPPER_BIF_DISABLE_GC(term_to_binary, 1) BIF_RETTYPE term_to_binary_1(BIF_ALIST_1) { - Eterm res = erts_term_to_binary_int(BIF_P, BIF_ARG_1, 0, TERM_TO_BINARY_DFLAGS, NULL); + Eterm res = erts_term_to_binary_int(BIF_P, BIF_ARG_1, am_undefined, + 0, TERM_TO_BINARY_DFLAGS, NULL); + if (is_non_value(res)) { + ASSERT(!(BIF_P->flags & F_DISABLE_GC)); + BIF_ERROR(BIF_P, SYSTEM_LIMIT); + } if (is_tuple(res)) { erts_set_gc_state(BIF_P, 0); BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res); @@ -1326,7 +1350,12 @@ BIF_RETTYPE term_to_binary_2(BIF_ALIST_2) goto error; } - res = erts_term_to_binary_int(p, Term, level, flags, NULL); + res = erts_term_to_binary_int(p, Term, BIF_ARG_2, + level, flags, NULL); + if (is_non_value(res)) { + ASSERT(!(BIF_P->flags & F_DISABLE_GC)); + BIF_ERROR(BIF_P, SYSTEM_LIMIT); + } if (is_tuple(res)) { erts_set_gc_state(p, 0); BIF_TRAP1(&term_to_binary_trap_export,BIF_P,res); @@ -1875,8 +1904,17 @@ external_size_1(BIF_ALIST_1) { Process* p = BIF_P; Eterm Term = BIF_ARG_1; + Uint size; + + switch (erts_encode_ext_size(Term, &size)) { + case ERTS_EXT_SZ_SYSTEM_LIMIT: + BIF_ERROR(BIF_P, SYSTEM_LIMIT); + case ERTS_EXT_SZ_YIELD: + ERTS_INTERNAL_ERROR("Unexpected yield"); + case ERTS_EXT_SZ_OK: + break; + } - Uint size = erts_encode_ext_size(Term); if (IS_USMALL(0, size)) { BIF_RET(make_small(size)); } else { @@ -1919,7 +1957,15 @@ external_size_2(BIF_ALIST_2) goto error; } - size = erts_encode_ext_size_2(BIF_ARG_1, flags); + switch (erts_encode_ext_size_2(BIF_ARG_1, flags, &size)) { + case ERTS_EXT_SZ_SYSTEM_LIMIT: + BIF_ERROR(BIF_P, SYSTEM_LIMIT); + case ERTS_EXT_SZ_YIELD: + ERTS_INTERNAL_ERROR("Unexpected yield"); + case ERTS_EXT_SZ_OK: + break; + } + if (IS_USMALL(0, size)) { BIF_RET(make_small(size)); } else { @@ -2007,7 +2053,15 @@ erts_term_to_binary_simple(Process* p, Eterm Term, Uint size, int level, Uint fl Eterm erts_term_to_binary(Process* p, Eterm Term, int level, Uint flags) { Uint size; - size = encode_size_struct2(NULL, Term, flags) + 1 /* VERSION_MAGIC */; + switch (encode_size_struct_int(NULL, NULL, Term, flags, NULL, &size)) { + case ERTS_EXT_SZ_SYSTEM_LIMIT: + return THE_NON_VALUE; + case ERTS_EXT_SZ_YIELD: + ERTS_INTERNAL_ERROR("Unexpected yield"); + case ERTS_EXT_SZ_OK: + break; + } + size++; /* VERSION_MAGIC */; return erts_term_to_binary_simple(p, Term, size, level, flags); } @@ -2057,8 +2111,8 @@ static int ttb_context_destructor(Binary *context_bin) return 1; } -static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint flags, - Binary *context_b) +static Eterm erts_term_to_binary_int(Process* p, Eterm Term, Eterm opts, int level, + Uint flags, Binary *context_b) { Eterm *hp; Eterm res; @@ -2076,18 +2130,17 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla do { \ if (context_b == NULL) { \ context_b = erts_create_magic_binary(sizeof(TTBContext), \ - ttb_context_destructor); \ + ttb_context_destructor);\ context = ERTS_MAGIC_BIN_DATA(context_b); \ - sys_memcpy(context,&c_buff,sizeof(TTBContext)); \ + sys_memcpy(context,&c_buff,sizeof(TTBContext)); \ } \ } while (0) #define RETURN_STATE() \ do { \ - static const int TUPLE2_SIZE = 2 + 1; \ - hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE + TUPLE2_SIZE); \ + hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE + 1 + 3); \ c_term = erts_mk_magic_ref(&hp, &MSO(p), context_b); \ - res = TUPLE2(hp, Term, c_term); \ + res = TUPLE3(hp, Term, opts, c_term); \ BUMP_ALL_REDS(p); \ return res; \ } while (0); @@ -2113,11 +2166,17 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla int level; Uint flags; /* Try for fast path */ - if (encode_size_struct_int(&context->s.sc, NULL, Term, - context->s.sc.flags, &reds, &size) < 0) { + switch (encode_size_struct_int(&context->s.sc, NULL, Term, + context->s.sc.flags, &reds, &size)) { + case ERTS_EXT_SZ_SYSTEM_LIMIT: + BUMP_REDS(p, (initial_reds - reds) / TERM_TO_BINARY_LOOP_FACTOR); + return THE_NON_VALUE; + case ERTS_EXT_SZ_YIELD: EXPORT_CONTEXT(); /* Same state */ RETURN_STATE(); + case ERTS_EXT_SZ_OK: + break; } ++size; /* VERSION_MAGIC */ /* Move these to next state */ @@ -2379,8 +2438,7 @@ enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags) Eterm sysname = ((is_internal_pid(pid) && (dflags & DFLAG_INTERNAL_TAGS)) ? INTERNAL_LOCAL_SYSNAME : pid_node_name(pid)); Uint32 creation = pid_creation(pid); - - *ep++ = NEW_PID_EXT; + byte* tagp = ep++; /* insert atom here containing host and sysname */ ep = enc_atom(acmp, sysname, ep, dflags); @@ -2392,8 +2450,15 @@ enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags) ep += 4; put_int32(os, ep); ep += 4; - put_int32(creation, ep); - ep += 4; + if (creation <= ERTS_MAX_LOCAL_CREATION) { + *tagp = PID_EXT; + *ep++ = creation; + } else { + ASSERT(is_external_pid(pid)); + *tagp = NEW_PID_EXT; + put_int32(creation, ep); + ep += 4; + } return ep; } @@ -2513,7 +2578,7 @@ dec_pid(ErtsDistExternal *edep, ErtsHeapFactory* factory, byte* ep, if (tag == PID_EXT) { cre = get_int8(ep); ep += 1; - if (!is_tiny_creation(cre)) { + if (!is_valid_creation(cre)) { return NULL; } } else { @@ -2774,18 +2839,25 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_ref(obj)) ? INTERNAL_LOCAL_SYSNAME : ref_node_name(obj)); Uint32 creation = ref_creation(obj); + byte* tagp = ep++; ASSERT(dflags & DFLAG_EXTENDED_REFERENCES); erts_magic_ref_save_bin(obj); - *ep++ = NEWER_REFERENCE_EXT; i = ref_no_numbers(obj); put_int16(i, ep); ep += 2; ep = enc_atom(acmp, sysname, ep, dflags); - put_int32(creation, ep); - ep += 4; + if (creation <= ERTS_MAX_LOCAL_CREATION) { + *tagp = NEW_REFERENCE_EXT; + *ep++ = creation; + } else { + ASSERT(is_external_ref(obj)); + *tagp = NEWER_REFERENCE_EXT; + put_int32(creation, ep); + ep += 4; + } ref_num = ref_numbers(obj); for (j = 0; j < i; j++) { put_int32(ref_num[j], ep); @@ -2798,14 +2870,21 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_port(obj)) ? INTERNAL_LOCAL_SYSNAME : port_node_name(obj)); Uint32 creation = port_creation(obj); + byte* tagp = ep++; - *ep++ = NEW_PORT_EXT; ep = enc_atom(acmp, sysname, ep, dflags); j = port_number(obj); put_int32(j, ep); ep += 4; - put_int32(creation, ep); - ep += 4; + if (creation <= ERTS_MAX_LOCAL_CREATION) { + *tagp = PORT_EXT; + *ep++ = creation; + } else { + ASSERT(is_external_port(obj)); + *tagp = NEW_PORT_EXT; + put_int32(creation, ep); + ep += 4; + } break; } case LIST_DEF: @@ -3500,7 +3579,7 @@ dec_term_atom_common: if (tag == PORT_EXT) { cre = get_int8(ep); ep++; - if (!is_tiny_creation(cre)) { + if (!is_valid_creation(cre)) { goto error; } } @@ -3547,7 +3626,7 @@ dec_term_atom_common: cre = get_int8(ep); ep += 1; - if (!is_tiny_creation(cre)) { + if (!is_valid_creation(cre)) { goto error; } goto ref_ext_common; @@ -3561,7 +3640,7 @@ dec_term_atom_common: cre = get_int8(ep); ep += 1; - if (!is_tiny_creation(cre)) { + if (!is_valid_creation(cre)) { goto error; } r0 = get_int32(ep); @@ -4159,13 +4238,21 @@ error_hamt: to a sequence of bytes N.B. That this must agree with to_external2() above!!! (except for cached atoms) */ -static Uint encode_size_struct2(ErtsAtomCacheMap *acmp, Eterm obj, unsigned dflags) { - Uint res; - (void) encode_size_struct_int(NULL, acmp, obj, dflags, NULL, &res); - return res; +static Uint encode_size_struct2(ErtsAtomCacheMap *acmp, + Eterm obj, + unsigned dflags) { + Uint size; + ErtsExtSzRes res = encode_size_struct_int(NULL, acmp, obj, + dflags, NULL, &size); + /* + * encode_size_struct2() only allowed when + * we know the result will always be OK! + */ + ASSERT(res == ERTS_EXT_SZ_OK); (void) res; + return (Uint) size; } -static int +static ErtsExtSzRes encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, unsigned dflags, Sint *reds, Uint *res) { @@ -4198,7 +4285,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, ctx->obj = obj; ctx->result = result; WSTACK_SAVE(s, &ctx->wstack); - return -1; + return ERTS_EXT_SZ_YIELD; } switch (tag_val_def(obj)) { case NIL_DEF: @@ -4259,21 +4346,30 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, result += 1 + 4 + 1 + i; /* tag,size,sign,digits */ break; case EXTERNAL_PID_DEF: + if (external_pid_creation(obj) > ERTS_MAX_LOCAL_CREATION) + result += 3; + /*fall through*/ case PID_DEF: result += (1 + encode_size_struct2(acmp, pid_node_name(obj), dflags) + - 4 + 4 + 4); + 4 + 4 + 1); break; case EXTERNAL_REF_DEF: + if (external_ref_creation(obj) > ERTS_MAX_LOCAL_CREATION) + result += 3; + /*fall through*/ case REF_DEF: ASSERT(dflags & DFLAG_EXTENDED_REFERENCES); i = ref_no_numbers(obj); result += (1 + 2 + encode_size_struct2(acmp, ref_node_name(obj), dflags) + - 4 + 4*i); + 1 + 4*i); break; case EXTERNAL_PORT_DEF: + if (external_port_creation(obj) > ERTS_MAX_LOCAL_CREATION) + result += 3; + /*fall through*/ case PORT_DEF: result += (1 + encode_size_struct2(acmp, port_node_name(obj), dflags) + - 4 + 4); + 4 + 1); break; case LIST_DEF: { int is_str = is_external_string(obj, &m); @@ -4365,11 +4461,26 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, result += 32; /* Yes, including the tag */ } break; - case BINARY_DEF: - if (dflags & DFLAG_INTERNAL_TAGS) { + case BINARY_DEF: { + ProcBin* pb = (ProcBin*) binary_val(obj); + Uint tot_bytes = pb->size; + if (!(dflags & DFLAG_INTERNAL_TAGS)) { +#ifdef ARCH_64 + if (tot_bytes >= (Uint) 0xffffffff) { + if (pb->thing_word == HEADER_SUB_BIN) { + ErlSubBin* sub = (ErlSubBin*) pb; + tot_bytes += (sub->bitoffs + sub->bitsize+ 7) / 8; + } + if (tot_bytes > (Uint) 0xffffffff) { + WSTACK_DESTROY(s); + return ERTS_EXT_SZ_SYSTEM_LIMIT; + } + } +#endif + } + else { ProcBin* pb = (ProcBin*) binary_val(obj); Uint sub_extra = 0; - Uint tot_bytes = pb->size; if (pb->thing_word == HEADER_SUB_BIN) { ErlSubBin* sub = (ErlSubBin*) pb; pb = (ProcBin*) binary_val(sub->orig); @@ -4386,6 +4497,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, result += 1 + 4 + binary_size(obj) + 5; /* For unaligned binary */ break; + } case FUN_DEF: { ErlFunThing* funp = (ErlFunThing *) fun_val(obj); @@ -4418,7 +4530,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, break; default: - erts_exit(ERTS_ERROR_EXIT,"Internal data structure error (in encode_size_struct2)%x\n", + erts_exit(ERTS_ERROR_EXIT,"Internal data structure error (in encode_size_struct_int) %x\n", obj); } @@ -4458,7 +4570,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, *reds = r < 0 ? 0 : r; } *res = result; - return 0; + return ERTS_EXT_SZ_OK; } diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index 396cd9f802..b556c9076c 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -144,14 +144,6 @@ typedef struct erl_dist_external { ErtsAtomTranslationTable attab; } ErtsDistExternal; -#define ERTS_DIST_EXT_SIZE(EDEP) \ - (sizeof(ErtsDistExternal) \ - - (((EDEP)->flags & ERTS_DIST_EXT_ATOM_TRANS_TAB) \ - ? (ASSERT(0 <= (EDEP)->attab.size \ - && (EDEP)->attab.size <= ERTS_ATOM_CACHE_SIZE), \ - sizeof(Eterm)*(ERTS_ATOM_CACHE_SIZE - (EDEP)->attab.size)) \ - : sizeof(ErtsAtomTranslationTable))) - typedef struct { byte *extp; int exttmp; @@ -172,14 +164,21 @@ byte *erts_encode_ext_dist_header_setup(byte *, ErtsAtomCacheMap *, Uint, Eterm) byte *erts_encode_ext_dist_header_fragment(byte **, Uint, Eterm); Sint erts_encode_ext_dist_header_finalize(ErtsDistOutputBuf*, DistEntry *, Uint32 dflags, Sint reds); struct erts_dsig_send_context; -int erts_encode_dist_ext_size(Eterm, Uint32, ErtsAtomCacheMap*, Uint* szp); -int erts_encode_dist_ext_size_int(Eterm term, struct erts_dsig_send_context* ctx, Uint* szp); + +typedef enum { + ERTS_EXT_SZ_OK, + ERTS_EXT_SZ_YIELD, + ERTS_EXT_SZ_SYSTEM_LIMIT +} ErtsExtSzRes; + +ErtsExtSzRes erts_encode_dist_ext_size(Eterm, Uint32, ErtsAtomCacheMap*, Uint* szp); +ErtsExtSzRes erts_encode_dist_ext_size_ctx(Eterm term, struct erts_dsig_send_context* ctx, Uint* szp); struct TTBEncodeContext_; int erts_encode_dist_ext(Eterm, byte **, Uint32, ErtsAtomCacheMap *, struct TTBEncodeContext_ *, Sint* reds); -Uint erts_encode_ext_size(Eterm); -Uint erts_encode_ext_size_2(Eterm, unsigned); +ErtsExtSzRes erts_encode_ext_size(Eterm, Uint *szp); +ErtsExtSzRes erts_encode_ext_size_2(Eterm, unsigned, Uint *szp); Uint erts_encode_ext_size_ets(Eterm); void erts_encode_ext(Eterm, byte **); byte* erts_encode_ext_ets(Eterm, byte *, struct erl_off_heap_header** ext_off_heap); diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index f9bbe4167f..4c8d3d3dbe 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1216,10 +1216,11 @@ Uint64 erts_timestamp_millis(void); Export* erts_find_function(Eterm, Eterm, unsigned int, ErtsCodeIndex); -void *erts_calc_stacklimit(char *prev_c, UWord stacksize); -int erts_check_below_limit(char *ptr, char *limit); -int erts_check_above_limit(char *ptr, char *limit); -void *erts_ptr_id(void *ptr); +/* ERTS_NOINLINE prevents link-time optimization across modules */ +void *erts_calc_stacklimit(char *prev_c, UWord stacksize) ERTS_NOINLINE; +int erts_check_below_limit(char *ptr, char *limit) ERTS_NOINLINE; +int erts_check_above_limit(char *ptr, char *limit) ERTS_NOINLINE; +void *erts_ptr_id(void *ptr) ERTS_NOINLINE; Eterm store_external_or_ref_in_proc_(Process *, Eterm); Eterm store_external_or_ref_(Uint **, ErlOffHeap*, Eterm); diff --git a/erts/emulator/beam/instrs.tab b/erts/emulator/beam/instrs.tab index 692408e212..462ee77e6f 100644 --- a/erts/emulator/beam/instrs.tab +++ b/erts/emulator/beam/instrs.tab @@ -1001,7 +1001,7 @@ catch_end(Y) { } /* only x(2) is included in the rootset here */ if (E - HTOP < 3) { - SWAPOUT; + $GC_SWAPOUT(); PROCESS_MAIN_CHK_LOCKS(c_p); FCALLS -= erts_garbage_collect_nobump(c_p, 3, reg+2, 1, FCALLS); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index b961c639f5..45fef0c0e5 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -4450,6 +4450,7 @@ erts_port_call(Process* c_p, char input_buf[256]; char *bufp; byte *endp; + Uint uintsz; ErlDrvSizeT size; int try_call; erts_aint32_t sched_flags; @@ -4462,7 +4463,9 @@ erts_port_call(Process* c_p, try_call = !(sched_flags & ERTS_PTS_FLGS_FORCE_SCHEDULE_OP); - size = erts_encode_ext_size(data); + if (erts_encode_ext_size(data, &uintsz) != ERTS_EXT_SZ_OK) + return ERTS_PORT_OP_BADARG; + size = (ErlDrvSizeT) uintsz; if (!try_call) bufp = erts_alloc(ERTS_ALC_T_DRV_CALL_DATA, size); @@ -5295,44 +5298,31 @@ erts_get_port_names(Eterm id, ErlDrvPort drv_port) pnp->driver_name = NULL; } else { - int do_realloc = 1; - int len = -1; - size_t pnp_len = sizeof(ErtsPortNames); -#ifndef DEBUG - pnp_len += 100; /* In most cases 100 characters will be enough... */ - ASSERT(prt->common.id == id); -#endif - pnp = erts_alloc(ERTS_ALC_T_PORT_NAMES, pnp_len); - do { - int nlen; - char *name, *driver_name; - if (len > 0) { - erts_free(ERTS_ALC_T_PORT_NAMES, pnp); - pnp_len = sizeof(ErtsPortNames) + len; - pnp = erts_alloc(ERTS_ALC_T_PORT_NAMES, pnp_len); - } - name = prt->name; - len = nlen = name ? sys_strlen(name) + 1 : 0; - driver_name = (prt->drv_ptr ? prt->drv_ptr->name : NULL); - len += driver_name ? sys_strlen(driver_name) + 1 : 0; - if (len <= pnp_len - sizeof(ErtsPortNames)) { - if (!name) - pnp->name = NULL; - else { - pnp->name = ((char *) pnp) + sizeof(ErtsPortNames); - sys_strcpy(pnp->name, name); - } - if (!driver_name) - pnp->driver_name = NULL; - else { - pnp->driver_name = (((char *) pnp) - + sizeof(ErtsPortNames) - + nlen); - sys_strcpy(pnp->driver_name, driver_name); - } - do_realloc = 0; - } - } while (do_realloc); + int len; + int nlen; + char *driver_name; + + len = nlen = prt->name ? sys_strlen(prt->name) + 1 : 0; + driver_name = (prt->drv_ptr ? prt->drv_ptr->name : NULL); + len += driver_name ? sys_strlen(driver_name) + 1 : 0; + + pnp = erts_alloc(ERTS_ALC_T_PORT_NAMES, + sizeof(ErtsPortNames) + len); + + if (!prt->name) + pnp->name = NULL; + else { + pnp->name = ((char *) pnp) + sizeof(ErtsPortNames); + sys_strcpy(pnp->name, prt->name); + } + if (!driver_name) + pnp->driver_name = NULL; + else { + pnp->driver_name = (((char *) pnp) + + sizeof(ErtsPortNames) + + nlen); + sys_strcpy(pnp->driver_name, driver_name); + } } return pnp; } diff --git a/erts/emulator/beam/macros.tab b/erts/emulator/beam/macros.tab index 494fe8961e..1b5e5f66b0 100644 --- a/erts/emulator/beam/macros.tab +++ b/erts/emulator/beam/macros.tab @@ -63,10 +63,21 @@ JUMP(Fail) { Goto(*I); } +GC_SWAPOUT() { + // + // Since a garbage collection is expensive anyway, we can afford + // to save the instruction counter so that the correct function will + // be pointed in the crash dump if the garbage collection fails + // because of insufficient memory. + // + SWAPOUT; + c_p->i = I; +} + GC_TEST(Ns, Nh, Live) { Uint need = $Nh + $Ns; if (ERTS_UNLIKELY(E - HTOP < need)) { - SWAPOUT; + $GC_SWAPOUT(); PROCESS_MAIN_CHK_LOCKS(c_p); FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, $Live, FCALLS); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); @@ -79,7 +90,7 @@ GC_TEST(Ns, Nh, Live) { GC_TEST_PRESERVE(NeedHeap, Live, PreserveTerm) { Uint need = $NeedHeap; if (ERTS_UNLIKELY(E - HTOP < need)) { - SWAPOUT; + $GC_SWAPOUT(); reg[$Live] = $PreserveTerm; PROCESS_MAIN_CHK_LOCKS(c_p); FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, $Live+1, FCALLS); diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 7a125b0f67..10ca74cd60 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1690,9 +1690,14 @@ i_plus S1=c S2=c Fail Dst => move S1 x | i_plus x S2 Fail Dst i_plus xy xyc j? d -i_minus x x j? d -i_minus c x j? d -i_minus s s j? d +# A minus instruction with a constant right operand will be +# converted to an i_increment instruction, except in guards or +# when the negated value of the constant won't fit in a guard. +# Therefore, it very rare. +i_minus S1 S2=c Fail Dst => move S2 x | i_minus S1 x Fail Dst + +i_minus xy xy j? d +i_minus c xy j? d i_times j? s s d diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index a6312293cc..c261c8e117 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -63,6 +63,14 @@ # endif #endif +#ifndef ERTS_NOINLINE +# if ERTS_AT_LEAST_GCC_VSN__(3,1,1) +# define ERTS_NOINLINE __attribute__((__noinline__)) +# else +# define ERTS_NOINLINE +# endif +#endif + #if defined(DEBUG) || defined(ERTS_ENABLE_LOCK_CHECK) # undef ERTS_CAN_INLINE # define ERTS_CAN_INLINE 0 diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c index a3069e419a..9eb020d070 100644 --- a/erts/emulator/beam/time.c +++ b/erts/emulator/beam/time.c @@ -316,7 +316,7 @@ struct ErtsTimerWheel_ { #define ERTS_TW_SLOT_AT_ONCE (-1) #define ERTS_TW_BUMP_LATER_WHEEL(TIW) \ - ((tiw)->pos + ERTS_TW_LATER_WHEEL_SLOT_SIZE >= (TIW)->later.pos) + ((TIW)->pos + ERTS_TW_LATER_WHEEL_SLOT_SIZE >= (TIW)->later.pos) static int bump_later_wheel(ErtsTimerWheel *tiw, int *yield_count_p); @@ -701,7 +701,8 @@ remove_timer(ErtsTimerWheel *tiw, ErtsTWheelTimer *p) if (slot < ERTS_TW_SOON_WHEEL_END_SLOT) { if (empty_slot && tiw->true_next_timeout_time - && p->timeout_pos == tiw->next_timeout_pos) { + && p->timeout_pos == tiw->next_timeout_pos + && tiw->yield_slot == ERTS_TW_SLOT_INACTIVE) { tiw->true_next_timeout_time = 0; } if (--tiw->soon.nto == 0) @@ -714,7 +715,8 @@ remove_timer(ErtsTimerWheel *tiw, ErtsTWheelTimer *p) ErtsMonotonicTime tpos = tiw->later.min_tpos; tpos &= ERTS_TW_LATER_WHEEL_POS_MASK; tpos -= ERTS_TW_LATER_WHEEL_SLOT_SIZE; - if (tpos == tiw->next_timeout_pos) + if (tpos == tiw->next_timeout_pos + && tiw->yield_slot == ERTS_TW_SLOT_INACTIVE) tiw->true_next_timeout_time = 0; } if (--tiw->later.nto == 0) { @@ -908,7 +910,6 @@ erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time) { ErtsMonotonicTime tmp_slots = bump_to - tiw->pos; - tmp_slots = (bump_to - tiw->pos); if (tmp_slots < ERTS_TW_SOON_WHEEL_SIZE) slots = (int) tmp_slots; else diff --git a/erts/emulator/internal_doc/CarrierMigration.md b/erts/emulator/internal_doc/CarrierMigration.md index bb3d8aac28..40f6031ca8 100644 --- a/erts/emulator/internal_doc/CarrierMigration.md +++ b/erts/emulator/internal_doc/CarrierMigration.md @@ -1,6 +1,9 @@ Carrier Migration ================= +Introduction +------------ + The ERTS memory allocators manage memory blocks in two types of raw memory chunks. We call these chunks of raw memory *carriers*. Single-block carriers which only contain one large block, @@ -141,11 +144,11 @@ Since the carrier has been unlinked from the data structure of available free blocks, no more allocations will be made in the carrier. -The allocator instance that created a carrier is called its **owner**. +The allocator instance that created a carrier is called its *owner*. Ownership never changes. The allocator instance that has the responsibility to perform deallocations in a -carrier is called its **employer**. The employer may also perform allocations if +carrier is called its *employer*. The employer may also perform allocations if the carrier is not in the pool. Employment may change when a carrier is fetched from or inserted into the pool. @@ -153,14 +156,14 @@ Deallocations in a carrier, while it remains in the pool, is always performed the owner. That is, all pooled carriers are employed by their owners. Each carrier has an atomic word containing a pointer to the employing allocator -instance and three bit flags; IN_POOL, BUSY and HOMECOMING. +instance and three bit flags; IN\_POOL, BUSY and HOMECOMING. When fetching a carrier from the pool, employment may change and further deallocations in the carrier will be redirected to the new employer using the delayed dealloc functionality. When a foreign allocator instance abandons a carrier back into the pool, it will -also pass it back to its **owner** using the delayed dealloc queue. When doing +also pass it back to its *owner* using the delayed dealloc queue. When doing this it will set the HOMECOMING bit flag to mark it as "enqueued". The owner will later clear the HOMECOMING bit when the carrier is dequeued. This mechanism prevents a carrier from being enqueued again before it has been dequeued. @@ -180,14 +183,14 @@ back to the owner for deallocation using the delayed dealloc functionality. In short: -* The allocator instance that created a carrier **owns** it. -* An empty carrier is always deallocated by its **owner**. -* **Ownership** never changes. -* The allocator instance that uses a carrier **employs** it. -* An **employer** can abandon a carrier into the pool. +* The allocator instance that created a carrier *owns* it. +* An empty carrier is always deallocated by its *owner*. +* *Ownership* never changes. +* The allocator instance that uses a carrier *employs* it. +* An *employer* can abandon a carrier into the pool. * Pooled carriers are not allocated from. -* Pooled carriers are always **employed** by their **owner**. -* **Employment** can only change from **owner** to a foreign allocator +* Pooled carriers are always *employed* by their *owner*. +* *Employment* can only change from *owner* to a foreign allocator when a carrier is fetched from the pool. @@ -229,7 +232,7 @@ carrier. When the cluster gets to the same size as the search limit, all searches will essentially fail. To counter the "bad cluster" problem and also ease the contention, the -search will now always start by first looking at the allocators **own** +search will now always start by first looking at the allocators *own* carriers. That is, carriers that were initially created by the allocator itself and later had been abandoned to the pool. If none of our own abandoned carrier would do, then the search continues into the diff --git a/erts/emulator/internal_doc/CodeLoading.md b/erts/emulator/internal_doc/CodeLoading.md index 151b9cd57c..0b2e3070e7 100644 --- a/erts/emulator/internal_doc/CodeLoading.md +++ b/erts/emulator/internal_doc/CodeLoading.md @@ -45,7 +45,7 @@ free to schedule other work while the second loader is waiting. (See `erts_release_code_write_permission`). The ability to prepare several modules in parallel is not currently -used as almost all code loading is serialized by the code_server +used as almost all code loading is serialized by the code\_server process. The BIF interface is however prepared for this. erlang:prepare_loading(Module, Code) -> LoaderState @@ -71,8 +71,8 @@ structures. These *code access structures* are * Export table. One entry for every exported function. * Module table. One entry for each loaded module. -* "beam_catches". Identifies jump destinations for catch instructions. -* "beam_ranges". Map code address to function and line in source file. +* "beam\_catches". Identifies jump destinations for catch instructions. +* "beam\_ranges". Map code address to function and line in source file. The most frequently used of these structures is the export table that is accessed in run time for every executed external function call to diff --git a/erts/emulator/internal_doc/GarbageCollection.md b/erts/emulator/internal_doc/GarbageCollection.md index 1d9e3f4160..a1627b3233 100644 --- a/erts/emulator/internal_doc/GarbageCollection.md +++ b/erts/emulator/internal_doc/GarbageCollection.md @@ -1,6 +1,6 @@ # Erlang Garbage Collector -Erlang manages dynamic memory with a [tracing garbage collector](https://en.wikipedia.org/wiki/Tracing_garbage_collection). More precisely a per process generational semi-space copying collector using [Cheney's](#cheney) copy collection algorithm together with a global large object space. +Erlang manages dynamic memory with a [tracing garbage collector](https://en.wikipedia.org/wiki/Tracing_garbage_collection). More precisely a per process generational semi-space copying collector using Cheney's copy collection algorithm together with a global large object space. (See C. J. Cheney in [References](#references).) ## Overview @@ -12,12 +12,11 @@ Terms are created on the heap by evaluating expressions. There are two major typ Let's look at an example that returns a tuple with the newly created data. -```erlang -data(Foo) -> - Cons = [42|Foo], - Literal = {text, "hello world!"}, - {tag, Cons, Literal}. -``` + + data(Foo) -> + Cons = [42|Foo], + Literal = {text, "hello world!"}, + {tag, Cons, Literal}. In this example we first create a new cons cell with an integer and a tuple with some text. Then a tuple of size three wrapping the other values with an atom tag is created and returned. @@ -25,7 +24,6 @@ On the heap tuples require a word size for each of its elements as well as for t Compiling this code to beam assembly (`erlc -S`) shows exactly what is happening. -```erlang ... {test_heap,6,1}. {put_list,{integer,42},{x,0},{x,1}}. @@ -34,9 +32,8 @@ Compiling this code to beam assembly (`erlc -S`) shows exactly what is happening {put,{x,1}}. {put,{literal,{text,"hello world!"}}}. return. -``` -Looking at the assembler code we can see three things; The heap requirement in this function turns out to be only six words, as seen by the `{test_heap,6,1}` instruction. All the allocations are combined to a single instruction. The bulk of the data `{text, "hello world!"}` is a *literal*. Literals, sometimes referred to as constants, are not allocated in the function since they are a part of the module and allocated at load time. +Looking at the assembler code we can see three things: The heap requirement in this function turns out to be only six words, as seen by the `{test_heap,6,1}` instruction. All the allocations are combined to a single instruction. The bulk of the data `{text, "hello world!"}` is a *literal*. Literals, sometimes referred to as constants, are not allocated in the function since they are a part of the module and allocated at load time. If there is not enough space available on the heap to satisfy the `test_heap` instructions request for memory, then a garbage collection is initiated. It may happen immediately in the `test_heap` instruction, or it can be delayed until a later time depending on what state the process is in. If the garbage collection is delayed, any memory needed will be allocated in heap fragments. Heap fragments are extra memory blocks that are a part of the young heap, but are not allocated in the contigious area where terms normally reside. See [The young heap](#the-young-heap) for more details. @@ -50,11 +47,9 @@ It follows all the pointers from the root-set to the heap and copies each term w After the header word has been copied a [*move marker*](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.h#L45-L46) is destructively placed in it pointing to the term in the *to space*. Any other term that points to the already moved term will [see this move marker](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.c#L1125) and copy the referring pointer instead. For example, if the have the following Erlang code: -```erlang -foo(Arg) -> - T = {test, Arg}, - {wrapper, T, T, T}. -``` + foo(Arg) -> + T = {test, Arg}, + {wrapper, T, T, T}. Only one copy of T exists on the heap and during the garbage collection only the first time T is encountered will it be copied. @@ -86,15 +81,15 @@ In the next garbage collection, any pointers to the old heap will be ignored and Generational garbage collection aims to increase performance at the expense of memory. This is achieved because only the young, smaller, heap is considered in most garbage collections. -The generational [hypothesis](#ungar) predicts that most terms tend to die young, and for an immutable language such as Erlang, young terms die even faster than in other languages. So for most usage patterns the data in the new heap will die very soon after it is allocated. This is good because it limits the amount of data copied to the old heap and also because the garbage collection algorithm used is proportional to the amount of live data on the heap. +The generational hypothesis predicts that most terms tend to die young (see D. Ungar in [References](#references)), and for an immutable language such as Erlang, young terms die even faster than in other languages. So for most usage patterns the data in the new heap will die very soon after it is allocated. This is good because it limits the amount of data copied to the old heap and also because the garbage collection algorithm used is proportional to the amount of live data on the heap. One critical issue to note here is that any term on the young heap can reference terms on the old heap but *no* term on the old heap may refer to a term on the young heap. This is due to the nature of the copy algorithm. Anything referenced by an old heap term is not included in the reference tree, root-set and its followers, and hence is not copied. If it was, the data would be lost, fire and brimstone would rise to cover the earth. Fortunately, this comes naturally for Erlang because the terms are immutable and thus there can be no pointers modified on the old heap to point to the young heap. -To reclaim data from the old heap, both young and old heaps are included during the collection and copied to a common *to space*. Both the *from space* of the young and old heap are then deallocated and the procedure will start over from the beginning. This type of garbage collection is called a full sweep and is triggered when the size of the area under the high-watermark is larger than the size of the free area of the old heap. It can also be triggered by doing a manual call to [erlang:garbage_collect()](http://erlang.org/doc/man/erlang.html#garbage_collect-0), or by running into the young garbage collection limit set by [spawn_opt(fun(),[{fullsweep_after, N}])](http://erlang.org/doc/man/erlang.html#spawn_opt-4) where N is the number of young garbage collections to do before forcing a garbage collection of both young and old heap. +To reclaim data from the old heap, both young and old heaps are included during the collection and copied to a common *to space*. Both the *from space* of the young and old heap are then deallocated and the procedure will start over from the beginning. This type of garbage collection is called a full sweep and is triggered when the size of the area under the high-watermark is larger than the size of the free area of the old heap. It can also be triggered by doing a manual call to [erlang:garbage_collect()](http://erlang.org/doc/man/erlang.html#garbage_collect-0), or by running into the young garbage collection limit set by [spawn\_opt(fun(),[{fullsweep\_after, N}\])](http://erlang.org/doc/man/erlang.html#spawn_opt-4) where N is the number of young garbage collections to do before forcing a garbage collection of both young and old heap. ## The young heap -The young heap, or the allocation heap, consists of the stack and heap as described in the Overview. However, it also includes any heap fragments that are attached to the heap. All of the heap fragments are considered to be above the high-watermark and part of the young generation. Heap fragments contain terms that either did not fit on the heap, or were created by another process and then attached to the heap. For instance if the bif binary_to_term created a term which does not fit on the current heap without doing a garbage collection, it will create a heap-fragment for the term and then schedule a garbage collection for later. Also if a message is sent to the process, the payload may be placed in a heap-fragment and that fragment is added to young heap when the message is matched in a receive clause. +The young heap, or the allocation heap, consists of the stack and heap as described in the Overview. However, it also includes any heap fragments that are attached to the heap. All of the heap fragments are considered to be above the high-watermark and part of the young generation. Heap fragments contain terms that either did not fit on the heap, or were created by another process and then attached to the heap. For instance if the bif `binary_to_term/1` created a term which does not fit on the current heap without doing a garbage collection, it will create a heap-fragment for the term and then schedule a garbage collection for later. Also if a message is sent to the process, the payload may be placed in a heap-fragment and that fragment is added to young heap when the message is matched in a receive clause. This procedure differs from how it worked prior to Erlang/OTP 19.0. Before 19.0, only a contiguous memory block where the young heap and stack resided was considered to be part of the young heap. Heap fragments and messages were immediately copied into the young heap before they could be inspected by the Erlang program. The behaviour introduced in 19.0 is superior in many ways - most significantly it reduces the number of necessary copy operations and the root set for garbage collection. @@ -118,21 +113,19 @@ The old heap is always one step ahead in the heap growth stages than the young h When garbage collecting a heap (young or old) all literals are left in place and not copied. To figure out if a term should be copied or not when doing a garbage collection the following pseudo code is used: -```c -if (erts_is_literal(ptr) || (on_old_heap(ptr) && !fullsweep)) { - /* literal or non fullsweep - do not copy */ -} else { - copy(ptr); -} -``` + if (erts_is_literal(ptr) || (on_old_heap(ptr) && !fullsweep)) { + /* literal or non fullsweep - do not copy */ + } else { + copy(ptr); + } The [`erts_is_literal`](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/global.h#L1452-L1465) check works differently on different architectures and operating systems. -On 64 bit systems that allow mapping of unreserved virtual memory areas (most operating systems except Windows), an area of size 1 GB (by default) is mapped and then all literals are placed within that area. Then all that has to be done to determine if something is a literal or not is [two quick pointer checks](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_alloc.h#L322-L324). This system relies on the fact that a memory page that has not been touched yet does not take any actual space. So even if 1 GB of virtual memory is mapped, only the memory which is actually needed for literals is allocated in ram. The size of the literal area is configurable through the +MIscs erts_alloc option. +On 64 bit systems that allow mapping of unreserved virtual memory areas (most operating systems except Windows), an area of size 1 GB (by default) is mapped and then all literals are placed within that area. Then all that has to be done to determine if something is a literal or not is [two quick pointer checks](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_alloc.h#L322-L324). This system relies on the fact that a memory page that has not been touched yet does not take any actual space. So even if 1 GB of virtual memory is mapped, only the memory which is actually needed for literals is allocated in ram. The size of the literal area is configurable through the +MIscs erts\_alloc option. On 32 bit systems, there is not enough virtual memory space to allocate 1 GB for just literals, so instead small 256 KB sized literal regions are created on demand and a card mark bit-array of the entire 32 bit memory space is then used to determine if a term is a literal or not. Since the total memory space is only 32 bits, the card mark bit-array is only 256 words large. On a 64 bit system the same bit-array would have to be 1 tera words large, so this technique is only viable on 32 bit systems. Doing [lookups in the array](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_alloc.h#L316-L319) is a little more expensive then just doing the pointer checks that can be done in 64 bit systems, but not extremely so. -On 64 bit windows, on which erts_alloc cannot do unreserved virtual memory mappings, a [special tag](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_term.h#L59) within the Erlang term object is used to determine if something [is a literal or not](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_term.h#L248-L252). This is very cheap, however, the tag is only available on 64 bit machines, and it is possible to do a great deal of other nice optimizations with this tag in the future (like for instance a more compact list implementation) so it is not used on operating systems where it is not needed. +On 64 bit windows, on which erts\_alloc cannot do unreserved virtual memory mappings, a [special tag](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_term.h#L59) within the Erlang term object is used to determine if something [is a literal or not](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_term.h#L248-L252). This is very cheap, however, the tag is only available on 64 bit machines, and it is possible to do a great deal of other nice optimizations with this tag in the future (like for instance a more compact list implementation) so it is not used on operating systems where it is not needed. This behaviour is different from how it worked prior to Erlang/OTP 19.0. Before 19.0 the literal check was done by checking if the pointer pointed to the young or old heap block. If it did not, then it was considered a literal. This lead to considerable overhead and strange memory usage scenarios, so it was removed in 19.0. @@ -182,6 +175,8 @@ Using `on_heap` will force all messages to be part of on the young heap which wi Which one of these strategies is best depends a lot on what the process is doing and how it interacts with other processes. So, as always, profile the application and see how it behaves with the different options. - <a name="cheney">[1]</a>: C. J. Cheney. A nonrecursive list compacting algorithm. Commun. ACM, 13(11):677–678, Nov. 1970. +## References + +C. J. Cheney. A nonrecursive list compacting algorithm. Commun. ACM, 13(11):677–678, Nov. 1970. - <a name="ungar">[2]</a>: D. Ungar. Generation scavenging: A non-disruptive high performance storage reclamation algorithm. SIGSOFT Softw. Eng. Notes, 9(3):157–167, Apr. 1984. +D. Ungar. Generation scavenging: A non-disruptive high performance storage reclamation algorithm. SIGSOFT Softw. Eng. Notes, 9(3):157–167, Apr. 1984. diff --git a/erts/emulator/internal_doc/PTables.md b/erts/emulator/internal_doc/PTables.md index 6fe0e7665d..ef61963a40 100644 --- a/erts/emulator/internal_doc/PTables.md +++ b/erts/emulator/internal_doc/PTables.md @@ -85,13 +85,13 @@ following: 3. Depending on use, issue appropriate memory barrier. A common barrier used is a barrier with acquire semantics. On - x86/x86_64 this maps to a compiler barrier preventing the compiler + x86/x86\_64 this maps to a compiler barrier preventing the compiler to reorder instructions, but on other hardware often some kind of light weight hardware memory barrier is also needed. When comparing with a locked approach, at least one heavy weight memory barrier will be issued when locking the lock on most, if - not all, hardware architectures (including x86/x86_64), and often + not all, hardware architectures (including x86/x86\_64), and often some kind of light weight memory barrier will be issued when unlocking the lock. diff --git a/erts/emulator/internal_doc/SuperCarrier.md b/erts/emulator/internal_doc/SuperCarrier.md index acf722ea37..f52c6613d5 100644 --- a/erts/emulator/internal_doc/SuperCarrier.md +++ b/erts/emulator/internal_doc/SuperCarrier.md @@ -5,7 +5,7 @@ A super carrier is large memory area, allocated at VM start, which can be used during runtime to allocate normal carriers from. The super carrier feature was introduced in OTP R16B03. It is -enabled with command line option +MMscs <size in Mb> +enabled with command line option +MMscs <size in Mb> and can be configured with other options. Problem @@ -65,7 +65,7 @@ carrier is full. ### Implementation ### -The entire super carrier implementation is kept in erl_mmap.c. The +The entire super carrier implementation is kept in erl\_mmap.c. The name suggest that it can be viewed as our own mmap implementation. A super carrier needs to satisfy two slightly different kinds of @@ -98,8 +98,8 @@ other. ### Data structures ### -The MBC area is called **sa** as in super aligned and the SBC area is -called **sua** as in super un-aligned. +The MBC area is called *sa* as in super aligned and the SBC area is +called *sua* as in super un-aligned. Note that the "super" in super alignment and the "super" in super carrier has nothing to do with each other. We could have choosen @@ -128,7 +128,7 @@ down or up. We need to keep track of all the free segments in order to reuse them for new carrier allocations. One initial idea was to use the same mechanism that is used to keep track of free blocks within MBCs -(alloc_util and the different strategies). However, that would not be +(alloc\_util and the different strategies). However, that would not be as straight forward as one can think and can also waste quite a lot of memory as it uses prepended block headers. The granularity of the super carrier is one memory page (usually 4kb). We want to allocate diff --git a/erts/emulator/internal_doc/Tracing.md b/erts/emulator/internal_doc/Tracing.md index 7f97f64765..196ae0dd4e 100644 --- a/erts/emulator/internal_doc/Tracing.md +++ b/erts/emulator/internal_doc/Tracing.md @@ -37,6 +37,7 @@ what different type of break actions that are enabled. Same Same but Different ----------------------- + Even though `trace_pattern` use the same technique as the non-blocking code loading with replicated generations of data structures and an atomic switch, the implementations are quite separate from each @@ -72,6 +73,7 @@ aligned write operation on all hardware architectures we use. Adding a new Breakpoint ----------------------- + This is a simplified sequence describing what `trace_pattern` goes through when adding a new breakpoint. @@ -82,7 +84,7 @@ through when adding a new breakpoint. instruction word in the breakpoint. 3. Write a pointer to the breakpoint at offset -4 from the first - instruction "func_info" header. + instruction "func\_info" header. 4. Set the staging part of the breakpoint as enabled with specified breakpoint data. @@ -139,7 +141,7 @@ and removing breakpoints. 2. Allocate new breakpoint structures with a disabled active part and the original beam instruction. Write a pointer to the breakpoint in - "func_info" header at offset -4. + "func\_info" header at offset -4. 3. Update the staging part of all affected breakpoints. Disable breakpoints that are to be removed. diff --git a/erts/emulator/internal_doc/beam_makeops.md b/erts/emulator/internal_doc/beam_makeops.md index 1da8d2ab05..2880099b70 100644 --- a/erts/emulator/internal_doc/beam_makeops.md +++ b/erts/emulator/internal_doc/beam_makeops.md @@ -403,7 +403,7 @@ A line with `//` is also a comment. It is recommended to only use this style of comments in files that define implementations of instructions. -A long line can be broken into shorter lines by a placing a`\` before +A long line can be broken into shorter lines by a placing a `\` before the newline. ### Variable definitions ### @@ -1159,7 +1159,6 @@ implementation of `gen_element()`: return op; } -} ### Defining the implementation ### @@ -1452,7 +1451,7 @@ optionally additional heap space. ##### The NEXT_INSTRUCTION pre-bound variable ##### -The NEXT_INSTRUCTION is a pre-bound variable that is available in +The NEXT\_INSTRUCTION is a pre-bound variable that is available in all instructions. It expands to the address of the next instruction. Here is an example: @@ -1545,7 +1544,7 @@ register, the pointer will no longer be valid. (Y registers are stored on the stack.) In those circumstances, `$REFRESH_GEN_DEST()` must be invoked -to set up the pointer again. **beam\_makeops** will notice +to set up the pointer again. **beam\_makeops** will notice if there is a call to a function that does a garbage collection and `$REFRESH_GEN_DEST()` is not called. diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c index 252aa3c835..8a69052935 100644 --- a/erts/emulator/nifs/common/net_nif.c +++ b/erts/emulator/nifs/common/net_nif.c @@ -274,10 +274,10 @@ ENET_NIF_FUNCS static ERL_NIF_TERM ncommand(ErlNifEnv* env, ERL_NIF_TERM cmd); static ERL_NIF_TERM ngethostname(ErlNifEnv* env); -static ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, - const SocketAddress* saP, - SOCKLEN_T saLen, - int flags); +static ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, + const ESockAddress* saP, + SOCKLEN_T saLen, + int flags); static ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env, char* host, char* serv); @@ -627,12 +627,12 @@ ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - ERL_NIF_TERM result; - ERL_NIF_TERM eSockAddr, eFlags; - int flags = 0; // Just in case... - SocketAddress sa; - SOCKLEN_T saLen = 0; // Just in case... - char* xres; + ERL_NIF_TERM result; + ERL_NIF_TERM eSockAddr, eFlags; + int flags = 0; // Just in case... + ESockAddress sa; + SOCKLEN_T saLen = 0; // Just in case... + char* xres; NDBG( ("NET", "nif_getnameinfo -> entry (%d)\r\n", argc) ); @@ -674,10 +674,10 @@ ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, */ #if !defined(__WIN32__) static -ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, - const SocketAddress* saP, - SOCKLEN_T saLen, - int flags) +ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, + const ESockAddress* saP, + SOCKLEN_T saLen, + int flags) { ERL_NIF_TERM result; char host[HOSTNAME_LEN]; @@ -1429,7 +1429,7 @@ ERL_NIF_TERM encode_address_info(ErlNifEnv* env, type = encode_address_info_type(env, addrInfoP->ai_socktype); proto = encode_address_info_proto(env, addrInfoP->ai_protocol); esock_encode_sockaddr(env, - (SocketAddress*) addrInfoP->ai_addr, + (ESockAddress*) addrInfoP->ai_addr, addrInfoP->ai_addrlen, &addr); diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index 043303a60d..38c28a6de5 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -82,7 +82,7 @@ typedef union { struct sockaddr_un un; #endif -} SocketAddress; +} ESockAddress; /* *** Boolean *type* stuff... *** */ @@ -388,5 +388,7 @@ GLOBAL_ERROR_REASON_ATOM_DEFS #define REALLOC_BIN(SZ, BP) enif_realloc_binary((SZ), (BP)) #define FREE_BIN(BP) enif_release_binary((BP)) +/* Copy term T into environment E */ +#define CP_TERM(E, T) enif_make_copy((E), (T)) #endif // SOCKET_INT_H__ diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 870ab63bdf..ee3b9f2a98 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -32,7 +32,7 @@ * * esock_dbg_printf("DEMONP", "[%d] %s: %T\r\n", * descP->sock, slogan, - * MON2T(env, &monP->mon)); + * esock_make_monitor_term(env, &mon)); * */ @@ -119,7 +119,6 @@ - /* AND HERE WE MAY HAVE A BUNCH OF DEFINES....SEE INET DRIVER.... */ @@ -332,6 +331,20 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #include "socket_int.h" #include "socket_util.h" + +#if defined(ERTS_INLINE) +# define ESOCK_INLINE ERTS_INLINE +#else +# if defined(__GNUC__) +# define ESOCK_INLINE __inline__ +# elif defined(__WIN32__) +# define ESOCK_INLINE __inline +# else +# define ESOCK_INLINE +# endif +#endif + + #if defined(SOL_IPV6) || defined(IPPROTO_IPV6) #define HAVE_IPV6 #endif @@ -654,7 +667,9 @@ typedef union { * * * =================================================================== */ +/* Global socket debug */ #define SGDBG( proto ) ESOCK_DBG_PRINTF( data.dbg , proto ) +/* Socket specific debug */ #define SSDBG( __D__ , proto ) ESOCK_DBG_PRINTF( (__D__)->dbg , proto ) @@ -759,25 +774,32 @@ static unsigned long one_value = 1; typedef struct { - int is_active; ErlNifMonitor mon; + BOOLEAN_T isActive; } ESockMonitor; typedef struct { - ErlNifPid pid; // PID of the requesting process - ESockMonitor mon; // Monitor to the requesting process - ERL_NIF_TERM ref; // The (unique) reference (ID) of the request -} SocketRequestor; + ErlNifPid pid; // PID of the requesting process + ESockMonitor mon; // Monitor to the requesting process -typedef struct socket_request_queue_element { - struct socket_request_queue_element* nextP; - SocketRequestor data; -} SocketRequestQueueElement; + /* We need an environment for the copy of the ref we store here. + * We will also use this environment for any messages we send + * (with the ref in it). Such as the select message (used in the + * select call) or the abort message. + */ + ErlNifEnv* env; + ERL_NIF_TERM ref; // The (unique) reference (ID) of the request +} ESockRequestor; + +typedef struct esock_request_queue_element { + struct esock_request_queue_element* nextP; + ESockRequestor data; +} ESockRequestQueueElement; typedef struct { - SocketRequestQueueElement* first; - SocketRequestQueueElement* last; -} SocketRequestQueue; + ESockRequestQueueElement* first; + ESockRequestQueueElement* last; +} ESockRequestQueue; typedef struct { @@ -791,21 +813,18 @@ typedef struct { int protocol; unsigned int state; - SocketAddress remote; + ESockAddress remote; unsigned int addrLen; - ErlNifEnv* env; - /* +++ Controller (owner) process +++ */ ErlNifPid ctrlPid; - // ErlNifMonitor ctrlMon; ESockMonitor ctrlMon; /* +++ Write stuff +++ */ ErlNifMutex* writeMtx; - SocketRequestor currentWriter; - SocketRequestor* currentWriterP; // NULL or points to currentWriter - SocketRequestQueue writersQ; + ESockRequestor currentWriter; + ESockRequestor* currentWriterP; // NULL or points to currentWriter + ESockRequestQueue writersQ; BOOLEAN_T isWritable; Uint32 writePkgCnt; Uint32 writeByteCnt; @@ -815,9 +834,9 @@ typedef struct { /* +++ Read stuff +++ */ ErlNifMutex* readMtx; - SocketRequestor currentReader; - SocketRequestor* currentReaderP; // NULL or points to currentReader - SocketRequestQueue readersQ; + ESockRequestor currentReader; + ESockRequestor* currentReaderP; // NULL or points to currentReader + ESockRequestQueue readersQ; BOOLEAN_T isReadable; ErlNifBinary rbuffer; // DO WE NEED THIS Uint32 readCapacity; // DO WE NEED THIS @@ -828,11 +847,12 @@ typedef struct { /* +++ Accept stuff +++ */ ErlNifMutex* accMtx; - SocketRequestor currentAcceptor; - SocketRequestor* currentAcceptorP; // NULL or points to currentAcceptor - SocketRequestQueue acceptorsQ; + ESockRequestor currentAcceptor; + ESockRequestor* currentAcceptorP; // NULL or points to currentAcceptor + ESockRequestQueue acceptorsQ; /* +++ Config & Misc stuff +++ */ + ErlNifMutex* cfgMtx; size_t rBufSz; // Read buffer size (when data length = 0) /* rNum and rNumCnt are used (together with rBufSz) when calling the recv * function with the Length argument set to 0 (zero). @@ -856,7 +876,7 @@ typedef struct { ERL_NIF_TERM closeRef; BOOLEAN_T closeLocal; -} SocketDescriptor; +} ESockDescriptor; /* Global stuff. @@ -880,7 +900,7 @@ typedef struct { Uint32 numProtoTCP; Uint32 numProtoUDP; Uint32 numProtoSCTP; -} SocketData; +} ESockData; /* ---------------------------------------------------------------------- @@ -980,122 +1000,131 @@ static ERL_NIF_TERM nopen(ErlNifEnv* env, int type, int protocol, char* netns); -static ERL_NIF_TERM nbind(ErlNifEnv* env, - SocketDescriptor* descP, - SocketAddress* sockAddrP, - unsigned int addrLen); -static ERL_NIF_TERM nconnect(ErlNifEnv* env, - SocketDescriptor* descP); -static ERL_NIF_TERM nlisten(ErlNifEnv* env, - SocketDescriptor* descP, - int backlog); -static ERL_NIF_TERM naccept(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM ref); -static ERL_NIF_TERM naccept_listening(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM ref); -static ERL_NIF_TERM naccept_listening_error(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM ref, - ErlNifPid caller, - int save_errno); -static ERL_NIF_TERM naccept_listening_accept(ErlNifEnv* env, - SocketDescriptor* descP, - SOCKET accSock, - ErlNifPid caller, - SocketAddress* remote); -static ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM ref); -static ERL_NIF_TERM naccept_accepting_current(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM ref); -static ERL_NIF_TERM naccept_accepting_current_accept(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - SOCKET accSock, - SocketAddress* remote); -static ERL_NIF_TERM naccept_accepting_current_error(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef, - int save_errno); -static ERL_NIF_TERM naccept_accepting_other(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM ref, - ErlNifPid caller); -static ERL_NIF_TERM naccept_busy_retry(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM ref, - ErlNifPid* pid, - unsigned int nextState); -static BOOLEAN_T naccept_accepted(ErlNifEnv* env, - SocketDescriptor* descP, - SOCKET accSock, - ErlNifPid pid, - SocketAddress* remote, - ERL_NIF_TERM* result); -static ERL_NIF_TERM nsend(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM sendRef, - ErlNifBinary* dataP, - int flags); -static ERL_NIF_TERM nsendto(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM sendRef, - ErlNifBinary* dataP, - int flags, - SocketAddress* toAddrP, - unsigned int toAddrLen); -static ERL_NIF_TERM nsendmsg(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM sendRef, - ERL_NIF_TERM eMsgHdr, - int flags); -static ERL_NIF_TERM nrecv(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sendRef, - ERL_NIF_TERM recvRef, - int len, - int flags); -static ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM recvRef, - Uint16 bufSz, - int flags); -static ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM recvRef, - Uint16 bufLen, - Uint16 ctrlLen, - int flags); -static ERL_NIF_TERM nclose(ErlNifEnv* env, - SocketDescriptor* descP); -static ERL_NIF_TERM nshutdown(ErlNifEnv* env, - SocketDescriptor* descP, - int how); -static ERL_NIF_TERM nsetopt(ErlNifEnv* env, - SocketDescriptor* descP, - BOOLEAN_T isEncoded, - BOOLEAN_T isOTP, - int level, - int eOpt, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nbind(ErlNifEnv* env, + ESockDescriptor* descP, + ESockAddress* sockAddrP, + unsigned int addrLen); +static ERL_NIF_TERM nconnect(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM nlisten(ErlNifEnv* env, + ESockDescriptor* descP, + int backlog); +static ERL_NIF_TERM naccept(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM ref); +static ERL_NIF_TERM naccept_listening(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM ref); +static ERL_NIF_TERM naccept_listening_error(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM accRef, + ErlNifPid caller, + int save_errno); +static ERL_NIF_TERM naccept_listening_accept(ErlNifEnv* env, + ESockDescriptor* descP, + SOCKET accSock, + ErlNifPid caller, + ESockAddress* remote); +static ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM ref); +static ERL_NIF_TERM naccept_accepting_current(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM ref); +static ERL_NIF_TERM naccept_accepting_current_accept(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + SOCKET accSock, + ESockAddress* remote); +static ERL_NIF_TERM naccept_accepting_current_error(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef, + int save_errno); +static ERL_NIF_TERM naccept_accepting_other(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM ref, + ErlNifPid caller); +static ERL_NIF_TERM naccept_busy_retry(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM accRef, + ErlNifPid* pid, + unsigned int nextState); +static BOOLEAN_T naccept_accepted(ErlNifEnv* env, + ESockDescriptor* descP, + SOCKET accSock, + ErlNifPid pid, + ESockAddress* remote, + ERL_NIF_TERM* result); +static ERL_NIF_TERM nsend(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + ErlNifBinary* dataP, + int flags); +static ERL_NIF_TERM nsendto(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + ErlNifBinary* dataP, + int flags, + ESockAddress* toAddrP, + unsigned int toAddrLen); +static ERL_NIF_TERM nsendmsg(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + ERL_NIF_TERM eMsgHdr, + int flags); +static ERL_NIF_TERM nrecv(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sendRef, + ERL_NIF_TERM recvRef, + int len, + int flags); +static ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + Uint16 bufSz, + int flags); +static ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + Uint16 bufLen, + Uint16 ctrlLen, + int flags); +static ERL_NIF_TERM nclose(ErlNifEnv* env, + ESockDescriptor* descP); +static BOOLEAN_T nclose_check(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM* reason); +static ERL_NIF_TERM nclose_do(ErlNifEnv* env, + ESockDescriptor* descP); +static ERL_NIF_TERM nshutdown(ErlNifEnv* env, + ESockDescriptor* descP, + int how); +static ERL_NIF_TERM nsetopt(ErlNifEnv* env, + ESockDescriptor* descP, + BOOLEAN_T isEncoded, + BOOLEAN_T isOTP, + int level, + int eOpt, + ERL_NIF_TERM eVal); /* Set OTP level options */ -static ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal); /* *** nsetopt_otp_debug *** * *** nsetopt_otp_iow *** * *** nsetopt_otp_ctrl_proc *** @@ -1111,171 +1140,171 @@ static ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, NSETOPT_OTP_FUNC_DEF(rcvctrlbuf); \ NSETOPT_OTP_FUNC_DEF(sndctrlbuf); #define NSETOPT_OTP_FUNC_DEF(F) \ - static ERL_NIF_TERM nsetopt_otp_##F(ErlNifEnv* env, \ - SocketDescriptor* descP, \ - ERL_NIF_TERM eVal) + static ERL_NIF_TERM nsetopt_otp_##F(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ERL_NIF_TERM eVal) NSETOPT_OTP_FUNCS #undef NSETOPT_OTP_FUNC_DEF /* Set native options */ -static ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int eOpt, - ERL_NIF_TERM eVal); -static ERL_NIF_TERM nsetopt_level(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int eOpt, - ERL_NIF_TERM eVal); -static ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int eOpt, + ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_level(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int eOpt, + ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal); /* *** Handling set of socket options for level = socket *** */ #if defined(SO_BINDTODEVICE) -static ERL_NIF_TERM nsetopt_lvl_sock_bindtodevice(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_bindtodevice(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SO_BROADCAST) -static ERL_NIF_TERM nsetopt_lvl_sock_broadcast(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_broadcast(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SO_DEBUG) -static ERL_NIF_TERM nsetopt_lvl_sock_debug(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_debug(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SO_DONTROUTE) -static ERL_NIF_TERM nsetopt_lvl_sock_dontroute(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_dontroute(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SO_KEEPALIVE) -static ERL_NIF_TERM nsetopt_lvl_sock_keepalive(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_keepalive(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SO_LINGER) -static ERL_NIF_TERM nsetopt_lvl_sock_linger(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_linger(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SO_OOBINLINE) -static ERL_NIF_TERM nsetopt_lvl_sock_oobinline(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_oobinline(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SO_PEEK_OFF) -static ERL_NIF_TERM nsetopt_lvl_sock_peek_off(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_peek_off(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SO_PRIORITY) -static ERL_NIF_TERM nsetopt_lvl_sock_priority(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_priority(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SO_RCVBUF) -static ERL_NIF_TERM nsetopt_lvl_sock_rcvbuf(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_rcvbuf(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SO_RCVLOWAT) -static ERL_NIF_TERM nsetopt_lvl_sock_rcvlowat(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_rcvlowat(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SO_RCVTIMEO) -static ERL_NIF_TERM nsetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SO_REUSEADDR) -static ERL_NIF_TERM nsetopt_lvl_sock_reuseaddr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_reuseaddr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SO_REUSEPORT) -static ERL_NIF_TERM nsetopt_lvl_sock_reuseport(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_reuseport(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SO_SNDBUF) -static ERL_NIF_TERM nsetopt_lvl_sock_sndbuf(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_sndbuf(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SO_SNDLOWAT) -static ERL_NIF_TERM nsetopt_lvl_sock_sndlowat(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_sndlowat(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SO_SNDTIMEO) -static ERL_NIF_TERM nsetopt_lvl_sock_sndtimeo(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_sndtimeo(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SO_TIMESTAMP) -static ERL_NIF_TERM nsetopt_lvl_sock_timestamp(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sock_timestamp(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif -static ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal); /* *** Handling set of socket options for level = ip *** */ #if defined(IP_ADD_MEMBERSHIP) -static ERL_NIF_TERM nsetopt_lvl_ip_add_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_add_membership(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_ADD_SOURCE_MEMBERSHIP) -static ERL_NIF_TERM nsetopt_lvl_ip_add_source_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_add_source_membership(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_BLOCK_SOURCE) -static ERL_NIF_TERM nsetopt_lvl_ip_block_source(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_block_source(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_DROP_MEMBERSHIP) -static ERL_NIF_TERM nsetopt_lvl_ip_drop_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_drop_membership(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_DROP_SOURCE_MEMBERSHIP) -static ERL_NIF_TERM nsetopt_lvl_ip_drop_source_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_drop_source_membership(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_FREEBIND) -static ERL_NIF_TERM nsetopt_lvl_ip_freebind(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_freebind(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_HDRINCL) -static ERL_NIF_TERM nsetopt_lvl_ip_hdrincl(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_hdrincl(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_MINTTL) -static ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE) -static ERL_NIF_TERM nsetopt_lvl_ip_msfilter(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_msfilter(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); static BOOLEAN_T decode_ip_msfilter_mode(ErlNifEnv* env, ERL_NIF_TERM eVal, Uint32* mode); @@ -1285,322 +1314,322 @@ static ERL_NIF_TERM nsetopt_lvl_ip_msfilter_set(ErlNifEnv* env, SOCKLEN_T optLen); #endif #if defined(IP_MTU_DISCOVER) -static ERL_NIF_TERM nsetopt_lvl_ip_mtu_discover(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_mtu_discover(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_MULTICAST_ALL) -static ERL_NIF_TERM nsetopt_lvl_ip_multicast_all(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_multicast_all(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_MULTICAST_IF) -static ERL_NIF_TERM nsetopt_lvl_ip_multicast_if(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_multicast_if(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_MULTICAST_LOOP) -static ERL_NIF_TERM nsetopt_lvl_ip_multicast_loop(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_multicast_loop(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_MULTICAST_TTL) -static ERL_NIF_TERM nsetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_NODEFRAG) -static ERL_NIF_TERM nsetopt_lvl_ip_nodefrag(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_nodefrag(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_PKTINFO) -static ERL_NIF_TERM nsetopt_lvl_ip_pktinfo(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_pktinfo(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_RECVDSTADDR) -static ERL_NIF_TERM nsetopt_lvl_ip_recvdstaddr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_recvdstaddr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_RECVERR) -static ERL_NIF_TERM nsetopt_lvl_ip_recverr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_recverr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_RECVIF) -static ERL_NIF_TERM nsetopt_lvl_ip_recvif(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_recvif(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_RECVOPTS) -static ERL_NIF_TERM nsetopt_lvl_ip_recvopts(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_recvopts(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_RECVORIGDSTADDR) -static ERL_NIF_TERM nsetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_RECVTOS) -static ERL_NIF_TERM nsetopt_lvl_ip_recvtos(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_recvtos(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_RECVTTL) -static ERL_NIF_TERM nsetopt_lvl_ip_recvttl(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_recvttl(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_RETOPTS) -static ERL_NIF_TERM nsetopt_lvl_ip_retopts(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_retopts(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_ROUTER_ALERT) -static ERL_NIF_TERM nsetopt_lvl_ip_router_alert(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_router_alert(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_SENDSRCADDR) -static ERL_NIF_TERM nsetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_TOS) -static ERL_NIF_TERM nsetopt_lvl_ip_tos(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_tos(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_TRANSPARENT) -static ERL_NIF_TERM nsetopt_lvl_ip_transparent(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_transparent(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_TTL) -static ERL_NIF_TERM nsetopt_lvl_ip_ttl(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_ttl(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_UNBLOCK_SOURCE) -static ERL_NIF_TERM nsetopt_lvl_ip_unblock_source(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ip_unblock_source(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_DROP_MEMBERSHIP) || defined(IP_ADD_MEMBERSHIP) static -ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal, - int opt); +ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal, + int opt); #endif #if defined(IP_ADD_SOURCE_MEMBERSHIP) || defined(IP_DROP_SOURCE_MEMBERSHIP) || defined(IP_BLOCK_SOURCE) || defined(IP_UNBLOCK_SOURCE) static -ERL_NIF_TERM nsetopt_lvl_ip_update_source(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal, - int opt); +ERL_NIF_TERM nsetopt_lvl_ip_update_source(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal, + int opt); #endif /* *** Handling set of socket options for level = ipv6 *** */ #if defined(HAVE_IPV6) -static ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal); #if defined(IPV6_ADDRFORM) -static ERL_NIF_TERM nsetopt_lvl_ipv6_addrform(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_addrform(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_ADD_MEMBERSHIP) -static ERL_NIF_TERM nsetopt_lvl_ipv6_add_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_add_membership(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_AUTHHDR) -static ERL_NIF_TERM nsetopt_lvl_ipv6_authhdr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_authhdr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_DROP_MEMBERSHIP) -static ERL_NIF_TERM nsetopt_lvl_ipv6_drop_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_drop_membership(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_DSTOPTS) -static ERL_NIF_TERM nsetopt_lvl_ipv6_dstopts(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_dstopts(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_FLOWINFO) -static ERL_NIF_TERM nsetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_HOPLIMIT) -static ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_HOPOPTS) -static ERL_NIF_TERM nsetopt_lvl_ipv6_hopopts(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_hopopts(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_MTU) -static ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_MTU_DISCOVER) -static ERL_NIF_TERM nsetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_MULTICAST_HOPS) -static ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_MULTICAST_IF) -static ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_MULTICAST_LOOP) -static ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_RECVERR) -static ERL_NIF_TERM nsetopt_lvl_ipv6_recverr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_recverr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) -static ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_ROUTER_ALERT) -static ERL_NIF_TERM nsetopt_lvl_ipv6_router_alert(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_router_alert(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_RTHDR) -static ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_UNICAST_HOPS) -static ERL_NIF_TERM nsetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_V6ONLY) -static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IPV6_ADD_MEMBERSHIP) || defined(IPV6_DROP_MEMBERSHIP) -static ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal, - int opt); +static ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal, + int opt); #endif #endif // defined(HAVE_IPV6) -static ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal); #if defined(TCP_CONGESTION) -static ERL_NIF_TERM nsetopt_lvl_tcp_congestion(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_tcp_congestion(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(TCP_MAXSEG) -static ERL_NIF_TERM nsetopt_lvl_tcp_maxseg(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_tcp_maxseg(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(TCP_NODELAY) -static ERL_NIF_TERM nsetopt_lvl_tcp_nodelay(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); -#endif -static ERL_NIF_TERM nsetopt_lvl_udp(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_tcp_nodelay(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +static ERL_NIF_TERM nsetopt_lvl_udp(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal); #if defined(UDP_CORK) -static ERL_NIF_TERM nsetopt_lvl_udp_cork(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_udp_cork(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(HAVE_SCTP) -static ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal); #if defined(SCTP_ASSOCINFO) -static ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SCTP_AUTOCLOSE) -static ERL_NIF_TERM nsetopt_lvl_sctp_autoclose(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sctp_autoclose(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SCTP_DISABLE_FRAGMENTS) -static ERL_NIF_TERM nsetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SCTP_EVENTS) -static ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SCTP_INITMSG) -static ERL_NIF_TERM nsetopt_lvl_sctp_initmsg(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sctp_initmsg(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SCTP_MAXSEG) -static ERL_NIF_TERM nsetopt_lvl_sctp_maxseg(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sctp_maxseg(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SCTP_NODELAY) -static ERL_NIF_TERM nsetopt_lvl_sctp_nodelay(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sctp_nodelay(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(SCTP_RTOINFO) -static ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal); #endif #endif // defined(HAVE_SCTP) -static ERL_NIF_TERM ngetopt(ErlNifEnv* env, - SocketDescriptor* descP, - BOOLEAN_T isEncoded, - BOOLEAN_T isOTP, - int level, - ERL_NIF_TERM eOpt); +static ERL_NIF_TERM ngetopt(ErlNifEnv* env, + ESockDescriptor* descP, + BOOLEAN_T isEncoded, + BOOLEAN_T isOTP, + int level, + ERL_NIF_TERM eOpt); -static ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt); +static ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt); /* *** ngetopt_otp_debug *** * *** ngetopt_otp_iow *** * *** ngetopt_otp_ctrl_proc *** @@ -1625,518 +1654,595 @@ static ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, NGETOPT_OTP_FUNC_DEF(protocol); #define NGETOPT_OTP_FUNC_DEF(F) \ static ERL_NIF_TERM ngetopt_otp_##F(ErlNifEnv* env, \ - SocketDescriptor* descP) + ESockDescriptor* descP) NGETOPT_OTP_FUNCS #undef NGETOPT_OTP_FUNC_DEF -static ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - ERL_NIF_TERM eOpt); -static ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt, - SOCKOPTLEN_T valueSz); -static ERL_NIF_TERM ngetopt_level(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int eOpt); -static ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt); +static ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + ERL_NIF_TERM eOpt); +static ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + SOCKOPTLEN_T valueSz); +static ERL_NIF_TERM ngetopt_level(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int eOpt); +static ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt); #if defined(SO_ACCEPTCONN) -static ERL_NIF_TERM ngetopt_lvl_sock_acceptconn(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_acceptconn(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_BINDTODEVICE) -static ERL_NIF_TERM ngetopt_lvl_sock_bindtodevice(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_bindtodevice(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_BROADCAST) -static ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_DEBUG) -static ERL_NIF_TERM ngetopt_lvl_sock_debug(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_debug(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_DOMAIN) -static ERL_NIF_TERM ngetopt_lvl_sock_domain(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_domain(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_DONTROUTE) -static ERL_NIF_TERM ngetopt_lvl_sock_dontroute(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_dontroute(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_KEEPALIVE) -static ERL_NIF_TERM ngetopt_lvl_sock_keepalive(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_keepalive(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_LINGER) -static ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_OOBINLINE) -static ERL_NIF_TERM ngetopt_lvl_sock_oobinline(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_oobinline(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_PEEK_OFF) -static ERL_NIF_TERM ngetopt_lvl_sock_peek_off(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_peek_off(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_PRIORITY) -static ERL_NIF_TERM ngetopt_lvl_sock_priority(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_priority(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_PROTOCOL) -static ERL_NIF_TERM ngetopt_lvl_sock_protocol(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_protocol(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_RCVBUF) -static ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_RCVLOWAT) -static ERL_NIF_TERM ngetopt_lvl_sock_rcvlowat(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_rcvlowat(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_RCVTIMEO) -static ERL_NIF_TERM ngetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_REUSEADDR) -static ERL_NIF_TERM ngetopt_lvl_sock_reuseaddr(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_reuseaddr(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_REUSEPORT) -static ERL_NIF_TERM ngetopt_lvl_sock_reuseport(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_reuseport(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_SNDBUF) -static ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_SNDLOWAT) -static ERL_NIF_TERM ngetopt_lvl_sock_sndlowat(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_sndlowat(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_SNDTIMEO) -static ERL_NIF_TERM ngetopt_lvl_sock_sndtimeo(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_sndtimeo(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_TIMESTAMP) -static ERL_NIF_TERM ngetopt_lvl_sock_timestamp(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_timestamp(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SO_TYPE) -static ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env, + ESockDescriptor* descP); #endif -static ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt); +static ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt); #if defined(IP_FREEBIND) -static ERL_NIF_TERM ngetopt_lvl_ip_freebind(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_freebind(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_HDRINCL) -static ERL_NIF_TERM ngetopt_lvl_ip_hdrincl(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_hdrincl(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_MINTTL) -static ERL_NIF_TERM ngetopt_lvl_ip_minttl(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_minttl(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_MTU) -static ERL_NIF_TERM ngetopt_lvl_ip_mtu(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_mtu(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_MTU_DISCOVER) -static ERL_NIF_TERM ngetopt_lvl_ip_mtu_discover(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_mtu_discover(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_MULTICAST_ALL) -static ERL_NIF_TERM ngetopt_lvl_ip_multicast_all(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_multicast_all(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_MULTICAST_IF) -static ERL_NIF_TERM ngetopt_lvl_ip_multicast_if(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_multicast_if(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_MULTICAST_LOOP) -static ERL_NIF_TERM ngetopt_lvl_ip_multicast_loop(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_multicast_loop(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_MULTICAST_TTL) -static ERL_NIF_TERM ngetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_NODEFRAG) -static ERL_NIF_TERM ngetopt_lvl_ip_nodefrag(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_nodefrag(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_PKTINFO) -static ERL_NIF_TERM ngetopt_lvl_ip_pktinfo(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_pktinfo(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_RECVDSTADDR) -static ERL_NIF_TERM ngetopt_lvl_ip_recvdstaddr(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_recvdstaddr(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_RECVERR) -static ERL_NIF_TERM ngetopt_lvl_ip_recverr(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_recverr(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_RECVIF) -static ERL_NIF_TERM ngetopt_lvl_ip_recvif(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_recvif(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_RECVOPTS) -static ERL_NIF_TERM ngetopt_lvl_ip_recvopts(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_recvopts(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_RECVORIGDSTADDR) -static ERL_NIF_TERM ngetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_RECVTOS) -static ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_RECVTTL) -static ERL_NIF_TERM ngetopt_lvl_ip_recvttl(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_recvttl(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_RETOPTS) -static ERL_NIF_TERM ngetopt_lvl_ip_retopts(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_retopts(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_ROUTER_ALERT) -static ERL_NIF_TERM ngetopt_lvl_ip_router_alert(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_router_alert(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_SENDSRCADDR) -static ERL_NIF_TERM ngetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_TOS) -static ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_TRANSPARENT) -static ERL_NIF_TERM ngetopt_lvl_ip_transparent(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_transparent(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IP_TTL) -static ERL_NIF_TERM ngetopt_lvl_ip_ttl(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ip_ttl(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(HAVE_IPV6) -static ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt); +static ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt); #if defined(IPV6_AUTHHDR) -static ERL_NIF_TERM ngetopt_lvl_ipv6_authhdr(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ipv6_authhdr(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IPV6_DSTOPTS) -static ERL_NIF_TERM ngetopt_lvl_ipv6_dstopts(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ipv6_dstopts(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IPV6_FLOWINFO) -static ERL_NIF_TERM ngetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IPV6_HOPLIMIT) -static ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IPV6_HOPOPTS) -static ERL_NIF_TERM ngetopt_lvl_ipv6_hopopts(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ipv6_hopopts(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IPV6_MTU) -static ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IPV6_MTU_DISCOVER) -static ERL_NIF_TERM ngetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IPV6_MULTICAST_HOPS) -static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IPV6_MULTICAST_IF) -static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IPV6_MULTICAST_LOOP) -static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IPV6_RECVERR) -static ERL_NIF_TERM ngetopt_lvl_ipv6_recverr(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ipv6_recverr(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) -static ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IPV6_ROUTER_ALERT) -static ERL_NIF_TERM ngetopt_lvl_ipv6_router_alert(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ipv6_router_alert(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IPV6_RTHDR) -static ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IPV6_UNICAST_HOPS) -static ERL_NIF_TERM ngetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(IPV6_V6ONLY) -static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, + ESockDescriptor* descP); #endif #endif // defined(HAVE_IPV6) -static ERL_NIF_TERM ngetopt_lvl_tcp(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt); +static ERL_NIF_TERM ngetopt_lvl_tcp(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt); #if defined(TCP_CONGESTION) -static ERL_NIF_TERM ngetopt_lvl_tcp_congestion(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_tcp_congestion(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(TCP_MAXSEG) -static ERL_NIF_TERM ngetopt_lvl_tcp_maxseg(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_tcp_maxseg(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(TCP_NODELAY) -static ERL_NIF_TERM ngetopt_lvl_tcp_nodelay(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_tcp_nodelay(ErlNifEnv* env, + ESockDescriptor* descP); #endif -static ERL_NIF_TERM ngetopt_lvl_udp(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt); +static ERL_NIF_TERM ngetopt_lvl_udp(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt); #if defined(UDP_CORK) -static ERL_NIF_TERM ngetopt_lvl_udp_cork(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_udp_cork(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(HAVE_SCTP) -static ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt); +static ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt); #if defined(SCTP_ASSOCINFO) -static ERL_NIF_TERM ngetopt_lvl_sctp_associnfo(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sctp_associnfo(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SCTP_AUTOCLOSE) -static ERL_NIF_TERM ngetopt_lvl_sctp_autoclose(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sctp_autoclose(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SCTP_DISABLE_FRAGMENTS) -static ERL_NIF_TERM ngetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SCTP_MAXSEG) -static ERL_NIF_TERM ngetopt_lvl_sctp_maxseg(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sctp_maxseg(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SCTP_INITMSG) -static ERL_NIF_TERM ngetopt_lvl_sctp_initmsg(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sctp_initmsg(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SCTP_NODELAY) -static ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env, + ESockDescriptor* descP); #endif #if defined(SCTP_RTOINFO) -static ERL_NIF_TERM ngetopt_lvl_sctp_rtoinfo(ErlNifEnv* env, - SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_lvl_sctp_rtoinfo(ErlNifEnv* env, + ESockDescriptor* descP); #endif #endif // defined(HAVE_SCTP) -static ERL_NIF_TERM nsockname(ErlNifEnv* env, - SocketDescriptor* descP); -static ERL_NIF_TERM npeername(ErlNifEnv* env, - SocketDescriptor* descP); -static ERL_NIF_TERM ncancel(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM op, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef); -static ERL_NIF_TERM ncancel_connect(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM opRef); -static ERL_NIF_TERM ncancel_accept(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef); -static ERL_NIF_TERM ncancel_accept_current(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef); -static ERL_NIF_TERM ncancel_accept_waiting(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM opRef); -static ERL_NIF_TERM ncancel_send(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef); -static ERL_NIF_TERM ncancel_send_current(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef); -static ERL_NIF_TERM ncancel_send_waiting(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM opRef); -static ERL_NIF_TERM ncancel_recv(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef); -static ERL_NIF_TERM ncancel_recv_current(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef); -static ERL_NIF_TERM ncancel_recv_waiting(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM opRef); -static ERL_NIF_TERM ncancel_read_select(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM opRef); -static ERL_NIF_TERM ncancel_write_select(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM opRef); -static ERL_NIF_TERM ncancel_mode_select(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM opRef, - int smode, - int rmode); +static ERL_NIF_TERM nsockname(ErlNifEnv* env, + ESockDescriptor* descP); +static ERL_NIF_TERM npeername(ErlNifEnv* env, + ESockDescriptor* descP); +static ERL_NIF_TERM ncancel(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM op, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_connect(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_accept(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_accept_current(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM ncancel_accept_waiting(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_send(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_send_current(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM ncancel_send_waiting(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_recv(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_recv_current(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM ncancel_recv_waiting(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_read_select(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_write_select(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_mode_select(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef, + int smode, + int rmode); #if defined(USE_SETOPT_STR_OPT) -static ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt, - int max, - ERL_NIF_TERM eVal); -#endif -static ERL_NIF_TERM nsetopt_bool_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt, - ERL_NIF_TERM eVal); -static ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt, - ERL_NIF_TERM eVal); -static ERL_NIF_TERM nsetopt_timeval_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + int max, + ERL_NIF_TERM eVal); +#endif +static ERL_NIF_TERM nsetopt_bool_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_timeval_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal); #if defined(USE_GETOPT_STR_OPT) -static ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt, - int max); -#endif -static ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt); -static ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt); -static ERL_NIF_TERM ngetopt_timeval_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt); - -static BOOLEAN_T send_check_writer(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM ref, - ERL_NIF_TERM* checkResult); -static ERL_NIF_TERM send_check_result(ErlNifEnv* env, - SocketDescriptor* descP, - ssize_t written, - ssize_t dataSize, - int saveErrno, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM sendRef); -static BOOLEAN_T recv_check_reader(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM ref, - ERL_NIF_TERM* checkResult); -static char* recv_init_current_reader(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM ref); -static ERL_NIF_TERM recv_update_current_reader(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef); -static void recv_error_current_reader(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM reason); -static ERL_NIF_TERM recv_check_result(ErlNifEnv* env, - SocketDescriptor* descP, - int read, - int toRead, - int saveErrno, - ErlNifBinary* bufP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM recvRef); -static ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, - SocketDescriptor* descP, - int read, - int saveErrno, - ErlNifBinary* bufP, - SocketAddress* fromAddrP, - unsigned int fromAddrLen, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM recvRef); -static ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, - SocketDescriptor* descP, - int read, - int saveErrno, - struct msghdr* msgHdrP, - ErlNifBinary* dataBufP, - ErlNifBinary* ctrlBufP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM recvRef); - -static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, - SocketDescriptor* descP); -static ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, - SocketDescriptor* descP); - -extern char* encode_msghdr(ErlNifEnv* env, - SocketDescriptor* descP, - int read, - struct msghdr* msgHdrP, - ErlNifBinary* dataBufP, - ErlNifBinary* ctrlBufP, - ERL_NIF_TERM* eSockAddr); -extern char* encode_cmsghdrs(ErlNifEnv* env, - SocketDescriptor* descP, - ErlNifBinary* cmsgBinP, - struct msghdr* msgHdrP, - ERL_NIF_TERM* eCMsgHdr); -extern char* decode_cmsghdrs(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eCMsgHdr, - char* cmsgHdrBufP, - size_t cmsgHdrBufLen, - size_t* cmsgHdrBufUsed); -extern char* decode_cmsghdr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eCMsgHdr, - char* bufP, - size_t rem, - size_t* used); +static ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + int max); +#endif +static ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt); +static ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt); +static ERL_NIF_TERM ngetopt_timeval_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt); + +static BOOLEAN_T send_check_writer(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM ref, + ERL_NIF_TERM* checkResult); +static ERL_NIF_TERM send_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + ssize_t written, + ssize_t dataSize, + int saveErrno, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef); +static ERL_NIF_TERM send_check_ok(ErlNifEnv* env, + ESockDescriptor* descP, + ssize_t written, + ssize_t dataSize, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM send_check_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int saveErrno, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM send_check_retry(ErlNifEnv* env, + ESockDescriptor* descP, + ssize_t written, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef); +static BOOLEAN_T recv_check_reader(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM ref, + ERL_NIF_TERM* checkResult); +static char* recv_init_current_reader(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM ref); +static ERL_NIF_TERM recv_update_current_reader(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef); +static void recv_error_current_reader(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM reason); +static ERL_NIF_TERM recv_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + int toRead, + int saveErrno, + ErlNifBinary* bufP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recv_check_full(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + int toRead, + ErlNifBinary* bufP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recv_check_full_maybe_done(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + int toRead, + ErlNifBinary* bufP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recv_check_full_done(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + ErlNifBinary* bufP, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM recv_check_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int saveErrno, + ErlNifBinary* buf1P, + ErlNifBinary* buf2P, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recv_check_fail_closed(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recv_check_partial(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + int toRead, + ErlNifBinary* bufP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recv_check_partial_done(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + ErlNifBinary* bufP, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM recv_check_partial_part(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + ErlNifBinary* bufP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recv_check_retry(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recv_check_fail_gen(ErlNifEnv* env, + ESockDescriptor* descP, + int saveErrno, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + int saveErrno, + ErlNifBinary* bufP, + ESockAddress* fromAddrP, + unsigned int fromAddrLen, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + int saveErrno, + struct msghdr* msgHdrP, + ErlNifBinary* dataBufP, + ErlNifBinary* ctrlBufP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recvmsg_check_msg(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + struct msghdr* msgHdrP, + ErlNifBinary* dataBufP, + ErlNifBinary* ctrlBufP, + ERL_NIF_TERM sockRef); + +static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, + ESockDescriptor* descP); +static ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, + ESockDescriptor* descP); + +extern char* encode_msghdr(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + struct msghdr* msgHdrP, + ErlNifBinary* dataBufP, + ErlNifBinary* ctrlBufP, + ERL_NIF_TERM* eSockAddr); +extern char* encode_cmsghdrs(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifBinary* cmsgBinP, + struct msghdr* msgHdrP, + ERL_NIF_TERM* eCMsgHdr); +extern char* decode_cmsghdrs(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eCMsgHdr, + char* cmsgHdrBufP, + size_t cmsgHdrBufLen, + size_t* cmsgHdrBufUsed); +extern char* decode_cmsghdr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eCMsgHdr, + char* bufP, + size_t rem, + size_t* used); static char* encode_cmsghdr_level(ErlNifEnv* env, int level, ERL_NIF_TERM* eLevel); @@ -2182,26 +2288,26 @@ static char* encode_cmsghdr_data_ipv6(ErlNifEnv* env, size_t dataLen, ERL_NIF_TERM* eCMsgHdrData); #endif -extern char* encode_msghdr_flags(ErlNifEnv* env, - SocketDescriptor* descP, - int msgFlags, - ERL_NIF_TERM* flags); -static char* decode_cmsghdr_data(ErlNifEnv* env, - SocketDescriptor* descP, - char* bufP, - size_t rem, - int level, - int type, - ERL_NIF_TERM eData, - size_t* used); -static char* decode_cmsghdr_final(SocketDescriptor* descP, - char* bufP, - size_t rem, - int level, - int type, - char* data, - int sz, - size_t* used); +extern char* encode_msghdr_flags(ErlNifEnv* env, + ESockDescriptor* descP, + int msgFlags, + ERL_NIF_TERM* flags); +static char* decode_cmsghdr_data(ErlNifEnv* env, + ESockDescriptor* descP, + char* bufP, + size_t rem, + int level, + int type, + ERL_NIF_TERM eData, + size_t* used); +static char* decode_cmsghdr_final(ESockDescriptor* descP, + char* bufP, + size_t rem, + int level, + int type, + char* data, + int sz, + size_t* used); static BOOLEAN_T decode_sock_linger(ErlNifEnv* env, ERL_NIF_TERM eVal, struct linger* valP); @@ -2244,12 +2350,18 @@ static BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, // static void encode_bool(BOOLEAN_T val, ERL_NIF_TERM* eVal); static ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val); -static void inform_waiting_procs(ErlNifEnv* env, - char* role, - SocketDescriptor* descP, - SocketRequestQueue* q, - BOOLEAN_T free, - ERL_NIF_TERM reason); +static void socket_stop_handle_current(ErlNifEnv* env, + const char* role, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ESockRequestor* reqP); +static void inform_waiting_procs(ErlNifEnv* env, + const char* role, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ESockRequestQueue* q, + BOOLEAN_T free, + ERL_NIF_TERM reason); static int socket_setopt(int sock, int level, @@ -2257,9 +2369,9 @@ static int socket_setopt(int sock, const void* optVal, const socklen_t optLen); -static BOOLEAN_T verify_is_connected(SocketDescriptor* descP, int* err); +static BOOLEAN_T verify_is_connected(ESockDescriptor* descP, int* err); -static SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event); +static ESockDescriptor* alloc_descriptor(SOCKET sock, HANDLE event); static BOOLEAN_T edomain2domain(int edomain, int* domain); @@ -2303,21 +2415,13 @@ static void dec_socket(int domain, int type, int protocol); ACTIVATE_NEXT_FUNC_DEF(writer) \ ACTIVATE_NEXT_FUNC_DEF(reader) -#define ACTIVATE_NEXT_FUNC_DEF(F) \ - static BOOLEAN_T activate_next_##F(ErlNifEnv* env, \ - SocketDescriptor* descP, \ - ERL_NIF_TERM sockRef); +#define ACTIVATE_NEXT_FUNC_DEF(F) \ + static BOOLEAN_T activate_next_##F(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ERL_NIF_TERM sockRef); ACTIVATE_NEXT_FUNCS_DEFS #undef ACTIVATE_NEXT_FUNC_DEF - -/* -static BOOLEAN_T activate_next(ErlNifEnv* env, - SocketDescriptor* descP, - SocketRequestor* reqP, - SocketRequestQueue* q, - ERL_NIF_TERM sockRef); -*/ - + /* *** acceptor_search4pid | writer_search4pid | reader_search4pid *** * *** acceptor_push | writer_push | reader_push *** * *** acceptor_pop | writer_pop | reader_pop *** @@ -2333,48 +2437,51 @@ static BOOLEAN_T activate_next(ErlNifEnv* env, ESOCK_OPERATOR_FUNCS_DEF(writer) \ ESOCK_OPERATOR_FUNCS_DEF(reader) -#define ESOCK_OPERATOR_FUNCS_DEF(O) \ - static BOOLEAN_T O##_search4pid(ErlNifEnv* env, \ - SocketDescriptor* descP, \ - ErlNifPid* pid); \ - static ERL_NIF_TERM O##_push(ErlNifEnv* env, \ - SocketDescriptor* descP, \ - ErlNifPid pid, \ - ERL_NIF_TERM ref); \ - static BOOLEAN_T O##_pop(ErlNifEnv* env, \ - SocketDescriptor* descP, \ - SocketRequestor* reqP); \ - static BOOLEAN_T O##_unqueue(ErlNifEnv* env, \ - SocketDescriptor* descP, \ - const ErlNifPid* pid); +#define ESOCK_OPERATOR_FUNCS_DEF(O) \ + static BOOLEAN_T O##_search4pid(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ErlNifPid* pid); \ + static ERL_NIF_TERM O##_push(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ErlNifPid pid, \ + ERL_NIF_TERM ref); \ + static BOOLEAN_T O##_pop(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ESockRequestor* reqP); \ + static BOOLEAN_T O##_unqueue(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + const ErlNifPid* pid); ESOCK_OPERATOR_FUNCS_DEFS #undef ESOCK_OPERATOR_FUNCS_DEF -static BOOLEAN_T requestor_pop(SocketRequestQueue* q, - SocketRequestor* reqP); - -static BOOLEAN_T qsearch4pid(ErlNifEnv* env, - SocketRequestQueue* q, - ErlNifPid* pid); -static void qpush(SocketRequestQueue* q, - SocketRequestQueueElement* e); -static SocketRequestQueueElement* qpop(SocketRequestQueue* q); -static BOOLEAN_T qunqueue(ErlNifEnv* env, - SocketDescriptor* descP, - const char* slogan, - SocketRequestQueue* q, - const ErlNifPid* pid); - -static int esock_monitor(const char* slogan, - ErlNifEnv* env, - SocketDescriptor* descP, - const ErlNifPid* pid, - ESockMonitor* mon); -static int esock_demonitor(const char* slogan, - ErlNifEnv* env, - SocketDescriptor* descP, - ESockMonitor* monP); +static BOOLEAN_T requestor_pop(ESockRequestQueue* q, + ESockRequestor* reqP); + +static BOOLEAN_T qsearch4pid(ErlNifEnv* env, + ESockRequestQueue* q, + ErlNifPid* pid); +static void qpush(ESockRequestQueue* q, + ESockRequestQueueElement* e); +static ESockRequestQueueElement* qpop(ESockRequestQueue* q); +static BOOLEAN_T qunqueue(ErlNifEnv* env, + ESockDescriptor* descP, + const char* slogan, + ESockRequestQueue* q, + const ErlNifPid* pid); + +static int esock_monitor(const char* slogan, + ErlNifEnv* env, + ESockDescriptor* descP, + const ErlNifPid* pid, + ESockMonitor* mon); +static int esock_demonitor(const char* slogan, + ErlNifEnv* env, + ESockDescriptor* descP, + ESockMonitor* monP); static void esock_monitor_init(ESockMonitor* mon); +static ERL_NIF_TERM esock_make_monitor_term(ErlNifEnv* env, + const ESockMonitor* monP); + #endif // if defined(__WIN32__) @@ -2396,48 +2503,62 @@ static void socket_down(ErlNifEnv* env, #if !defined(__WIN32__) -static void socket_down_acceptor(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - const ErlNifPid* pid); -static void socket_down_writer(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - const ErlNifPid* pid); -static void socket_down_reader(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - const ErlNifPid* pid); - -static char* esock_send_close_msg(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef); +static void socket_down_acceptor(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pid); +static void socket_down_writer(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pid); +static void socket_down_reader(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pid); + +static char* esock_send_close_msg(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifPid* pid); static char* esock_send_abort_msg(ErlNifEnv* env, ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef, + ErlNifEnv* msgEnv, ERL_NIF_TERM reason, ErlNifPid* pid); -static char* esock_send_socket_msg(ErlNifEnv* env, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM tag, - ERL_NIF_TERM info, - ErlNifPid* pid, - ErlNifEnv* msg_env); static char* esock_send_msg(ErlNifEnv* env, - ERL_NIF_TERM msg, ErlNifPid* pid, - ErlNifEnv* msg_env); + ERL_NIF_TERM msg, + ErlNifEnv* msgEnv); + +static ERL_NIF_TERM mk_abort_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef, + ERL_NIF_TERM reason); +static ERL_NIF_TERM mk_close_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM closeRef); +static ERL_NIF_TERM mk_select_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM selectRef); +static ERL_NIF_TERM mk_socket_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM tag, + ERL_NIF_TERM info); +static ERL_NIF_TERM mk_socket(ErlNifEnv* env, + ERL_NIF_TERM sockRef); static int esock_select_read(ErlNifEnv* env, ErlNifEvent event, void* obj, const ErlNifPid* pid, - ERL_NIF_TERM ref); + ERL_NIF_TERM sockRef, + ERL_NIF_TERM selectRef); static int esock_select_write(ErlNifEnv* env, ErlNifEvent event, void* obj, const ErlNifPid* pid, - ERL_NIF_TERM ref); + ERL_NIF_TERM sockRef, + ERL_NIF_TERM selectRef); static int esock_select_stop(ErlNifEnv* env, ErlNifEvent event, void* obj); @@ -2786,7 +2907,30 @@ static ErlNifResourceTypeInit socketInit = { }; // Initiated when the nif is loaded -static SocketData data; +static ESockData data; + + +/* These two (inline) functions are primarily intended for debugging, + * that is, to make it easy to add debug printouts. + */ +static ESOCK_INLINE void esock_free_env(const char* slogan, ErlNifEnv* env) +{ + SGDBG( ("SOCKET", "env free - %s: 0x%lX\r\n", slogan, env) ); + // esock_dbg_printf("SOCK ENV", "free - %s: 0x%lX\r\n", slogan, env); + + if (env != NULL) enif_free_env(env); +} + + +static ESOCK_INLINE ErlNifEnv* esock_alloc_env(const char* slogan) +{ + ErlNifEnv* env = enif_alloc_env(); + + SGDBG( ("SOCKET", "env alloc - %s: 0x%lX\r\n", slogan, env) ); + // esock_dbg_printf("SOCK ENV", "alloc - %s: 0x%lX\r\n", slogan, env); + + return env; +} /* ---------------------------------------------------------------------- @@ -4282,13 +4426,13 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, int domain, int type, int protocol, char* netns) { - SocketDescriptor* descP; - ERL_NIF_TERM res; - int save_errno = 0; - SOCKET sock; - HANDLE event; + ESockDescriptor* descP; + ERL_NIF_TERM res; + int save_errno = 0; + SOCKET sock; + HANDLE event; #ifdef HAVE_SETNS - int current_ns = 0; + int current_ns = 0; #endif SGDBG( ("SOCKET", "nopen -> entry with" @@ -4502,11 +4646,11 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP; - ERL_NIF_TERM eSockAddr; - SocketAddress sockAddr; - unsigned int addrLen; - char* xres; + ESockDescriptor* descP; + ERL_NIF_TERM eSockAddr; + ESockAddress sockAddr; + unsigned int addrLen; + char* xres; SGDBG( ("SOCKET", "nif_bind -> entry with argc: %d\r\n", argc) ); @@ -4544,10 +4688,10 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, #if !defined(__WIN32__) static -ERL_NIF_TERM nbind(ErlNifEnv* env, - SocketDescriptor* descP, - SocketAddress* sockAddrP, - unsigned int addrLen) +ERL_NIF_TERM nbind(ErlNifEnv* env, + ESockDescriptor* descP, + ESockAddress* sockAddrP, + unsigned int addrLen) { int port, ntohs_port; @@ -4563,7 +4707,7 @@ ERL_NIF_TERM nbind(ErlNifEnv* env, port = which_address_port(sockAddrP); SSDBG( descP, ("SOCKET", "nbind -> port: %d\r\n", port) ); if (port == 0) { - SOCKLEN_T len = sizeof(SocketAddress); + SOCKLEN_T len = sizeof(ESockAddress); sys_memzero((char *) sockAddrP, len); sock_name(descP->sock, &sockAddrP->sa, &len); port = which_address_port(sockAddrP); @@ -4603,16 +4747,17 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP; - ERL_NIF_TERM res, eSockAddr; - char* xres; + ESockDescriptor* descP; + ERL_NIF_TERM res, eSockAddr, sockRef; + char* xres; SGDBG( ("SOCKET", "nif_connect -> entry with argc: %d\r\n", argc) ); /* Extract arguments and perform preliminary validation */ + sockRef = argv[0]; if ((argc != 2) || - !enif_get_resource(env, argv[0], sockets, (void**) &descP)) { + !enif_get_resource(env, sockRef, sockets, (void**) &descP)) { return enif_make_badarg(env); } eSockAddr = argv[1]; @@ -4624,16 +4769,24 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, "\r\n", descP->sock, argv[0], eSockAddr) ); if ((xres = esock_decode_sockaddr(env, eSockAddr, - &descP->remote, &descP->addrLen)) != NULL) { + &descP->remote, + &descP->addrLen)) != NULL) { return esock_make_error_str(env, xres); } + /* Only a *!%&$*# would send an opened but non-connected socket + * somewhere (before its actually usable), but just to be on the + * safe side we do the best we can to avoid complications... + */ + MLOCK(descP->readMtx); MLOCK(descP->writeMtx); + MLOCK(descP->cfgMtx); - res = nconnect(env, descP); + res = nconnect(env, descP, sockRef); + MUNLOCK(descP->cfgMtx); MUNLOCK(descP->writeMtx); MUNLOCK(descP->readMtx); @@ -4645,8 +4798,9 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, #if !defined(__WIN32__) static -ERL_NIF_TERM nconnect(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM nconnect(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef) { ERL_NIF_TERM res, ref; int code, sres, save_errno = 0; @@ -4691,7 +4845,8 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, (save_errno == EINPROGRESS))) { /* Unix & OSE!! */ ref = MKREF(env); descP->state = SOCKET_STATE_CONNECTING; - if ((sres = esock_select_write(env, descP->sock, descP, NULL, ref)) < 0) { + if ((sres = esock_select_write(env, descP->sock, descP, NULL, + sockRef, ref)) < 0) { res = esock_make_error(env, MKT2(env, esock_atom_select_failed, @@ -4721,6 +4876,9 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, * * Description: * Make socket ready for input and output. + * This function is called if we where made to wait when we called the + * nif_connect function (we made a select, and the select message has + * now been received). * * Arguments: * Socket (ref) - Points to the socket descriptor. @@ -4733,7 +4891,7 @@ ERL_NIF_TERM nif_finalize_connection(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP; + ESockDescriptor* descP; /* Extract arguments and perform preliminary validation */ @@ -4753,8 +4911,8 @@ ERL_NIF_TERM nif_finalize_connection(ErlNifEnv* env, */ #if !defined(__WIN32__) static -ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, + ESockDescriptor* descP) { int error; @@ -4780,7 +4938,7 @@ ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, */ #if !defined(__WIN32__) static -BOOLEAN_T verify_is_connected(SocketDescriptor* descP, int* err) +BOOLEAN_T verify_is_connected(ESockDescriptor* descP, int* err) { /* * *** This is strange *** @@ -4848,8 +5006,8 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP; - int backlog; + ESockDescriptor* descP; + int backlog; SGDBG( ("SOCKET", "nif_listen -> entry with argc: %d\r\n", argc) ); @@ -4876,9 +5034,9 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env, #if !defined(__WIN32__) static -ERL_NIF_TERM nlisten(ErlNifEnv* env, - SocketDescriptor* descP, - int backlog) +ERL_NIF_TERM nlisten(ErlNifEnv* env, + ESockDescriptor* descP, + int backlog) { /* @@ -4930,8 +5088,8 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP; - ERL_NIF_TERM sockRef, ref, res; + ESockDescriptor* descP; + ERL_NIF_TERM sockRef, ref, res; SGDBG( ("SOCKET", "nif_accept -> entry with argc: %d\r\n", argc) ); @@ -4944,13 +5102,29 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env, } ref = argv[1]; + MLOCK(descP->accMtx); + SSDBG( descP, ("SOCKET", "nif_accept -> args when sock = %d:" - "\r\n Socket: %T" - "\r\n ReqRef: %T" - "\r\n", descP->sock, argv[0], ref) ); - - MLOCK(descP->accMtx); + "\r\n Socket: %T" + "\r\n ReqRef: %T" + "\r\nwhen" + "\r\n State: %s" + "\r\n Current Acceptor Addr: 0x%lX" + "\r\n Current Acceptor pid: %T" + "\r\n Current Acceptor mon: %T" + "\r\n Current Acceptor env: 0x%lX" + "\r\n Current Acceptor ref: %T" + "\r\n", + descP->sock, + sockRef, ref, + ((descP->state == SOCKET_STATE_LISTENING) ? "listening" : + ((descP->state == SOCKET_STATE_ACCEPTING) ? "accepting" : "other")), + descP->currentAcceptorP, + descP->currentAcceptor.pid, + esock_make_monitor_term(env, &descP->currentAcceptor.mon), + descP->currentAcceptor.env, + descP->currentAcceptor.ref) ); res = naccept(env, descP, sockRef, ref); @@ -4964,10 +5138,10 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env, #if !defined(__WIN32__) static -ERL_NIF_TERM naccept(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM ref) +ERL_NIF_TERM naccept(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM ref) { ERL_NIF_TERM res; @@ -4976,7 +5150,7 @@ ERL_NIF_TERM naccept(ErlNifEnv* env, switch (descP->state) { case SOCKET_STATE_LISTENING: - res = naccept_listening(env, descP, ref); + res = naccept_listening(env, descP, sockRef, ref); break; case SOCKET_STATE_ACCEPTING: @@ -4994,15 +5168,17 @@ ERL_NIF_TERM naccept(ErlNifEnv* env, /* *** naccept_listening *** - * We have no active acceptor (and no acceptors in queue). + * + * We have no active acceptor (and therefor no acceptors in queue). */ #if !defined(__WIN32__) static -ERL_NIF_TERM naccept_listening(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM ref) +ERL_NIF_TERM naccept_listening(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM accRef) { - SocketAddress remote; + ESockAddress remote; unsigned int n; SOCKET accSock; int save_errno; @@ -5026,7 +5202,8 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, ("SOCKET", "naccept_listening -> accept failed (%d)\r\n", save_errno) ); - res = naccept_listening_error(env, descP, ref, caller, save_errno); + res = naccept_listening_error(env, descP, sockRef, accRef, + caller, save_errno); } else { @@ -5045,38 +5222,45 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, /* *** naccept_listening_error *** + * * The accept call resultet in an error - handle it. * There are only two cases: * 1) BLOCK => Attempt a "retry" * 2) Other => Return the value (converted to an atom) */ static -ERL_NIF_TERM naccept_listening_error(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM ref, - ErlNifPid caller, - int save_errno) +ERL_NIF_TERM naccept_listening_error(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM accRef, + ErlNifPid caller, + int save_errno) { ERL_NIF_TERM res; if (save_errno == ERRNO_BLOCK) { /* *** Try again later *** */ - SSDBG( descP, ("SOCKET", "naccept_listening_error -> would block\r\n") ); + + SSDBG( descP, + ("SOCKET", "naccept_listening_error -> would block\r\n") ); descP->currentAcceptor.pid = caller; if (MONP("naccept_listening -> current acceptor", env, descP, &descP->currentAcceptor.pid, - &descP->currentAcceptor.mon) != 0) - return esock_make_error(env, atom_exmon); - - descP->currentAcceptor.ref = enif_make_copy(descP->env, ref); - descP->currentAcceptorP = &descP->currentAcceptor; - - res = naccept_busy_retry(env, descP, ref, NULL, SOCKET_STATE_ACCEPTING); - - + &descP->currentAcceptor.mon) != 0) { + enif_set_pid_undefined(&descP->currentAcceptor.pid); + res = esock_make_error(env, atom_exmon); + } else { + descP->currentAcceptor.env = esock_alloc_env("current acceptor"); + descP->currentAcceptor.ref = CP_TERM(descP->currentAcceptor.env, + accRef); + descP->currentAcceptorP = &descP->currentAcceptor; + res = naccept_busy_retry(env, descP, + sockRef, accRef, + NULL, SOCKET_STATE_ACCEPTING); + } } else { SSDBG( descP, ("SOCKET", @@ -5089,14 +5273,15 @@ ERL_NIF_TERM naccept_listening_error(ErlNifEnv* env, /* *** naccept_listening_accept *** + * * The accept call was successful (accepted) - handle the new connection. */ static -ERL_NIF_TERM naccept_listening_accept(ErlNifEnv* env, - SocketDescriptor* descP, - SOCKET accSock, - ErlNifPid caller, - SocketAddress* remote) +ERL_NIF_TERM naccept_listening_accept(ErlNifEnv* env, + ESockDescriptor* descP, + SOCKET accSock, + ErlNifPid caller, + ESockAddress* remote) { ERL_NIF_TERM res; @@ -5109,16 +5294,17 @@ ERL_NIF_TERM naccept_listening_accept(ErlNifEnv* env, /* *** naccept_accepting *** + * * We have an active acceptor and possibly acceptors waiting in queue. * If the pid of the calling process is not the pid of the "current process", * push the requester onto the (acceptor) queue. */ #if !defined(__WIN32__) static -ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM ref) +ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM ref) { ErlNifPid caller; ERL_NIF_TERM res; @@ -5162,12 +5348,12 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, * Handles when the current acceptor makes another attempt. */ static -ERL_NIF_TERM naccept_accepting_current(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM accRef) +ERL_NIF_TERM naccept_accepting_current(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM accRef) { - SocketAddress remote; + ESockAddress remote; unsigned int n; SOCKET accSock; int save_errno; @@ -5207,24 +5393,24 @@ ERL_NIF_TERM naccept_accepting_current(ErlNifEnv* env, * handle the new connection. */ static -ERL_NIF_TERM naccept_accepting_current_accept(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - SOCKET accSock, - SocketAddress* remote) +ERL_NIF_TERM naccept_accepting_current_accept(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + SOCKET accSock, + ESockAddress* remote) { ERL_NIF_TERM res; if (naccept_accepted(env, descP, accSock, descP->currentAcceptor.pid, remote, &res)) { - /* We should really go through the queue until we succeed to activate - * a waiting acceptor. For now we just pop once and hope for the best... - * This will leave any remaining acceptors *hanging*... - * - * We need a "activate-next" function. - * - */ + /* Clean out the old cobweb's before trying to invite a new spider */ + + descP->currentAcceptor.ref = esock_atom_undefined; + enif_set_pid_undefined(&descP->currentAcceptor.pid); + esock_free_env("naccept_accepting_current_accept - " + "current-accept-env", + descP->currentAcceptor.env); if (!activate_next_acceptor(env, descP, sockRef)) { @@ -5236,9 +5422,8 @@ ERL_NIF_TERM naccept_accepting_current_accept(ErlNifEnv* env, descP->state = SOCKET_STATE_LISTENING; descP->currentAcceptorP = NULL; - descP->currentAcceptor.ref = esock_atom_undefined; - enif_set_pid_undefined(&descP->currentAcceptor.pid); - esock_monitor_init(&descP->currentAcceptor.mon); + descP->currentAcceptor.env = NULL; + MON_INIT(&descP->currentAcceptor.mon); } } @@ -5254,14 +5439,14 @@ ERL_NIF_TERM naccept_accepting_current_accept(ErlNifEnv* env, * 2) Other => Return the value (converted to an atom) */ static -ERL_NIF_TERM naccept_accepting_current_error(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef, - int save_errno) +ERL_NIF_TERM naccept_accepting_current_error(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef, + int save_errno) { - SocketRequestor req; - ERL_NIF_TERM res, reason; + ESockRequestor req; + ERL_NIF_TERM res, reason; if (save_errno == ERRNO_BLOCK) { @@ -5274,7 +5459,8 @@ ERL_NIF_TERM naccept_accepting_current_error(ErlNifEnv* env, "naccept_accepting_current_error -> " "would block: try again\r\n") ); - res = naccept_busy_retry(env, descP, opRef, &descP->currentAcceptor.pid, + res = naccept_busy_retry(env, descP, sockRef, opRef, + &descP->currentAcceptor.pid, /* No state change */ descP->state); @@ -5287,7 +5473,8 @@ ERL_NIF_TERM naccept_accepting_current_error(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "naccept_accepting_current_error -> abort %T\r\n", req.pid) ); - esock_send_abort_msg(env, sockRef, req.ref, reason, &req.pid); + esock_send_abort_msg(env, sockRef, req.ref, req.env, + reason, &req.pid); DEMONP("naccept_accepting_current_error -> pop'ed writer", env, descP, &req.mon); } @@ -5304,10 +5491,10 @@ ERL_NIF_TERM naccept_accepting_current_error(ErlNifEnv* env, * acceptor queue. */ static -ERL_NIF_TERM naccept_accepting_other(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM ref, - ErlNifPid caller) +ERL_NIF_TERM naccept_accepting_other(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM ref, + ErlNifPid caller) { ERL_NIF_TERM result; @@ -5323,25 +5510,28 @@ ERL_NIF_TERM naccept_accepting_other(ErlNifEnv* env, /* *** naccept_busy_retry *** + * * Perform a retry select. If successful, set nextState. */ #if !defined(__WIN32__) static -ERL_NIF_TERM naccept_busy_retry(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM ref, - ErlNifPid* pid, - unsigned int nextState) +ERL_NIF_TERM naccept_busy_retry(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM accRef, + ErlNifPid* pid, + unsigned int nextState) { int sres; ERL_NIF_TERM res, reason; - if ((sres = esock_select_read(env, descP->sock, descP, pid, ref)) < 0) { + if ((sres = esock_select_read(env, descP->sock, descP, pid, + sockRef, accRef)) < 0) { reason = MKT2(env, esock_atom_select_failed, MKI(env, sres)); res = esock_make_error(env, reason); } else { descP->state = nextState; - res = esock_make_error(env, esock_atom_eagain); + res = esock_make_error(env, esock_atom_eagain); // OK!! } return res; @@ -5350,20 +5540,21 @@ ERL_NIF_TERM naccept_busy_retry(ErlNifEnv* env, /* *** naccept_accepted *** + * * Generic function handling a successful accept. */ static -BOOLEAN_T naccept_accepted(ErlNifEnv* env, - SocketDescriptor* descP, - SOCKET accSock, - ErlNifPid pid, - SocketAddress* remote, - ERL_NIF_TERM* result) +BOOLEAN_T naccept_accepted(ErlNifEnv* env, + ESockDescriptor* descP, + SOCKET accSock, + ErlNifPid pid, + ESockAddress* remote, + ERL_NIF_TERM* result) { - SocketDescriptor* accDescP; - HANDLE accEvent; - ERL_NIF_TERM accRef; - int save_errno; + ESockDescriptor* accDescP; + HANDLE accEvent; + ERL_NIF_TERM accRef; + int save_errno; /* * We got one @@ -5389,7 +5580,7 @@ BOOLEAN_T naccept_accepted(ErlNifEnv* env, accDescP->rBufSz = descP->rBufSz; // Inherit buffer size accDescP->rNum = descP->rNum; // Inherit buffer uses accDescP->rNumCnt = 0; - accDescP->rCtrlSz = descP->rCtrlSz; // Inherit buffer siez + accDescP->rCtrlSz = descP->rCtrlSz; // Inherit buffer size accDescP->wCtrlSz = descP->wCtrlSz; // Inherit buffer size accRef = enif_make_resource(env, accDescP); @@ -5401,6 +5592,7 @@ BOOLEAN_T naccept_accepted(ErlNifEnv* env, &accDescP->ctrlPid, &accDescP->ctrlMon) != 0) { sock_close(accSock); + enif_set_pid_undefined(&descP->ctrlPid); *result = esock_make_error(env, atom_exmon); return FALSE; } @@ -5442,12 +5634,12 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP; - ERL_NIF_TERM sockRef, sendRef; - ErlNifBinary sndData; - unsigned int eflags; - int flags; - ERL_NIF_TERM res; + ESockDescriptor* descP; + ERL_NIF_TERM sockRef, sendRef; + ErlNifBinary sndData; + unsigned int eflags; + int flags; + ERL_NIF_TERM res; SGDBG( ("SOCKET", "nif_send -> entry with argc: %d\r\n", argc) ); @@ -5484,11 +5676,6 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, * this time (resulting in an select). The write of the * other process must be made to wait until current * is done! - * Basically, we need a write queue! - * - * A 'writing' field (boolean), which is set if we did - * not manage to write the entire message and reset every - * time we do. */ res = nsend(env, descP, sockRef, sendRef, &sndData, flags); @@ -5496,6 +5683,7 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, MUNLOCK(descP->writeMtx); return res; + #endif // if defined(__WIN32__) } @@ -5510,12 +5698,12 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, */ #if !defined(__WIN32__) static -ERL_NIF_TERM nsend(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM sendRef, - ErlNifBinary* sndDataP, - int flags) +ERL_NIF_TERM nsend(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + ErlNifBinary* sndDataP, + int flags) { int save_errno; ssize_t written; @@ -5570,16 +5758,16 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP; - ERL_NIF_TERM sockRef, sendRef; - ErlNifBinary sndData; - unsigned int eflags; - int flags; - ERL_NIF_TERM eSockAddr; - SocketAddress remoteAddr; - unsigned int remoteAddrLen; - char* xres; - ERL_NIF_TERM res; + ESockDescriptor* descP; + ERL_NIF_TERM sockRef, sendRef; + ErlNifBinary sndData; + unsigned int eflags; + int flags; + ERL_NIF_TERM eSockAddr; + ESockAddress remoteAddr; + unsigned int remoteAddrLen; + char* xres; + ERL_NIF_TERM res; SGDBG( ("SOCKET", "nif_sendto -> entry with argc: %d\r\n", argc) ); @@ -5617,7 +5805,8 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, if ((xres = esock_decode_sockaddr(env, eSockAddr, &remoteAddr, &remoteAddrLen)) != NULL) { - SSDBG( descP, ("SOCKET", "nif_sendto -> sockaddr decode: %s\r\n", xres) ); + SSDBG( descP, + ("SOCKET", "nif_sendto -> sockaddr decode: %s\r\n", xres) ); return esock_make_error_str(env, xres); } @@ -5633,20 +5822,21 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, "\r\n", res) ); return res; + #endif // if defined(__WIN32__) } #if !defined(__WIN32__) static -ERL_NIF_TERM nsendto(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM sendRef, - ErlNifBinary* dataP, - int flags, - SocketAddress* toAddrP, - unsigned int toAddrLen) +ERL_NIF_TERM nsendto(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + ErlNifBinary* dataP, + int flags, + ESockAddress* toAddrP, + unsigned int toAddrLen) { int save_errno; ssize_t written; @@ -5706,10 +5896,10 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - ERL_NIF_TERM res, sockRef, sendRef, eMsgHdr; - SocketDescriptor* descP; - unsigned int eflags; - int flags; + ERL_NIF_TERM res, sockRef, sendRef, eMsgHdr; + ESockDescriptor* descP; + unsigned int eflags; + int flags; SGDBG( ("SOCKET", "nif_sendmsg -> entry with argc: %d\r\n", argc) ); @@ -5751,21 +5941,22 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, "\r\n", res) ); return res; + #endif // if defined(__WIN32__) } #if !defined(__WIN32__) static -ERL_NIF_TERM nsendmsg(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM sendRef, - ERL_NIF_TERM eMsgHdr, - int flags) +ERL_NIF_TERM nsendmsg(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + ERL_NIF_TERM eMsgHdr, + int flags) { ERL_NIF_TERM res, eAddr, eIOV, eCtrl; - SocketAddress addr; + ESockAddress addr; struct msghdr msgHdr; ErlNifBinary* iovBins; struct iovec* iov; @@ -5908,6 +6099,7 @@ ERL_NIF_TERM nsendmsg(ErlNifEnv* env, if (ctrlBuf != NULL) FREE(ctrlBuf); return res; + } #endif // if !defined(__WIN32__) @@ -5928,10 +6120,10 @@ ERL_NIF_TERM nsendmsg(ErlNifEnv* env, #ifdef FOBAR static -ERL_NIF_TERM nwritev(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sendRef, - ERL_NIF_TERM data) +ERL_NIF_TERM nwritev(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sendRef, + ERL_NIF_TERM data) { ERL_NIF_TERM tail; ErlNifIOVec vec; @@ -6006,12 +6198,12 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP; - ERL_NIF_TERM sockRef, recvRef; - int len; - unsigned int eflags; - int flags; - ERL_NIF_TERM res; + ESockDescriptor* descP; + ERL_NIF_TERM sockRef, recvRef; + int len; + unsigned int eflags; + int flags; + ERL_NIF_TERM res; if ((argc != 4) || !GET_INT(env, argv[2], &len) || @@ -6036,11 +6228,6 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, * this time (resulting in an select). The read of the * other process must be made to wait until current * is done! - * Basically, we need a read queue! - * - * A 'reading' field (boolean), which is set if we did - * not manage to read the entire message and reset every - * time we do. */ res = nrecv(env, descP, sockRef, recvRef, len, flags); @@ -6048,23 +6235,24 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, MUNLOCK(descP->readMtx); return res; + #endif // if defined(__WIN32__) } -/* The (read) buffer handling *must* be optimized! +/* The (read) buffer handling should be optimized! * But for now we make it easy for ourselves by * allocating a binary (of the specified or default * size) and then throwing it away... */ #if !defined(__WIN32__) static -ERL_NIF_TERM nrecv(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM recvRef, - int len, - int flags) +ERL_NIF_TERM nrecv(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + int len, + int flags) { ssize_t read; ErlNifBinary buf; @@ -6150,12 +6338,12 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP; - ERL_NIF_TERM sockRef, recvRef; - unsigned int bufSz; - unsigned int eflags; - int flags; - ERL_NIF_TERM res; + ESockDescriptor* descP; + ERL_NIF_TERM sockRef, recvRef; + unsigned int bufSz; + unsigned int eflags; + int flags; + ERL_NIF_TERM res; SGDBG( ("SOCKET", "nif_recvfrom -> entry with argc: %d\r\n", argc) ); @@ -6222,14 +6410,14 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, */ #if !defined(__WIN32__) static -ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM recvRef, - Uint16 len, - int flags) +ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + Uint16 len, + int flags) { - SocketAddress fromAddr; + ESockAddress fromAddr; unsigned int addrLen; ssize_t read; int save_errno; @@ -6319,13 +6507,13 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP; - ERL_NIF_TERM sockRef, recvRef; - unsigned int bufSz; - unsigned int ctrlSz; - unsigned int eflags; - int flags; - ERL_NIF_TERM res; + ESockDescriptor* descP; + ERL_NIF_TERM sockRef, recvRef; + unsigned int bufSz; + unsigned int ctrlSz; + unsigned int eflags; + int flags; + ERL_NIF_TERM res; SGDBG( ("SOCKET", "nif_recvmsg -> entry with argc: %d\r\n", argc) ); @@ -6394,13 +6582,13 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, */ #if !defined(__WIN32__) static -ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM recvRef, - Uint16 bufLen, - Uint16 ctrlLen, - int flags) +ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + Uint16 bufLen, + Uint16 ctrlLen, + int flags) { unsigned int addrLen; ssize_t read; @@ -6412,7 +6600,7 @@ ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, ErlNifBinary data[1]; // Shall we always use 1? ErlNifBinary ctrl; ERL_NIF_TERM readerCheck; - SocketAddress addr; + ESockAddress addr; SSDBG( descP, ("SOCKET", "nrecvmsg -> entry with" "\r\n bufSz: %d (%d)" @@ -6502,7 +6690,7 @@ ERL_NIF_TERM nif_close(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP; + ESockDescriptor* descP; if ((argc != 1) || !enif_get_resource(env, argv[0], sockets, (void**) &descP)) { @@ -6519,15 +6707,11 @@ ERL_NIF_TERM nif_close(ErlNifEnv* env, #if !defined(__WIN32__) static -ERL_NIF_TERM nclose(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM nclose(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM reply, reason; BOOLEAN_T doClose; - int selectRes; - int domain = descP->domain; - int type = descP->type; - int protocol = descP->protocol; SSDBG( descP, ("SOCKET", "nclose -> [%d] entry (0x%lX, 0x%lX, 0x%lX, 0x%lX)\r\n", @@ -6539,12 +6723,48 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, MLOCK(descP->closeMtx); + doClose = nclose_check(env, descP, &reason); + + if (doClose) { + reply = nclose_do(env, descP); + } else { + reply = esock_make_error(env, reason); + } + + MUNLOCK(descP->closeMtx); + + SSDBG( descP, + ("SOCKET", "nclose -> [%d] done when: " + "\r\n state: 0x%lX" + "\r\n reply: %T" + "\r\n", descP->sock, descP->state, reply) ); + + return reply; +} + + + +/* *** nclose_check *** + * + * Check if we should try to perform the first stage close. + */ +static +BOOLEAN_T nclose_check(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM* reason) +{ + BOOLEAN_T doClose; + if (descP->state == SOCKET_STATE_CLOSED) { - reason = atom_closed; + doClose = FALSE; + *reason = atom_closed; + } else if (descP->state == SOCKET_STATE_CLOSING) { - reason = atom_closing; + doClose = FALSE; + *reason = atom_closing; + } else { /* Store the PID of the caller, @@ -6554,90 +6774,116 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, */ if (enif_self(env, &descP->closerPid) == NULL) { - MUNLOCK(descP->closeMtx); - return esock_make_error(env, atom_exself); - } - /* Monitor the caller, since we should complete this operation even if - * the caller dies (for whatever reason). - * - * <KOLLA> - * - * Can we actiually use this for anything? - * - * </KOLLA> - */ - - if (MONP("nclose -> closer", - env, descP, - &descP->closerPid, - &descP->closerMon) != 0) { - MUNLOCK(descP->closeMtx); - return esock_make_error(env, atom_exmon); - } - - descP->closeLocal = TRUE; - descP->state = SOCKET_STATE_CLOSING; - descP->isReadable = FALSE; - descP->isWritable = FALSE; - doClose = TRUE; - } + doClose = FALSE; + *reason = atom_exself; - if (doClose) { - descP->closeEnv = enif_alloc_env(); - descP->closeRef = MKREF(descP->closeEnv); - selectRes = esock_select_stop(env, descP->sock, descP); - if (selectRes & ERL_NIF_SELECT_STOP_CALLED) { - /* Prep done - inform the caller it can finalize (close) directly */ - SSDBG( descP, - ("SOCKET", "nclose -> [%d] stop was called\r\n", descP->sock) ); - dec_socket(domain, type, protocol); - reply = esock_atom_ok; - } else if (selectRes & ERL_NIF_SELECT_STOP_SCHEDULED) { - /* The stop callback function has been *scheduled* which means that we - * have to wait for it to complete. */ - SSDBG( descP, - ("SOCKET", "nclose -> [%d] stop was scheduled\r\n", - descP->sock) ); - dec_socket(domain, type, protocol); // SHALL WE DO THIS AT finalize? - reply = esock_make_ok2(env, descP->closeRef); } else { - SSDBG( descP, - ("SOCKET", "nclose -> [%d] stop failed: %d\r\n", - descP->sock, selectRes) ); - - /* <KOLLA> + /* Monitor the caller, since we should complete this + * operation even if the caller dies (for whatever reason). + * + * <KOLLA> * - * WE SHOULD REALLY HAVE A WAY TO CLOBBER THE SOCKET, - * SO WE DON'T LET STUFF LEAK. - * NOW, BECAUSE WE FAILED TO SELECT, WE CANNOT FINISH - * THE CLOSE, WHAT TO DO? ABORT? + * Can we actually use this for anything? * * </KOLLA> */ - // No point in having this? - DEMONP("nclose -> closer", env, descP, &descP->closerMon); + if (MONP("nclose_check -> closer", + env, descP, + &descP->closerPid, + &descP->closerMon) != 0) { + + doClose = FALSE; + *reason = atom_exmon; + + } else { - reason = MKT2(env, atom_select, MKI(env, selectRes)); - reply = esock_make_error(env, reason); + descP->closeLocal = TRUE; + descP->state = SOCKET_STATE_CLOSING; + descP->isReadable = FALSE; + descP->isWritable = FALSE; + doClose = TRUE; + *reason = esock_atom_undefined; // NOT used !! + + } } + } + + return doClose; + +} + + + +/* *** nclose_do *** + * + * Perform (do) the first stage close. + */ +static +ERL_NIF_TERM nclose_do(ErlNifEnv* env, + ESockDescriptor* descP) +{ + int domain = descP->domain; + int type = descP->type; + int protocol = descP->protocol; + int sres; + ERL_NIF_TERM reply, reason; + + descP->closeEnv = esock_alloc_env("nclose-do - close-env"); + descP->closeRef = MKREF(descP->closeEnv); + + sres = esock_select_stop(env, descP->sock, descP); + + if (sres & ERL_NIF_SELECT_STOP_CALLED) { + + /* Prep done - inform the caller it can finalize (close) directly */ + SSDBG( descP, + ("SOCKET", "nclose -> [%d] stop was called\r\n", descP->sock) ); + + dec_socket(domain, type, protocol); + reply = esock_atom_ok; + + } else if (sres & ERL_NIF_SELECT_STOP_SCHEDULED) { + + /* The stop callback function has been *scheduled* which means that we + * have to wait for it to complete. */ + SSDBG( descP, + ("SOCKET", "nclose -> [%d] stop was scheduled\r\n", + descP->sock) ); + + dec_socket(domain, type, protocol); // SHALL WE DO THIS AT finalize? + reply = esock_make_ok2(env, enif_make_copy(env, descP->closeRef)); } else { - reply = esock_make_error(env, reason); - } - MUNLOCK(descP->closeMtx); + SSDBG( descP, + ("SOCKET", "nclose -> [%d] stop failed: %d\r\n", + descP->sock, sres) ); - SSDBG( descP, - ("SOCKET", "nclose -> [%d] done when: " - "\r\n state: 0x%lX" - "\r\n reply: %T" - "\r\n", descP->sock, descP->state, reply) ); + /* <KOLLA> + * + * WE SHOULD REALLY HAVE A WAY TO CLOBBER THE SOCKET, + * SO WE DON'T LET STUFF LEAK. + * NOW, BECAUSE WE FAILED TO SELECT, WE CANNOT FINISH + * THE CLOSE, WHAT TO DO? ABORT? + * + * </KOLLA> + */ + + // Do we need this? + DEMONP("nclose_do -> closer", env, descP, &descP->closerMon); + + reason = MKT2(env, esock_atom_select_failed, MKI(env, sres)); + reply = esock_make_error(env, reason); + } return reply; } + + + #endif // if !defined(__WIN32__) @@ -6660,7 +6906,7 @@ ERL_NIF_TERM nif_finalize_close(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP; + ESockDescriptor* descP; /* Extract arguments and perform preliminary validation */ @@ -6679,8 +6925,8 @@ ERL_NIF_TERM nif_finalize_close(ErlNifEnv* env, */ #if !defined(__WIN32__) static -ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM reply; @@ -6744,9 +6990,9 @@ ERL_NIF_TERM nif_shutdown(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP; - unsigned int ehow; - int how; + ESockDescriptor* descP; + unsigned int ehow; + int how; if ((argc != 2) || !enif_get_resource(env, argv[0], sockets, (void**) &descP) || @@ -6768,9 +7014,9 @@ ERL_NIF_TERM nif_shutdown(ErlNifEnv* env, #if !defined(__WIN32__) static -ERL_NIF_TERM nshutdown(ErlNifEnv* env, - SocketDescriptor* descP, - int how) +ERL_NIF_TERM nshutdown(ErlNifEnv* env, + ESockDescriptor* descP, + int how) { ERL_NIF_TERM reply; @@ -6825,13 +7071,13 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP = NULL; - int eLevel, level = -1; - int eOpt; - ERL_NIF_TERM eIsEncoded; - ERL_NIF_TERM eVal; - BOOLEAN_T isEncoded, isOTP; - ERL_NIF_TERM result; + ESockDescriptor* descP = NULL; + int eLevel, level = -1; + int eOpt; + ERL_NIF_TERM eIsEncoded; + ERL_NIF_TERM eVal; + BOOLEAN_T isEncoded, isOTP; + ERL_NIF_TERM result; SGDBG( ("SOCKET", "nif_setopt -> entry with argc: %d\r\n", argc) ); @@ -6873,27 +7119,32 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, level, eLevel, eOpt, eVal) ); + MLOCK(descP->cfgMtx); + result = nsetopt(env, descP, isEncoded, isOTP, level, eOpt, eVal); + MUNLOCK(descP->cfgMtx); + SSDBG( descP, ("SOCKET", "nif_setopt -> done when" "\r\n result: %T" "\r\n", result) ); return result; + #endif // if defined(__WIN32__) } #if !defined(__WIN32__) static -ERL_NIF_TERM nsetopt(ErlNifEnv* env, - SocketDescriptor* descP, - BOOLEAN_T isEncoded, - BOOLEAN_T isOTP, - int level, - int eOpt, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt(ErlNifEnv* env, + ESockDescriptor* descP, + BOOLEAN_T isEncoded, + BOOLEAN_T isOTP, + int level, + int eOpt, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; @@ -6916,10 +7167,10 @@ ERL_NIF_TERM nsetopt(ErlNifEnv* env, /* nsetopt_otp - Handle OTP (level) options */ static -ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; @@ -6966,9 +7217,9 @@ ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, /* nsetopt_otp_debug - Handle the OTP (level) debug options */ static -ERL_NIF_TERM nsetopt_otp_debug(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_otp_debug(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { descP->dbg = esock_decode_bool(eVal); @@ -6979,9 +7230,9 @@ ERL_NIF_TERM nsetopt_otp_debug(ErlNifEnv* env, /* nsetopt_otp_iow - Handle the OTP (level) iow options */ static -ERL_NIF_TERM nsetopt_otp_iow(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_otp_iow(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { descP->iow = esock_decode_bool(eVal); @@ -6993,12 +7244,11 @@ ERL_NIF_TERM nsetopt_otp_iow(ErlNifEnv* env, /* nsetopt_otp_ctrl_proc - Handle the OTP (level) controlling_process options */ static -ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { ErlNifPid caller, newCtrlPid; - // ErlNifMonitor newCtrlMon; ESockMonitor newCtrlMon; int xres; @@ -7053,9 +7303,9 @@ ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env, * Where N is the max number of reads. */ static -ERL_NIF_TERM nsetopt_otp_rcvbuf(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_otp_rcvbuf(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { const ERL_NIF_TERM* t; // The array of the elements of the tuple int tsz; // The size of the tuple - should be 2 @@ -7108,9 +7358,9 @@ ERL_NIF_TERM nsetopt_otp_rcvbuf(ErlNifEnv* env, /* nsetopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf option */ static -ERL_NIF_TERM nsetopt_otp_rcvctrlbuf(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_otp_rcvctrlbuf(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { size_t val; char* xres; @@ -7131,9 +7381,9 @@ ERL_NIF_TERM nsetopt_otp_rcvctrlbuf(ErlNifEnv* env, /* nsetopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf option */ static -ERL_NIF_TERM nsetopt_otp_sndctrlbuf(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_otp_sndctrlbuf(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { size_t val; char* xres; @@ -7155,11 +7405,11 @@ ERL_NIF_TERM nsetopt_otp_sndctrlbuf(ErlNifEnv* env, * in "native mode" (option is provided as is and value as a binary). */ static -ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal) { ErlNifBinary val; ERL_NIF_TERM result; @@ -7195,11 +7445,11 @@ ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, /* nsetopt_level - A "proper" level (option) has been specified */ static -ERL_NIF_TERM nsetopt_level(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int eOpt, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_level(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int eOpt, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; @@ -7265,10 +7515,10 @@ ERL_NIF_TERM nsetopt_level(ErlNifEnv* env, /* nsetopt_lvl_socket - Level *SOCKET* option */ static -ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; @@ -7404,9 +7654,9 @@ ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, #if defined(SO_BINDTODEVICE) static -ERL_NIF_TERM nsetopt_lvl_sock_bindtodevice(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_bindtodevice(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_str_opt(env, descP, SOL_SOCKET, SO_BROADCAST, @@ -7417,9 +7667,9 @@ ERL_NIF_TERM nsetopt_lvl_sock_bindtodevice(ErlNifEnv* env, #if defined(SO_BROADCAST) static -ERL_NIF_TERM nsetopt_lvl_sock_broadcast(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_broadcast(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_BROADCAST, eVal); } @@ -7428,9 +7678,9 @@ ERL_NIF_TERM nsetopt_lvl_sock_broadcast(ErlNifEnv* env, #if defined(SO_DEBUG) static -ERL_NIF_TERM nsetopt_lvl_sock_debug(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_debug(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_DEBUG, eVal); } @@ -7439,9 +7689,9 @@ ERL_NIF_TERM nsetopt_lvl_sock_debug(ErlNifEnv* env, #if defined(SO_DONTROUTE) static -ERL_NIF_TERM nsetopt_lvl_sock_dontroute(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_dontroute(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_DONTROUTE, eVal); } @@ -7450,9 +7700,9 @@ ERL_NIF_TERM nsetopt_lvl_sock_dontroute(ErlNifEnv* env, #if defined(SO_KEEPALIVE) static -ERL_NIF_TERM nsetopt_lvl_sock_keepalive(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_keepalive(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_KEEPALIVE, eVal); } @@ -7461,9 +7711,9 @@ ERL_NIF_TERM nsetopt_lvl_sock_keepalive(ErlNifEnv* env, #if defined(SO_LINGER) static -ERL_NIF_TERM nsetopt_lvl_sock_linger(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_linger(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; struct linger val; @@ -7487,9 +7737,9 @@ ERL_NIF_TERM nsetopt_lvl_sock_linger(ErlNifEnv* env, #if defined(SO_OOBINLINE) static -ERL_NIF_TERM nsetopt_lvl_sock_oobinline(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_oobinline(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_OOBINLINE, eVal); } @@ -7498,9 +7748,9 @@ ERL_NIF_TERM nsetopt_lvl_sock_oobinline(ErlNifEnv* env, #if defined(SO_PEEK_OFF) static -ERL_NIF_TERM nsetopt_lvl_sock_peek_off(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_peek_off(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_PEEK_OFF, eVal); } @@ -7509,9 +7759,9 @@ ERL_NIF_TERM nsetopt_lvl_sock_peek_off(ErlNifEnv* env, #if defined(SO_PRIORITY) static -ERL_NIF_TERM nsetopt_lvl_sock_priority(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_priority(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_PRIORITY, eVal); } @@ -7520,9 +7770,9 @@ ERL_NIF_TERM nsetopt_lvl_sock_priority(ErlNifEnv* env, #if defined(SO_RCVBUF) static -ERL_NIF_TERM nsetopt_lvl_sock_rcvbuf(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_rcvbuf(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_RCVBUF, eVal); } @@ -7531,9 +7781,9 @@ ERL_NIF_TERM nsetopt_lvl_sock_rcvbuf(ErlNifEnv* env, #if defined(SO_RCVLOWAT) static -ERL_NIF_TERM nsetopt_lvl_sock_rcvlowat(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_rcvlowat(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_RCVLOWAT, eVal); } @@ -7542,9 +7792,9 @@ ERL_NIF_TERM nsetopt_lvl_sock_rcvlowat(ErlNifEnv* env, #if defined(SO_RCVTIMEO) static -ERL_NIF_TERM nsetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_timeval_opt(env, descP, SOL_SOCKET, SO_RCVTIMEO, eVal); } @@ -7553,9 +7803,9 @@ ERL_NIF_TERM nsetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, #if defined(SO_REUSEADDR) static -ERL_NIF_TERM nsetopt_lvl_sock_reuseaddr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_reuseaddr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEADDR, eVal); } @@ -7564,9 +7814,9 @@ ERL_NIF_TERM nsetopt_lvl_sock_reuseaddr(ErlNifEnv* env, #if defined(SO_REUSEPORT) static -ERL_NIF_TERM nsetopt_lvl_sock_reuseport(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_reuseport(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEPORT, eVal); } @@ -7575,9 +7825,9 @@ ERL_NIF_TERM nsetopt_lvl_sock_reuseport(ErlNifEnv* env, #if defined(SO_SNDBUF) static -ERL_NIF_TERM nsetopt_lvl_sock_sndbuf(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_sndbuf(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_SNDBUF, eVal); } @@ -7586,9 +7836,9 @@ ERL_NIF_TERM nsetopt_lvl_sock_sndbuf(ErlNifEnv* env, #if defined(SO_SNDLOWAT) static -ERL_NIF_TERM nsetopt_lvl_sock_sndlowat(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_sndlowat(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_SNDLOWAT, eVal); } @@ -7597,9 +7847,9 @@ ERL_NIF_TERM nsetopt_lvl_sock_sndlowat(ErlNifEnv* env, #if defined(SO_SNDTIMEO) static -ERL_NIF_TERM nsetopt_lvl_sock_sndtimeo(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_sndtimeo(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { SSDBG( descP, ("SOCKET", "nsetopt_lvl_sock_sndtimeo -> entry with" @@ -7613,9 +7863,9 @@ ERL_NIF_TERM nsetopt_lvl_sock_sndtimeo(ErlNifEnv* env, #if defined(SO_TIMESTAMP) static -ERL_NIF_TERM nsetopt_lvl_sock_timestamp(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_timestamp(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_TIMESTAMP, eVal); } @@ -7626,10 +7876,10 @@ ERL_NIF_TERM nsetopt_lvl_sock_timestamp(ErlNifEnv* env, /* nsetopt_lvl_ip - Level *IP* option(s) */ static -ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; @@ -7843,9 +8093,9 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, */ #if defined(IP_ADD_MEMBERSHIP) static -ERL_NIF_TERM nsetopt_lvl_ip_add_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_add_membership(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_lvl_ip_update_membership(env, descP, eVal, IP_ADD_MEMBERSHIP); } @@ -7863,9 +8113,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_add_membership(ErlNifEnv* env, */ #if defined(IP_ADD_SOURCE_MEMBERSHIP) static -ERL_NIF_TERM nsetopt_lvl_ip_add_source_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_add_source_membership(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_lvl_ip_update_source(env, descP, eVal, IP_ADD_SOURCE_MEMBERSHIP); @@ -7884,9 +8134,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_add_source_membership(ErlNifEnv* env, */ #if defined(IP_BLOCK_SOURCE) static -ERL_NIF_TERM nsetopt_lvl_ip_block_source(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_block_source(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_lvl_ip_update_source(env, descP, eVal, IP_BLOCK_SOURCE); } @@ -7906,9 +8156,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_block_source(ErlNifEnv* env, */ #if defined(IP_DROP_MEMBERSHIP) static -ERL_NIF_TERM nsetopt_lvl_ip_drop_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_drop_membership(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_lvl_ip_update_membership(env, descP, eVal, IP_DROP_MEMBERSHIP); @@ -7928,9 +8178,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_drop_membership(ErlNifEnv* env, */ #if defined(IP_DROP_SOURCE_MEMBERSHIP) static -ERL_NIF_TERM nsetopt_lvl_ip_drop_source_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_drop_source_membership(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_lvl_ip_update_source(env, descP, eVal, IP_DROP_SOURCE_MEMBERSHIP); @@ -7943,9 +8193,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_drop_source_membership(ErlNifEnv* env, */ #if defined(IP_FREEBIND) static -ERL_NIF_TERM nsetopt_lvl_ip_freebind(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_freebind(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -7963,9 +8213,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_freebind(ErlNifEnv* env, */ #if defined(IP_HDRINCL) static -ERL_NIF_TERM nsetopt_lvl_ip_hdrincl(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_hdrincl(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -7983,9 +8233,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_hdrincl(ErlNifEnv* env, */ #if defined(IP_MINTTL) static -ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8005,9 +8255,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env, */ #if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE) static -ERL_NIF_TERM nsetopt_lvl_ip_msfilter(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_msfilter(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; @@ -8058,7 +8308,7 @@ ERL_NIF_TERM nsetopt_lvl_ip_msfilter(ErlNifEnv* env, return esock_make_error(env, esock_atom_einval); } - if (!decode_ip_msfilter_mode(env, eFMode, &msfP->imsf_fmode)) { + if (!decode_ip_msfilter_mode(env, eFMode, (Uint32*) &msfP->imsf_fmode)) { FREE(msfP); return esock_make_error(env, esock_atom_einval); } @@ -8138,9 +8388,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_msfilter_set(ErlNifEnv* env, */ #if defined(IP_MTU_DISCOVER) static -ERL_NIF_TERM nsetopt_lvl_ip_mtu_discover(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_mtu_discover(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; int val; @@ -8177,9 +8427,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_mtu_discover(ErlNifEnv* env, */ #if defined(IP_MULTICAST_ALL) static -ERL_NIF_TERM nsetopt_lvl_ip_multicast_all(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_multicast_all(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8198,9 +8448,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_multicast_all(ErlNifEnv* env, */ #if defined(IP_MULTICAST_IF) static -ERL_NIF_TERM nsetopt_lvl_ip_multicast_if(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_multicast_if(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; struct in_addr ifAddr; @@ -8235,9 +8485,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_multicast_if(ErlNifEnv* env, */ #if defined(IP_MULTICAST_LOOP) static -ERL_NIF_TERM nsetopt_lvl_ip_multicast_loop(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_multicast_loop(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8254,9 +8504,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_multicast_loop(ErlNifEnv* env, */ #if defined(IP_MULTICAST_TTL) static -ERL_NIF_TERM nsetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8273,9 +8523,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, */ #if defined(IP_NODEFRAG) static -ERL_NIF_TERM nsetopt_lvl_ip_nodefrag(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_nodefrag(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8292,9 +8542,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_nodefrag(ErlNifEnv* env, */ #if defined(IP_PKTINFO) static -ERL_NIF_TERM nsetopt_lvl_ip_pktinfo(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_pktinfo(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8311,9 +8561,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_pktinfo(ErlNifEnv* env, */ #if defined(IP_RECVDSTADDR) static -ERL_NIF_TERM nsetopt_lvl_ip_recvdstaddr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_recvdstaddr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8330,9 +8580,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_recvdstaddr(ErlNifEnv* env, */ #if defined(IP_RECVERR) static -ERL_NIF_TERM nsetopt_lvl_ip_recverr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_recverr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8349,9 +8599,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_recverr(ErlNifEnv* env, */ #if defined(IP_RECVIF) static -ERL_NIF_TERM nsetopt_lvl_ip_recvif(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_recvif(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8368,9 +8618,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_recvif(ErlNifEnv* env, */ #if defined(IP_RECVOPTS) static -ERL_NIF_TERM nsetopt_lvl_ip_recvopts(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_recvopts(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8387,9 +8637,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_recvopts(ErlNifEnv* env, */ #if defined(IP_RECVORIGDSTADDR) static -ERL_NIF_TERM nsetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8406,9 +8656,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env, */ #if defined(IP_RECVTOS) static -ERL_NIF_TERM nsetopt_lvl_ip_recvtos(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_recvtos(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8425,9 +8675,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_recvtos(ErlNifEnv* env, */ #if defined(IP_RECVTTL) static -ERL_NIF_TERM nsetopt_lvl_ip_recvttl(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_recvttl(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8444,9 +8694,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_recvttl(ErlNifEnv* env, */ #if defined(IP_RETOPTS) static -ERL_NIF_TERM nsetopt_lvl_ip_retopts(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_retopts(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8463,9 +8713,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_retopts(ErlNifEnv* env, */ #if defined(IP_ROUTER_ALERT) static -ERL_NIF_TERM nsetopt_lvl_ip_router_alert(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_router_alert(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8482,9 +8732,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_router_alert(ErlNifEnv* env, */ #if defined(IP_SENDSRCADDR) static -ERL_NIF_TERM nsetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8501,9 +8751,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env, */ #if defined(IP_TOS) static -ERL_NIF_TERM nsetopt_lvl_ip_tos(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_tos(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8534,9 +8784,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_tos(ErlNifEnv* env, */ #if defined(IP_TRANSPARENT) static -ERL_NIF_TERM nsetopt_lvl_ip_transparent(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_transparent(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8554,9 +8804,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_transparent(ErlNifEnv* env, */ #if defined(IP_TTL) static -ERL_NIF_TERM nsetopt_lvl_ip_ttl(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_ttl(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IP) int level = SOL_IP; @@ -8581,9 +8831,9 @@ ERL_NIF_TERM nsetopt_lvl_ip_ttl(ErlNifEnv* env, */ #if defined(IP_UNBLOCK_SOURCE) static -ERL_NIF_TERM nsetopt_lvl_ip_unblock_source(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ip_unblock_source(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_lvl_ip_update_source(env, descP, eVal, IP_UNBLOCK_SOURCE); } @@ -8593,10 +8843,10 @@ ERL_NIF_TERM nsetopt_lvl_ip_unblock_source(ErlNifEnv* env, #if defined(IP_ADD_MEMBERSHIP) || defined(IP_DROP_MEMBERSHIP) static -ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal, - int opt) +ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal, + int opt) { ERL_NIF_TERM result, eMultiAddr, eInterface; struct ip_mreq mreq; @@ -8647,10 +8897,10 @@ ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env, #if defined(IP_ADD_SOURCE_MEMBERSHIP) || defined(IP_DROP_SOURCE_MEMBERSHIP) || defined(IP_BLOCK_SOURCE) || defined(IP_UNBLOCK_SOURCE) static -ERL_NIF_TERM nsetopt_lvl_ip_update_source(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal, - int opt) +ERL_NIF_TERM nsetopt_lvl_ip_update_source(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal, + int opt) { ERL_NIF_TERM result, eMultiAddr, eInterface, eSourceAddr; struct ip_mreq_source mreq; @@ -8714,10 +8964,10 @@ ERL_NIF_TERM nsetopt_lvl_ip_update_source(ErlNifEnv* env, */ #if defined(HAVE_IPV6) static -ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; @@ -8859,9 +9109,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, #if defined(IPV6_ADDRFORM) static -ERL_NIF_TERM nsetopt_lvl_ipv6_addrform(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_addrform(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; int res, edomain, domain; @@ -8901,9 +9151,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_addrform(ErlNifEnv* env, #if defined(IPV6_ADD_MEMBERSHIP) static -ERL_NIF_TERM nsetopt_lvl_ipv6_add_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_add_membership(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_lvl_ipv6_update_membership(env, descP, eVal, IPV6_ADD_MEMBERSHIP); @@ -8913,9 +9163,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_add_membership(ErlNifEnv* env, #if defined(IPV6_AUTHHDR) static -ERL_NIF_TERM nsetopt_lvl_ipv6_authhdr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_authhdr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_AUTHHDR, eVal); } @@ -8924,9 +9174,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_authhdr(ErlNifEnv* env, #if defined(IPV6_DROP_MEMBERSHIP) static -ERL_NIF_TERM nsetopt_lvl_ipv6_drop_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_drop_membership(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_lvl_ipv6_update_membership(env, descP, eVal, IPV6_DROP_MEMBERSHIP); @@ -8936,9 +9186,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_drop_membership(ErlNifEnv* env, #if defined(IPV6_DSTOPTS) static -ERL_NIF_TERM nsetopt_lvl_ipv6_dstopts(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_dstopts(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -8953,9 +9203,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_dstopts(ErlNifEnv* env, #if defined(IPV6_FLOWINFO) static -ERL_NIF_TERM nsetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -8970,9 +9220,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, #if defined(IPV6_HOPLIMIT) static -ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -8987,9 +9237,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, #if defined(IPV6_HOPOPTS) static -ERL_NIF_TERM nsetopt_lvl_ipv6_hopopts(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_hopopts(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -9004,9 +9254,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_hopopts(ErlNifEnv* env, #if defined(IPV6_MTU) static -ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -9025,9 +9275,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv* env, */ #if defined(IPV6_MTU_DISCOVER) static -ERL_NIF_TERM nsetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; int val; @@ -9063,9 +9313,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, #if defined(IPV6_MULTICAST_HOPS) static -ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -9081,9 +9331,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, #if defined(IPV6_MULTICAST_IF) static -ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -9099,9 +9349,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, #if defined(IPV6_MULTICAST_LOOP) static -ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -9116,9 +9366,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, #if defined(IPV6_RECVERR) static -ERL_NIF_TERM nsetopt_lvl_ipv6_recverr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_recverr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -9133,9 +9383,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_recverr(ErlNifEnv* env, #if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) static -ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -9155,9 +9405,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, #if defined(IPV6_ROUTER_ALERT) static -ERL_NIF_TERM nsetopt_lvl_ipv6_router_alert(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_router_alert(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -9173,9 +9423,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_router_alert(ErlNifEnv* env, #if defined(IPV6_RTHDR) static -ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -9190,9 +9440,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env, #if defined(IPV6_UNICAST_HOPS) static -ERL_NIF_TERM nsetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -9208,9 +9458,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, #if defined(IPV6_V6ONLY) static -ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -9225,10 +9475,10 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, #if defined(IPV6_ADD_MEMBERSHIP) || defined(IPV6_DROP_MEMBERSHIP) static -ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal, - int opt) +ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal, + int opt) { ERL_NIF_TERM result, eMultiAddr, eInterface; struct ipv6_mreq mreq; @@ -9283,10 +9533,10 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env, /* nsetopt_lvl_tcp - Level *TCP* option(s) */ static -ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; @@ -9327,9 +9577,9 @@ ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env, */ #if defined(TCP_CONGESTION) static -ERL_NIF_TERM nsetopt_lvl_tcp_congestion(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_tcp_congestion(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { int max = SOCKET_OPT_TCP_CONGESTION_NAME_MAX+1; @@ -9342,9 +9592,9 @@ ERL_NIF_TERM nsetopt_lvl_tcp_congestion(ErlNifEnv* env, */ #if defined(TCP_MAXSEG) static -ERL_NIF_TERM nsetopt_lvl_tcp_maxseg(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_tcp_maxseg(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_int_opt(env, descP, IPPROTO_TCP, TCP_MAXSEG, eVal); } @@ -9355,9 +9605,9 @@ ERL_NIF_TERM nsetopt_lvl_tcp_maxseg(ErlNifEnv* env, */ #if defined(TCP_NODELAY) static -ERL_NIF_TERM nsetopt_lvl_tcp_nodelay(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_tcp_nodelay(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_bool_opt(env, descP, IPPROTO_TCP, TCP_NODELAY, eVal); } @@ -9368,10 +9618,10 @@ ERL_NIF_TERM nsetopt_lvl_tcp_nodelay(ErlNifEnv* env, /* nsetopt_lvl_udp - Level *UDP* option(s) */ static -ERL_NIF_TERM nsetopt_lvl_udp(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_udp(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; @@ -9400,9 +9650,9 @@ ERL_NIF_TERM nsetopt_lvl_udp(ErlNifEnv* env, */ #if defined(UDP_CORK) static -ERL_NIF_TERM nsetopt_lvl_udp_cork(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_udp_cork(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_bool_opt(env, descP, IPPROTO_UDP, UDP_CORK, eVal); } @@ -9415,10 +9665,10 @@ ERL_NIF_TERM nsetopt_lvl_udp_cork(ErlNifEnv* env, */ #if defined(HAVE_SCTP) static -ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; @@ -9489,9 +9739,9 @@ ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, */ #if defined(SCTP_ASSOCINFO) static -ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; ERL_NIF_TERM eAssocId, eMaxRxt, eNumPeerDests; @@ -9614,9 +9864,9 @@ ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env, */ #if defined(SCTP_AUTOCLOSE) static -ERL_NIF_TERM nsetopt_lvl_sctp_autoclose(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sctp_autoclose(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_int_opt(env, descP, IPPROTO_SCTP, SCTP_AUTOCLOSE, eVal); } @@ -9627,9 +9877,9 @@ ERL_NIF_TERM nsetopt_lvl_sctp_autoclose(ErlNifEnv* env, */ #if defined(SCTP_DISABLE_FRAGMENTS) static -ERL_NIF_TERM nsetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_bool_opt(env, descP, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, eVal); } @@ -9640,9 +9890,9 @@ ERL_NIF_TERM nsetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, */ #if defined(SCTP_EVENTS) static -ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; ERL_NIF_TERM eDataIn, eAssoc, eAddr, eSndFailure; @@ -9752,9 +10002,9 @@ ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, */ #if defined(SCTP_INITMSG) static -ERL_NIF_TERM nsetopt_lvl_sctp_initmsg(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sctp_initmsg(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; ERL_NIF_TERM eNumOut, eMaxIn, eMaxAttempts, eMaxInitTO; @@ -9836,9 +10086,9 @@ ERL_NIF_TERM nsetopt_lvl_sctp_initmsg(ErlNifEnv* env, */ #if defined(SCTP_MAXSEG) static -ERL_NIF_TERM nsetopt_lvl_sctp_maxseg(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sctp_maxseg(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_int_opt(env, descP, IPPROTO_SCTP, SCTP_MAXSEG, eVal); } @@ -9849,9 +10099,9 @@ ERL_NIF_TERM nsetopt_lvl_sctp_maxseg(ErlNifEnv* env, */ #if defined(SCTP_NODELAY) static -ERL_NIF_TERM nsetopt_lvl_sctp_nodelay(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sctp_nodelay(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { return nsetopt_bool_opt(env, descP, IPPROTO_SCTP, SCTP_NODELAY, eVal); } @@ -9862,9 +10112,9 @@ ERL_NIF_TERM nsetopt_lvl_sctp_nodelay(ErlNifEnv* env, */ #if defined(SCTP_RTOINFO) static -ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; ERL_NIF_TERM eAssocId, eInitial, eMax, eMin; @@ -9925,11 +10175,6 @@ ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env, #else SIZE CHECK FOR ASSOC ID FAILED #endif - /* - if (!GET_INT(env, eAssocId, &tmpAssocId)) - return esock_make_error(env, esock_atom_einval); - rtoInfo.srto_assoc_id = (typeof(rtoInfo.srto_assoc_id)) tmpAssocId; - */ if (!GET_UINT(env, eInitial, &rtoInfo.srto_initial)) return esock_make_error(env, esock_atom_einval); @@ -9971,11 +10216,11 @@ ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env, /* nsetopt_bool_opt - set an option that has an (integer) bool value */ static -ERL_NIF_TERM nsetopt_bool_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_bool_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; BOOLEAN_T val; @@ -9998,11 +10243,11 @@ ERL_NIF_TERM nsetopt_bool_opt(ErlNifEnv* env, /* nsetopt_int_opt - set an option that has an integer value */ static -ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; int val; @@ -10037,12 +10282,12 @@ ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env, */ #if defined(USE_SETOPT_STR_OPT) static -ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt, - int max, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + int max, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; char* val = MALLOC(max); @@ -10070,11 +10315,11 @@ ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, /* nsetopt_timeval_opt - set an option that has an (timeval) bool value */ static -ERL_NIF_TERM nsetopt_timeval_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_timeval_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; struct timeval timeVal; @@ -10303,10 +10548,11 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP; - int eLevel, level = -1; - ERL_NIF_TERM eIsEncoded, eOpt; - BOOLEAN_T isEncoded, isOTP; + ESockDescriptor* descP; + int eLevel, level = -1; + ERL_NIF_TERM eIsEncoded, eOpt; + BOOLEAN_T isEncoded, isOTP; + ERL_NIF_TERM result; SGDBG( ("SOCKET", "nif_getopt -> entry with argc: %d\r\n", argc) ); @@ -10335,7 +10581,14 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, if (!elevel2level(isEncoded, eLevel, &isOTP, &level)) return esock_make_error(env, esock_atom_einval); - return ngetopt(env, descP, isEncoded, isOTP, level, eOpt); + MLOCK(descP->cfgMtx); + + result = ngetopt(env, descP, isEncoded, isOTP, level, eOpt); + + MUNLOCK(descP->cfgMtx); + + return result; + #endif // if defined(__WIN32__) } @@ -10343,12 +10596,12 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, #if !defined(__WIN32__) static -ERL_NIF_TERM ngetopt(ErlNifEnv* env, - SocketDescriptor* descP, - BOOLEAN_T isEncoded, - BOOLEAN_T isOTP, - int level, - ERL_NIF_TERM eOpt) +ERL_NIF_TERM ngetopt(ErlNifEnv* env, + ESockDescriptor* descP, + BOOLEAN_T isEncoded, + BOOLEAN_T isOTP, + int level, + ERL_NIF_TERM eOpt) { ERL_NIF_TERM result; int opt; @@ -10391,9 +10644,9 @@ ERL_NIF_TERM ngetopt(ErlNifEnv* env, /* ngetopt_otp - Handle OTP (level) options */ static -ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt) +ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt) { ERL_NIF_TERM result; @@ -10461,8 +10714,8 @@ ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, /* ngetopt_otp_debug - Handle the OTP (level) debug option */ static -ERL_NIF_TERM ngetopt_otp_debug(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_otp_debug(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM eVal = esock_encode_bool(descP->dbg); @@ -10473,8 +10726,8 @@ ERL_NIF_TERM ngetopt_otp_debug(ErlNifEnv* env, /* ngetopt_otp_iow - Handle the OTP (level) iow option */ static -ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM eVal = esock_encode_bool(descP->iow); @@ -10485,8 +10738,8 @@ ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env, /* ngetopt_otp_ctrl_proc - Handle the OTP (level) controlling_process option */ static -ERL_NIF_TERM ngetopt_otp_ctrl_proc(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_otp_ctrl_proc(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM eVal = MKPID(env, &descP->ctrlPid); @@ -10498,8 +10751,8 @@ ERL_NIF_TERM ngetopt_otp_ctrl_proc(ErlNifEnv* env, /* ngetopt_otp_rcvbuf - Handle the OTP (level) rcvbuf option */ static -ERL_NIF_TERM ngetopt_otp_rcvbuf(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_otp_rcvbuf(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM eVal; @@ -10516,8 +10769,8 @@ ERL_NIF_TERM ngetopt_otp_rcvbuf(ErlNifEnv* env, /* ngetopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf option */ static -ERL_NIF_TERM ngetopt_otp_rcvctrlbuf(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_otp_rcvctrlbuf(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM eVal = MKI(env, descP->rCtrlSz); @@ -10528,8 +10781,8 @@ ERL_NIF_TERM ngetopt_otp_rcvctrlbuf(ErlNifEnv* env, /* ngetopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf option */ static -ERL_NIF_TERM ngetopt_otp_sndctrlbuf(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_otp_sndctrlbuf(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM eVal = MKI(env, descP->wCtrlSz); @@ -10540,8 +10793,8 @@ ERL_NIF_TERM ngetopt_otp_sndctrlbuf(ErlNifEnv* env, /* ngetopt_otp_fd - Handle the OTP (level) fd option */ static -ERL_NIF_TERM ngetopt_otp_fd(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_otp_fd(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM eVal = MKI(env, descP->sock); @@ -10552,8 +10805,8 @@ ERL_NIF_TERM ngetopt_otp_fd(ErlNifEnv* env, /* ngetopt_otp_domain - Handle the OTP (level) domain option */ static -ERL_NIF_TERM ngetopt_otp_domain(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_otp_domain(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM result, reason; int val = descP->domain; @@ -10588,8 +10841,8 @@ ERL_NIF_TERM ngetopt_otp_domain(ErlNifEnv* env, /* ngetopt_otp_type - Handle the OTP (level) type options. */ static -ERL_NIF_TERM ngetopt_otp_type(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_otp_type(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM result, reason; int val = descP->type; @@ -10629,8 +10882,8 @@ ERL_NIF_TERM ngetopt_otp_type(ErlNifEnv* env, /* ngetopt_otp_protocol - Handle the OTP (level) protocol options. */ static -ERL_NIF_TERM ngetopt_otp_protocol(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_otp_protocol(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM result, reason; int val = descP->protocol; @@ -10670,10 +10923,10 @@ ERL_NIF_TERM ngetopt_otp_protocol(ErlNifEnv* env, * format: {NativeOpt :: integer(), ValueSize :: non_neg_integer()} */ static -ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - ERL_NIF_TERM eOpt) +ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + ERL_NIF_TERM eOpt) { ERL_NIF_TERM result = enif_make_badarg(env); int opt; @@ -10728,11 +10981,11 @@ ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, static -ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt, - SOCKOPTLEN_T valueSz) +ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + SOCKOPTLEN_T valueSz) { ERL_NIF_TERM result = esock_make_error(env, esock_atom_einval); int res; @@ -10798,10 +11051,10 @@ ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, /* ngetopt_level - A "proper" level (option) has been specified */ static -ERL_NIF_TERM ngetopt_level(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int eOpt) +ERL_NIF_TERM ngetopt_level(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int eOpt) { ERL_NIF_TERM result; @@ -10865,9 +11118,9 @@ ERL_NIF_TERM ngetopt_level(ErlNifEnv* env, /* ngetopt_lvl_socket - Level *SOCKET* option */ static -ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt) +ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt) { ERL_NIF_TERM result; @@ -11025,8 +11278,8 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, #if defined(SO_ACCEPTCONN) static -ERL_NIF_TERM ngetopt_lvl_sock_acceptconn(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_acceptconn(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_ACCEPTCONN); } @@ -11035,8 +11288,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_acceptconn(ErlNifEnv* env, #if defined(SO_BINDTODEVICE) static -ERL_NIF_TERM ngetopt_lvl_sock_bindtodevice(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_bindtodevice(ErlNifEnv* env, + ESockDescriptor* descP) { SSDBG( descP, ("SOCKET", "ngetopt_lvl_sock_bindtodevice -> entry with\r\n") ); @@ -11048,8 +11301,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_bindtodevice(ErlNifEnv* env, #if defined(SO_BROADCAST) static -ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_BROADCAST); } @@ -11058,8 +11311,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env, #if defined(SO_DEBUG) static -ERL_NIF_TERM ngetopt_lvl_sock_debug(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_debug(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_DEBUG); } @@ -11068,8 +11321,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_debug(ErlNifEnv* env, #if defined(SO_DOMAIN) static -ERL_NIF_TERM ngetopt_lvl_sock_domain(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_domain(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM result, reason; int val; @@ -11113,8 +11366,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_domain(ErlNifEnv* env, #if defined(SO_DONTROUTE) static -ERL_NIF_TERM ngetopt_lvl_sock_dontroute(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_dontroute(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_DONTROUTE); } @@ -11123,8 +11376,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_dontroute(ErlNifEnv* env, #if defined(SO_KEEPALIVE) static -ERL_NIF_TERM ngetopt_lvl_sock_keepalive(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_keepalive(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_KEEPALIVE); } @@ -11133,8 +11386,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_keepalive(ErlNifEnv* env, #if defined(SO_LINGER) static -ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM result; struct linger val; @@ -11163,8 +11416,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, #if defined(SO_OOBINLINE) static -ERL_NIF_TERM ngetopt_lvl_sock_oobinline(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_oobinline(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_OOBINLINE); } @@ -11173,8 +11426,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_oobinline(ErlNifEnv* env, #if defined(SO_PEEK_OFF) static -ERL_NIF_TERM ngetopt_lvl_sock_peek_off(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_peek_off(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_PEEK_OFF); } @@ -11183,8 +11436,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_peek_off(ErlNifEnv* env, #if defined(SO_PRIORITY) static -ERL_NIF_TERM ngetopt_lvl_sock_priority(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_priority(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_PRIORITY); } @@ -11193,8 +11446,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_priority(ErlNifEnv* env, #if defined(SO_PROTOCOL) static -ERL_NIF_TERM ngetopt_lvl_sock_protocol(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_protocol(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM result, reason; int val; @@ -11240,8 +11493,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_protocol(ErlNifEnv* env, #if defined(SO_RCVBUF) static -ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_RCVBUF); } @@ -11250,8 +11503,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env, #if defined(SO_RCVLOWAT) static -ERL_NIF_TERM ngetopt_lvl_sock_rcvlowat(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_rcvlowat(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_RCVLOWAT); } @@ -11260,8 +11513,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_rcvlowat(ErlNifEnv* env, #if defined(SO_RCVTIMEO) static -ERL_NIF_TERM ngetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_timeval_opt(env, descP, SOL_SOCKET, SO_RCVTIMEO); } @@ -11270,8 +11523,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, #if defined(SO_REUSEADDR) static -ERL_NIF_TERM ngetopt_lvl_sock_reuseaddr(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_reuseaddr(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEADDR); } @@ -11280,8 +11533,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_reuseaddr(ErlNifEnv* env, #if defined(SO_REUSEPORT) static -ERL_NIF_TERM ngetopt_lvl_sock_reuseport(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_reuseport(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEPORT); } @@ -11290,8 +11543,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_reuseport(ErlNifEnv* env, #if defined(SO_SNDBUF) static -ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_SNDBUF); } @@ -11300,8 +11553,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env, #if defined(SO_SNDLOWAT) static -ERL_NIF_TERM ngetopt_lvl_sock_sndlowat(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_sndlowat(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_SNDLOWAT); } @@ -11310,8 +11563,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_sndlowat(ErlNifEnv* env, #if defined(SO_SNDTIMEO) static -ERL_NIF_TERM ngetopt_lvl_sock_sndtimeo(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_sndtimeo(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_timeval_opt(env, descP, SOL_SOCKET, SO_SNDTIMEO); } @@ -11320,8 +11573,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_sndtimeo(ErlNifEnv* env, #if defined(SO_TIMESTAMP) static -ERL_NIF_TERM ngetopt_lvl_sock_timestamp(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_timestamp(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_TIMESTAMP); } @@ -11330,8 +11583,8 @@ ERL_NIF_TERM ngetopt_lvl_sock_timestamp(ErlNifEnv* env, #if defined(SO_TYPE) static -ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM result, reason; int val; @@ -11376,9 +11629,9 @@ ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env, /* ngetopt_lvl_ip - Level *IP* option(s) */ static -ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt) +ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt) { ERL_NIF_TERM result; @@ -11552,8 +11805,8 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, */ #if defined(IP_MINTTL) static -ERL_NIF_TERM ngetopt_lvl_ip_minttl(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_minttl(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11570,8 +11823,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_minttl(ErlNifEnv* env, */ #if defined(IP_FREEBIND) static -ERL_NIF_TERM ngetopt_lvl_ip_freebind(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_freebind(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11588,8 +11841,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_freebind(ErlNifEnv* env, */ #if defined(IP_HDRINCL) static -ERL_NIF_TERM ngetopt_lvl_ip_hdrincl(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_hdrincl(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11606,8 +11859,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_hdrincl(ErlNifEnv* env, */ #if defined(IP_MTU) static -ERL_NIF_TERM ngetopt_lvl_ip_mtu(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_mtu(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11624,8 +11877,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_mtu(ErlNifEnv* env, */ #if defined(IP_MTU_DISCOVER) static -ERL_NIF_TERM ngetopt_lvl_ip_mtu_discover(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_mtu_discover(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM result; ERL_NIF_TERM eMtuDisc; @@ -11658,8 +11911,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_mtu_discover(ErlNifEnv* env, */ #if defined(IP_MULTICAST_ALL) static -ERL_NIF_TERM ngetopt_lvl_ip_multicast_all(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_multicast_all(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11676,8 +11929,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_multicast_all(ErlNifEnv* env, */ #if defined(IP_MULTICAST_IF) static -ERL_NIF_TERM ngetopt_lvl_ip_multicast_if(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_multicast_if(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM result; ERL_NIF_TERM eAddr; @@ -11713,8 +11966,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_multicast_if(ErlNifEnv* env, */ #if defined(IP_MULTICAST_LOOP) static -ERL_NIF_TERM ngetopt_lvl_ip_multicast_loop(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_multicast_loop(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11731,8 +11984,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_multicast_loop(ErlNifEnv* env, */ #if defined(IP_MULTICAST_TTL) static -ERL_NIF_TERM ngetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11749,8 +12002,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, */ #if defined(IP_NODEFRAG) static -ERL_NIF_TERM ngetopt_lvl_ip_nodefrag(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_nodefrag(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11767,8 +12020,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_nodefrag(ErlNifEnv* env, */ #if defined(IP_PKTINFO) static -ERL_NIF_TERM ngetopt_lvl_ip_pktinfo(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_pktinfo(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11785,8 +12038,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_pktinfo(ErlNifEnv* env, */ #if defined(IP_RECVTOS) static -ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11803,8 +12056,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, */ #if defined(IP_RECVDSTADDR) static -ERL_NIF_TERM ngetopt_lvl_ip_recvdstaddr(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_recvdstaddr(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11821,8 +12074,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvdstaddr(ErlNifEnv* env, */ #if defined(IP_RECVERR) static -ERL_NIF_TERM ngetopt_lvl_ip_recverr(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_recverr(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11839,8 +12092,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_recverr(ErlNifEnv* env, */ #if defined(IP_RECVIF) static -ERL_NIF_TERM ngetopt_lvl_ip_recvif(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_recvif(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11857,8 +12110,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvif(ErlNifEnv* env, */ #if defined(IP_RECVOPTS) static -ERL_NIF_TERM ngetopt_lvl_ip_recvopts(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_recvopts(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11875,8 +12128,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvopts(ErlNifEnv* env, */ #if defined(IP_RECVORIGDSTADDR) static -ERL_NIF_TERM ngetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11893,8 +12146,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env, */ #if defined(IP_RECVTTL) static -ERL_NIF_TERM ngetopt_lvl_ip_recvttl(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_recvttl(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11911,8 +12164,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvttl(ErlNifEnv* env, */ #if defined(IP_RETOPTS) static -ERL_NIF_TERM ngetopt_lvl_ip_retopts(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_retopts(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11929,8 +12182,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_retopts(ErlNifEnv* env, */ #if defined(IP_ROUTER_ALERT) static -ERL_NIF_TERM ngetopt_lvl_ip_router_alert(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_router_alert(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11947,8 +12200,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_router_alert(ErlNifEnv* env, */ #if defined(IP_SENDSRCADDR) static -ERL_NIF_TERM ngetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11965,8 +12218,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env, */ #if defined(IP_TOS) static -ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -11995,8 +12248,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env, */ #if defined(IP_TRANSPARENT) static -ERL_NIF_TERM ngetopt_lvl_ip_transparent(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_transparent(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -12014,8 +12267,8 @@ ERL_NIF_TERM ngetopt_lvl_ip_transparent(ErlNifEnv* env, */ #if defined(IP_TTL) static -ERL_NIF_TERM ngetopt_lvl_ip_ttl(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_ttl(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -12033,9 +12286,9 @@ ERL_NIF_TERM ngetopt_lvl_ip_ttl(ErlNifEnv* env, */ #if defined(HAVE_IPV6) static -ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt) +ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt) { ERL_NIF_TERM result; @@ -12157,8 +12410,8 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, #if defined(IPV6_AUTHHDR) static -ERL_NIF_TERM ngetopt_lvl_ipv6_authhdr(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ipv6_authhdr(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_AUTHHDR); } @@ -12167,8 +12420,8 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_authhdr(ErlNifEnv* env, #if defined(IPV6_DSTOPTS) static -ERL_NIF_TERM ngetopt_lvl_ipv6_dstopts(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ipv6_dstopts(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -12182,8 +12435,8 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_dstopts(ErlNifEnv* env, #if defined(IPV6_FLOWINFO) static -ERL_NIF_TERM ngetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -12198,8 +12451,8 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, #if defined(IPV6_HOPLIMIT) static -ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -12214,8 +12467,8 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, #if defined(IPV6_HOPOPTS) static -ERL_NIF_TERM ngetopt_lvl_ipv6_hopopts(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ipv6_hopopts(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -12230,8 +12483,8 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_hopopts(ErlNifEnv* env, #if defined(IPV6_MTU) static -ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -12248,8 +12501,8 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv* env, */ #if defined(IPV6_MTU_DISCOVER) static -ERL_NIF_TERM ngetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM result; ERL_NIF_TERM eMtuDisc; @@ -12280,8 +12533,8 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, #if defined(IPV6_MULTICAST_HOPS) static -ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -12296,8 +12549,8 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, #if defined(IPV6_MULTICAST_IF) static -ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -12312,8 +12565,8 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, #if defined(IPV6_MULTICAST_LOOP) static -ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -12328,8 +12581,8 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, #if defined(IPV6_RECVERR) static -ERL_NIF_TERM ngetopt_lvl_ipv6_recverr(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ipv6_recverr(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -12344,8 +12597,8 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_recverr(ErlNifEnv* env, #if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) static -ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -12365,8 +12618,8 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, #if defined(IPV6_ROUTER_ALERT) static -ERL_NIF_TERM ngetopt_lvl_ipv6_router_alert(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ipv6_router_alert(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -12381,8 +12634,8 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_router_alert(ErlNifEnv* env, #if defined(IPV6_RTHDR) static -ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -12397,8 +12650,8 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, #if defined(IPV6_UNICAST_HOPS) static -ERL_NIF_TERM ngetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -12413,8 +12666,8 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, #if defined(IPV6_V6ONLY) static -ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, + ESockDescriptor* descP) { #if defined(SOL_IPV6) int level = SOL_IPV6; @@ -12434,9 +12687,9 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, /* ngetopt_lvl_tcp - Level *TCP* option(s) */ static -ERL_NIF_TERM ngetopt_lvl_tcp(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt) +ERL_NIF_TERM ngetopt_lvl_tcp(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt) { ERL_NIF_TERM result; @@ -12472,8 +12725,8 @@ ERL_NIF_TERM ngetopt_lvl_tcp(ErlNifEnv* env, */ #if defined(TCP_CONGESTION) static -ERL_NIF_TERM ngetopt_lvl_tcp_congestion(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_tcp_congestion(ErlNifEnv* env, + ESockDescriptor* descP) { int max = SOCKET_OPT_TCP_CONGESTION_NAME_MAX+1; @@ -12486,8 +12739,8 @@ ERL_NIF_TERM ngetopt_lvl_tcp_congestion(ErlNifEnv* env, */ #if defined(TCP_MAXSEG) static -ERL_NIF_TERM ngetopt_lvl_tcp_maxseg(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_tcp_maxseg(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_int_opt(env, descP, IPPROTO_TCP, TCP_MAXSEG); } @@ -12498,8 +12751,8 @@ ERL_NIF_TERM ngetopt_lvl_tcp_maxseg(ErlNifEnv* env, */ #if defined(TCP_NODELAY) static -ERL_NIF_TERM ngetopt_lvl_tcp_nodelay(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_tcp_nodelay(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_bool_opt(env, descP, IPPROTO_TCP, TCP_NODELAY); } @@ -12510,9 +12763,9 @@ ERL_NIF_TERM ngetopt_lvl_tcp_nodelay(ErlNifEnv* env, /* ngetopt_lvl_udp - Level *UDP* option(s) */ static -ERL_NIF_TERM ngetopt_lvl_udp(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt) +ERL_NIF_TERM ngetopt_lvl_udp(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt) { ERL_NIF_TERM result; @@ -12536,8 +12789,8 @@ ERL_NIF_TERM ngetopt_lvl_udp(ErlNifEnv* env, */ #if defined(UDP_CORK) static -ERL_NIF_TERM ngetopt_lvl_udp_cork(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_udp_cork(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_bool_opt(env, descP, IPPROTO_UDP, UDP_CORK); } @@ -12549,9 +12802,9 @@ ERL_NIF_TERM ngetopt_lvl_udp_cork(ErlNifEnv* env, */ #if defined(HAVE_SCTP) static -ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, - SocketDescriptor* descP, - int eOpt) +ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt) { ERL_NIF_TERM result; @@ -12632,8 +12885,8 @@ ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, */ #if defined(SCTP_ASSOCINFO) static -ERL_NIF_TERM ngetopt_lvl_sctp_associnfo(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sctp_associnfo(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM result; struct sctp_assocparams val; @@ -12683,8 +12936,8 @@ ERL_NIF_TERM ngetopt_lvl_sctp_associnfo(ErlNifEnv* env, */ #if defined(SCTP_AUTOCLOSE) static -ERL_NIF_TERM ngetopt_lvl_sctp_autoclose(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sctp_autoclose(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_int_opt(env, descP, IPPROTO_SCTP, SCTP_AUTOCLOSE); } @@ -12695,8 +12948,8 @@ ERL_NIF_TERM ngetopt_lvl_sctp_autoclose(ErlNifEnv* env, */ #if defined(SCTP_DISABLE_FRAGMENTS) static -ERL_NIF_TERM ngetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_bool_opt(env, descP, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS); } @@ -12708,8 +12961,8 @@ ERL_NIF_TERM ngetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, */ #if defined(SCTP_INITMSG) static -ERL_NIF_TERM ngetopt_lvl_sctp_initmsg(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sctp_initmsg(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM result; struct sctp_initmsg val; @@ -12757,8 +13010,8 @@ ERL_NIF_TERM ngetopt_lvl_sctp_initmsg(ErlNifEnv* env, */ #if defined(SCTP_MAXSEG) static -ERL_NIF_TERM ngetopt_lvl_sctp_maxseg(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sctp_maxseg(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_int_opt(env, descP, IPPROTO_SCTP, SCTP_MAXSEG); } @@ -12769,8 +13022,8 @@ ERL_NIF_TERM ngetopt_lvl_sctp_maxseg(ErlNifEnv* env, */ #if defined(SCTP_NODELAY) static -ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env, + ESockDescriptor* descP) { return ngetopt_bool_opt(env, descP, IPPROTO_SCTP, SCTP_NODELAY); } @@ -12792,8 +13045,8 @@ ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env, */ #if defined(SCTP_RTOINFO) static -ERL_NIF_TERM ngetopt_lvl_sctp_rtoinfo(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_sctp_rtoinfo(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM result; struct sctp_rtoinfo val; @@ -12844,10 +13097,10 @@ ERL_NIF_TERM ngetopt_lvl_sctp_rtoinfo(ErlNifEnv* env, /* ngetopt_bool_opt - get an (integer) bool option */ static -ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt) +ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt) { ERL_NIF_TERM result; int val; @@ -12885,10 +13138,10 @@ ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env, /* ngetopt_int_opt - get an integer option */ static -ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt) +ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt) { ERL_NIF_TERM result; int val; @@ -12911,10 +13164,10 @@ ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, /* ngetopt_timeval_opt - get an timeval option */ static -ERL_NIF_TERM ngetopt_timeval_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt) +ERL_NIF_TERM ngetopt_timeval_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt) { ERL_NIF_TERM result; struct timeval val; @@ -12961,11 +13214,11 @@ ERL_NIF_TERM ngetopt_timeval_opt(ErlNifEnv* env, */ #if defined(USE_GETOPT_STR_OPT) static -ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt, - int max) +ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + int max) { ERL_NIF_TERM result; char* val = MALLOC(max); @@ -13021,8 +13274,8 @@ ERL_NIF_TERM nif_sockname(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP; - ERL_NIF_TERM res; + ESockDescriptor* descP; + ERL_NIF_TERM res; SGDBG( ("SOCKET", "nif_sockname -> entry with argc: %d\r\n", argc) ); @@ -13053,12 +13306,12 @@ ERL_NIF_TERM nif_sockname(ErlNifEnv* env, #if !defined(__WIN32__) static -ERL_NIF_TERM nsockname(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM nsockname(ErlNifEnv* env, + ESockDescriptor* descP) { - SocketAddress sa; - SocketAddress* saP = &sa; - unsigned int sz = sizeof(SocketAddress); + ESockAddress sa; + ESockAddress* saP = &sa; + unsigned int sz = sizeof(ESockAddress); sys_memzero((char*) saP, sz); if (IS_SOCKET_ERROR(sock_name(descP->sock, (struct sockaddr*) saP, &sz))) { @@ -13095,8 +13348,8 @@ ERL_NIF_TERM nif_peername(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP; - ERL_NIF_TERM res; + ESockDescriptor* descP; + ERL_NIF_TERM res; SGDBG( ("SOCKET", "nif_peername -> entry with argc: %d\r\n", argc) ); @@ -13127,12 +13380,12 @@ ERL_NIF_TERM nif_peername(ErlNifEnv* env, #if !defined(__WIN32__) static -ERL_NIF_TERM npeername(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM npeername(ErlNifEnv* env, + ESockDescriptor* descP) { - SocketAddress sa; - SocketAddress* saP = &sa; - unsigned int sz = sizeof(SocketAddress); + ESockAddress sa; + ESockAddress* saP = &sa; + unsigned int sz = sizeof(ESockAddress); sys_memzero((char*) saP, sz); if (IS_SOCKET_ERROR(sock_peer(descP->sock, (struct sockaddr*) saP, &sz))) { @@ -13170,8 +13423,8 @@ ERL_NIF_TERM nif_cancel(ErlNifEnv* env, #if defined(__WIN32__) return enif_raise_exception(env, MKA(env, "notsup")); #else - SocketDescriptor* descP; - ERL_NIF_TERM op, sockRef, opRef, result; + ESockDescriptor* descP; + ERL_NIF_TERM op, sockRef, opRef, result; SGDBG( ("SOCKET", "nif_cancel -> entry with argc: %d\r\n", argc) ); @@ -13208,11 +13461,11 @@ ERL_NIF_TERM nif_cancel(ErlNifEnv* env, #if !defined(__WIN32__) static -ERL_NIF_TERM ncancel(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM op, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef) +ERL_NIF_TERM ncancel(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM op, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef) { /* <KOLLA> * @@ -13250,9 +13503,9 @@ ERL_NIF_TERM ncancel(ErlNifEnv* env, * */ static -ERL_NIF_TERM ncancel_connect(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM opRef) +ERL_NIF_TERM ncancel_connect(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef) { return ncancel_write_select(env, descP, opRef); } @@ -13269,10 +13522,10 @@ ERL_NIF_TERM ncancel_connect(ErlNifEnv* env, * */ static -ERL_NIF_TERM ncancel_accept(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef) +ERL_NIF_TERM ncancel_accept(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef) { ERL_NIF_TERM res; @@ -13312,9 +13565,9 @@ ERL_NIF_TERM ncancel_accept(ErlNifEnv* env, * in the acceptor queue). */ static -ERL_NIF_TERM ncancel_accept_current(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef) +ERL_NIF_TERM ncancel_accept_current(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef) { ERL_NIF_TERM res; @@ -13352,9 +13605,9 @@ ERL_NIF_TERM ncancel_accept_current(ErlNifEnv* env, * remove them from the acceptor queue. */ static -ERL_NIF_TERM ncancel_accept_waiting(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM opRef) +ERL_NIF_TERM ncancel_accept_waiting(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef) { ErlNifPid caller; @@ -13379,10 +13632,10 @@ ERL_NIF_TERM ncancel_accept_waiting(ErlNifEnv* env, * Its either the current writer or one of the waiting writers. */ static -ERL_NIF_TERM ncancel_send(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef) +ERL_NIF_TERM ncancel_send(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef) { ERL_NIF_TERM res; @@ -13423,9 +13676,9 @@ ERL_NIF_TERM ncancel_send(ErlNifEnv* env, * in the writer queue). */ static -ERL_NIF_TERM ncancel_send_current(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef) +ERL_NIF_TERM ncancel_send_current(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef) { ERL_NIF_TERM res; @@ -13459,9 +13712,9 @@ ERL_NIF_TERM ncancel_send_current(ErlNifEnv* env, * remove them from the writer queue. */ static -ERL_NIF_TERM ncancel_send_waiting(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM opRef) +ERL_NIF_TERM ncancel_send_waiting(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef) { ErlNifPid caller; @@ -13486,10 +13739,10 @@ ERL_NIF_TERM ncancel_send_waiting(ErlNifEnv* env, * Its either the current reader or one of the waiting readers. */ static -ERL_NIF_TERM ncancel_recv(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef) +ERL_NIF_TERM ncancel_recv(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef) { ERL_NIF_TERM res; @@ -13529,9 +13782,9 @@ ERL_NIF_TERM ncancel_recv(ErlNifEnv* env, * in the reader queue). */ static -ERL_NIF_TERM ncancel_recv_current(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef) +ERL_NIF_TERM ncancel_recv_current(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef) { ERL_NIF_TERM res; @@ -13565,9 +13818,9 @@ ERL_NIF_TERM ncancel_recv_current(ErlNifEnv* env, * remove them from the reader queue. */ static -ERL_NIF_TERM ncancel_recv_waiting(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM opRef) +ERL_NIF_TERM ncancel_recv_waiting(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef) { ErlNifPid caller; @@ -13587,9 +13840,9 @@ ERL_NIF_TERM ncancel_recv_waiting(ErlNifEnv* env, static -ERL_NIF_TERM ncancel_read_select(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM opRef) +ERL_NIF_TERM ncancel_read_select(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef) { return ncancel_mode_select(env, descP, opRef, ERL_NIF_SELECT_READ, @@ -13598,9 +13851,9 @@ ERL_NIF_TERM ncancel_read_select(ErlNifEnv* env, static -ERL_NIF_TERM ncancel_write_select(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM opRef) +ERL_NIF_TERM ncancel_write_select(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef) { return ncancel_mode_select(env, descP, opRef, ERL_NIF_SELECT_WRITE, @@ -13609,11 +13862,11 @@ ERL_NIF_TERM ncancel_write_select(ErlNifEnv* env, static -ERL_NIF_TERM ncancel_mode_select(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM opRef, - int smode, - int rmode) +ERL_NIF_TERM ncancel_mode_select(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef, + int smode, + int rmode) { int selectRes = esock_select_cancel(env, descP->sock, smode, descP); @@ -13643,15 +13896,16 @@ ERL_NIF_TERM ncancel_mode_select(ErlNifEnv* env, /* *** send_check_writer *** * - * Checks if we have a current writer and if that is us. If not, then we must - * be made to wait for our turn. This is done by pushing us unto the writer queue. + * Checks if we have a current writer and if that is us. + * If not (current writer), then we must be made to wait + * for our turn. This is done by pushing us unto the writer queue. */ #if !defined(__WIN32__) static -BOOLEAN_T send_check_writer(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM ref, - ERL_NIF_TERM* checkResult) +BOOLEAN_T send_check_writer(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM ref, + ERL_NIF_TERM* checkResult) { if (descP->currentWriterP != NULL) { ErlNifPid caller; @@ -13665,7 +13919,8 @@ BOOLEAN_T send_check_writer(ErlNifEnv* env, /* Not the "current writer", so (maybe) push onto queue */ SSDBG( descP, - ("SOCKET", "send_check_writer -> not (current) writer\r\n") ); + ("SOCKET", + "send_check_writer -> not (current) writer\r\n") ); if (!writer_search4pid(env, descP, &caller)) *checkResult = writer_push(env, descP, caller, ref); @@ -13683,7 +13938,8 @@ BOOLEAN_T send_check_writer(ErlNifEnv* env, } - *checkResult = esock_atom_ok; // Does not actually matter in this case, but ... + // Does not actually matter in this case, but ... + *checkResult = esock_atom_ok; return TRUE; } @@ -13702,17 +13958,18 @@ BOOLEAN_T send_check_writer(ErlNifEnv* env, * If the write fail, we give up and return with the appropriate error code. * * What about the remaining writers!! + * */ static -ERL_NIF_TERM send_check_result(ErlNifEnv* env, - SocketDescriptor* descP, - ssize_t written, - ssize_t dataSize, - int saveErrno, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM sendRef) +ERL_NIF_TERM send_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + ssize_t written, + ssize_t dataSize, + int saveErrno, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef) { - int sres; + ERL_NIF_TERM res; SSDBG( descP, ("SOCKET", "send_check_result -> entry with" @@ -13723,143 +13980,219 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, if (written >= dataSize) { - cnt_inc(&descP->writePkgCnt, 1); - cnt_inc(&descP->writeByteCnt, written); - if (descP->currentWriterP != NULL) - DEMONP("send_check_result -> current writer", - env, descP, &descP->currentWriter.mon); + res = send_check_ok(env, descP, written, dataSize, sockRef); + + } else if (written < 0) { + + /* Some kind of send failure - check what kind */ + + if ((saveErrno != EAGAIN) && (saveErrno != EINTR)) { + + res = send_check_fail(env, descP, saveErrno, sockRef); + + } else { + + /* Ok, try again later */ + + SSDBG( descP, ("SOCKET", "send_check_result -> try again\r\n") ); + + res = send_check_retry(env, descP, written, sockRef, sendRef); + + } + + } else { + + /* Not the entire package */ SSDBG( descP, ("SOCKET", "send_check_result -> " - "everything written (%d,%d) - done\r\n", dataSize, written) ); + "not entire package written (%d of %d)\r\n", + written, dataSize) ); - /* Ok, this write is done maybe activate the next (if any) */ + res = send_check_retry(env, descP, written, sockRef, sendRef); - if (!activate_next_writer(env, descP, sockRef)) { - descP->currentWriterP = NULL; - descP->currentWriter.ref = esock_atom_undefined; - enif_set_pid_undefined(&descP->currentWriter.pid); - esock_monitor_init(&descP->currentWriter.mon); - } + } - return esock_atom_ok; + SSDBG( descP, ("SOCKET", "send_check_result -> done: %T\r\n", res) ); - } else if (written < 0) { + return res; +} - /* Some kind of send failure - check what kind */ - if ((saveErrno != EAGAIN) && (saveErrno != EINTR)) { - SocketRequestor req; - ERL_NIF_TERM res, reason; +/* *** send_check_ok *** + * + * Processing done upon successful send. + */ +static +ERL_NIF_TERM send_check_ok(ErlNifEnv* env, + ESockDescriptor* descP, + ssize_t written, + ssize_t dataSize, + ERL_NIF_TERM sockRef) +{ + cnt_inc(&descP->writePkgCnt, 1); + cnt_inc(&descP->writeByteCnt, written); - /* - * An actual failure - we (and everyone waiting) give up - */ + if (descP->currentWriterP != NULL) { + DEMONP("send_check_ok -> current writer", + env, descP, &descP->currentWriter.mon); + esock_free_env("send_check_ok", descP->currentWriter.env); + } - cnt_inc(&descP->writeFails, 1); + SSDBG( descP, + ("SOCKET", "send_check_ok -> " + "everything written (%d,%d) - done\r\n", dataSize, written) ); - SSDBG( descP, - ("SOCKET", - "send_check_result -> error: %d\r\n", saveErrno) ); + /* + * Ok, this write is done maybe activate the next (if any) + */ - reason = MKA(env, erl_errno_id(saveErrno)); - res = esock_make_error(env, reason); + if (!activate_next_writer(env, descP, sockRef)) { + descP->currentWriterP = NULL; + descP->currentWriter.env = NULL; + descP->currentWriter.ref = esock_atom_undefined; + enif_set_pid_undefined(&descP->currentWriter.pid); + esock_monitor_init(&descP->currentWriter.mon); + } - if (descP->currentWriterP != NULL) { + return esock_atom_ok; +} - DEMONP("send_check_result -> current writer", - env, descP, &descP->currentWriter.mon); - while (writer_pop(env, descP, &req)) { - SSDBG( descP, - ("SOCKET", "send_check_result -> abort %T\r\n", - req.pid) ); - esock_send_abort_msg(env, sockRef, req.ref, - reason, &req.pid); - DEMONP("send_check_result -> pop'ed writer", - env, descP, &req.mon); - } - } - - return res; - } else { - /* Ok, try again later */ +/* *** send_check_failure *** + * + * Processing done upon failed send. + * An actual failure - we (and everyone waiting) give up. + */ +static +ERL_NIF_TERM send_check_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int saveErrno, + ERL_NIF_TERM sockRef) +{ + ESockRequestor req; + ERL_NIF_TERM reason; - SSDBG( descP, ("SOCKET", "send_check_result -> try again\r\n") ); - } + cnt_inc(&descP->writeFails, 1); - } - else { - SSDBG( descP, - ("SOCKET", "send_check_result -> " - "not entire package written (%d of %d)\r\n", written, dataSize) ); + SSDBG( descP, ("SOCKET", "send_check_fail -> error: %d\r\n", saveErrno) ); + + reason = MKA(env, erl_errno_id(saveErrno)); + + if (descP->currentWriterP != NULL) { + + DEMONP("send_check_fail -> current writer", + env, descP, &descP->currentWriter.mon); + + while (writer_pop(env, descP, &req)) { + SSDBG( descP, + ("SOCKET", "send_check_fail -> abort %T\r\n", req.pid) ); + esock_send_abort_msg(env, sockRef, req.ref, req.env, + reason, &req.pid); + DEMONP("send_check_fail -> pop'ed writer", env, descP, &req.mon); + } } - /* We failed to write the *entire* packet (anything less then size - * of the packet, which is 0 <= written < sizeof packet), - * so schedule the rest for later. - */ + return esock_make_error(env, reason); +} + + + +/* *** send_check_retry *** + * + * Processing done upon uncomplete or blocked send. + * + * We failed to write the *entire* packet (anything less + * then size of the packet, which is 0 <= written < sizeof + * packet, so schedule the rest for later. + */ +static +ERL_NIF_TERM send_check_retry(ErlNifEnv* env, + ESockDescriptor* descP, + ssize_t written, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef) +{ + int sres; + ErlNifPid caller; + ERL_NIF_TERM res; if (descP->currentWriterP == NULL) { - ErlNifPid caller; if (enif_self(env, &caller) == NULL) return esock_make_error(env, atom_exself); descP->currentWriter.pid = caller; - if (MONP("send_check_result -> current writer", + + if (MONP("send_check_retry -> current writer", env, descP, &descP->currentWriter.pid, - &descP->currentWriter.mon) != 0) + &descP->currentWriter.mon) != 0) { + enif_set_pid_undefined(&descP->currentWriter.pid); return esock_make_error(env, atom_exmon); - descP->currentWriter.ref = enif_make_copy(descP->env, sendRef); - descP->currentWriterP = &descP->currentWriter; + } else { + descP->currentWriter.env = esock_alloc_env("current-writer"); + descP->currentWriter.ref = CP_TERM(descP->currentWriter.env, sendRef); + descP->currentWriterP = &descP->currentWriter; + } } cnt_inc(&descP->writeWaits, 1); - sres = esock_select_write(env, descP->sock, descP, NULL, sendRef); - + sres = esock_select_write(env, descP->sock, descP, NULL, sockRef, sendRef); + if (written >= 0) { + + /* Partial *write* success */ + if (sres < 0) { /* Returned: {error, Reason} * Reason: {select_failed, sres, written} */ - return esock_make_error(env, - MKT3(env, - esock_atom_select_failed, - MKI(env, sres), - MKI(env, written))); + res = esock_make_error(env, + MKT3(env, + esock_atom_select_failed, + MKI(env, sres), + MKI(env, written))); } else { - return esock_make_ok2(env, MKI(env, written)); + res = esock_make_ok2(env, MKI(env, written)); } + } else { + if (sres < 0) { /* Returned: {error, Reason} * Reason: {select_failed, sres} */ - return esock_make_error(env, - MKT2(env, - esock_atom_select_failed, - MKI(env, sres))); + res = esock_make_error(env, + MKT2(env, + esock_atom_select_failed, + MKI(env, sres))); } else { - return esock_make_error(env, esock_atom_eagain); + res = esock_make_error(env, esock_atom_eagain); } } + + return res; } + /* *** recv_check_reader *** * - * Checks if we have a current reader and if that is us. If not, then we must - * be made to wait for our turn. This is done by pushing us unto the reader queue. + * Checks if we have a current reader and if that is us. If not, + * then we must be made to wait for our turn. This is done by pushing + * us unto the reader queue. + * Note that we do *not* actually initiate the currentReader structure + * here, since we do not actually know yet if we need to! We do that in + * the [recv|recvfrom|recvmsg]_check_result function. */ static -BOOLEAN_T recv_check_reader(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM ref, - ERL_NIF_TERM* checkResult) +BOOLEAN_T recv_check_reader(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM ref, + ERL_NIF_TERM* checkResult) { if (descP->currentReaderP != NULL) { ErlNifPid caller; @@ -13875,7 +14208,8 @@ BOOLEAN_T recv_check_reader(ErlNifEnv* env, /* Not the "current reader", so (maybe) push onto queue */ SSDBG( descP, - ("SOCKET", "recv_check_reader -> not (current) reader\r\n") ); + ("SOCKET", + "recv_check_reader -> not (current) reader\r\n") ); if (!reader_search4pid(env, descP, &caller)) tmp = reader_push(env, descP, caller, ref); @@ -13894,7 +14228,8 @@ BOOLEAN_T recv_check_reader(ErlNifEnv* env, } - *checkResult = esock_atom_ok; // Does not actually matter in this case, but ... + // Does not actually matter in this case, but ... + *checkResult = esock_atom_ok; return TRUE; } @@ -13907,9 +14242,9 @@ BOOLEAN_T recv_check_reader(ErlNifEnv* env, * Including monitoring the calling process. */ static -char* recv_init_current_reader(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM recvRef) +char* recv_init_current_reader(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM recvRef) { if (descP->currentReaderP == NULL) { ErlNifPid caller; @@ -13922,10 +14257,26 @@ char* recv_init_current_reader(ErlNifEnv* env, env, descP, &descP->currentReader.pid, &descP->currentReader.mon) != 0) { + enif_set_pid_undefined(&descP->currentReader.pid); return str_exmon; + } else { + + descP->currentReader.env = esock_alloc_env("current-reader"); + descP->currentReader.ref = CP_TERM(descP->currentReader.env, + recvRef); + descP->currentReaderP = &descP->currentReader; } - descP->currentReader.ref = enif_make_copy(descP->env, recvRef); - descP->currentReaderP = &descP->currentReader; + } else { + + /* + * This is a retry: + * We have done, for instance, recv(Sock, X), but only received Y < X. + * We then call recv again with size = X-Y. So, we then get a new ref. + * + * Make use of the existing environment + */ + + descP->currentReader.ref = CP_TERM(descP->currentReader.env, recvRef); } return NULL; @@ -13941,17 +14292,21 @@ char* recv_init_current_reader(ErlNifEnv* env, */ static -ERL_NIF_TERM recv_update_current_reader(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef) +ERL_NIF_TERM recv_update_current_reader(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef) { ERL_NIF_TERM res = esock_atom_ok; if (descP->currentReaderP != NULL) { - DEMONP("recv_update_current_reader -> current reader", + DEMONP("recv_update_current_reader", env, descP, &descP->currentReader.mon); - + + esock_free_env("recv_update_current_reader - current-read-env", + descP->currentReader.env); + descP->currentReader.env = NULL; + if (!activate_next_reader(env, descP, sockRef)) { SSDBG( descP, @@ -13979,12 +14334,12 @@ ERL_NIF_TERM recv_update_current_reader(ErlNifEnv* env, * nif_abort message will be sent (with reaf and reason). */ static -void recv_error_current_reader(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM reason) +void recv_error_current_reader(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM reason) { - SocketRequestor req; + ESockRequestor req; if (descP->currentReaderP != NULL) { @@ -13995,7 +14350,8 @@ void recv_error_current_reader(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "recv_error_current_reader -> abort %T\r\n", req.pid) ); - esock_send_abort_msg(env, sockRef, req.ref, reason, &req.pid); + esock_send_abort_msg(env, sockRef, req.ref, req.env, + reason, &req.pid); DEMONP("recv_error_current_reader -> pop'ed reader", env, descP, &req.mon); } @@ -14009,18 +14365,16 @@ void recv_error_current_reader(ErlNifEnv* env, * Process the result of a call to recv. */ static -ERL_NIF_TERM recv_check_result(ErlNifEnv* env, - SocketDescriptor* descP, - int read, - int toRead, - int saveErrno, - ErlNifBinary* bufP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM recvRef) +ERL_NIF_TERM recv_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + int toRead, + int saveErrno, + ErlNifBinary* bufP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) { - char* xres; - int sres; - ERL_NIF_TERM res, data; + ERL_NIF_TERM res; SSDBG( descP, ("SOCKET", "recv_check_result -> entry with" @@ -14044,8 +14398,8 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, res = esock_make_error(env, atom_closed); /* - * When a stream socket peer has performed an orderly shutdown, the return - * value will be 0 (the traditional "end-of-file" return). + * When a stream socket peer has performed an orderly shutdown, + * the return value will be 0 (the traditional "end-of-file" return). * * *We* do never actually try to read 0 bytes from a stream socket! * @@ -14056,381 +14410,515 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, FREE_BIN(bufP); - return res; + } else { + + /* There is a special case: If the provided 'to read' value is + * zero (0) (only for type =/= stream). + * That means that we reads as much as we can, using the default + * read buffer size. + */ + + if (bufP->size == read) { + + /* +++ We filled the buffer +++ */ + + SSDBG( descP, + ("SOCKET", + "recv_check_result -> [%d] filled the buffer\r\n", + toRead) ); + + res = recv_check_full(env, descP, read, toRead, bufP, + sockRef, recvRef); + + } else if (read < 0) { + + /* +++ Error handling +++ */ + res = recv_check_fail(env, descP, saveErrno, bufP, NULL, + sockRef, recvRef); + + } else { + + /* +++ We did not fill the buffer +++ */ + + SSDBG( descP, + ("SOCKET", + "recv_check_result -> [%d] " + "did not fill the buffer (%d of %d)\r\n", + toRead, read, bufP->size) ); + + res = recv_check_partial(env, descP, read, toRead, bufP, + sockRef, recvRef); + + } } - - /* There is a special case: If the provided 'to read' value is - * zero (0) (only for type =/= stream). - * That means that we reads as much as we can, using the default - * read buffer size. - */ - if (bufP->size == read) { + return res; +} + + + +/* *** recv_check_full *** + * + * This function is called if we filled the allocated buffer. + * But are we done yet? + * + * toRead = 0 means: Give me everything you have => maybe + * toRead > 0 means: Yes + */ +static +ERL_NIF_TERM recv_check_full(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + int toRead, + ErlNifBinary* bufP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + ERL_NIF_TERM res; - /* +++ We filled the buffer +++ */ + if (toRead == 0) { + + /* +++ Give us everything you have got => * + * (maybe) needs to continue +++ */ + + /* Send up each chunk of data for each of the read + * and let the erlang code assemble it: {ok, false, Bin} + * (when complete it should return {ok, true, Bin}). + * We need to read atleast one more time to be sure if its + * done... + * + * Also, we need to check if the rNumCnt has reached its max (rNum), + * in which case we will assume the read to be done! + */ SSDBG( descP, - ("SOCKET", - "recv_check_result -> [%d] filled the buffer\r\n", toRead) ); - - if (toRead == 0) { - - /* +++ Give us everything you have got => * - * (maybe) needs to continue +++ */ - - /* How do we do this? - * Either: - * 1) Send up each chunk of data for each of the read - * and let the erlang code assemble it: {ok, false, Bin} - * (when complete it should return {ok, true, Bin}). - * We need to read atleast one more time to be sure if its - * done... - * 2) Or put it in a buffer here, and then let the erlang code - * know that it should call again (special return value) - * (continuous binary realloc "here"). - * - * => We choose alt 1 for now. - * - * Also, we need to check if the rNumCnt has reached its max (rNum), - * in which case we will assume the read to be done! - */ + ("SOCKET", "recv_check_full -> shall we continue reading" + "\r\n read: %d" + "\r\n rNum: %d" + "\r\n rNumCnt: %d" + "\r\n", read, descP->rNum, descP->rNumCnt) ); - cnt_inc(&descP->readByteCnt, read); + res = recv_check_full_maybe_done(env, descP, read, toRead, bufP, + sockRef, recvRef); - SSDBG( descP, - ("SOCKET", "recv_check_result -> shall we continue reading" - "\r\n read: %d" - "\r\n rNum: %d" - "\r\n rNumCnt: %d" - "\r\n", read, descP->rNum, descP->rNumCnt) ); + } else { - if (descP->rNum > 0) { + /* +++ We got exactly as much as we requested => We are done +++ */ - descP->rNumCnt++; - if (descP->rNumCnt >= descP->rNum) { + SSDBG( descP, + ("SOCKET", + "recv_check_full -> [%d] " + "we got exactly what we could fit\r\n", toRead) ); - descP->rNumCnt = 0; + res = recv_check_full_done(env, descP, read, bufP, sockRef); - cnt_inc(&descP->readPkgCnt, 1); - - recv_update_current_reader(env, descP, sockRef); - - /* This transfers "ownership" of the *allocated* binary to an - * erlang term (no need for an explicit free). - */ - data = MKBIN(env, bufP); - - return esock_make_ok3(env, atom_true, data); + } - } - } + return res; - /* Yes, we *do* need to continue reading */ +} - if ((xres = recv_init_current_reader(env, - descP, recvRef)) != NULL) { - descP->rNumCnt = 0; - FREE_BIN(bufP); - return esock_make_error_str(env, xres); - } - /* This transfers "ownership" of the *allocated* binary to an - * erlang term (no need for an explicit free). - */ - data = MKBIN(env, bufP); - SSDBG( descP, - ("SOCKET", - "recv_check_result -> [%d] " - "we are done for now - read more\r\n", toRead) ); +/* *** recv_check_full_maybe_done *** + * + * Send up each chunk of data for each of the read + * and let the erlang code assemble it: {ok, false, Bin} + * (when complete it should return {ok, true, Bin}). + * We need to read atleast one more time to be sure if its + * done... + * + * Also, we need to check if the rNumCnt has reached its max (rNum), + * in which case we will assume the read to be done! + */ +static +ERL_NIF_TERM recv_check_full_maybe_done(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + int toRead, + ErlNifBinary* bufP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + char* xres; - return esock_make_ok3(env, atom_false, data); + cnt_inc(&descP->readByteCnt, read); - } else { + if (descP->rNum > 0) { - /* +++ We got exactly as much as we requested => We are done +++ */ + descP->rNumCnt++; + if (descP->rNumCnt >= descP->rNum) { - cnt_inc(&descP->readPkgCnt, 1); - cnt_inc(&descP->readByteCnt, read); + descP->rNumCnt = 0; - SSDBG( descP, - ("SOCKET", - "recv_check_result -> [%d] " - "we got exactly what we could fit\r\n", toRead) ); + cnt_inc(&descP->readPkgCnt, 1); recv_update_current_reader(env, descP, sockRef); /* This transfers "ownership" of the *allocated* binary to an * erlang term (no need for an explicit free). */ - data = MKBIN(env, bufP); - return esock_make_ok3(env, atom_true, data); + return esock_make_ok3(env, atom_true, MKBIN(env, bufP)); } + } - } else if (read < 0) { - - /* +++ Error handling +++ */ + /* Yes, we *do* need to continue reading */ + if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) { + descP->rNumCnt = 0; FREE_BIN(bufP); + return esock_make_error_str(env, xres); + } - if (saveErrno == ECONNRESET) { + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ - res = esock_make_error(env, atom_closed); + SSDBG( descP, + ("SOCKET", + "recv_check_full_maybe_done -> [%d] " + "we are done for now - read more\r\n", toRead) ); - /* +++ Oups - closed +++ */ + return esock_make_ok3(env, atom_false, MKBIN(env, bufP)); +} - SSDBG( descP, ("SOCKET", - "recv_check_result -> [%d] closed\r\n", toRead) ); - /* <KOLLA> - * - * IF THE CURRENT PROCESS IS *NOT* THE CONTROLLING - * PROCESS, WE NEED TO INFORM IT!!! - * - * ALL WAITING PROCESSES MUST ALSO GET THE ERROR!! - * HANDLED BY THE STOP (CALLBACK) FUNCTION? - * - * SINCE THIS IS A REMOTE CLOSE, WE DON'T NEED TO WAIT - * FOR OUTPUT TO BE WRITTEN (NO ONE WILL READ), JUST - * ABORT THE SOCKET REGARDLESS OF LINGER??? - * - * </KOLLA> - */ - descP->closeLocal = FALSE; - descP->state = SOCKET_STATE_CLOSING; +/* *** recv_check_full_done *** + * + * A successful recv and we filled the buffer. + */ +static +ERL_NIF_TERM recv_check_full_done(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + ErlNifBinary* bufP, + ERL_NIF_TERM sockRef) +{ + ERL_NIF_TERM data; - recv_error_current_reader(env, descP, sockRef, res); + cnt_inc(&descP->readPkgCnt, 1); + cnt_inc(&descP->readByteCnt, read); - if ((sres = esock_select_stop(env, descP->sock, descP)) < 0) { - esock_warning_msg("Failed stop select (closed) " - "for current reader (%T): %d\r\n", - recvRef, sres); - } + recv_update_current_reader(env, descP, sockRef); - return res; + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ + data = MKBIN(env, bufP); - } else if ((saveErrno == ERRNO_BLOCK) || - (saveErrno == EAGAIN)) { + return esock_make_ok3(env, atom_true, data); +} - SSDBG( descP, ("SOCKET", - "recv_check_result -> [%d] eagain\r\n", toRead) ); - descP->rNumCnt = 0; - if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) - return esock_make_error_str(env, xres); - - SSDBG( descP, ("SOCKET", "recv_check_result -> SELECT for more\r\n") ); - - if ((sres = esock_select_read(env, descP->sock, descP, - NULL, recvRef)) < 0) { - res = esock_make_error(env, - MKT2(env, - esock_atom_select_failed, - MKI(env, sres))); - } else { - res = esock_make_error(env, esock_atom_eagain); - } - return res; - } else { - ERL_NIF_TERM res = esock_make_error_errno(env, saveErrno); +/* *** recv_check_fail *** + * + * Handle recv failure. + */ +static +ERL_NIF_TERM recv_check_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int saveErrno, + ErlNifBinary* buf1P, + ErlNifBinary* buf2P, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + ERL_NIF_TERM res; - SSDBG( descP, ("SOCKET", "recv_check_result -> [%d] errno: %d\r\n", - toRead, saveErrno) ); + FREE_BIN(buf1P); if (buf2P != NULL) FREE_BIN(buf2P); - recv_error_current_reader(env, descP, sockRef, res); + if (saveErrno == ECONNRESET) { - return res; - } + /* +++ Oups - closed +++ */ + + SSDBG( descP, ("SOCKET", "recv_check_fail -> closed\r\n") ); + + res = recv_check_fail_closed(env, descP, sockRef, recvRef); + + } else if ((saveErrno == ERRNO_BLOCK) || + (saveErrno == EAGAIN)) { + + SSDBG( descP, ("SOCKET", "recv_check_fail -> eagain\r\n") ); + + res = recv_check_retry(env, descP, sockRef, recvRef); } else { - /* +++ We did not fill the buffer +++ */ + SSDBG( descP, ("SOCKET", "recv_check_fail -> errno: %d\r\n", + saveErrno) ); - SSDBG( descP, - ("SOCKET", - "recv_check_result -> [%d] " - "did not fill the buffer (%d of %d)\r\n", - toRead, read, bufP->size) ); + res = recv_check_fail_gen(env, descP, saveErrno, sockRef); + } - if (toRead == 0) { + return res; +} - /* +++ We got it all, but since we +++ - * +++ did not fill the buffer, we +++ - * +++ must split it into a sub-binary. +++ - */ - SSDBG( descP, ("SOCKET", - "recv_check_result -> [%d] split buffer\r\n", toRead) ); - descP->rNumCnt = 0; - cnt_inc(&descP->readPkgCnt, 1); - cnt_inc(&descP->readByteCnt, read); +/* *** recv_check_fail_closed *** + * + * We detected that the socket was closed wile reading. + * Inform current and waiting readers. + */ +static +ERL_NIF_TERM recv_check_fail_closed(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + ERL_NIF_TERM res = esock_make_error(env, atom_closed); + int sres; - recv_update_current_reader(env, descP, sockRef); + /* <KOLLA> + * + * IF THE CURRENT PROCESS IS *NOT* THE CONTROLLING + * PROCESS, WE NEED TO INFORM IT!!! + * + * ALL WAITING PROCESSES MUST ALSO GET THE ERROR!! + * HANDLED BY THE STOP (CALLBACK) FUNCTION? + * + * SINCE THIS IS A REMOTE CLOSE, WE DON'T NEED TO WAIT + * FOR OUTPUT TO BE WRITTEN (NO ONE WILL READ), JUST + * ABORT THE SOCKET REGARDLESS OF LINGER??? + * + * </KOLLA> + */ - /* This transfers "ownership" of the *allocated* binary to an - * erlang term (no need for an explicit free). - */ - data = MKBIN(env, bufP); - data = MKSBIN(env, data, 0, read); + descP->closeLocal = FALSE; + descP->state = SOCKET_STATE_CLOSING; - SSDBG( descP, - ("SOCKET", "recv_check_result -> [%d] done\r\n", toRead) ); + recv_error_current_reader(env, descP, sockRef, res); - return esock_make_ok3(env, atom_true, data); + if ((sres = esock_select_stop(env, descP->sock, descP)) < 0) { + esock_warning_msg("Failed stop select (closed) " + "for current reader (%T): %d\r\n", + recvRef, sres); + } - } else { + return res; +} - /* +++ We got only a part of what was expected +++ - * +++ => select for more more later and +++ - * +++ deliver what we got. +++ */ - SSDBG( descP, ("SOCKET", "recv_check_result -> [%d] " - "only part of message - expect more\r\n", toRead) ); - if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) { - FREE_BIN(bufP); - return esock_make_error_str(env, xres); - } +/* *** recv_check_retry *** + * + * The recv call would have blocked, so retry. + */ +static +ERL_NIF_TERM recv_check_retry(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + int sres; + char* xres; + ERL_NIF_TERM reason; - data = MKBIN(env, bufP); - data = MKSBIN(env, data, 0, read); + descP->rNumCnt = 0; + if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) + return esock_make_error_str(env, xres); + + SSDBG( descP, ("SOCKET", "recv_check_retry -> SELECT for more\r\n") ); + + if ((sres = esock_select_read(env, descP->sock, descP, NULL, + sockRef, recvRef)) < 0) { + /* Ouch + * Now what? We have copied ref into *its own* environment! + */ + reason = MKT2(env, esock_atom_select_failed, MKI(env, sres)); + } else { + reason = esock_atom_eagain; + } - cnt_inc(&descP->readByteCnt, read); + return esock_make_error(env, reason); - /* SELECT for more data */ +} - if ((sres = esock_select_read(env, descP->sock, descP, - NULL, recvRef)) < 0) { - /* Result: {error, Reason} - * Reason: {select_failed, sres, data} - */ - res = esock_make_error(env, - MKT3(env, - esock_atom_select_failed, - MKI(env, sres), - data)); - } else { - res = esock_make_ok3(env, atom_false, data); - } - - /* This transfers "ownership" of the *allocated* binary to an - * erlang term (no need for an explicit free). - */ - return res; - } + + +/* *** recv_check_fail_gen *** + * + * The recv call had a "general" failure. + */ +static +ERL_NIF_TERM recv_check_fail_gen(ErlNifEnv* env, + ESockDescriptor* descP, + int saveErrno, + ERL_NIF_TERM sockRef) +{ + ERL_NIF_TERM res = esock_make_error_errno(env, saveErrno); + + recv_error_current_reader(env, descP, sockRef, res); + + return res; +} + + + +/* *** recv_check_partial *** + * + * Handle a sucessful recv which only partly filled the specified buffer. + */ +static +ERL_NIF_TERM recv_check_partial(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + int toRead, + ErlNifBinary* bufP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + ERL_NIF_TERM res; + + if (toRead == 0) { + + /* +++ We got it all, but since we +++ + * +++ did not fill the buffer, we +++ + * +++ must split it into a sub-binary. +++ + */ + + SSDBG( descP, + ("SOCKET", + "recv_check_partial -> [%d] split buffer\r\n", toRead) ); + + res = recv_check_partial_done(env, descP, read, bufP, sockRef); + + } else { + + SSDBG( descP, ("SOCKET", "recv_check_partial -> [%d] " + "only part of message - expect more\r\n", toRead) ); + + res = recv_check_partial_part(env, descP, read, bufP, sockRef, recvRef); } + + return res; } -/* The recvfrom function delivers one (1) message. If our buffer - * is to small, the message will be truncated. So, regardless - * if we filled the buffer or not, we have got what we are going - * to get regarding this message. + +/* *** recv_check_partial_done *** + * + * A successful but only partial recv, which fulfilled the required read. */ static -ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, - SocketDescriptor* descP, - int read, - int saveErrno, - ErlNifBinary* bufP, - SocketAddress* fromAddrP, - unsigned int fromAddrLen, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM recvRef) +ERL_NIF_TERM recv_check_partial_done(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + ErlNifBinary* bufP, + ERL_NIF_TERM sockRef) { - char* xres; - int sres; - ERL_NIF_TERM data, res; + ERL_NIF_TERM data; - SSDBG( descP, - ("SOCKET", "recvfrom_check_result -> entry with" - "\r\n read: %d" - "\r\n saveErrno: %d" - "\r\n recvRef: %T" - "\r\n", read, saveErrno, recvRef) ); + descP->rNumCnt = 0; + cnt_inc(&descP->readPkgCnt, 1); + cnt_inc(&descP->readByteCnt, read); + recv_update_current_reader(env, descP, sockRef); - /* There is a special case: If the provided 'to read' value is - * zero (0). That means that we reads as much as we can, using - * the default read buffer size. + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). */ + data = MKBIN(env, bufP); + data = MKSBIN(env, data, 0, read); - if (read < 0) { + SSDBG( descP, + ("SOCKET", "recv_check_partial_done -> [%d] done\r\n", read) ); - /* +++ Error handling +++ */ + return esock_make_ok3(env, atom_true, data); +} - if (saveErrno == ECONNRESET) { - res = esock_make_error(env, atom_closed); - /* +++ Oups - closed +++ */ - SSDBG( descP, ("SOCKET", "recvfrom_check_result -> closed\r\n") ); +/* *** recv_check_partial_part *** + * + * A successful but only partial recv, which only partly fulfilled + * the required read. + */ +static +ERL_NIF_TERM recv_check_partial_part(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + ErlNifBinary* bufP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + ERL_NIF_TERM res, reason, data; + char* xres; + int sres; - /* <KOLLA> - * IF THE CURRENT PROCESS IS *NOT* THE CONTROLLING - * PROCESS, WE NEED TO INFORM IT!!! - * - * ALL WAITING PROCESSES MUST ALSO GET THE ERROR!! - * - * </KOLLA> - */ + if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) { + FREE_BIN(bufP); + return esock_make_error_str(env, xres); + } - descP->closeLocal = FALSE; - descP->state = SOCKET_STATE_CLOSING; + data = MKBIN(env, bufP); + data = MKSBIN(env, data, 0, read); - recv_error_current_reader(env, descP, sockRef, res); + cnt_inc(&descP->readByteCnt, read); - if ((sres = esock_select_stop(env, descP->sock, descP)) < 0) { - esock_warning_msg("Failed stop select (closed) " - "for current reader (%T): %d\r\n", - recvRef, sres); - } + /* SELECT for more data */ - FREE_BIN(bufP); + sres = esock_select_read(env, descP->sock, descP, NULL, + sockRef, recvRef); + if (sres < 0) { + /* Result: {error, Reason} + * Reason: {select_failed, sres, data} + */ + reason = MKT3(env, esock_atom_select_failed, MKI(env, sres), data); + res = esock_make_error(env, reason); - return res; + } else { - } else if ((saveErrno == ERRNO_BLOCK) || - (saveErrno == EAGAIN)) { + res = esock_make_ok3(env, atom_false, data); - SSDBG( descP, ("SOCKET", "recvfrom_check_result -> eagain\r\n") ); + } - FREE_BIN(bufP); + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ + return res; +} - if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) - return esock_make_error_str(env, xres); - - if ((sres = esock_select_read(env, descP->sock, descP, - NULL, recvRef)) < 0) { - res = esock_make_error(env, - MKT2(env, - esock_atom_select_failed, - MKI(env, sres))); - } else { - res = esock_make_error(env, esock_atom_eagain); - } - return res; - } else { - res = esock_make_error_errno(env, saveErrno); - SSDBG( descP, - ("SOCKET", - "recvfrom_check_result -> errno: %d\r\n", saveErrno) ); - - recv_error_current_reader(env, descP, sockRef, res); - FREE_BIN(bufP); +/* The recvfrom function delivers one (1) message. If our buffer + * is to small, the message will be truncated. So, regardless + * if we filled the buffer or not, we have got what we are going + * to get regarding this message. + */ +static +ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + int saveErrno, + ErlNifBinary* bufP, + ESockAddress* fromAddrP, + unsigned int fromAddrLen, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + ERL_NIF_TERM data, res; - return res; - } + SSDBG( descP, + ("SOCKET", "recvfrom_check_result -> entry with" + "\r\n read: %d" + "\r\n saveErrno: %d" + "\r\n recvRef: %T" + "\r\n", read, saveErrno, recvRef) ); + + if (read < 0) { + + /* +++ Error handling +++ */ + + res = recv_check_fail(env, descP, saveErrno, bufP, NULL, + sockRef, recvRef); } else { @@ -14443,7 +14931,9 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, &eSockAddr); if (read == bufP->size) { + data = MKBIN(env, bufP); + } else { /* +++ We got a chunk of data but +++ @@ -14458,30 +14948,34 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, recv_update_current_reader(env, descP, sockRef); - return esock_make_ok2(env, MKT2(env, eSockAddr, data)); + res = esock_make_ok2(env, MKT2(env, eSockAddr, data)); } + + return res; + } -/* The recvmsg function delivers one (1) message. If our buffer +/* *** recvmsg_check_result *** + * + * The recvmsg function delivers one (1) message. If our buffer * is to small, the message will be truncated. So, regardless * if we filled the buffer or not, we have got what we are going * to get regarding this message. */ static -ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, - SocketDescriptor* descP, - int read, - int saveErrno, - struct msghdr* msgHdrP, - ErlNifBinary* dataBufP, - ErlNifBinary* ctrlBufP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM recvRef) +ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + int saveErrno, + struct msghdr* msgHdrP, + ErlNifBinary* dataBufP, + ErlNifBinary* ctrlBufP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) { - int sres; ERL_NIF_TERM res; SSDBG( descP, @@ -14503,8 +14997,8 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, if ((read == 0) && (descP->type == SOCK_STREAM)) { /* - * When a stream socket peer has performed an orderly shutdown, the return - * value will be 0 (the traditional "end-of-file" return). + * When a stream socket peer has performed an orderly shutdown, + * the return value will be 0 (the traditional "end-of-file" return). * * *We* do never actually try to read 0 bytes from a stream socket! */ @@ -14526,119 +15020,76 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, /* +++ Error handling +++ */ - if (saveErrno == ECONNRESET) { + res = recv_check_fail(env, descP, saveErrno, dataBufP, ctrlBufP, + sockRef, recvRef); - /* +++ Oups - closed +++ */ + } else { - SSDBG( descP, ("SOCKET", "recvmsg_check_result -> closed\r\n") ); + /* +++ We sucessfully got a message - time to encode it +++ */ - /* <KOLLA> - * IF THE CURRENT PROCESS IS *NOT* THE CONTROLLING - * PROCESS, WE NEED TO INFORM IT!!! - * - * ALL WAITING PROCESSES MUST ALSO GET THE ERROR!! - * - * </KOLLA> - */ + res = recvmsg_check_msg(env, descP, read, msgHdrP, + dataBufP, ctrlBufP, sockRef); - res = esock_make_error(env, atom_closed); - descP->closeLocal = FALSE; - descP->state = SOCKET_STATE_CLOSING; - - recv_error_current_reader(env, descP, sockRef, res); + } - if ((sres = esock_select_stop(env, descP->sock, descP)) < 0) { - esock_warning_msg("Failed stop select (closed) " - "for current reader (%T): %d\r\n", - recvRef, sres); - } + return res; - FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); +} - return res;; - } else if ((saveErrno == ERRNO_BLOCK) || - (saveErrno == EAGAIN)) { - char* xres; - SSDBG( descP, ("SOCKET", "recvmsg_check_result -> eagain\r\n") ); - - FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); - - if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) - return esock_make_error_str(env, xres); - - if ((sres = esock_select_read(env, descP->sock, descP, - NULL, recvRef)) < 0) { - res = esock_make_error(env, - MKT2(env, - esock_atom_select_failed, - MKI(env, sres))); - } else { - res = esock_make_error(env, esock_atom_eagain); - } +/* *** recvmsg_check_msg *** + * + * We successfully read one message. Time to process. + */ +static +ERL_NIF_TERM recvmsg_check_msg(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + struct msghdr* msgHdrP, + ErlNifBinary* dataBufP, + ErlNifBinary* ctrlBufP, + ERL_NIF_TERM sockRef) +{ + ERL_NIF_TERM res, eMsgHdr; + char* xres; - return res; + /* + * <KOLLA> + * + * The return value of recvmsg is the *total* number of bytes + * that where successfully read. This data has been put into + * the *IO vector*. + * + * </KOLLA> + */ - } else { + if ((xres = encode_msghdr(env, descP, + read, msgHdrP, dataBufP, ctrlBufP, + &eMsgHdr)) != NULL) { - res = esock_make_error_errno(env, saveErrno); + SSDBG( descP, + ("SOCKET", + "recvmsg_check_result -> " + "(msghdr) encode failed: %s\r\n", xres) ); - SSDBG( descP, - ("SOCKET", - "recvmsg_check_result -> errno: %d\r\n", saveErrno) ); - - recv_error_current_reader(env, descP, sockRef, res); + recv_update_current_reader(env, descP, sockRef); - FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); + FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); - return res; - } + res = esock_make_error_str(env, xres); } else { - /* +++ We sucessfully got a message - time to encode it +++ */ - - ERL_NIF_TERM eMsgHdr; - char* xres; - - /* - * <KOLLA> - * - * The return value of recvmsg is the *total* number of bytes - * that where successfully read. This data has been put into - * the *IO vector*. - * - * </KOLLA> - */ - - if ((xres = encode_msghdr(env, descP, - read, msgHdrP, dataBufP, ctrlBufP, - &eMsgHdr)) != NULL) { - - SSDBG( descP, - ("SOCKET", - "recvmsg_check_result -> " - "(msghdr) encode failed: %s\r\n", xres) ); - - recv_update_current_reader(env, descP, sockRef); - - FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); - - return esock_make_error_str(env, xres); - } else { - - SSDBG( descP, - ("SOCKET", - "recvmsg_check_result -> " - "(msghdr) encode ok: %T\r\n", eMsgHdr) ); - - recv_update_current_reader(env, descP, sockRef); + SSDBG( descP, + ("SOCKET", "recvmsg_check_result -> (msghdr) encode ok\r\n") ); - return esock_make_ok2(env, eMsgHdr); - } + recv_update_current_reader(env, descP, sockRef); + res = esock_make_ok2(env, eMsgHdr); } + + return res; } @@ -14656,13 +15107,13 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, */ extern -char* encode_msghdr(ErlNifEnv* env, - SocketDescriptor* descP, - int read, - struct msghdr* msgHdrP, - ErlNifBinary* dataBufP, - ErlNifBinary* ctrlBufP, - ERL_NIF_TERM* eSockAddr) +char* encode_msghdr(ErlNifEnv* env, + ESockDescriptor* descP, + int read, + struct msghdr* msgHdrP, + ErlNifBinary* dataBufP, + ErlNifBinary* ctrlBufP, + ERL_NIF_TERM* eSockAddr) { char* xres; ERL_NIF_TERM addr, iov, ctrl, flags; @@ -14677,7 +15128,7 @@ char* encode_msghdr(ErlNifEnv* env, */ if (msgHdrP->msg_namelen != 0) { if ((xres = esock_encode_sockaddr(env, - (SocketAddress*) msgHdrP->msg_name, + (ESockAddress*) msgHdrP->msg_name, msgHdrP->msg_namelen, &addr)) != NULL) return xres; @@ -14705,10 +15156,9 @@ char* encode_msghdr(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "encode_msghdr -> components encoded:" "\r\n addr: %T" - "\r\n iov: %T" "\r\n ctrl: %T" "\r\n flags: %T" - "\r\n", addr, iov, ctrl, flags) ); + "\r\n", addr, ctrl, flags) ); { ERL_NIF_TERM keys[] = {esock_atom_addr, esock_atom_iov, @@ -14725,9 +15175,7 @@ char* encode_msghdr(ErlNifEnv* env, if (!MKMA(env, keys, vals, numKeys, &tmp)) return ESOCK_STR_EINVAL; - SSDBG( descP, ("SOCKET", "encode_msghdr -> msghdr: " - "\r\n %T" - "\r\n", tmp) ); + SSDBG( descP, ("SOCKET", "encode_msghdr -> msghdr encoded\r\n") ); *eSockAddr = tmp; } @@ -14761,21 +15209,35 @@ char* encode_msghdr(ErlNifEnv* env, */ extern -char* encode_cmsghdrs(ErlNifEnv* env, - SocketDescriptor* descP, - ErlNifBinary* cmsgBinP, - struct msghdr* msgHdrP, - ERL_NIF_TERM* eCMsgHdr) +char* encode_cmsghdrs(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifBinary* cmsgBinP, + struct msghdr* msgHdrP, + ERL_NIF_TERM* eCMsgHdr) { ERL_NIF_TERM ctrlBuf = MKBIN(env, cmsgBinP); // The *entire* binary SocketTArray cmsghdrs = TARRAY_CREATE(128); struct cmsghdr* firstP = CMSG_FIRSTHDR(msgHdrP); struct cmsghdr* currentP; - SSDBG( descP, ("SOCKET", "encode_cmsghdrs -> entry\r\n") ); + SSDBG( descP, ("SOCKET", "encode_cmsghdrs -> entry when" + "\r\n msg ctrl len: %d" + "\r\n (ctrl) firstP: 0x%lX" + "\r\n", + msgHdrP->msg_controllen, firstP) ); for (currentP = firstP; - currentP != NULL; + /* + * In *old* versions of darwin, the CMSG_FIRSTHDR does not + * check the msg_controllen, so we do it here. + * We should really test this stuff during configure, + * but for now, this will have to do. + */ +#if defined(__DARWIN__) + (msgHdrP->msg_controllen >= sizeof(struct cmsghdr)) && (currentP != NULL); +#else + (currentP != NULL); +#endif currentP = CMSG_NXTHDR(msgHdrP, currentP)) { SSDBG( descP, @@ -14788,9 +15250,23 @@ char* encode_cmsghdrs(ErlNifEnv* env, */ if (((CHARP(currentP) + currentP->cmsg_len) - CHARP(firstP)) > msgHdrP->msg_controllen) { + /* Ouch, fatal error - give up * We assume we cannot trust any data if this is wrong. */ + + SSDBG( descP, + ("SOCKET", "encode_cmsghdrs -> check failed when: " + "\r\n currentP: 0x%lX" + "\r\n (current) cmsg_len: %d" + "\r\n firstP: 0x%lX" + "\r\n => %d" + "\r\n msg ctrl len: %d" + "\r\n", + CHARP(currentP), currentP->cmsg_len, CHARP(firstP), + (CHARP(currentP) + currentP->cmsg_len) - CHARP(firstP), + msgHdrP->msg_controllen) ); + TARRAY_DELETE(cmsghdrs); return ESOCK_STR_EINVAL; } else { @@ -14883,12 +15359,12 @@ char* encode_cmsghdrs(ErlNifEnv* env, */ extern -char* decode_cmsghdrs(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eCMsgHdr, - char* cmsgHdrBufP, - size_t cmsgHdrBufLen, - size_t* cmsgHdrBufUsed) +char* decode_cmsghdrs(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eCMsgHdr, + char* cmsgHdrBufP, + size_t cmsgHdrBufLen, + size_t* cmsgHdrBufUsed) { ERL_NIF_TERM elem, tail, list; char* bufP; @@ -14966,12 +15442,12 @@ char* decode_cmsghdrs(ErlNifEnv* env, * which means that the data is already coded. */ extern -char* decode_cmsghdr(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eCMsgHdr, - char* bufP, - size_t rem, - size_t* used) +char* decode_cmsghdr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eCMsgHdr, + char* bufP, + size_t rem, + size_t* used) { SSDBG( descP, ("SOCKET", "decode_cmsghdr -> entry with" "\r\n eCMsgHdr: %T" @@ -15038,14 +15514,14 @@ char* decode_cmsghdr(ErlNifEnv* env, * an integer and ip_tos() respectively. */ static -char* decode_cmsghdr_data(ErlNifEnv* env, - SocketDescriptor* descP, - char* bufP, - size_t rem, - int level, - int type, - ERL_NIF_TERM eData, - size_t* used) +char* decode_cmsghdr_data(ErlNifEnv* env, + ESockDescriptor* descP, + char* bufP, + size_t rem, + int level, + int type, + ERL_NIF_TERM eData, + size_t* used) { char* xres; @@ -15137,14 +15613,14 @@ char* decode_cmsghdr_data(ErlNifEnv* env, * This does the final create of the cmsghdr (including the data copy). */ static -char* decode_cmsghdr_final(SocketDescriptor* descP, - char* bufP, - size_t rem, - int level, - int type, - char* data, - int sz, - size_t* used) +char* decode_cmsghdr_final(ESockDescriptor* descP, + char* bufP, + size_t rem, + int level, + int type, + char* data, + int sz, + size_t* used) { int len = CMSG_LEN(sz); int space = CMSG_SPACE(sz); @@ -15823,10 +16299,10 @@ char* encode_cmsghdr_data_ipv6(ErlNifEnv* env, */ extern -char* encode_msghdr_flags(ErlNifEnv* env, - SocketDescriptor* descP, - int msgFlags, - ERL_NIF_TERM* flags) +char* encode_msghdr_flags(ErlNifEnv* env, + ESockDescriptor* descP, + int msgFlags, + ERL_NIF_TERM* flags) { SSDBG( descP, ("SOCKET", "encode_cmsghdrs_flags -> entry with" @@ -16281,18 +16757,22 @@ ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val) * */ static -SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) +ESockDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) { - SocketDescriptor* descP; + ESockDescriptor* descP; - if ((descP = enif_alloc_resource(sockets, sizeof(SocketDescriptor))) != NULL) { + if ((descP = enif_alloc_resource(sockets, sizeof(ESockDescriptor))) != NULL) { char buf[64]; /* Buffer used for building the mutex name */ // This needs to be released when the socket is closed! - descP->env = enif_alloc_env(); + // descP->env = enif_alloc_env(); - sprintf(buf, "socket[w,%d]", sock); + sprintf(buf, "esock[w,%d]", sock); descP->writeMtx = MCREATE(buf); + enif_set_pid_undefined(&descP->currentWriter.pid); + MON_INIT(&descP->currentWriter.mon); + descP->currentWriter.env = NULL; + descP->currentWriter.ref = esock_atom_undefined; descP->currentWriterP = NULL; // currentWriter not used descP->writersQ.first = NULL; descP->writersQ.last = NULL; @@ -16303,8 +16783,12 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) descP->writeWaits = 0; descP->writeFails = 0; - sprintf(buf, "socket[r,%d]", sock); + sprintf(buf, "esock[r,%d]", sock); descP->readMtx = MCREATE(buf); + enif_set_pid_undefined(&descP->currentReader.pid); + MON_INIT(&descP->currentReader.mon); + descP->currentReader.env = NULL; + descP->currentReader.ref = esock_atom_undefined; descP->currentReaderP = NULL; // currentReader not used descP->readersQ.first = NULL; descP->readersQ.last = NULL; @@ -16314,17 +16798,25 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) descP->readTries = 0; descP->readWaits = 0; - sprintf(buf, "socket[acc,%d]", sock); + sprintf(buf, "esock[acc,%d]", sock); descP->accMtx = MCREATE(buf); + enif_set_pid_undefined(&descP->currentAcceptor.pid); + MON_INIT(&descP->currentAcceptor.mon); + descP->currentAcceptor.env = NULL; + descP->currentAcceptor.ref = esock_atom_undefined; descP->currentAcceptorP = NULL; // currentAcceptor not used descP->acceptorsQ.first = NULL; descP->acceptorsQ.last = NULL; - sprintf(buf, "socket[close,%d]", sock); + sprintf(buf, "esock[close,%d]", sock); descP->closeMtx = MCREATE(buf); descP->closeEnv = NULL; descP->closeRef = esock_atom_undefined; + enif_set_pid_undefined(&descP->closerPid); + MON_INIT(&descP->closerMon); + sprintf(buf, "esock[cfg,%d]", sock); + descP->cfgMtx = MCREATE(buf); descP->rBufSz = SOCKET_RECV_BUFFER_SIZE_DEFAULT; descP->rNum = 0; descP->rNumCnt = 0; @@ -16336,11 +16828,9 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) descP->sock = sock; descP->event = event; - MON_INIT(&descP->currentWriter.mon); - MON_INIT(&descP->currentReader.mon); - MON_INIT(&descP->currentAcceptor.mon); + enif_set_pid_undefined(&descP->ctrlPid); MON_INIT(&descP->ctrlMon); - MON_INIT(&descP->closerMon); + } return descP; @@ -16851,176 +17341,226 @@ size_t my_strnlen(const char *s, size_t maxlen) #endif -/* Send an error closed message to the specified process: +/* Send an close message to the specified process: + * A message in the form: * - * This message is for processes that are waiting in the - * erlang API functions for a select message. + * {'$socket', Socket, close, CloseRef} + * + * This message is for processes that is waiting in the + * erlang API (close-) function for the socket to be "closed" + * (actually that the 'stop' callback function has been called). */ -/* static -char* send_msg_error_closed(ErlNifEnv* env, - ErlNifPid* pid) +char* esock_send_close_msg(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifPid* pid) { - return send_msg_error(env, atom_closed, pid); + ERL_NIF_TERM sockRef, msg; + ErlNifEnv* menv; + + if (descP->closeEnv != NULL) { + sockRef = enif_make_resource(descP->closeEnv, descP); + msg = mk_close_msg(descP->closeEnv, sockRef, descP->closeRef); + menv = descP->closeEnv; + } else { + sockRef = enif_make_resource(env, descP); + msg = mk_close_msg(env, sockRef, descP->closeRef); + menv = NULL; // This has the effect that the message will be copied + } + + return esock_send_msg(env, pid, msg, menv); } -*/ -/* Send an error message to the specified process: + +/* Send an abort message to the specified process: * A message in the form: * - * {error, Reason} + * {'$socket', Socket, abort, {RecvRef, Reason}} * - * This message is for processes that are waiting in the + * This message is for processes that is waiting in the * erlang API functions for a select message. */ -/* static -char* send_msg_error(ErlNifEnv* env, - ERL_NIF_TERM reason, - ErlNifPid* pid) +char* esock_send_abort_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef, + ErlNifEnv* msgEnv, + ERL_NIF_TERM reason, + ErlNifPid* pid) { - ERL_NIF_TERM msg = enif_make_tuple2(env, atom_error, reason); + ERL_NIF_TERM msg = mk_abort_msg(msgEnv, + /* sockRef not in msgEnv so copy */ + CP_TERM(msgEnv, sockRef), + opRef, reason); - return send_msg(env, msg, pid); + return esock_send_msg(env, pid, msg, msgEnv); } -*/ -/* Send an close message to the specified process: - * A message in the form: +/* Send a message to the specified process. + */ +static +char* esock_send_msg(ErlNifEnv* env, + ErlNifPid* pid, + ERL_NIF_TERM msg, + ErlNifEnv* msgEnv) +{ + int res = enif_send(env, pid, msgEnv, msg); + if (msgEnv) + esock_free_env("esock_msg_send - msg-env", msgEnv); + + if (!res) + return str_exsend; + else + return NULL; +} + + + +/* *** mk_abort_msg *** * - * {'$socket', SockRef, close, CloseRef} + * Create the abort message, which has the following form: * - * This message is for processes that is waiting in the - * erlang API (close-) function for the socket to be "closed" - * (actually that the 'stop' callback function has been called). + * {'$socket', Socket, abort, {OpRef, Reason}} + * + * This message is for processes that are waiting in the + * erlang API functions for a select (or this) message. */ static -char* esock_send_close_msg(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef) +ERL_NIF_TERM mk_abort_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef, + ERL_NIF_TERM reason) { - ERL_NIF_TERM sr = ((descP->closeEnv != NULL) ? - enif_make_copy(descP->closeEnv, sockRef) : - sockRef); - char* res = esock_send_socket_msg(env, - sr, - esock_atom_close, - descP->closeRef, - &descP->closerPid, - descP->closeEnv); + ERL_NIF_TERM info = MKT2(env, opRef, reason); + + return mk_socket_msg(env, sockRef, esock_atom_abort, info); +} - descP->closeEnv = NULL; - return res; +/* *** mk_close_msg *** + * + * Construct a close (socket) message. It has the form: + * + * {'$socket', Socket, close, closeRef} + * + */ +static +ERL_NIF_TERM mk_close_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM closeRef) +{ + return mk_socket_msg(env, sockRef, esock_atom_close, closeRef); } -/* Send an abort message to the specified process: - * A message in the form: +/* *** mk_select_msg *** * - * {'$socket', SockRef, abort, {RecvRef, Reason}} + * Construct a select (socket) message. It has the form: + * + * {'$socket', Socket, select, selectRef} * - * This message is for processes that is waiting in the - * erlang API functions for a select message. */ static -char* esock_send_abort_msg(ErlNifEnv* env, +ERL_NIF_TERM mk_select_msg(ErlNifEnv* env, ERL_NIF_TERM sockRef, - ERL_NIF_TERM recvRef, - ERL_NIF_TERM reason, - ErlNifPid* pid) + ERL_NIF_TERM selectRef) { - ErlNifEnv* msg_env = enif_alloc_env(); - ERL_NIF_TERM info = MKT2(msg_env, - enif_make_copy(msg_env, recvRef), - enif_make_copy(msg_env, reason)); - - return esock_send_socket_msg(env, sockRef, esock_atom_abort, info, pid, - msg_env); + return mk_socket_msg(env, sockRef, atom_select, selectRef); } -/* *** esock_send_socket_msg *** - * - * This function sends a general purpose socket message to an erlang - * process. A general 'socket' message has the ("erlang") form: +/* *** mk_socket_msg *** * - * {'$socket', SockRef, Tag, Info} + * Construct the socket message: * - * Where + * {'$socket', Socket, Tag, Info} * - * SockRef: reference() - * Tag: atom() - * Info: term() + * Socket :: socket() (#socket{}) + * Tag :: atom() + * Info :: term() * */ - static -char* esock_send_socket_msg(ErlNifEnv* env, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM tag, - ERL_NIF_TERM info, - ErlNifPid* pid, - ErlNifEnv* msg_env) -{ - ERL_NIF_TERM msg; - if (!msg_env) { - msg_env = enif_alloc_env(); - sockRef = enif_make_copy(msg_env, sockRef); - tag = enif_make_copy(msg_env, tag); - info = enif_make_copy(msg_env, info); - } - msg = MKT4(msg_env, esock_atom_socket_tag, sockRef, tag, info); +ERL_NIF_TERM mk_socket_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM tag, + ERL_NIF_TERM info) +{ + ERL_NIF_TERM socket = mk_socket(env, sockRef); - return esock_send_msg(env, msg, pid, msg_env); + return MKT4(env, esock_atom_socket_tag, socket, tag, info); } -/* Send a message to the specified process. +/* *** mk_socket *** + * + * Simple utility function that construct the socket resord: + * + * #socket{ref = SockRef} => {socket, SockRef :: reference()} */ static -char* esock_send_msg(ErlNifEnv* env, - ERL_NIF_TERM msg, - ErlNifPid* pid, - ErlNifEnv* msg_env) +ERL_NIF_TERM mk_socket(ErlNifEnv* env, + ERL_NIF_TERM sockRef) { - int res = enif_send(env, pid, msg_env, msg); - if (msg_env) - enif_free_env(msg_env); - - if (!res) - return str_exsend; - else - return NULL; + return MKT2(env, esock_atom_socket, sockRef); } -#endif // #if defined(__WIN32__) +#endif // #if defined(__WIN32__) + /* ---------------------------------------------------------------------- * S e l e c t W r a p p e r F u n c t i o n s * ---------------------------------------------------------------------- */ +/* *** esock_select_read *** + * + * Perform a read select. When the select is triggered, a 'select' + * message (see mk_select_msg) will be sent. + * + * There are two ways to handle the select message: + * 1) Create "your own" environment and create the message using it + * and then pass it on to the select function. + * 2) Or, to create the message using any available environment, + * and then pass a NULL pointer to the select function. + * This will have the effect that the select function will + * create its own environment and then copy the message to it. + * We choose the second alternative. + */ static int esock_select_read(ErlNifEnv* env, - ErlNifEvent event, - void* obj, - const ErlNifPid* pid, - ERL_NIF_TERM ref) + ErlNifEvent event, // The file descriptor + void* obj, // The socket descriptor object + const ErlNifPid* pid, // Destination + ERL_NIF_TERM sockRef, // Socket + ERL_NIF_TERM selectRef) // "ID" of the operation { - return enif_select(env, event, (ERL_NIF_SELECT_READ), obj, pid, ref); + ERL_NIF_TERM selectMsg = mk_select_msg(env, sockRef, selectRef); + + return enif_select_read(env, event, obj, pid, selectMsg, NULL); + } +/* *** esock_select_write *** + * + * Perform a write select. When the select is triggered, a 'select' + * message (see mk_select_msg) will be sent. + * The sockRef is copied to the msgEnv when the socket message is created, + * so no need to do that here, but the selectRef needs to be copied. + */ static int esock_select_write(ErlNifEnv* env, - ErlNifEvent event, - void* obj, - const ErlNifPid* pid, - ERL_NIF_TERM ref) + ErlNifEvent event, // The file descriptor + void* obj, // The socket descriptor + const ErlNifPid* pid, // Destination + ERL_NIF_TERM sockRef, // Socket + ERL_NIF_TERM selectRef) // "ID" of the operation { - return enif_select(env, event, (ERL_NIF_SELECT_WRITE), obj, pid, ref); + ERL_NIF_TERM selectMsg = mk_select_msg(env, sockRef, selectRef); + + return enif_select_write(env, event, obj, pid, selectMsg, NULL); } @@ -17058,6 +17598,8 @@ int esock_select_cancel(ErlNifEnv* env, * Return value indicates if a new requestor was activated or not. */ +#if !defined(__WIN32__) + #define ACTIVATE_NEXT_FUNCS \ ACTIVATE_NEXT_FUNC_DECL(acceptor, read, currentAcceptor, acceptorsQ) \ ACTIVATE_NEXT_FUNC_DECL(writer, write, currentWriter, writersQ) \ @@ -17065,14 +17607,15 @@ int esock_select_cancel(ErlNifEnv* env, #define ACTIVATE_NEXT_FUNC_DECL(F, S, R, Q) \ static \ - BOOLEAN_T activate_next_##F(ErlNifEnv* env, \ - SocketDescriptor* descP, \ - ERL_NIF_TERM sockRef) \ + BOOLEAN_T activate_next_##F(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ERL_NIF_TERM sockRef) \ { \ - BOOLEAN_T popped, activated; \ - int sres; \ - SocketRequestor* reqP = &descP->R; \ - SocketRequestQueue* q = &descP->Q; \ + BOOLEAN_T popped, activated; \ + int sres; \ + ERL_NIF_TERM reason; \ + ESockRequestor* reqP = &descP->R; \ + ESockRequestQueue* q = &descP->Q; \ \ popped = FALSE; \ do { \ @@ -17088,15 +17631,22 @@ int esock_select_cancel(ErlNifEnv* env, "\r\n ref: %T" \ "\r\n", reqP->pid, reqP->ref) ); \ \ + /* We need to copy req ref to 'env' */ \ if ((sres = esock_select_##S(env, descP->sock, descP, \ - &reqP->pid, reqP->ref)) < 0) { \ + &reqP->pid, sockRef, \ + CP_TERM(env, reqP->ref))) < 0) { \ + \ /* We need to inform this process, reqP->pid, */ \ /* that we failed to select, so we don't leave */ \ /* it hanging. */ \ /* => send abort */ \ \ - esock_send_abort_msg(env, sockRef, reqP->ref, \ - sres, &reqP->pid); \ + reason = MKT2(env, \ + esock_atom_select_failed, \ + MKI(env, sres)); \ + esock_send_abort_msg(env, sockRef, \ + reqP->ref, reqP->env, \ + reason, &reqP->pid); \ \ } else { \ \ @@ -17128,6 +17678,7 @@ ACTIVATE_NEXT_FUNCS #undef ACTIVATE_NEXT_FUNC_DECL +#endif // if !defined(__WIN32__) /* ---------------------------------------------------------------------- @@ -17154,13 +17705,13 @@ ACTIVATE_NEXT_FUNCS REQ_SEARCH4PID_FUNC_DECL(writer, writersQ) \ REQ_SEARCH4PID_FUNC_DECL(reader, readersQ) -#define REQ_SEARCH4PID_FUNC_DECL(F, Q) \ - static \ - BOOLEAN_T F##_search4pid(ErlNifEnv* env, \ - SocketDescriptor* descP, \ - ErlNifPid* pid) \ - { \ - return qsearch4pid(env, &descP->Q, pid); \ +#define REQ_SEARCH4PID_FUNC_DECL(F, Q) \ + static \ + BOOLEAN_T F##_search4pid(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ErlNifPid* pid) \ + { \ + return qsearch4pid(env, &descP->Q, pid); \ } REQ_SEARCH4PID_FUNCS #undef REQ_SEARCH4PID_FUNC_DECL @@ -17181,28 +17732,28 @@ REQ_SEARCH4PID_FUNCS REQ_PUSH_FUNC_DECL(writer, writersQ) \ REQ_PUSH_FUNC_DECL(reader, readersQ) -#define REQ_PUSH_FUNC_DECL(F, Q) \ - static \ - ERL_NIF_TERM F##_push(ErlNifEnv* env, \ - SocketDescriptor* descP, \ - ErlNifPid pid, \ - ERL_NIF_TERM ref) \ - { \ - SocketRequestQueueElement* e = MALLOC(sizeof(SocketRequestQueueElement)); \ - SocketRequestor* reqP = &e->data; \ - \ - reqP->pid = pid; \ - reqP->ref = enif_make_copy(descP->env, ref); \ - \ - if (MONP("reader_push -> " #F " request", \ - env, descP, &pid, &reqP->mon) != 0) { \ - FREE(reqP); \ - return esock_make_error(env, atom_exmon); \ - } \ - \ - qpush(&descP->Q, e); \ - \ - return esock_make_error(env, esock_atom_eagain); \ +#define REQ_PUSH_FUNC_DECL(F, Q) \ + static \ + ERL_NIF_TERM F##_push(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ErlNifPid pid, \ + ERL_NIF_TERM ref) \ + { \ + ESockRequestQueueElement* e = MALLOC(sizeof(ESockRequestQueueElement)); \ + ESockRequestor* reqP = &e->data; \ + \ + reqP->pid = pid; \ + if (MONP("reader_push -> " #F " request", \ + env, descP, &pid, &reqP->mon) != 0) { \ + FREE(reqP); \ + return esock_make_error(env, atom_exmon); \ + } \ + reqP->env = esock_alloc_env(#F "_push"); \ + reqP->ref = enif_make_copy(reqP->env, ref); \ + \ + qpush(&descP->Q, e); \ + \ + return esock_make_error(env, esock_atom_eagain); \ } REQ_PUSH_FUNCS #undef REQ_PUSH_FUNC_DECL @@ -17221,13 +17772,13 @@ REQ_PUSH_FUNCS REQ_POP_FUNC_DECL(writer, writersQ) \ REQ_POP_FUNC_DECL(reader, readersQ) -#define REQ_POP_FUNC_DECL(F, Q) \ - static \ - BOOLEAN_T F##_pop(ErlNifEnv* env, \ - SocketDescriptor* descP, \ - SocketRequestor* reqP) \ - { \ - return requestor_pop(&descP->Q, reqP); \ +#define REQ_POP_FUNC_DECL(F, Q) \ + static \ + BOOLEAN_T F##_pop(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ESockRequestor* reqP) \ + { \ + return requestor_pop(&descP->Q, reqP); \ } REQ_POP_FUNCS #undef REQ_POP_FUNC_DECL @@ -17247,14 +17798,14 @@ REQ_POP_FUNCS REQ_UNQUEUE_FUNC_DECL(writer, writersQ) \ REQ_UNQUEUE_FUNC_DECL(reader, readersQ) -#define REQ_UNQUEUE_FUNC_DECL(F, Q) \ - static \ - BOOLEAN_T F##_unqueue(ErlNifEnv* env, \ - SocketDescriptor* descP, \ - const ErlNifPid* pid) \ - { \ - return qunqueue(env, descP, "qunqueue -> waiting " #F, \ - &descP->Q, pid); \ +#define REQ_UNQUEUE_FUNC_DECL(F, Q) \ + static \ + BOOLEAN_T F##_unqueue(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + const ErlNifPid* pid) \ + { \ + return qunqueue(env, descP, "qunqueue -> waiting " #F, \ + &descP->Q, pid); \ } REQ_UNQUEUE_FUNCS #undef REQ_UNQUEUE_FUNC_DECL @@ -17266,21 +17817,23 @@ REQ_UNQUEUE_FUNCS * Pop an requestor from its queue. */ static -BOOLEAN_T requestor_pop(SocketRequestQueue* q, - SocketRequestor* reqP) +BOOLEAN_T requestor_pop(ESockRequestQueue* q, + ESockRequestor* reqP) { - SocketRequestQueueElement* e = qpop(q); + ESockRequestQueueElement* e = qpop(q); if (e != NULL) { reqP->pid = e->data.pid; reqP->mon = e->data.mon; + reqP->env = e->data.env; reqP->ref = e->data.ref; FREE(e); return TRUE; } else { - /* (writers) Queue was empty */ + /* Queue was empty */ enif_set_pid_undefined(&reqP->pid); - // *reqP->mon = NULL; we have no null value for monitors + MON_INIT(&reqP->mon); + reqP->env = NULL; reqP->ref = esock_atom_undefined; // Just in case return FALSE; } @@ -17289,11 +17842,11 @@ BOOLEAN_T requestor_pop(SocketRequestQueue* q, static -BOOLEAN_T qsearch4pid(ErlNifEnv* env, - SocketRequestQueue* q, - ErlNifPid* pid) +BOOLEAN_T qsearch4pid(ErlNifEnv* env, + ESockRequestQueue* q, + ErlNifPid* pid) { - SocketRequestQueueElement* tmp = q->first; + ESockRequestQueueElement* tmp = q->first; while (tmp != NULL) { if (COMPARE_PIDS(&tmp->data.pid, pid) == 0) @@ -17307,8 +17860,8 @@ BOOLEAN_T qsearch4pid(ErlNifEnv* env, static -void qpush(SocketRequestQueue* q, - SocketRequestQueueElement* e) +void qpush(ESockRequestQueue* q, + ESockRequestQueueElement* e) { if (q->first != NULL) { q->last->nextP = e; @@ -17323,9 +17876,9 @@ void qpush(SocketRequestQueue* q, static -SocketRequestQueueElement* qpop(SocketRequestQueue* q) +ESockRequestQueueElement* qpop(ESockRequestQueue* q) { - SocketRequestQueueElement* e = q->first; + ESockRequestQueueElement* e = q->first; if (e != NULL) { /* Atleast one element in the queue */ @@ -17344,14 +17897,14 @@ SocketRequestQueueElement* qpop(SocketRequestQueue* q) static -BOOLEAN_T qunqueue(ErlNifEnv* env, - SocketDescriptor* descP, - const char* slogan, - SocketRequestQueue* q, - const ErlNifPid* pid) +BOOLEAN_T qunqueue(ErlNifEnv* env, + ESockDescriptor* descP, + const char* slogan, + ESockRequestQueue* q, + const ErlNifPid* pid) { - SocketRequestQueueElement* e = q->first; - SocketRequestQueueElement* p = NULL; + ESockRequestQueueElement* e = q->first; + ESockRequestQueueElement* p = NULL; /* Check if it was one of the waiting acceptor processes */ while (e != NULL) { @@ -17446,11 +17999,11 @@ void cnt_dec(Uint32* cnt, Uint32 dec) #if !defined(__WIN32__) static -int esock_monitor(const char* slogan, - ErlNifEnv* env, - SocketDescriptor* descP, - const ErlNifPid* pid, - ESockMonitor* monP) +int esock_monitor(const char* slogan, + ErlNifEnv* env, + ESockDescriptor* descP, + const ErlNifPid* pid, + ESockMonitor* monP) { int res; @@ -17458,10 +18011,10 @@ int esock_monitor(const char* slogan, res = enif_monitor_process(env, descP, pid, &monP->mon); if (res != 0) { - monP->is_active = 0; + monP->isActive = FALSE; SSDBG( descP, ("SOCKET", "[%d] monitor failed: %d\r\n", descP->sock, res) ); } else { - monP->is_active = 1; + monP->isActive = TRUE; } return res; @@ -17469,14 +18022,14 @@ int esock_monitor(const char* slogan, static -int esock_demonitor(const char* slogan, - ErlNifEnv* env, - SocketDescriptor* descP, - ESockMonitor* monP) +int esock_demonitor(const char* slogan, + ErlNifEnv* env, + ESockDescriptor* descP, + ESockMonitor* monP) { int res; - if (!monP->is_active) + if (!monP->isActive) return 1; SSDBG( descP, ("SOCKET", "[%d] %s: try demonitor\r\n", descP->sock, slogan) ); @@ -17497,20 +18050,23 @@ int esock_demonitor(const char* slogan, static void esock_monitor_init(ESockMonitor* monP) { - monP->is_active = 0; + monP->isActive = FALSE; } -#endif // if !defined(__WIN32__) - -/* static -int esock_monitor_compare(const ErlNifMonitor* mon1, - const ESockMonitor* mon2) +ERL_NIF_TERM esock_make_monitor_term(ErlNifEnv* env, const ESockMonitor* monP) { - return enif_compare_monitors(mon1, &mon2->mon); + if (monP->isActive) + return enif_make_monitor_term(env, &monP->mon); + else + return esock_atom_undefined; } -*/ + + + +#endif // if !defined(__WIN32__) + /* ---------------------------------------------------------------------- @@ -17526,16 +18082,13 @@ static void socket_dtor(ErlNifEnv* env, void* obj) { #if !defined(__WIN32__) - SocketDescriptor* descP = (SocketDescriptor*) obj; - - enif_clear_env(descP->env); - enif_free_env(descP->env); - descP->env = NULL; + ESockDescriptor* descP = (ESockDescriptor*) obj; MDESTROY(descP->writeMtx); MDESTROY(descP->readMtx); MDESTROY(descP->accMtx); MDESTROY(descP->closeMtx); + MDESTROY(descP->cfgMtx); #endif } @@ -17559,8 +18112,8 @@ static void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) { #if !defined(__WIN32__) - SocketDescriptor* descP = (SocketDescriptor*) obj; - ERL_NIF_TERM sockRef; + ESockDescriptor* descP = (ESockDescriptor*) obj; + ERL_NIF_TERM sockRef; SSDBG( descP, ("SOCKET", "socket_stop -> entry when %s" @@ -17573,6 +18126,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) MLOCK(descP->writeMtx); MLOCK(descP->readMtx); MLOCK(descP->accMtx); + MLOCK(descP->cfgMtx); if (!is_direct_call) MLOCK(descP->closeMtx); SSDBG( descP, ("SOCKET", "socket_stop -> " @@ -17622,37 +18176,19 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) */ if (descP->currentWriterP != NULL) { + /* We have a (current) writer and *may* therefor also have * writers waiting. */ - DEMONP("socket_stop -> current writer", - env, descP, &descP->currentWriter.mon); + socket_stop_handle_current(env, + "writer", + descP, sockRef, &descP->currentWriter); - SSDBG( descP, ("SOCKET", "socket_stop -> handle current writer\r\n") ); - if (COMPARE_PIDS(&descP->closerPid, &descP->currentWriter.pid) != 0) { - SSDBG( descP, ("SOCKET", "socket_stop -> " - "send abort message to current writer %T\r\n", - descP->currentWriter.pid) ); - if (esock_send_abort_msg(env, - sockRef, - descP->currentWriter.ref, - atom_closed, - &descP->currentWriter.pid) != NULL) { - /* Shall we really do this? - * This happens if the controlling process has been killed! - */ - esock_warning_msg("Failed sending abort (%T) message to " - "current writer %T\r\n", - descP->currentWriter.ref, - descP->currentWriter.pid); - } - } - /* And also deal with the waiting writers (in the same way) */ SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting writer(s)\r\n") ); inform_waiting_procs(env, "writer", - descP, &descP->writersQ, TRUE, atom_closed); + descP, sockRef, &descP->writersQ, TRUE, atom_closed); } @@ -17669,38 +18205,14 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * readers waiting. */ - DEMONP("socket_stop -> current reader", - env, descP, &descP->currentReader.mon); + socket_stop_handle_current(env, + "reader", + descP, sockRef, &descP->currentReader); - SSDBG( descP, ("SOCKET", "socket_stop -> handle current reader\r\n") ); - if (COMPARE_PIDS(&descP->closerPid, &descP->currentReader.pid) != 0) { - SSDBG( descP, ("SOCKET", "socket_stop -> " - "send abort message to current reader %T\r\n", - descP->currentReader.pid) ); - /* - esock_dbg_printf("SOCKET", "socket_stop -> " - "send abort message to current reader %T\r\n", - descP->currentReader.pid); - */ - if (esock_send_abort_msg(env, - sockRef, - descP->currentReader.ref, - atom_closed, - &descP->currentReader.pid) != NULL) { - /* Shall we really do this? - * This happens if the controlling process has been killed! - */ - esock_warning_msg("Failed sending abort (%T) message to " - "current reader %T\r\n", - descP->currentReader.ref, - descP->currentReader.pid); - } - } - /* And also deal with the waiting readers (in the same way) */ SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting reader(s)\r\n") ); inform_waiting_procs(env, "reader", - descP, &descP->readersQ, TRUE, atom_closed); + descP, sockRef, &descP->readersQ, TRUE, atom_closed); } @@ -17713,37 +18225,19 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) */ if (descP->currentAcceptorP != NULL) { + /* We have a (current) acceptor and *may* therefor also have * acceptors waiting. */ - - DEMONP("socket_stop -> current acceptor", - env, descP, &descP->currentAcceptor.mon); - - SSDBG( descP, ("SOCKET", "socket_stop -> handle current acceptor\r\n") ); - if (COMPARE_PIDS(&descP->closerPid, &descP->currentAcceptor.pid) != 0) { - SSDBG( descP, ("SOCKET", "socket_stop -> " - "send abort message to current acceptor %T\r\n", - descP->currentAcceptor.pid) ); - if (esock_send_abort_msg(env, - sockRef, - descP->currentAcceptor.ref, - atom_closed, - &descP->currentAcceptor.pid) != NULL) { - /* Shall we really do this? - * This happens if the controlling process has been killed! - */ - esock_warning_msg("Failed sending abort (%T) message to " - "current acceptor %T\r\n", - descP->currentAcceptor.ref, - descP->currentAcceptor.pid); - } - } - + + socket_stop_handle_current(env, + "acceptor", + descP, sockRef, &descP->currentAcceptor); + /* And also deal with the waiting acceptors (in the same way) */ SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting acceptor(s)\r\n") ); inform_waiting_procs(env, "acceptor", - descP, &descP->acceptorsQ, TRUE, atom_closed); + descP, sockRef, &descP->acceptorsQ, TRUE, atom_closed); } @@ -17756,15 +18250,15 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) */ if (descP->sock != INVALID_SOCKET) { - + if (descP->closeLocal) { if (!is_direct_call) { /* +++ send close message to the waiting process +++ */ - esock_send_close_msg(env, descP, sockRef); - + esock_send_close_msg(env, descP, &descP->closerPid); + DEMONP("socket_stop -> closer", env, descP, &descP->closerMon); } else { @@ -17773,11 +18267,8 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * since the message send takes care of it if scheduled. */ - if (descP->closeEnv != NULL) { - enif_clear_env(descP->closeEnv); - enif_free_env(descP->closeEnv); - descP->closeEnv = NULL; - } + if (descP->closeEnv != NULL) + esock_free_env("socket_stop - close-env", descP->closeEnv); } } @@ -17787,6 +18278,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) SSDBG( descP, ("SOCKET", "socket_stop -> unlock all mutex(s)\r\n") ); if (!is_direct_call) MUNLOCK(descP->closeMtx); + MUNLOCK(descP->cfgMtx); MUNLOCK(descP->accMtx); MUNLOCK(descP->readMtx); MUNLOCK(descP->writeMtx); @@ -17799,29 +18291,60 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) +/* *** socket_stop_handle_current *** + * + * Handle current requestor (reader, writer or acceptor) during + * socket stop. + */ +#if !defined(__WIN32__) +static +void socket_stop_handle_current(ErlNifEnv* env, + const char* role, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ESockRequestor* reqP) +{ + SSDBG( descP, ("SOCKET", "socket_stop -> handle current %s\r\n", role) ); + + DEMONP("socket_stop_handle_current", env, descP, &reqP->mon); + + if (COMPARE_PIDS(&descP->closerPid, &reqP->pid) != 0) { + + SSDBG( descP, ("SOCKET", "socket_stop_handle_current -> " + "send abort message to current %s %T\r\n", + role, reqP->pid) ); + + if (esock_send_abort_msg(env, sockRef, reqP->ref, reqP->env, + atom_closed, &reqP->pid) != NULL) { + + esock_warning_msg("Failed sending abort (%T) message to " + "current %s %T\r\n", + reqP->ref, role, reqP->pid); + } + } +} + + + /* This function traverse the queue and sends the specified * nif_abort message with the specified reason to each member, * and if the 'free' argument is TRUE, the queue will be emptied. */ -#if !defined(__WIN32__) -static void inform_waiting_procs(ErlNifEnv* env, - char* role, - SocketDescriptor* descP, - SocketRequestQueue* q, - BOOLEAN_T free, - ERL_NIF_TERM reason) +static +void inform_waiting_procs(ErlNifEnv* env, + const char* role, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ESockRequestQueue* q, + BOOLEAN_T free, + ERL_NIF_TERM reason) { - SocketRequestQueueElement* currentP = q->first; - SocketRequestQueueElement* nextP; - ERL_NIF_TERM sockRef = enif_make_resource(env, descP); + ESockRequestQueueElement* currentP = q->first; + ESockRequestQueueElement* nextP; - /* - esock_dbg_printf("SOCKET", "inform_waiting_procs -> entry with: " - "\r\n role: %s" - "\r\n free: %s" - "\r\n reason: %T" - "\r\n", role, B2S(free), reason); - */ + SSDBG( descP, + ("SOCKET", + "inform_waiting_procs -> handle waiting %s(s)\r\n", role) ); while (currentP != NULL) { @@ -17835,18 +18358,14 @@ static void inform_waiting_procs(ErlNifEnv* env, */ SSDBG( descP, - ("SOCKET", "inform_waiting_procs -> abort request %T (from %T)\r\n", + ("SOCKET", + "inform_waiting_procs -> abort request %T (from %T)\r\n", currentP->data.ref, currentP->data.pid) ); - /* - esock_dbg_printf("SOCKET", "inform_waiting_procs -> " - "try sending abort to %s %T " - "\r\n", role, currentP->data.pid); - */ - if (esock_send_abort_msg(env, sockRef, currentP->data.ref, + currentP->data.env, reason, ¤tP->data.pid) != NULL) { @@ -17884,9 +18403,9 @@ void socket_down(ErlNifEnv* env, const ErlNifMonitor* mon) { #if !defined(__WIN32__) - SocketDescriptor* descP = (SocketDescriptor*) obj; - int sres; - ERL_NIF_TERM sockRef; + ESockDescriptor* descP = (ESockDescriptor*) obj; + int sres; + ERL_NIF_TERM sockRef; SSDBG( descP, ("SOCKET", "socket_down -> entry with" "\r\n sock: %d" @@ -17912,7 +18431,7 @@ void socket_down(ErlNifEnv* env, descP->closeLocal = TRUE; descP->closerPid = *pid; MON_INIT(&descP->closerMon); - + sres = esock_select_stop(env, descP->sock, descP); if (sres & ERL_NIF_SELECT_STOP_CALLED) { @@ -18035,10 +18554,10 @@ void socket_down(ErlNifEnv* env, */ #if !defined(__WIN32__) static -void socket_down_acceptor(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - const ErlNifPid* pid) +void socket_down_acceptor(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pid) { if (COMPARE_PIDS(&descP->currentAcceptor.pid, pid) == 0) { @@ -18080,10 +18599,10 @@ void socket_down_acceptor(ErlNifEnv* env, * */ static -void socket_down_writer(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - const ErlNifPid* pid) +void socket_down_writer(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pid) { if (COMPARE_PIDS(&descP->currentWriter.pid, pid) == 0) { @@ -18121,10 +18640,10 @@ void socket_down_writer(ErlNifEnv* env, * */ static -void socket_down_reader(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM sockRef, - const ErlNifPid* pid) +void socket_down_reader(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pid) { if (COMPARE_PIDS(&descP->currentReader.pid, pid) == 0) { diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 5e18355308..8ad95cb6b7 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -213,10 +213,10 @@ char* esock_decode_iov(ErlNifEnv* env, */ extern -char* esock_decode_sockaddr(ErlNifEnv* env, - ERL_NIF_TERM eSockAddr, - SocketAddress* sockAddrP, - unsigned int* addrLen) +char* esock_decode_sockaddr(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + ESockAddress* sockAddrP, + unsigned int* addrLen) { ERL_NIF_TERM efam; int fam; @@ -279,10 +279,10 @@ char* esock_decode_sockaddr(ErlNifEnv* env, */ extern -char* esock_encode_sockaddr(ErlNifEnv* env, - SocketAddress* sockAddrP, - unsigned int addrLen, - ERL_NIF_TERM* eSockAddr) +char* esock_encode_sockaddr(ErlNifEnv* env, + ESockAddress* sockAddrP, + unsigned int addrLen, + ERL_NIF_TERM* eSockAddr) { char* xres; diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index 1b5d003155..84b1c8085f 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -57,15 +57,15 @@ char* esock_decode_iov(ErlNifEnv* env, size_t len, ssize_t* totSize); extern -char* esock_decode_sockaddr(ErlNifEnv* env, - ERL_NIF_TERM eSockAddr, - SocketAddress* sockAddrP, - unsigned int* addrLen); -extern -char* esock_encode_sockaddr(ErlNifEnv* env, - SocketAddress* sockAddrP, - unsigned int addrLen, - ERL_NIF_TERM* eSockAddr); +char* esock_decode_sockaddr(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + ESockAddress* sockAddrP, + unsigned int* addrLen); +extern +char* esock_encode_sockaddr(ErlNifEnv* env, + ESockAddress* sockAddrP, + unsigned int addrLen, + ERL_NIF_TERM* eSockAddr); extern char* esock_decode_sockaddr_in4(ErlNifEnv* env, diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index 80e8030d74..fb18c837ab 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -534,6 +534,7 @@ erts_io_notify_port_task_executed(ErtsPortTaskType type, if (state->active_events & ERTS_POLL_EV_OUT) oready(state->driver.select->outport, state); state->active_events = 0; + active_events = 0; } } @@ -842,6 +843,8 @@ driver_select(ErlDrvPort ix, ErlDrvEvent e, int mode, int on) ret = 0; goto done_unknown; } + /* For some reason (don't know why), we do not clean all + events when doing ERL_DRV_USE_NO_CALLBACK. */ else if ((mode&ERL_DRV_USE_NO_CALLBACK) == ERL_DRV_USE) { mode |= (ERL_DRV_READ | ERL_DRV_WRITE); } @@ -2491,6 +2494,10 @@ drvmode2str(int mode) { case ERL_DRV_WRITE|ERL_DRV_USE: return "WRITE|USE"; case ERL_DRV_READ|ERL_DRV_WRITE|ERL_DRV_USE: return "READ|WRITE|USE"; case ERL_DRV_USE: return "USE"; + case ERL_DRV_READ|ERL_DRV_USE_NO_CALLBACK: return "READ|USE_NO_CB"; + case ERL_DRV_WRITE|ERL_DRV_USE_NO_CALLBACK: return "WRITE|USE_NO_CB"; + case ERL_DRV_READ|ERL_DRV_WRITE|ERL_DRV_USE_NO_CALLBACK: return "READ|WRITE|USE_NO_CB"; + case ERL_DRV_USE_NO_CALLBACK: return "USE_NO_CB"; case ERL_DRV_READ: return "READ"; case ERL_DRV_WRITE: return "WRITE"; case ERL_DRV_READ|ERL_DRV_WRITE: return "READ|WRITE"; diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index 9662996039..1b125056f5 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -924,7 +924,7 @@ update_pollset(ErtsPollSet *ps, int fd, ErtsPollOp op, ErtsPollEvents events) ERTS_EV_SET(&evts[len++], fd, EVFILT_WRITE, flags, (void *) ERTS_POLL_EV_OUT); } #else - uint32_t flags = EV_ADD; + uint32_t flags = EV_ADD|EV_ENABLE; if (ps->oneshot) flags |= EV_ONESHOT; @@ -932,9 +932,27 @@ update_pollset(ErtsPollSet *ps, int fd, ErtsPollOp op, ErtsPollEvents events) erts_atomic_dec_nob(&ps->no_of_user_fds); /* We don't do anything when a delete is issued. The fds will be removed when they are triggered, or when they are closed. */ - events = 0; + if (ps->oneshot) + events = 0; + else { + flags = EV_DELETE; + events = ERTS_POLL_EV_IN; + } } else if (op == ERTS_POLL_OP_ADD) { erts_atomic_inc_nob(&ps->no_of_user_fds); + /* Only allow EV_IN in non-oneshot poll-sets */ + ASSERT(ps->oneshot || events == ERTS_POLL_EV_IN); + } else if (!ps->oneshot) { + ASSERT(op == ERTS_POLL_OP_MOD); + /* If we are not oneshot and do a mod we should disable the FD. + We assume that it is only the read side that is active as + currently only read is selected upon in the non-oneshot + poll-sets. */ + if (!events) + flags = EV_DISABLE; + else + flags = EV_ENABLE; + events = ERTS_POLL_EV_IN; } if (events & ERTS_POLL_EV_IN) { @@ -961,16 +979,15 @@ update_pollset(ErtsPollSet *ps, int fd, ErtsPollOp op, ErtsPollEvents events) for (i = 0; i < len; i++) { const char *flags = "UNKNOWN"; if (evts[i].flags == (EV_DELETE)) flags = "EV_DELETE"; - if (evts[i].flags == (EV_ADD|EV_ONESHOT)) flags = "EV_ADD|EV_ONESHOT"; if (evts[i].flags == (EV_ADD)) flags = "EV_ADD"; + if (evts[i].flags == (EV_ADD|EV_ONESHOT)) flags = "EV_ADD|EV_ONESHOT"; + if (evts[i].flags == (EV_ENABLE)) flags = "EV_ENABLE"; + if (evts[i].flags == (EV_DISABLE)) flags = "EV_DISABLE"; + if (evts[i].flags == (EV_ADD|EV_DISABLE)) flags = "EV_ADD|EV_DISABLE"; #ifdef EV_DISPATCH if (evts[i].flags == (EV_ADD|EV_DISPATCH)) flags = "EV_ADD|EV_DISPATCH"; - if (evts[i].flags == (EV_ADD|EV_DISABLE)) flags = "EV_ADD|EV_DISABLE"; if (evts[i].flags == (EV_ENABLE|EV_DISPATCH)) flags = "EV_ENABLE|EV_DISPATCH"; - if (evts[i].flags == (EV_ENABLE)) flags = "EV_ENABLE"; - if (evts[i].flags == (EV_DISABLE)) flags = "EV_DISABLE"; if (evts[i].flags == (EV_DISABLE|EV_DISPATCH)) flags = "EV_DISABLE|EV_DISABLE"; - if (evts[i].flags == (EV_DISABLE)) flags = "EV_DISABLE"; #endif keventbp += sprintf(keventbp, "%s{%lu, %s, %s}",i > 0 ? ", " : "", diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c index 042a091db1..664d677ebd 100644 --- a/erts/emulator/sys/unix/sys_drivers.c +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -1006,10 +1006,8 @@ static void clear_fd_data(ErtsSysFdData *fdd) static void nbio_stop_fd(ErlDrvPort prt, ErtsSysFdData *fdd, int use) { - driver_select(prt, abs(fdd->fd), use ? ERL_DRV_USE_NO_CALLBACK : 0|DO_READ|DO_WRITE, 0); clear_fd_data(fdd); SET_BLOCKING(abs(fdd->fd)); - } static void fd_stop(ErlDrvData ev) /* Does not close the fds */ @@ -1026,10 +1024,12 @@ static void fd_stop(ErlDrvData ev) /* Does not close the fds */ if (dd->ifd) { sz += sizeof(ErtsSysFdData); + driver_select(prt, abs(dd->ifd->fd), ERL_DRV_USE_NO_CALLBACK|DO_READ|DO_WRITE, 0); nbio_stop_fd(prt, dd->ifd, 1); } if (dd->ofd && dd->ofd != dd->ifd) { sz += sizeof(ErtsSysFdData); + driver_select(prt, abs(dd->ofd->fd), ERL_DRV_USE_NO_CALLBACK|DO_WRITE, 0); nbio_stop_fd(prt, dd->ofd, 1); } diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index 8c2054cb51..019af2162f 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -124,6 +124,7 @@ MODULES= \ send_term_SUITE \ sensitive_SUITE \ signal_SUITE \ + small_SUITE \ smoke_test_SUITE \ $(SOCKET_MODULES) \ statistics_SUITE \ @@ -206,14 +207,10 @@ ERL_COMPILE_FLAGS += # ---------------------------------------------------- make_emakefile: $(NO_OPT_ERL_FILES) $(NATIVE_ERL_FILES) - # This special rule can be removed when communication with R7B nodes - # is no longer supported. - $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) +compressed -o$(EBIN) \ - '*_SUITE_make' > $(EMAKEFILE) $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) +compressed -o$(EBIN) \ $(MODULES) >> $(EMAKEFILE) - $(ERL_TOP)/make/make_emakefile +no_copt +no_postopt $(ERL_COMPILE_FLAGS) \ - -o$(EBIN) $(NO_OPT_MODULES) >> $(EMAKEFILE) + $(ERL_TOP)/make/make_emakefile +no_copt +no_postopt +no_ssa_opt +no_bsm_opt \ + $(ERL_COMPILE_FLAGS) -o$(EBIN) $(NO_OPT_MODULES) >> $(EMAKEFILE) $(ERL_TOP)/make/make_emakefile +native $(ERL_COMPILE_FLAGS) \ -o$(EBIN) $(NATIVE_MODULES) >> $(EMAKEFILE) diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 563d60cc3f..4fb339926e 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -71,7 +71,8 @@ term2bin_tuple_fallbacks/1, robustness/1,otp_8117/1, otp_8180/1, trapping/1, large/1, - error_after_yield/1, cmp_old_impl/1]). + error_after_yield/1, cmp_old_impl/1, + t2b_system_limit/1]). %% Internal exports. -export([sleeper/0,trapping_loop/4]). @@ -79,7 +80,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,4}}]. -all() -> +all() -> [copy_terms, conversions, deep_lists, deep_bitstr_lists, t_split_binary, bad_split, bad_list_to_binary, bad_binary_to_list, terms, @@ -90,7 +91,8 @@ all() -> b2t_used_big, bad_binary_to_term_2, safe_binary_to_term2, bad_binary_to_term, bad_terms, t_hash, bad_size, - bad_term_to_binary, more_bad_terms, otp_5484, otp_5933, + bad_term_to_binary, t2b_system_limit, more_bad_terms, + otp_5484, otp_5933, ordering, unaligned_order, gc_test, bit_sized_binary_sizes, otp_6817, otp_8117, deep, term2bin_tuple_fallbacks, @@ -462,6 +464,54 @@ bad_term_to_binary(Config) when is_list(Config) -> ok. +t2b_system_limit(Config) when is_list(Config) -> + case erlang:system_info(wordsize) of + 8 -> + case proplists:get_value(system_total_memory, + memsup:get_system_memory_data()) of + Memory when is_integer(Memory), + Memory > 6*1024*1024*1024 -> + test_t2b_system_limit(), + garbage_collect(), + ok; + _ -> + {skipped, "Not enough memory on this machine"} + end; + 4 -> + {skipped, "Only interesting on 64-bit builds"} + end. + +test_t2b_system_limit() -> + io:format("Creating HugeBin~n", []), + Bits = ((1 bsl 32)+1)*8, + HugeBin = <<0:Bits>>, + + io:format("Testing term_to_binary(HugeBin)~n", []), + {'EXIT',{system_limit,[{erlang,term_to_binary, + [HugeBin], + _} |_]}} = (catch term_to_binary(HugeBin)), + + io:format("Testing term_to_binary(HugeBin, [compressed])~n", []), + {'EXIT',{system_limit,[{erlang,term_to_binary, + [HugeBin, [compressed]], + _} |_]}} = (catch term_to_binary(HugeBin, [compressed])), + + %% Check that it works also after we have trapped... + io:format("Creating HugeListBin~n", []), + HugeListBin = [lists:duplicate(2000000,2000000), HugeBin], + + io:format("Testing term_to_binary(HugeListBin)~n", []), + {'EXIT',{system_limit,[{erlang,term_to_binary, + [HugeListBin], + _} |_]}} = (catch term_to_binary(HugeListBin)), + + io:format("Testing term_to_binary(HugeListBin, [compressed])~n", []), + {'EXIT',{system_limit,[{erlang,term_to_binary, + [HugeListBin, [compressed]], + _} |_]}} = (catch term_to_binary(HugeListBin, [compressed])), + + ok. + %% Tests binary_to_term/1 and term_to_binary/1. terms(Config) when is_list(Config) -> diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index ad05cb3689..8fab4f5bc4 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -16,8 +16,6 @@ %% limitations under the License. %% %% %CopyrightEnd% -%% -%% Purpose : Common utilities used by several optimization passes. %% -module(bs_construct_SUITE). @@ -27,9 +25,9 @@ test1/1, test2/1, test3/1, test4/1, test5/1, testf/1, not_used/1, in_guard/1, mem_leak/1, coerce_to_float/1, bjorn/1, append_empty_is_same/1, - huge_float_field/1, huge_binary/1, system_limit/1, badarg/1, + huge_float_field/1, system_limit/1, badarg/1, copy_writable_binary/1, kostis/1, dynamic/1, bs_add/1, - otp_7422/1, zero_width/1, bad_append/1, bs_add_overflow/1]). + otp_7422/1, zero_width/1, bad_append/1, bs_append_overflow/1]). -include_lib("common_test/include/ct.hrl"). @@ -40,9 +38,9 @@ suite() -> all() -> [test1, test2, test3, test4, test5, testf, not_used, in_guard, mem_leak, coerce_to_float, bjorn, append_empty_is_same, - huge_float_field, huge_binary, system_limit, badarg, + huge_float_field, system_limit, badarg, copy_writable_binary, kostis, dynamic, bs_add, otp_7422, zero_width, - bad_append, bs_add_overflow]. + bad_append, bs_append_overflow]. init_per_suite(Config) -> Config. @@ -543,56 +541,6 @@ huge_float_field(Config) when is_list(Config) -> huge_float_check({'EXIT',{system_limit,_}}) -> ok; huge_float_check({'EXIT',{badarg,_}}) -> ok. -huge_binary(Config) when is_list(Config) -> - ct:timetrap({seconds, 60}), - 16777216 = size(<<0:(id(1 bsl 26)),(-1):(id(1 bsl 26))>>), - garbage_collect(), - FreeMem = free_mem(), - io:format("Free memory (Mb): ~p\n", [FreeMem]), - {Shift,Return} = case free_mem() of - undefined -> - %% This test has to be inlined inside the case to - %% use a literal Shift - garbage_collect(), - id(<<0:((1 bsl 32)-1)>>), - {32,ok}; - Mb when Mb > 600 -> - garbage_collect(), - id(<<0:((1 bsl 32)-1)>>), - {32,ok}; - Mb when Mb > 300 -> - garbage_collect(), - id(<<0:((1 bsl 31)-1)>>), - {31,"Limit huge binaries to 256 Mb"}; - Mb when Mb > 200 -> - garbage_collect(), - id(<<0:((1 bsl 30)-1)>>), - {30,"Limit huge binary to 128 Mb"}; - _ -> - garbage_collect(), - id(<<0:((1 bsl 29)-1)>>), - {29,"Limit huge binary to 64 Mb"} - end, - garbage_collect(), - id(<<0:((1 bsl Shift)-1)>>), - garbage_collect(), - id(<<0:(id((1 bsl Shift)-1))>>), - garbage_collect(), - case Return of - ok -> ok; - Comment -> {comment, Comment} - end. - -%% Return the amount of free memory in Mb. -free_mem() -> - {ok,Apps} = application:ensure_all_started(os_mon), - Mem = memsup:get_system_memory_data(), - [ok = application:stop(App)||App <- Apps], - case proplists:get_value(free_memory,Mem) of - undefined -> undefined; - Val -> Val div (1024*1024) - end. - system_limit(Config) when is_list(Config) -> WordSize = erlang:system_info(wordsize), BitsPerWord = WordSize * 8, @@ -904,33 +852,37 @@ append_unit_8(Bin) -> append_unit_16(Bin) -> <<Bin/binary-unit:16,0:1>>. -%% Produce a large result of bs_add that, if cast to signed int, would overflow -%% into a negative number that fits a smallnum. -bs_add_overflow(_Config) -> +%% Test that the bs_append instruction will correctly check for +%% overflow by producing a binary whose total size would exceed the +%% maximum allowed size for a binary on a 32-bit computer. + +bs_append_overflow(_Config) -> Memsize = memsize(), io:format("Memsize = ~w Bytes~n", [Memsize]), case erlang:system_info(wordsize) of 8 -> + %% Not possible to test on a 64-bit computer. {skip, "64-bit architecture"}; _ when Memsize < (2 bsl 30) -> - {skip, "Less then 2 GB of memory"}; + {skip, "Less than 2 GB of memory"}; 4 -> - {'EXIT', {system_limit, _}} = (catch bs_add_overflow_signed()), - {'EXIT', {system_limit, _}} = (catch bs_add_overflow_unsigned()), + {'EXIT', {system_limit, _}} = (catch bs_append_overflow_signed()), + erlang:garbage_collect(), + {'EXIT', {system_limit, _}} = (catch bs_append_overflow_unsigned()), + erlang:garbage_collect(), ok end. -bs_add_overflow_signed() -> - %% Produce a large result of bs_add that, if cast to signed int, would +bs_append_overflow_signed() -> + %% Produce a large binary that, if cast to signed int, would %% overflow into a negative number that fits a smallnum. Large = <<0:((1 bsl 30)-1)>>, <<Large/bits, Large/bits, Large/bits, Large/bits, Large/bits, Large/bits, Large/bits, Large/bits, Large/bits>>. -bs_add_overflow_unsigned() -> - %% Produce a large result of bs_add that goes beyond the limit of an - %% unsigned word. This used to succeed but produced an incorrect result +bs_append_overflow_unsigned() -> + %% The following would succeed but would produce an incorrect result %% where B =:= C! A = <<0:((1 bsl 32)-8)>>, B = <<2, 3>>, diff --git a/erts/emulator/test/bs_match_int_SUITE.erl b/erts/emulator/test/bs_match_int_SUITE.erl index e913dc98b0..454e55d017 100644 --- a/erts/emulator/test/bs_match_int_SUITE.erl +++ b/erts/emulator/test/bs_match_int_SUITE.erl @@ -234,34 +234,49 @@ mml_choose(<<_A:8>>) -> single_byte_binary; mml_choose(<<_A:8,_T/binary>>) -> multi_byte_binary. match_huge_int(Config) when is_list(Config) -> - Sz = 1 bsl 27, - Bin = <<0:Sz,13:8>>, - skip_huge_int_1(Sz, Bin), - 0 = match_huge_int_1(Sz, Bin), - - %% Test overflowing the size of an integer field. - nomatch = overflow_huge_int_skip_32(Bin), - case erlang:system_info(wordsize) of - 4 -> - nomatch = overflow_huge_int_32(Bin); - 8 -> - %% An attempt will be made to allocate heap space for - %% the bignum (which will probably fail); only if the - %% allocation succeeds will the matching fail because - %% the binary is too small. - ok - end, - nomatch = overflow_huge_int_skip_64(Bin), - nomatch = overflow_huge_int_64(Bin), - - %% Test overflowing the size of an integer field using variables as sizes. - Sizes = case erlang:system_info(wordsize) of - 4 -> lists:seq(25, 32); - 8 -> [] - end ++ lists:seq(50, 64), - ok = overflow_huge_int_unit128(Bin, Sizes), - - ok. + case ?MODULE of + bs_match_int_no_opt_SUITE -> + %% This test case is written with the assumption that + %% bs_skip2 instructions are used when the value of the + %% extracted segment will not be used. In OTP 21 and earlier, that + %% assumption was always true, because the bs_skip optimization + %% was included in v3_codegen and could not be disabled. + %% In OTP 22, the bs_skip optimization is done by beam_ssa_opt + %% and is disabled. + %% + %% On memory-constrained computers, using bs_get_integer2 + %% instructions may cause the runtime system to terminate + %% because of insufficient memory. + {skip, "unoptimized code would use too much memory"}; + bs_match_int_SUITE -> + Sz = 1 bsl 27, + Bin = <<0:Sz,13:8>>, + skip_huge_int_1(Sz, Bin), + 0 = match_huge_int_1(Sz, Bin), + + %% Test overflowing the size of an integer field. + nomatch = overflow_huge_int_skip_32(Bin), + case erlang:system_info(wordsize) of + 4 -> + nomatch = overflow_huge_int_32(Bin); + 8 -> + %% An attempt will be made to allocate heap space for + %% the bignum (which will probably fail); only if the + %% allocation succeeds will the matching fail because + %% the binary is too small. + ok + end, + nomatch = overflow_huge_int_skip_64(Bin), + nomatch = overflow_huge_int_64(Bin), + + %% Test overflowing the size of an integer field using + %% variables as sizes. + Sizes = case erlang:system_info(wordsize) of + 4 -> lists:seq(25, 32); + 8 -> [] + end ++ lists:seq(50, 64), + ok = overflow_huge_int_unit128(Bin, Sizes) + end. overflow_huge_int_unit128(Bin, [Sz0|Sizes]) -> Sz = id(1 bsl Sz0), diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index 449821e5ad..7885d35d9d 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -19,7 +19,6 @@ %% -module(distribution_SUITE). --compile(r16). -define(VERSION_MAGIC, 131). @@ -39,6 +38,8 @@ -define(Line,). -export([all/0, suite/0, groups/0, + init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2, ping/1, bulk_send_small/1, group_leader/1, optimistic_dflags/1, @@ -53,7 +54,6 @@ dist_parallel_send/1, atom_roundtrip/1, unicode_atom_roundtrip/1, - atom_roundtrip_r16b/1, contended_atom_cache_entry/1, contended_unicode_atom_cache_entry/1, bad_dist_structure/1, @@ -67,7 +67,8 @@ message_latency_large_message/1, message_latency_large_link_exit/1, message_latency_large_monitor_exit/1, - message_latency_large_exit2/1]). + message_latency_large_exit2/1, + system_limit/1]). %% Internal exports. -export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0, @@ -75,7 +76,7 @@ optimistic_dflags_echo/0, optimistic_dflags_sender/1, roundtrip/1, bounce/1, do_dist_auto_connect/1, inet_rpc_server/1, dist_parallel_sender/3, dist_parallel_receiver/0, - dist_evil_parallel_receiver/0]). + dist_evil_parallel_receiver/0, make_busy/2]). %% epmd_module exports -export([start_link/0, register_node/2, register_node/3, port_please/2, address_please/3]). @@ -93,11 +94,10 @@ all() -> ref_port_roundtrip, nil_roundtrip, stop_dist, {group, trap_bif}, {group, dist_auto_connect}, dist_parallel_send, atom_roundtrip, unicode_atom_roundtrip, - atom_roundtrip_r16b, contended_atom_cache_entry, contended_unicode_atom_cache_entry, {group, message_latency}, {group, bad_dist}, {group, bad_dist_ext}, - start_epmd_false, epmd_module]. + start_epmd_false, epmd_module, system_limit]. groups() -> [{bulk_send, [], [bulk_send_small, bulk_send_big, bulk_send_bigbig]}, @@ -119,6 +119,28 @@ groups() -> message_latency_large_exit2]} ]. +init_per_suite(Config) -> + {ok, Apps} = application:ensure_all_started(os_mon), + [{started_apps, Apps} | Config]. + +end_per_suite(Config) -> + Apps = proplists:get_value(started_apps, Config), + [application:stop(App) || App <- lists:reverse(Apps)], + Config. + +init_per_group(message_latency, Config) -> + Free = free_memory(), + if Free < 2048 -> + {skip, "Not enough memory"}; + true -> + Config + end; +init_per_group(_, Config) -> + Config. + +end_per_group(_, Config) -> + Config. + %% Tests pinging a node in different ways. ping(Config) when is_list(Config) -> Times = 1024, @@ -1134,23 +1156,6 @@ atom_roundtrip(Config) when is_list(Config) -> stop_node(Node), ok. -atom_roundtrip_r16b(Config) when is_list(Config) -> - case test_server:is_release_available("r16b") of - true -> - ct:timetrap({minutes, 6}), - AtomData = unicode_atom_data(), - verify_atom_data(AtomData), - case start_node(Config, [], "r16b") of - {ok, Node} -> - do_atom_roundtrip(Node, AtomData), - stop_node(Node); - {error, timeout} -> - {skip,"Unable to start OTP R16B release"} - end; - false -> - {skip,"No OTP R16B available"} - end. - unicode_atom_roundtrip(Config) when is_list(Config) -> AtomData = unicode_atom_data(), verify_atom_data(AtomData), @@ -1456,11 +1461,14 @@ measure_latency_large_message(Nodename, DataFun) -> Echo = spawn(N, fun F() -> receive {From, Msg} -> From ! Msg, F() end end), - case erlang:system_info(build_type) of - debug -> + BuildType = erlang:system_info(build_type), + WordSize = erlang:system_info(wordsize), + + if + BuildType =/= opt; WordSize =:= 4 -> %% Test 3.2 MB and 32 MB and test the latency difference of sent messages Payloads = [{I, <<0:(I * 32 * 1024 * 8)>>} || I <- [1,10]]; - _ -> + true -> %% Test 32 MB and 320 MB and test the latency difference of sent messages Payloads = [{I, <<0:(I * 32 * 1024 * 1024 * 8)>>} || I <- [1,10]] end, @@ -1475,7 +1483,7 @@ measure_latency_large_message(Nodename, DataFun) -> stop_node(N), case {lists:max(Times), lists:min(Times)} of - {Max, Min} when Max * 0.25 > Min -> + {Max, Min} when Max * 0.25 > Min, BuildType =:= opt -> ct:fail({incorrect_latency, IndexTimes}); _ -> ok @@ -1500,13 +1508,19 @@ measure_latency(DataFun, Dropper, Echo, Payload) -> ok end || _ <- lists:seq(1,10)], - {TS, _} = + {TS, Times} = timer:tc(fun() -> [begin + T0 = erlang:monotonic_time(), Echo ! {self(), hello}, - receive hello -> ok end + receive hello -> ok end, + (erlang:monotonic_time() - T0) / 1000000 end || _ <- lists:seq(1,100)] end), + Avg = lists:sum(Times) / length(Times), + StdDev = math:sqrt(lists:sum([math:pow(V - Avg,2) || V <- Times]) / length(Times)), + ct:pal("Times: Avg: ~p Max: ~p Min: ~p Var: ~p", + [Avg, lists:max(Times), lists:min(Times), StdDev]), [begin Sender ! die, receive @@ -1524,6 +1538,144 @@ flush() -> ok end. +system_limit(Config) when is_list(Config) -> + case erlang:system_info(wordsize) of + 8 -> + case proplists:get_value(system_total_memory, + memsup:get_system_memory_data()) of + Memory when is_integer(Memory), + Memory > 6*1024*1024*1024 -> + test_system_limit(Config), + garbage_collect(), + ok; + _ -> + {skipped, "Not enough memory on this machine"} + end; + 4 -> + {skipped, "Only interesting on 64-bit builds"} + end. + +test_system_limit(Config) when is_list(Config) -> + Bits = ((1 bsl 32)+1)*8, + HugeBin = <<0:Bits>>, + HugeListBin = [lists:duplicate(2000000,2000000), HugeBin], + {ok, N1} = start_node(Config), + monitor_node(N1, true), + receive + {nodedown, N1} -> + ct:fail({unexpected_nodedown, N1}) + after 0 -> + ok + end, + P1 = spawn(N1, + fun () -> + receive after infinity -> ok end + end), + + io:format("~n** distributed send **~n~n", []), + try + P1 ! HugeBin, + exit(oops1) + catch + error:system_limit -> ok + end, + try + P1 ! HugeListBin, + exit(oops2) + catch + error:system_limit -> ok + end, + + io:format("~n** distributed exit **~n~n", []), + try + exit(P1, HugeBin), + exit(oops3) + catch + error:system_limit -> ok + end, + try + exit(P1, HugeListBin), + exit(oops4) + catch + error:system_limit -> ok + end, + + io:format("~n** distributed registered send **~n~n", []), + try + {missing_proc, N1} ! HugeBin, + exit(oops5) + catch + error:system_limit -> ok + end, + try + {missing_proc, N1} ! HugeListBin, + exit(oops6) + catch + error:system_limit -> ok + end, + receive + {nodedown, N1} -> + ct:fail({unexpected_nodedown, N1}) + after 0 -> + ok + end, + + %% + %% system_limit in exit reasons brings the + %% connection down... + %% + + io:format("~n** distributed link exit **~n~n", []), + spawn(fun () -> + link(P1), + exit(HugeBin) + end), + receive {nodedown, N1} -> ok end, + + {ok, N2} = start_node(Config), + monitor_node(N2, true), + P2 = spawn(N2, + fun () -> + receive after infinity -> ok end + end), + spawn(fun () -> + link(P2), + exit(HugeListBin) + end), + receive {nodedown, N2} -> ok end, + + io:format("~n** distributed monitor down **~n~n", []), + {ok, N3} = start_node(Config), + monitor_node(N3, true), + Go1 = make_ref(), + LP1 = spawn(fun () -> + receive Go1 -> ok end, + exit(HugeBin) + end), + _ = spawn(N3, + fun () -> + _ = erlang:monitor(process, LP1), + LP1 ! Go1, + receive after infinity -> ok end + end), + receive {nodedown, N3} -> ok end, + + {ok, N4} = start_node(Config), + monitor_node(N4, true), + Go2 = make_ref(), + LP2 = spawn(fun () -> + receive Go2 -> ok end, + exit(HugeListBin) + end), + _ = spawn(N4, + fun () -> + _ = erlang:monitor(process, LP2), + LP2 ! Go2, + receive after infinity -> ok end + end), + receive {nodedown, N4} -> ok end, + ok. + -define(COOKIE, ''). -define(DOP_LINK, 1). -define(DOP_SEND, 2). @@ -2845,3 +2997,23 @@ uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 -> Uint band 16#ff; uint8(Uint) -> exit({badarg, uint8, [Uint]}). + +free_memory() -> + %% Free memory in MB. + try + SMD = memsup:get_system_memory_data(), + {value, {free_memory, Free}} = lists:keysearch(free_memory, 1, SMD), + TotFree = (Free + + case lists:keysearch(cached_memory, 1, SMD) of + {value, {cached_memory, Cached}} -> Cached; + false -> 0 + end + + case lists:keysearch(buffered_memory, 1, SMD) of + {value, {buffered_memory, Buffed}} -> Buffed; + false -> 0 + end), + TotFree div (1024*1024) + catch + error : undef -> + ct:fail({"os_mon not built"}) + end. diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index bb0f3498ab..f6d7c55017 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -120,29 +120,6 @@ -define(heap_binary_size, 64). -init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> - CIOD = rpc(Config, - fun() -> - case catch erts_debug:get_internal_state(available_internal_state) of - true -> ok; - _ -> erts_debug:set_internal_state(available_internal_state, true) - end, - erts_debug:get_internal_state(check_io_debug) - end), - erlang:display({init_per_testcase, Case}), - 0 = element(1, CIOD), - [{testcase, Case}|Config]. - -end_per_testcase(Case, Config) -> - erlang:display({end_per_testcase, Case}), - CIOD = rpc(Config, - fun() -> - get_stable_check_io_info(), - erts_debug:get_internal_state(check_io_debug) - end), - 0 = element(1, CIOD), - ok. - suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {minutes, 1}}]. @@ -219,6 +196,48 @@ end_per_group(_GroupName, Config) -> end, Config. +init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> + CIOD = rpc(Config, + fun() -> + case catch erts_debug:get_internal_state(available_internal_state) of + true -> ok; + _ -> erts_debug:set_internal_state(available_internal_state, true) + end, + erts_debug:get_internal_state(check_io_debug) + end), + erlang:display({init_per_testcase, Case}), + 0 = element(1, CIOD), + [{testcase, Case}|Config]. + +end_per_testcase(Case, Config) -> + erlang:display({end_per_testcase, Case}), + try rpc(Config, fun() -> + get_stable_check_io_info(), + erts_debug:get_internal_state(check_io_debug) + end) of + CIOD -> + 0 = element(1, CIOD) + catch _E:_R:_ST -> + %% Logs some info about the system + ct_os_cmd("epmd -names"), + ct_os_cmd("ps aux"), + %% Restart the node + case proplists:get_value(node, Config) of + undefined -> + ok; + Node -> + timer:sleep(1000), %% Give the node time to die + [NodeName, _] = string:lexemes(atom_to_list(Node),"@"), + {ok, Node} = start_node_final( + list_to_atom(NodeName), + proplists:get_value(node_args, Config)) + end + end, + ok. + +ct_os_cmd(Cmd) -> + ct:log("~s: ~s",[Cmd,os:cmd(Cmd)]). + %% Test sending bad types to port with an outputv-capable driver. outputv_errors(Config) when is_list(Config) -> Path = proplists:get_value(data_dir, Config), @@ -998,7 +1017,9 @@ chkio_test({erts_poll_info, Before}, During = get_check_io_total(erlang:system_info(check_io)), erlang:display(During), - 0 = element(1, erts_debug:get_internal_state(check_io_debug)), + [0 = element(1, erts_debug:get_internal_state(check_io_debug)) || + %% The pollset is not stable when running the fallback testcase + Test /= ?CHKIO_USE_FALLBACK_POLLSET], io:format("During test: ~p~n", [During]), chk_chkio_port(Port), case erlang:port_control(Port, ?CHKIO_STOP, "") of @@ -2642,7 +2663,6 @@ start_node(Config) when is_list(Config) -> start_node(Name) -> start_node(Name, ""). start_node(NodeName, Args) -> - Pa = filename:dirname(code:which(?MODULE)), Name = list_to_atom(atom_to_list(?MODULE) ++ "-" ++ atom_to_list(NodeName) @@ -2650,7 +2670,17 @@ start_node(NodeName, Args) -> ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), - test_server:start_node(Name, slave, [{args, Args ++ " -pa "++Pa}]). + start_node_final(Name, Args). +start_node_final(Name, Args) -> + {ok, Pwd} = file:get_cwd(), + FinalArgs = [Args, " -pa ", filename:dirname(code:which(?MODULE))], + {ok, Node} = test_server:start_node(Name, slave, [{args, FinalArgs}]), + LogPath = Pwd ++ "/error_log." ++ atom_to_list(Name), + ct:pal("Logging to: ~s", [LogPath]), + rpc:call(Node, logger, add_handler, [file_handler, logger_std_h, + #{formatter => {logger_formatter,#{ single_line => false }}, + config => #{file => LogPath }}]), + {ok, Node}. stop_node(Node) -> test_server:stop_node(Node). diff --git a/erts/emulator/test/dump_SUITE.erl b/erts/emulator/test/dump_SUITE.erl index 3b860ebdf6..9f8ac42fa9 100644 --- a/erts/emulator/test/dump_SUITE.erl +++ b/erts/emulator/test/dump_SUITE.erl @@ -137,26 +137,43 @@ exiting_dump(Config) when is_list(Config) -> free_dump(Config) when is_list(Config) -> Dump = filename:join(proplists:get_value(priv_dir, Config),"signal_abort.dump"), - {ok, Node} = start_node(Config), - - Self = self(), - - Pid = spawn_link(Node, - fun() -> - Self ! ready, - receive - ok -> - unlink(Self), - exit(lists:duplicate(1000,1000)) - end - end), + {ok, NodeA} = start_node(Config), + {ok, NodeB} = start_node(Config), - true = rpc:call(Node, os, putenv, ["ERL_CRASH_DUMP",Dump]), - [erlang:monitor(process, Pid) || _ <- lists:seq(1,10000)], - receive ready -> unlink(Pid), Pid ! ok end, + Self = self(), - rpc:call(Node, erlang, halt, ["dump"]), + PidA = spawn_link( + NodeA, + fun() -> + Self ! ready, + receive + ok -> + spawn(fun() -> + erlang:system_monitor(self(), [busy_dist_port]), + timer:sleep(5), + receive + M -> + io:format("~p",[M]), + erlang:halt("dump") + end + end), + exit(lists:duplicate(1000000,100)) + end + end), + + spawn_link(NodeB, + fun() -> + [erlang:monitor(process, PidA) || _ <- lists:seq(1,10000)], + Self ! done, + receive _ -> ok end + end), + + receive done -> ok end, + true = rpc:call(NodeA, os, putenv, ["ERL_CRASH_DUMP",Dump]), + ct:pal("~p",[rpc:call(NodeA, distribution_SUITE, make_busy, [NodeB, 1000])]), + + receive ready -> unlink(PidA), PidA ! ok end, {ok, Bin} = get_dump_when_done(Dump), diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl index 55c5343739..045b351e02 100644 --- a/erts/emulator/test/efile_SUITE.erl +++ b/erts/emulator/test/efile_SUITE.erl @@ -105,34 +105,27 @@ open_files(Name) -> %% a /proc directory), let's read some zero sized files 500 times each, while %% ensuring that response isn't empty << >> proc_zero_sized_files(Config) when is_list(Config) -> - {Type, Flavor} = os:type(), - %% Some files which exist on Linux but might be missing on other systems - Inputs = ["/proc/cpuinfo", - "/proc/meminfo", - "/proc/partitions", - "/proc/swaps", - "/proc/version", - "/proc/uptime", - %% curproc is present on freebsd - "/proc/curproc/cmdline"], - case filelib:is_dir("/proc") of - false -> {skip, "/proc not found"}; % skip the test if no /proc - _ when Type =:= unix andalso Flavor =:= sunos -> - %% SunOS has a /proc, but no zero sized special files - {skip, "sunos does not have any zero sized special files"}; - true -> - %% Take away files which do not exist in proc - Inputs1 = lists:filter(fun filelib:is_file/1, Inputs), - - %% Fail if none of mentioned files exist in /proc, did we just get - %% a normal /proc directory without any special files? - ?assertNotEqual([], Inputs1), - + TestFiles0 = [%% Some files which exist on Linux but might be missing on + %% other systems + "/proc/cpuinfo", + "/proc/meminfo", + "/proc/partitions", + "/proc/swaps", + "/proc/version", + "/proc/uptime", + %% curproc is present on FreeBSD + "/proc/curproc/cmdline"], + + TestFiles = [F || F <- TestFiles0, filelib:is_file(F)], + + case TestFiles of + [_|_] -> %% For 6 inputs and 500 attempts each this do run anywhere %% between 500 and 3000 function calls. - lists:foreach( - fun(Filename) -> do_proc_zero_sized(Filename, 500) end, - Inputs1) + [do_proc_zero_sized(F, 500) || F <- TestFiles], + ok; + [] -> + {skip, "Failed to find any known zero-sized files"} end. %% @doc Test one file N times to also trigger possible leaking fds and memory diff --git a/erts/emulator/test/emulator_bench.spec b/erts/emulator/test/emulator_bench.spec index 2a180b440c..03638bfa23 100644 --- a/erts/emulator/test/emulator_bench.spec +++ b/erts/emulator/test/emulator_bench.spec @@ -1,2 +1,3 @@ {groups,"../emulator_test",estone_SUITE,[estone_bench]}. {groups,"../emulator_test",binary_SUITE,[iolist_size_benchmarks]}. +{groups,"../emulator_test",erts_debug_SUITE,[interpreter_size_bench]}. diff --git a/erts/emulator/test/erts_debug_SUITE.erl b/erts/emulator/test/erts_debug_SUITE.erl index f39dbedd8f..6798e3bf25 100644 --- a/erts/emulator/test/erts_debug_SUITE.erl +++ b/erts/emulator/test/erts_debug_SUITE.erl @@ -20,10 +20,12 @@ -module(erts_debug_SUITE). -include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). --export([all/0, suite/0, +-export([all/0, suite/0, groups/0, test_size/1,flat_size_big/1,df/1,term_type/1, - instructions/1, stack_check/1, alloc_blocks_size/1]). + instructions/1, stack_check/1, alloc_blocks_size/1, + interpreter_size_bench/1]). -export([do_alloc_blocks_size/0]). @@ -35,6 +37,15 @@ all() -> [test_size, flat_size_big, df, instructions, term_type, stack_check, alloc_blocks_size]. +groups() -> + [{interpreter_size_bench, [], [interpreter_size_bench]}]. + +interpreter_size_bench(_Config) -> + Size = erts_debug:interpreter_size(), + ct_event:notify(#event{name=benchmark_data, + data=[{value,Size}]}), + {comment,integer_to_list(Size)++" bytes"}. + test_size(Config) when is_list(Config) -> ConsCell1 = id([a|b]), ConsCell2 = id(ConsCell1), diff --git a/erts/emulator/test/erts_test_utils.erl b/erts/emulator/test/erts_test_utils.erl index e4e00a0a16..9c9eaa70ed 100644 --- a/erts/emulator/test/erts_test_utils.erl +++ b/erts/emulator/test/erts_test_utils.erl @@ -19,7 +19,7 @@ %% -module(erts_test_utils). --compile(r16). +-compile(r20). %% %% THIS MODULE IS ALSO USED BY *OTHER* APPLICATIONS TEST CODE diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl index 7f6caa08f1..2cbde621ce 100644 --- a/erts/emulator/test/fun_SUITE.erl +++ b/erts/emulator/test/fun_SUITE.erl @@ -513,6 +513,8 @@ refc(Config) when is_list(Config) -> Other -> ct:fail({unexpected,Other}) end, process_flag(trap_exit, false), + %% Wait to make sure that the process has terminated completely. + receive after 1 -> ok end, {refc,3} = erlang:fun_info(F1, refc), %% Garbage collect. Only the F2 fun will be left. diff --git a/erts/emulator/test/lcnt_SUITE.erl b/erts/emulator/test/lcnt_SUITE.erl index 87b97037d6..2dbaec9942 100644 --- a/erts/emulator/test/lcnt_SUITE.erl +++ b/erts/emulator/test/lcnt_SUITE.erl @@ -187,5 +187,8 @@ remove_untoggleable_locks([]) -> []; remove_untoggleable_locks([{resource_monitors, _, _, _} | T]) -> remove_untoggleable_locks(T); +remove_untoggleable_locks([{'socket[gcnt]', _, _, _} | T]) -> + %% Global lock used by socket NIF + remove_untoggleable_locks(T); remove_untoggleable_locks([H | T]) -> [H | remove_untoggleable_locks(T)]. diff --git a/erts/emulator/test/net_SUITE.erl b/erts/emulator/test/net_SUITE.erl index 1a973cacb2..6111fc76a5 100644 --- a/erts/emulator/test/net_SUITE.erl +++ b/erts/emulator/test/net_SUITE.erl @@ -127,12 +127,17 @@ api_basic_cases() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init_per_suite(Config) -> - case os:type() of - {win32, _} -> - not_yet_implemented(); - _ -> - %% ?LOGGER:start(), - Config + case lists:member(socket, erlang:loaded()) of + true -> + case os:type() of + {win32, _} -> + not_yet_implemented(); + _ -> + %% ?LOGGER:start(), + Config + end; + false -> + {skip, "esock disabled"} end. end_per_suite(_) -> diff --git a/erts/emulator/test/persistent_term_SUITE.erl b/erts/emulator/test/persistent_term_SUITE.erl index 93eb026ced..c9874e5679 100644 --- a/erts/emulator/test/persistent_term_SUITE.erl +++ b/erts/emulator/test/persistent_term_SUITE.erl @@ -25,7 +25,9 @@ basic/1,purging/1,sharing/1,get_trapping/1, info/1,info_trapping/1,killed_while_trapping/1, off_heap_values/1,keys/1,collisions/1, - init_restart/1]). + init_restart/1, put_erase_trapping/1, + killed_while_trapping_put/1, + killed_while_trapping_erase/1]). %% -export([test_init_restart_cmd/1]). @@ -37,7 +39,8 @@ suite() -> all() -> [basic,purging,sharing,get_trapping,info,info_trapping, killed_while_trapping,off_heap_values,keys,collisions, - init_restart]. + init_restart, put_erase_trapping, killed_while_trapping_put, + killed_while_trapping_erase]. init_per_suite(Config) -> %% Put a term in the dict so that we know that the testcases handle @@ -627,3 +630,69 @@ chk_not_stuck(Term) -> pget({_, Initial}) -> persistent_term:get() -- Initial. + + +killed_while_trapping_put(_Config) -> + erts_debug:set_internal_state(available_internal_state, true), + repeat( + fun() -> + NrOfPutsInChild = 10000, + do_puts(2500, my_value), + Pid = + spawn(fun() -> + do_puts(NrOfPutsInChild, my_value2) + end), + timer:sleep(1), + erlang:exit(Pid, kill), + do_erases(NrOfPutsInChild) + end, + 10), + erts_debug:set_internal_state(available_internal_state, false). + +killed_while_trapping_erase(_Config) -> + erts_debug:set_internal_state(available_internal_state, true), + repeat( + fun() -> + NrOfErases = 2500, + do_puts(NrOfErases, my_value), + Pid = + spawn(fun() -> + do_erases(NrOfErases) + end), + timer:sleep(1), + erlang:exit(Pid, kill), + do_erases(NrOfErases) + end, + 10), + erts_debug:set_internal_state(available_internal_state, false). + +put_erase_trapping(_Config) -> + NrOfItems = 5000, + erts_debug:set_internal_state(available_internal_state, true), + do_puts(NrOfItems, first), + do_puts(NrOfItems, second), + do_erases(NrOfItems), + erts_debug:set_internal_state(available_internal_state, false). + +do_puts(0, _) -> ok; +do_puts(NrOfPuts, ValuePrefix) -> + Key = {?MODULE, NrOfPuts}, + Value = {ValuePrefix, NrOfPuts}, + erts_debug:set_internal_state(reds_left, rand:uniform(250)), + persistent_term:put(Key, Value), + Value = persistent_term:get(Key), + do_puts(NrOfPuts - 1, ValuePrefix). + +do_erases(0) -> ok; +do_erases(NrOfErases) -> + Key = {?MODULE,NrOfErases}, + erts_debug:set_internal_state(reds_left, rand:uniform(500)), + persistent_term:erase(Key), + not_found = persistent_term:get(Key, not_found), + do_erases(NrOfErases - 1). + +repeat(_Fun, 0) -> + ok; +repeat(Fun, N) -> + Fun(), + repeat(Fun, N-1). diff --git a/erts/emulator/test/small_SUITE.erl b/erts/emulator/test/small_SUITE.erl new file mode 100644 index 0000000000..00a02e5560 --- /dev/null +++ b/erts/emulator/test/small_SUITE.erl @@ -0,0 +1,115 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(small_SUITE). + +-export([all/0, suite/0]). +-export([edge_cases/1]). + +-include_lib("common_test/include/ct.hrl"). + +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 1}}]. + +all() -> + [edge_cases]. + +edge_cases(Config) when is_list(Config) -> + {MinSmall, MaxSmall} = Limits = determine_small_limits(0), + ct:pal("Limits = ~p", [Limits]), + + true = (MaxSmall + 1) =:= MaxSmall + id(1), + true = (MinSmall - 1) =:= MinSmall - id(1), + true = (MaxSmall + 1) > id(MaxSmall), + true = (MinSmall - 1) < id(MinSmall), + -1 = MinSmall + id(MaxSmall), + -1 = MaxSmall + id(MinSmall), + + false = is_small(MinSmall * -1), + false = is_small(MinSmall - id(1)), + false = is_small(MinSmall - 1), + false = is_small(MaxSmall + id(1)), + + Lower = lists:seq(MinSmall, MinSmall + 128), + Upper = lists:seq(MaxSmall, MaxSmall - 128, -1), + Pow2 = seq_pow2(MinSmall, MaxSmall), + NearZero = lists:seq(-128, 128), + + ok = test_combinations([Lower, Upper, Pow2, NearZero], MinSmall, MaxSmall), + + ok. + +test_combinations([As | Rest]=TestVectors, MinS, MaxS) -> + [begin + _ = [arith_test(A, B, MinS, MaxS) || B <- Bs] + end || A <- As, Bs <- TestVectors], + test_combinations(Rest, MinS, MaxS); +test_combinations([], _MinS, _MaxS) -> + ok. + +%% Builds a sequence of all powers of 2 between MinSmall and MaxSmall +seq_pow2(MinSmall, MaxSmall) -> + sp2_1(MinSmall, MinSmall, MaxSmall). + +sp2_1(N, _MinS, MaxS) when N >= MaxS -> + []; +sp2_1(-1, MinS, MaxS) -> + [-1 | sp2_1(1, MinS, MaxS)]; +sp2_1(N, MinS, MaxS) when N < 0 -> + [N | sp2_1(N bsr 1, MinS, MaxS)]; +sp2_1(N, MinS, MaxS) when N > 0 -> + [N | sp2_1(N bsl 1, MinS, MaxS)]. + +arith_test(A, B, MinS, MaxS) -> + verify_kind(A + B, MinS, MaxS), + verify_kind(B + A, MinS, MaxS), + verify_kind(A - B, MinS, MaxS), + verify_kind(B - A, MinS, MaxS), + verify_kind(A * B, MinS, MaxS), + verify_kind(B * A, MinS, MaxS), + + true = A + B =:= apply(erlang, id('+'), [A, B]), + true = A - B =:= apply(erlang, id('-'), [A, B]), + true = A * B =:= apply(erlang, id('*'), [A, B]), + + true = A + B =:= B + id(A), + true = A - B =:= A + id(-B), + true = B - A =:= B + id(-A), + true = A * B =:= B * id(A), + + true = B =:= 0 orelse ((A * B) div id(B) =:= A), + true = A =:= 0 orelse ((B * A) div id(A) =:= B), + + ok. + +%% Verifies that N is a small when it should be +verify_kind(N, MinS, MaxS) -> + true = is_small(N) =:= (N >= MinS andalso N =< MaxS). + +is_small(N) when is_integer(N) -> + 0 =:= erts_debug:flat_size(N). + +determine_small_limits(N) -> + case is_small(-1 bsl N) of + true -> determine_small_limits(N + 1); + false -> {-1 bsl (N - 1), (1 bsl (N - 1)) - 1} + end. + +id(I) -> I. diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl index 2e3f40a350..e3545ccbf9 100644 --- a/erts/emulator/test/socket_SUITE.erl +++ b/erts/emulator/test/socket_SUITE.erl @@ -28,10 +28,14 @@ %% ESOCK_TEST_TRAFFIC: include %% ESOCK_TEST_TTEST: exclude %% +%% Variable that controls "verbosity" of the test case(s): +%% +%% ESOCK_TEST_QUIET: true (default) | false +%% %% Defines the runtime of the ttest cases %% (This is the time during which "measurement" is performed. %% the actual time it takes for the test case to complete -%% will be longer) +%% will be longer; setup, completion, ...) %% %% ESOCK_TEST_TTEST_RUNTIME: 10 seconds %% Format of values: <integer>[<unit>] @@ -1381,22 +1385,27 @@ ttest_ssockt_csockt_cases() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init_per_suite(Config) -> - case os:type() of - {win32, _} -> - not_yet_implemented(); - _ -> - case quiet_mode(Config) of - default -> - ?LOGGER:start(), - Config; - Quiet -> - ?LOGGER:start(Quiet), - [{esock_test_quiet, Quiet}|Config] - end + case lists:member(socket, erlang:loaded()) of + true -> + case os:type() of + {win32, _} -> + (catch not_yet_implemented()); + _ -> + case quiet_mode(Config) of + default -> + ?LOGGER:start(), + Config; + Quiet -> + ?LOGGER:start(Quiet), + [{esock_test_quiet, Quiet}|Config] + end + end; + false -> + {skip, "esock disabled"} end. end_per_suite(_) -> - ?LOGGER:stop(), + (catch ?LOGGER:stop()), ok. @@ -1643,6 +1652,8 @@ api_b_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) -> tc_try(api_b_sendmsg_and_recvmsg_udp4, fun() -> Send = fun(Sock, Data, Dest) -> + %% We need tests for this, + %% but this is not the place it. %% CMsgHdr = #{level => ip, %% type => tos, %% data => reliability}, @@ -1653,9 +1664,12 @@ api_b_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) -> socket:sendmsg(Sock, MsgHdr) end, Recv = fun(Sock) -> + %% We have some issues on old darwing... + socket:setopt(Sock, otp, debug, true), case socket:recvmsg(Sock) of {ok, #{addr := Source, iov := [Data]}} -> + socket:setopt(Sock, otp, debug, false), {ok, {Source, Data}}; {error, _} = ERROR -> ERROR @@ -1714,21 +1728,37 @@ api_b_send_and_recv_udp(InitState) -> end}, #{desc => "send req (to dst)", cmd => fun(#{sock_src := Sock, sa_dst := Dst, send := Send}) -> - ok = Send(Sock, ?BASIC_REQ, Dst) + Send(Sock, ?BASIC_REQ, Dst) end}, #{desc => "recv req (from src)", cmd => fun(#{sock_dst := Sock, sa_src := Src, recv := Recv}) -> - {ok, {Src, ?BASIC_REQ}} = Recv(Sock), - ok + case Recv(Sock) of + {ok, {Src, ?BASIC_REQ}} -> + ok; + {ok, UnexpData} -> + {error, {unexpected_data, UnexpData}}; + {error, _} = ERROR -> + %% At the moment there is no way to get + %% status or state for the socket... + ERROR + end end}, #{desc => "send rep (to src)", cmd => fun(#{sock_dst := Sock, sa_src := Src, send := Send}) -> - ok = Send(Sock, ?BASIC_REP, Src) + Send(Sock, ?BASIC_REP, Src) end}, #{desc => "recv rep (from dst)", cmd => fun(#{sock_src := Sock, sa_dst := Dst, recv := Recv}) -> - {ok, {Dst, ?BASIC_REP}} = Recv(Sock), - ok + case Recv(Sock) of + {ok, {Dst, ?BASIC_REP}} -> + ok; + {ok, UnexpData} -> + {error, {unexpected_data, UnexpData}}; + {error, _} = ERROR -> + %% At the moment there is no way to get + %% status or state for the socket... + ERROR + end end}, #{desc => "close src socket", cmd => fun(#{sock_src := Sock}) -> @@ -3585,8 +3615,8 @@ api_to_connect_tcp(InitState) -> ?SEV_IPRINT("client node ~p started", [Node]), {ok, State#{node => Node}}; - {error, Reason, _} -> - {error, Reason} + {error, Reason} -> + {skip, Reason} end end}, #{desc => "monitor client node", @@ -3921,7 +3951,7 @@ api_to_connect_tcp_await_timeout2(_ID, To, ServerSA, NewSock) -> case socket:connect(Sock, ServerSA, To) of {error, timeout} -> Stop = t(), - TDiff = tdiff(Start, Stop), + TDiff = Stop - Start, if (TDiff >= To) -> (catch socket:close(Sock)), @@ -4033,7 +4063,7 @@ api_to_accept_tcp(InitState) -> end}, #{desc => "validate timeout time", cmd => fun(#{start := Start, stop := Stop, timeout := To} = _State) -> - TDiff = tdiff(Start, Stop), + TDiff = Stop - Start, if (TDiff >= To) -> ok; @@ -4169,7 +4199,7 @@ api_to_maccept_tcp(InitState) -> end}, #{desc => "validate timeout time", cmd => fun(#{start := Start, stop := Stop, timeout := To} = _State) -> - TDiff = tdiff(Start, Stop), + TDiff = Stop - Start, if (TDiff >= To) -> ok; @@ -4242,7 +4272,7 @@ api_to_maccept_tcp(InitState) -> end}, #{desc => "validate timeout time", cmd => fun(#{start := Start, stop := Stop, timeout := To} = State) -> - TDiff = tdiff(Start, Stop), + TDiff = Stop - Start, if (TDiff >= To) -> State1 = maps:remove(start, State), @@ -4693,7 +4723,7 @@ api_to_receive_tcp(InitState) -> end}, #{desc => "validate timeout time", cmd => fun(#{start := Start, stop := Stop, timeout := To} = State) -> - TDiff = tdiff(Start, Stop), + TDiff = Stop - Start, if (TDiff >= To) -> State1 = maps:remove(start, State), @@ -5000,7 +5030,8 @@ api_to_receive_udp(InitState) -> Start = t(), case Recv(Sock, To) of {error, timeout} -> - {ok, State#{start => Start, stop => t()}}; + {ok, State#{start => Start, + stop => t()}}; {ok, _} -> {error, unexpected_sucsess}; {error, _} = ERROR -> @@ -5009,7 +5040,7 @@ api_to_receive_udp(InitState) -> end}, #{desc => "validate timeout time", cmd => fun(#{start := Start, stop := Stop, timeout := To} = _State) -> - TDiff = tdiff(Start, Stop), + TDiff = Stop - Start, if (TDiff >= To) -> ok; @@ -5021,7 +5052,7 @@ api_to_receive_udp(InitState) -> %% *** Termination *** #{desc => "close socket", cmd => fun(#{sock := Sock} = _State) -> - socket:setopt(Sock, otp, debug, true), + %% socket:setopt(Sock, otp, debug, true), sock_close(Sock), ok end}, @@ -5591,7 +5622,7 @@ sc_lc_receive_response_tcp(InitState) -> State1 = maps:remove(sock, State), {ok, State1}; {error, Reason} = ERROR -> - ?SEV_EPRINT("Unexpected read faulure: " + ?SEV_EPRINT("Unexpected read failure: " "~n ~p", [Reason]), ERROR end @@ -7218,8 +7249,8 @@ sc_rc_receive_response_tcp(InitState) -> {ok, Node} -> ?SEV_IPRINT("client node ~p started", [Node]), {ok, State#{node => Node}}; - {error, Reason, _} -> - {error, Reason} + {error, Reason} -> + {skip, Reason} end end}, #{desc => "monitor client node 1", @@ -8095,8 +8126,8 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> ?SEV_IPRINT("client node ~p started", [Node]), {ok, State#{node => Node}}; - {error, Reason, _} -> - {error, Reason} + {error, Reason} -> + {skip, Reason} end end}, #{desc => "monitor client node", @@ -8987,6 +9018,7 @@ traffic_send_and_recv_chunks_tcp(InitState) -> end}, #{desc => "recv (one big)", cmd => fun(#{tester := Tester, csock := Sock, size := Size} = _State) -> + %% socket:setopt(Sock, otp, debug, true), case socket:recv(Sock, Size) of {ok, Data} -> ?SEV_ANNOUNCE_READY(Tester, @@ -9045,8 +9077,8 @@ traffic_send_and_recv_chunks_tcp(InitState) -> ?SEV_IPRINT("(remote) client node ~p started", [Node]), {ok, State#{node => Node}}; - {error, Reason, _} -> - {error, Reason} + {error, Reason} -> + {skip, Reason} end end}, #{desc => "monitor client node", @@ -10169,7 +10201,7 @@ traffic_ping_pong_small_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) Num = ?TPP_SMALL_NUM, tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_udp4, fun() -> - ?TT(?SECS(20)), + ?TT(?SECS(60)), InitState = #{domain => inet, msg => Msg, num => Num}, @@ -10196,7 +10228,7 @@ traffic_ping_pong_small_sendmsg_and_recvmsg_udp6(_Config) when is_list(_Config) tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_udp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(20)), + ?TT(?SECS(30)), InitState = #{domain => inet, msg => Msg, num => Num}, @@ -10523,8 +10555,8 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> ?SEV_IPRINT("(remote) client node ~p started", [Node]), {ok, State#{node => Node}}; - {error, Reason, _} -> - {error, Reason} + {error, Reason} -> + {skip, Reason} end end}, #{desc => "monitor client node", @@ -11044,7 +11076,7 @@ tpp_tcp_client_msg_exchange_loop(Sock, _Send, _Recv, _Msg, end; tpp_tcp_client_msg_exchange_loop(Sock, Send, Recv, Data, Num, N, Sent, Received, Start) -> - %% d("tpp_tcp_client_msg_exchange_loop(~w,~w) try send", [Num,N]), + %% d("tpp_tcp_client_msg_exchange_loop(~w,~w) try send ~w", [Num,N,size(Data)]), case tpp_tcp_send_req(Sock, Send, Data) of {ok, SendSz} -> %% d("tpp_tcp_client_msg_exchange_loop(~w,~w) sent - " @@ -11057,11 +11089,13 @@ tpp_tcp_client_msg_exchange_loop(Sock, Send, Recv, Data, Received+RecvSz, Start); {error, RReason} -> - ?SEV_EPRINT("recv (~w of ~w): ~p", [N, Num, RReason]), + ?SEV_EPRINT("recv (~w of ~w): ~p: " + "~n ~p", [N, Num, RReason, mq()]), exit({recv, RReason, N}) end; {error, SReason} -> - ?SEV_EPRINT("send (~w of ~w): ~p", [N, Num, SReason]), + ?SEV_EPRINT("send (~w of ~w): ~p" + "~n ~p", [N, Num, SReason, mq()]), exit({send, SReason, N}) end. @@ -11121,7 +11155,7 @@ tpp_tcp_recv(Sock, Recv, Tag) -> tpp_tcp_recv(Sock, Recv, Tag, Remains, size(Msg), [Data]); {ok, <<Tag:32/integer, _/binary>>} -> {error, {invalid_msg_tag, Tag}}; - {error, _} = ERROR -> + {error, _R} = ERROR -> ERROR end. @@ -11135,7 +11169,7 @@ tpp_tcp_recv(Sock, Recv, Tag, Remaining, AccSz, Acc) -> tpp_tcp_recv(Sock, Recv, Tag, Remaining - size(Data), AccSz + size(Data), [Data | Acc]); - {error, _} = ERROR -> + {error, _R} = ERROR -> ERROR end. @@ -11173,6 +11207,14 @@ tpp_tcp_send_msg(Sock, Send, Msg, AccSz) when is_binary(Msg) -> %% size_of_iovec([B|IOVec], Sz) -> %% size_of_iovec(IOVec, Sz+size(B)). +mq() -> + mq(self()). + +mq(Pid) when is_pid(Pid) -> + Tag = messages, + {Tag, Msgs} = process_info(Pid, Tag), + Msgs. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -11198,7 +11240,7 @@ traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) -> MsgHdr = #{addr => Dest, iov => Data}, socket:sendmsg(Sock, MsgHdr) end, - Recv = fun(Sock, Sz) -> + Recv = fun(Sock, Sz) -> case socket:recvmsg(Sock, Sz, 0) of {ok, #{addr := Source, iov := [Data]}} -> @@ -11329,7 +11371,9 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> [{handler, Handler}]) end}, #{desc => "order handler to recv", - cmd => fun(#{handler := Handler} = _State) -> + cmd => fun(#{handler := Handler, + sock := _Sock} = _State) -> + %% socket:setopt(Sock, otp, debug, true), ?SEV_ANNOUNCE_CONTINUE(Handler, recv), ok end}, @@ -11425,8 +11469,8 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> ?SEV_IPRINT("(remote) client node ~p started", [Node]), {ok, State#{node => Node}}; - {error, Reason, _} -> - {error, Reason} + {error, Reason} -> + {skip, Reason} end end}, #{desc => "monitor client node", @@ -17272,8 +17316,8 @@ ttest_tcp(InitState) -> case start_node(Host, server) of {ok, Node} -> {ok, State#{node => Node}}; - {error, Reason, _} -> - {error, Reason} + {error, Reason} -> + {skip, Reason} end end}, #{desc => "monitor server node", @@ -17369,8 +17413,8 @@ ttest_tcp(InitState) -> case start_node(Host, client) of {ok, Node} -> {ok, State#{node => Node}}; - {error, Reason, _} -> - {error, Reason} + {error, Reason} -> + {skip, Reason} end end}, #{desc => "monitor client node", @@ -17686,7 +17730,28 @@ ttest_tcp_client_start(Node, %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This mechanism has only one purpose: So that we are able to kill +%% the node-starter process if it takes to long. The node-starter +%% runs on the local node. +%% This crapola is hopefully temporary, but we have seen that on +%% some platforms the ct_slave:start simply hangs. +-define(NODE_START_TIMEOUT, 10000). start_node(Host, NodeName) -> + start_node(Host, NodeName, ?NODE_START_TIMEOUT). + +start_node(Host, NodeName, Timeout) -> + {NodeStarter, _} = + spawn_monitor(fun() -> exit(start_unique_node(Host, NodeName)) end), + receive + {'DOWN', _, process, NodeStarter, Result} -> + %% i("Node Starter (~p) reported: ~p", [NodeStarter, Result]), + Result + after Timeout -> + exit(NodeStarter, kill), + {error, {failed_starting_node, NodeName, timeout}} + end. + +start_unique_node(Host, NodeName) -> UniqueNodeName = f("~w_~w", [NodeName, erlang:system_time(millisecond)]), case do_start_node(Host, UniqueNodeName) of {ok, _} = OK -> @@ -17720,7 +17785,7 @@ stop_node(Node) -> ERROR end. - + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -17877,9 +17942,15 @@ which_addr2(Domain, [_|IFO]) -> %% Here are all the *general* test vase condition functions. %% The idea is that this function shall test if the test host has -%% support for IPv6. If not there is no point in running IPv6 tests. +%% support for IPv6. If not, there is no point in running IPv6 tests. %% Currently we just skip. has_support_ipv6() -> + %% case socket:supports(ipv6) of + %% true -> + %% ok; + %% false -> + %% {error, not_supported} + %% end. not_yet_implemented(). @@ -17896,8 +17967,10 @@ skip(Reason) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% t() -> - os:timestamp(). + ts(ms). +ts(ms) -> + erlang:monotonic_time(milli_seconds). tdiff({A1, B1, C1} = _T1x, {A2, B2, C2} = _T2x) -> T1 = A1*1000000000+B1*1000+(C1 div 1000), @@ -17930,11 +18003,15 @@ set_tc_name(N) when is_list(N) -> %% get(tc_name). tc_begin(TC) -> + OldVal = process_flag(trap_exit, true), + put(old_trap_exit, OldVal), set_tc_name(TC), tc_print("begin ***", "~n----------------------------------------------------~n", ""). tc_end(Result) when is_list(Result) -> + OldVal = erase(old_trap_exit), + process_flag(trap_exit, OldVal), tc_print("done: ~s", [Result], "", "----------------------------------------------------~n~n"), ok. @@ -17965,26 +18042,44 @@ tc_try(Case, TCCondFun, TCFun) tc_end("ok") end catch - throw:{skip, _} = SKIP -> - tc_end("skipping"), + C:{skip, _} = SKIP when ((C =:= throw) orelse (C =:= exit)) -> + %% i("catched[tc] (skip): " + %% "~n C: ~p" + %% "~n SKIP: ~p" + %% "~n", [C, SKIP]), + tc_end( f("skipping(catched,~w,tc)", [C]) ), SKIP; - Class:Error:Stack -> - tc_end("failed"), - erlang:raise(Class, Error, Stack) + C:E:S -> + %% i("catched[tc]: " + %% "~n C: ~p" + %% "~n E: ~p" + %% "~n S: ~p" + %% "~n", [C, E, S]), + tc_end( f("failed(catched,~w,tc)", [C]) ), + erlang:raise(C, E, S) end; {skip, _} = SKIP -> - tc_end("skipping"), + tc_end("skipping(tc)"), SKIP; {error, Reason} -> - tc_end("failed"), + tc_end("failed(tc)"), exit({tc_cond_failed, Reason}) catch - throw:{skip, _} = SKIP -> - tc_end("skipping"), + C:{skip, _} = SKIP when ((C =:= throw) orelse (C =:= exit)) -> + %% i("catched[cond] (skip): " + %% "~n C: ~p" + %% "~n SKIP: ~p" + %% "~n", [C, SKIP]), + tc_end( f("skipping(catched,~w,cond)", [C]) ), SKIP; - Class:Error:Stack -> - tc_end("failed"), - erlang:raise(Class, Error, Stack) + C:E:S -> + %% i("catched[cond]: " + %% "~n C: ~p" + %% "~n E: ~p" + %% "~n S: ~p" + %% "~n", [C, E, S]), + tc_end( f("failed(catched,~w,cond)", [C]) ), + erlang:raise(C, E, S) end. diff --git a/erts/emulator/test/socket_test_evaluator.erl b/erts/emulator/test/socket_test_evaluator.erl index c5748ac21b..694f0d5f1e 100644 --- a/erts/emulator/test/socket_test_evaluator.erl +++ b/erts/emulator/test/socket_test_evaluator.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -106,12 +106,13 @@ start(Name, Seq, InitState) InitState2 = InitState#{parent => self()}, Pid = erlang:spawn_link( fun() -> init(Name, Seq, InitState2) end), - MRef = erlang:monitor(process, Pid), - #ev{name = Name, pid = Pid, mref = MRef} + %% MRef = erlang:monitor(process, Pid), + #ev{name = Name, pid = Pid}%, mref = MRef} end. init(Name, Seq, Init) -> put(sname, Name), + process_flag(trap_exit, true), loop(1, Seq, Init). loop(_ID, [], FinalState) -> @@ -125,21 +126,26 @@ loop(ID, [#{desc := Desc, {ok, NewState} -> loop(ID + 1, Cmds, NewState); {skip, Reason} -> + ?SEV_IPRINT("command ~w skip: " + "~n ~p", [ID, Reason]), exit({skip, Reason}); {error, Reason} -> - eprint("command ~w failed: " - "~n Reason: ~p", [ID, Reason]), + ?SEV_EPRINT("command ~w failed: " + "~n ~p", [ID, Reason]), exit({command_failed, ID, Reason, State}) catch - throw:{skip, R} = E:_ -> - eprint("command ~w skip: " - "~n Skip Reason: ~p", [ID, R]), + C:{skip, command} = E:_ when ((C =:= throw) orelse (C =:= exit)) -> + %% Secondary skip + exit(E); + C:{skip, R} = E:_ when ((C =:= throw) orelse (C =:= exit)) -> + ?SEV_IPRINT("command ~w skip catched(~w): " + "~n Reason: ~p", [ID, C, R]), exit(E); C:E:S -> - eprint("command ~w crashed: " - "~n Class: ~p" - "~n Error: ~p" - "~n Call Stack: ~p", [ID, C, E, S]), + ?SEV_EPRINT("command ~w crashed: " + "~n Class: ~p" + "~n Error: ~p" + "~n Call Stack: ~p", [ID, C, E, S]), exit({command_crashed, ID, {C,E,S}, State}) end. @@ -168,18 +174,32 @@ await_finish(Evs, OK, Fails) -> {Evs2, OK2, Fails2} = await_finish_normal(Pid, Evs, OK, Fails), await_finish(Evs2, OK2, Fails2); - %% The evaluator can skip the teat case: + %% The evaluator can skip the test case: {'DOWN', _MRef, process, Pid, {skip, Reason}} -> + %% ?SEV_IPRINT("await_finish -> skip (down) received: " + %% "~n Pid: ~p" + %% "~n Reason: ~p", [Pid, Reason]), await_finish_skip(Pid, Reason, Evs, OK); {'EXIT', Pid, {skip, Reason}} -> + %% ?SEV_IPRINT("await_finish -> skip (exit) received: " + %% "~n Pid: ~p" + %% "~n Reason: ~p", [Pid, Reason]), await_finish_skip(Pid, Reason, Evs, OK); %% Evaluator failed {'DOWN', _MRef, process, Pid, Reason} -> - {Evs2, OK2, Fails2} = await_finish_fail(Pid, Reason, Evs, OK, Fails), + %% ?SEV_IPRINT("await_finish -> fail (down) received: " + %% "~n Pid: ~p" + %% "~n Reason: ~p", [Pid, Reason]), + {Evs2, OK2, Fails2} = + await_finish_fail(Pid, Reason, Evs, OK, Fails), await_finish(Evs2, OK2, Fails2); {'EXIT', Pid, Reason} -> - {Evs2, OK2, Fails2} = await_finish_fail(Pid, Reason, Evs, OK, Fails), + %% ?SEV_IPRINT("await_finish -> fail (exit) received: " + %% "~n Pid: ~p" + %% "~n Reason: ~p", [Pid, Reason]), + {Evs2, OK2, Fails2} = + await_finish_fail(Pid, Reason, Evs, OK, Fails), await_finish(Evs2, OK2, Fails2) end. @@ -202,22 +222,83 @@ await_finish_normal(Pid, Evs, OK, Fails) -> end. await_finish_skip(Pid, Reason, Evs, OK) -> - case lists:keysearch(Pid, #ev.pid, Evs) of - {value, #ev{name = Name}} -> - iprint("evaluator '~s' (~p) issued SKIP: " - "~n ~p", [Name, Pid, Reason]); - false -> - case lists:member(Pid, OK) of - true -> - ok; - false -> - iprint("unknown process ~p issued SKIP: " - "~n ~p", [Pid, Reason]) - end - end, + Evs2 = + case lists:keysearch(Pid, #ev.pid, Evs) of + {value, #ev{name = Name}} -> + ?SEV_IPRINT("evaluator '~s' (~p) issued SKIP: " + "~n ~p", [Name, Pid, Reason]), + lists:keydelete(Pid, #ev.pid, Evs); + false -> + case lists:member(Pid, OK) of + true -> + ?SEV_IPRINT("already terminated (ok) process ~p skip" + "~n ~p", [Pid]), + ok; + false -> + ?SEV_IPRINT("unknown process ~p issued SKIP: " + "~n ~p", [Pid, Reason]), + iprint("unknown process ~p issued SKIP: " + "~n ~p", [Pid, Reason]) + end, + Evs + end, + await_evs_terminated(Evs2), ?LIB:skip(Reason). +await_evs_terminated(Evs) -> + Instructions = + [ + %% Just wait for the evaluators to die on their own + {fun() -> ?SEV_IPRINT("await (no action) evs termination") end, + fun(_) -> ok end}, + + %% Send them a skip message, causing the evaluators to + %% die with a skip reason. + {fun() -> ?SEV_IPRINT("await (send skip message) evs termination") end, + fun(#ev{pid = Pid}) -> Pid ! skip end}, + %% And if nothing else works, try to kill the remaining evaluators + {fun() -> ?SEV_IPRINT("await (issue exit kill) evs termination") end, + fun(#ev{pid = Pid}) -> exit(Pid, kill) end}], + + await_evs_terminated(Evs, Instructions). + +await_evs_terminated([], _) -> + ok; +await_evs_terminated(Evs, []) -> + {error, {failed_terminated, [P||#ev{pid=P} <- Evs]}}; +await_evs_terminated(Evs, [{Inform, Command}|Instructions]) -> + Inform(), + lists:foreach(Command, Evs), + RemEvs = await_evs_termination(Evs), + await_evs_terminated(RemEvs, Instructions). + +await_evs_termination(Evs) -> + await_evs_termination(Evs, 2000). + +await_evs_termination([], _Timeout) -> + []; +await_evs_termination(Evs, Timeout) -> + T = t(), + receive + {'DOWN', _MRef, process, Pid, _Reason} -> + %% ?SEV_IPRINT("await_evs_termination -> DOWN: " + %% "~n Pid: ~p" + %% "~n Reason: ~p", [Pid, Reason]), + Evs2 = lists:keydelete(Pid, #ev.pid, Evs), + await_evs_termination(Evs2, tdiff(T, t())); + {'EXIT', Pid, _Reason} -> + %% ?SEV_IPRINT("await_evs_termination -> EXIT: " + %% "~n Pid: ~p" + %% "~n Reason: ~p", [Pid, Reason]), + Evs2 = lists:keydelete(Pid, #ev.pid, Evs), + await_evs_termination(Evs2, tdiff(T, t())) + + after Timeout -> + Evs + end. + + await_finish_fail(Pid, Reason, Evs, OK, Fails) -> case lists:keysearch(Pid, #ev.pid, Evs) of {value, #ev{name = Name}} -> @@ -454,7 +535,7 @@ await_termination(Pid, ExpReason) -> {'DOWN', _, process, Pid, Reason} when (ExpReason =:= Reason) -> ok; {'DOWN', _, process, Pid, Reason} -> - {error, {unexpected_exit, ExpReason, Reason}} + {error, {unexpected_reason, ExpReason, Reason}} end. @@ -480,6 +561,10 @@ await(ExpPid, Name, Announcement, Slogan, OtherPids) is_atom(Slogan) andalso is_list(OtherPids) -> receive + skip -> + %% This means that another evaluator has issued a skip, + %% and we have been instructed to terminate as a result. + ?LIB:skip(command); {Announcement, Pid, Slogan, ?EXTRA_NOTHING} when (ExpPid =:= any) -> {ok, Pid}; {Announcement, Pid, Slogan, Extra} when (ExpPid =:= any) -> @@ -495,12 +580,15 @@ await(ExpPid, Name, Announcement, Slogan, OtherPids) {'DOWN', _, process, Pid, Reason} when (Pid =:= ExpPid) -> eprint("Unexpected DOWN from ~w (~p): " "~n ~p", [Name, Pid, Reason]), - {error, {unexpected_exit, Name}}; + {error, {unexpected_exit, Name, Reason}}; {'DOWN', _, process, OtherPid, Reason} -> case check_down(OtherPid, Reason, OtherPids) of ok -> iprint("DOWN from unknown process ~p: " - "~n ~p", [OtherPid, Reason]), + "~n ~p" + "~n when" + "~n OtherPids: " + "~n ~p", [OtherPid, Reason, OtherPids]), await(ExpPid, Name, Announcement, Slogan, OtherPids); {error, _} = ERROR -> ERROR @@ -527,7 +615,7 @@ check_down(Pid, DownReason, Pids) -> {value, {_, Name}} -> eprint("Unexpected DOWN from ~w (~p): " "~n ~p", [Name, Pid, DownReason]), - {error, {unexpected_exit, Name}}; + {error, {unexpected_exit, Name, DownReason}}; false -> ok end. @@ -561,3 +649,16 @@ print(Prefix, F, A) -> end, ?LOGGER:format("[~s]~s ~s" ++ F, [?LIB:formated_timestamp(), IDStr, Prefix | A]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +t() -> + os:timestamp(). + + +tdiff({A1, B1, C1} = _T1x, {A2, B2, C2} = _T2x) -> + T1 = A1*1000000000+B1*1000+(C1 div 1000), + T2 = A2*1000000000+B2*1000+(C2 div 1000), + T2 - T1. + diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index ae3099633a..d278ac86c7 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -93,25 +93,34 @@ wall_clock_zero_diff1(0) -> %% statistics(wall_clock) are compatible, and are within a small number %% of ms of the amount of real time we waited for. wall_clock_update(Config) when is_list(Config) -> - wall_clock_update1(6). + N = 10, + Inc = 200, + TotalTime = wall_clock_update1(N, Inc, 0), + Overhead = TotalTime - N * Inc, + IsDebug = test_server:is_debug(), -wall_clock_update1(N) when N > 0 -> - {T1_wc_time, _} = statistics(wall_clock), - receive after 1000 -> ok end, - {T2_wc_time, Wc_Diff} = statistics(wall_clock), - - Wc_Diff = T2_wc_time - T1_wc_time, - io:format("Wall clock diff = ~p; should be = 1000..1040~n", [Wc_Diff]), - case test_server:is_debug() of - false -> - true = Wc_Diff =< 1040; + %% Check that the average overhead is reasonable. + if + Overhead < N * 100 -> + ok; + IsDebug, Overhead < N * 1000 -> + ok; true -> - true = Wc_Diff =< 2000 %Be more tolerant in debug-compiled emulator. - end, - true = Wc_Diff >= 1000, - wall_clock_update1(N-1); -wall_clock_update1(0) -> - ok. + io:format("There was an overhead of ~p ms during ~p rounds.", + [Overhead,N]), + ct:fail(too_much_overhead) + end. + +wall_clock_update1(N, Inc, Total) when N > 0 -> + {Time1, _} = statistics(wall_clock), + receive after Inc -> ok end, + {Time2, WcDiff} = statistics(wall_clock), + WcDiff = Time2 - Time1, + io:format("Wall clock diff = ~p (expected at least ~p)\n", [WcDiff,Inc]), + true = WcDiff >= Inc, + wall_clock_update1(N-1, Inc, Total + WcDiff); +wall_clock_update1(0, _, Total) -> + Total. %%% Test statistics(runtime). |