diff options
122 files changed, 3439 insertions, 1003 deletions
diff --git a/HOWTO/INSTALL.md b/HOWTO/INSTALL.md index 8632f46264..551ecdd224 100644 --- a/HOWTO/INSTALL.md +++ b/HOWTO/INSTALL.md @@ -429,6 +429,10 @@ Some of the available `configure` options are: and **not supported**. This functionality **will** be subject to backward incompatible changes. Note that you should **not** enable the dirty scheduler functionality on production systems. It is only provided for testing. + This switch also imply `--enable-new-purge-strategy` (see below). +* `--enable-new-purge-strategy` - Enable the purge strategy that will be + introduced in ERTS version 9.0 (OTP 20). Note that this switch will be + removed in OTP 20. If you or your system has special requirements please read the `Makefile` for additional configuration information. diff --git a/erts/configure.in b/erts/configure.in index b1830979aa..883ce2db68 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -121,6 +121,7 @@ AS_HELP_STRING([--enable-bootstrap-only], enable_hipe=no enable_sctp=no enable_dirty_schedulers=no + enable_new_purge=no fi ]) @@ -139,6 +140,13 @@ AS_HELP_STRING([--enable-dirty-schedulers], [enable dirty scheduler support]), *) enable_dirty_schedulers=yes ;; esac ], enable_dirty_schedulers=default) +AC_ARG_ENABLE(new-purge-strategy, +AS_HELP_STRING([--enable-new-purge-strategy], [enable new code purge strategy]), +[ case "$enableval" in + no) enable_new_purge=no ;; + *) enable_new_purge=yes ;; + esac ], enable_new_purge=default) + AC_ARG_ENABLE(smp-support, AS_HELP_STRING([--enable-smp-support], [enable smp support]) AS_HELP_STRING([--disable-smp-support], [disable smp support]), @@ -1028,6 +1036,27 @@ esac AC_MSG_RESULT($DIRTY_SCHEDULER_SUPPORT) AC_SUBST(DIRTY_SCHEDULER_SUPPORT) +AC_MSG_CHECKING(whether the new code purge strategy should be enabled) +case $enable_new_purge-$enable_dirty_schedulers in + yes-*) + AC_MSG_RESULT(yes) + enable_new_purge=yes;; + default-yes) + AC_MSG_RESULT(yes; forced by dirty scheduler support) + enable_new_purge=yes;; + no-yes) + AC_MSG_ERROR([Dirty schedulers enabled, but new code purge strategy disabled]);; + *) + AC_MSG_RESULT(no) + enable_new_purge=no;; +esac + +if test $enable_new_purge = yes; then + AC_DEFINE(ERTS_NEW_PURGE_STRATEGY, 1, [Define if you want to use the new code purge strategy]) +fi +NEW_PURGE_STRATEGY=$enable_new_purge +AC_SUBST(NEW_PURGE_STRATEGY) + if test $ERTS_BUILD_SMP_EMU = yes; then if test $found_threads = no; then diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 2b627281fe..4ec5ab78d8 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -488,18 +488,6 @@ return term;</code> sending process is not alive. Deallocation of certain internal resources, such as process heap and process control block, is delayed until the dirty NIF has completed.</p> - <p>Known issue that are planned to be fixed:</p> - <list type="bulleted"> - <item> - <p>As purging of a module might need to garbage - collect a process to determine if it has - references to the module, a process executing a dirty - NIF can delay purging for a very long time. Delaying - a purge operation implies delaying <em>all</em> code - loading operations, which can cause severe problems for - the system as a whole.</p> - </item> - </list> </item> </taglist> </item> diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 2b7a3b85e5..18f1b2e833 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -84,22 +84,22 @@ <p>Time unit expressed in parts per second. That is, the time unit equals <c>1/PartsPerSecond</c> second.</p> </item> - <tag><c>seconds</c></tag> + <tag><c>second</c></tag> <item> <p>Symbolic representation of the time unit represented by the integer <c>1</c>.</p> </item> - <tag><c>milli_seconds</c></tag> + <tag><c>millisecond</c></tag> <item> <p>Symbolic representation of the time unit represented by the integer <c>1000</c>.</p> </item> - <tag><c>micro_seconds</c></tag> + <tag><c>microsecond</c></tag> <item> <p>Symbolic representation of the time unit represented by the integer <c>1000000</c>.</p> </item> - <tag><c>nano_seconds</c></tag> + <tag><c>nanosecond</c></tag> <item> <p>Symbolic representation of the time unit represented by the integer <c>1000000000</c>.</p> @@ -119,7 +119,7 @@ <p>One can get an approximation of the <c>native</c> time unit by calling <seealso marker="erlang:convert_time_unit/3"> - <c>erlang:convert_time_unit(1, seconds, native)</c></seealso>. + <c>erlang:convert_time_unit(1, second, native)</c></seealso>. The result equals the number of whole <c>native</c> time units per second. If the number of <c>native</c> time units per second does not @@ -149,6 +149,10 @@ <seealso marker="kernel:os#perf_counter/0"> <c>os:perf_counter/0</c></seealso>.</p> </item> + <tag><seealso marker="#type_deprecated_time_unit"><c>deprecated_time_unit()</c></seealso></tag> + <item><p> + Deprecated symbolic representations kept for backwards-compatibility. + </p></item> </taglist> <p>The <c>time_unit/0</c> type can be extended. To convert time values between time units, use @@ -156,6 +160,27 @@ <c>erlang:convert_time_unit/3</c></seealso>.</p> </desc> </datatype> + <datatype> + <name name="deprecated_time_unit"></name> + <desc><marker id="type_deprecated_time_unit"/> + <p>The <seealso marker="#type_time_unit"><c>time_unit()</c></seealso> + type also consist of the following <em>deprecated</em> symbolic + time units:</p> + <taglist> + <tag><c>seconds</c></tag> + <item><p>Same as <seealso marker="#type_time_unit"><c>second</c></seealso>.</p></item> + + <tag><c>milli_seconds</c></tag> + <item><p>Same as <seealso marker="#type_time_unit"><c>millisecond</c></seealso>.</p></item> + + <tag><c>micro_seconds</c></tag> + <item><p>Same as <seealso marker="#type_time_unit"><c>microsecond</c></seealso>.</p></item> + + <tag><c>nano_seconds</c></tag> + <item><p>Same as <seealso marker="#type_time_unit"><c>nanosecond</c></seealso>.</p></item> + </taglist> + </desc> + </datatype> </datatypes> <funcs> @@ -776,6 +801,29 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> by passing option <c>{allow_gc, false}</c>.</p> </item> </taglist> + <note> + <p> + Up until ERTS version 8.*, the check process code operation + checks for all types of references to the old code. That is, + direct references (e.g. return addresses on the process + stack), indirect references (<c>fun</c>s in process + context), and references to literals in the code. + </p> + <p> + As of ERTS version 9.0, the check process code operation + only checks for direct references to the code. Indirect + references via <c>fun</c>s will be ignored. If such + <c>fun</c>s exist and are used after a purge of the old + code, an exception will be raised upon usage (same as + the case when the <c>fun</c> is received by the process + after the purge). Literals will be taken care of (copied) + at a later stage. This behavior can as of ERTS version + 8.1 be enabled when + <seealso marker="doc/installation_guide:INSTALL#Advanced-configuration-and-build-of-ErlangOTP_Configuring">building OTP</seealso>, + and will automatically be enabled if dirty scheduler + support is enabled. + </p> + </note> <p>See also <seealso marker="kernel:code"> <c>kernel:code(3)</c></seealso>.</p> <p>Failures:</p> @@ -4407,12 +4455,12 @@ RealSystem = system + MissedSystem</code> </desc> </func> - <marker id="process_flag_min_heap_size"/> <func> <name name="process_flag" arity="2" clause_i="3"/> <fsummary>Set process flag min_heap_size for the calling process. </fsummary> <desc> + <marker id="process_flag_min_heap_size"/> <p>Changes the minimum heap size for the calling process.</p> <p>Returns the old value of the flag.</p> </desc> @@ -4429,13 +4477,13 @@ RealSystem = system + MissedSystem</code> </desc> </func> - <marker id="process_flag_max_heap_size"/> <func> <name name="process_flag" arity="2" clause_i="5"/> <fsummary>Set process flag max_heap_size for the calling process. </fsummary> <type name="max_heap_size"/> <desc> + <marker id="process_flag_max_heap_size"/> <p>This flag sets the maximum heap size for the calling process. If <c><anno>MaxHeapSize</anno></c> is an integer, the system default values for <c>kill</c> and <c>error_logger</c> are used. @@ -4503,13 +4551,13 @@ RealSystem = system + MissedSystem</code> </desc> </func> - <marker id="process_flag_message_queue_data"/> <func> <name name="process_flag" arity="2" clause_i="6"/> <fsummary>Set process flag message_queue_data for the calling process. </fsummary> <type name="message_queue_data"/> <desc> + <marker id="process_flag_message_queue_data"/> <p>This flag determines how messages in the message queue are stored, as follows:</p> <taglist> @@ -6697,12 +6745,12 @@ ok </desc> </func> - <marker id="system_flag_max_heap_size"></marker> <func> <name name="system_flag" arity="2" clause_i="8"/> <fsummary>Set system flag max_heap_size.</fsummary> <type name="max_heap_size"/> <desc> + <marker id="system_flag_max_heap_size"></marker> <p> Sets the default maximum heap size settings for processes. The size is specified in words. The new <c>max_heap_size</c> @@ -8451,7 +8499,7 @@ ok <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang monotonic time</seealso>. The time stamp (Ts) has the same format and value as produced by - <c>erlang:monotonic_time(nano_seconds)</c>.</p> + <c>erlang:monotonic_time(nanosecond)</c>.</p> </item> <tag><c>runnable_procs</c></tag> <item> @@ -8479,7 +8527,7 @@ ok <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang monotonic time</seealso> and a monotonically increasing integer. The time stamp (Ts) has the same format and value - as produced by <c>{erlang:monotonic_time(nano_seconds), + as produced by <c>{erlang:monotonic_time(nanosecond), erlang:unique_integer([monotonic])}</c>.</p> </item> <tag><c>timestamp</c></tag> @@ -8709,7 +8757,7 @@ ok <p>The <c>erlang:timestamp()</c> BIF is equivalent to:</p> <code type="none"> timestamp() -> - ErlangSystemTime = erlang:system_time(micro_seconds), + ErlangSystemTime = erlang:system_time(microsecond), MegaSecs = ErlangSystemTime div 1000000000000, Secs = ErlangSystemTime div 1000000 - MegaSecs*1000000, MicroSecs = ErlangSystemTime rem 1000000, @@ -8990,7 +9038,7 @@ timestamp() -> monotonic time</seealso> time stamp in all trace messages. The time stamp (Ts) has the same format and value as produced by <seealso marker="#monotonic_time-1"> - <c>erlang:monotonic_time(nano_seconds)</c></seealso>. + <c>erlang:monotonic_time(nanosecond)</c></seealso>. This flag overrides flag <c>cpu_timestamp</c>.</p> </item> <tag><c>strict_monotonic_timestamp</c></tag> @@ -9001,7 +9049,7 @@ timestamp() -> integer in all trace messages. The time stamp (Ts) has the same format and value as produced by <c>{</c> <seealso marker="#monotonic_time-1"> - <c>erlang:monotonic_time(nano_seconds)</c></seealso><c>,</c> + <c>erlang:monotonic_time(nanosecond)</c></seealso><c>,</c> <seealso marker="#unique_integer-1"> <c>erlang:unique_integer([monotonic])</c></seealso><c>}</c>. This flag overrides flag <c>cpu_timestamp</c>.</p> diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 6c4b30020a..e0260205e3 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -51,6 +51,7 @@ ARFLAGS=rc OMIT_OMIT_FP=no DIRTY_SCHEDULER_SUPPORT=@DIRTY_SCHEDULER_SUPPORT@ +NEW_PURGE_STRATEGY=@NEW_PURGE_STRATEGY@ ifeq ($(TYPE),debug) PURIFY = @@ -591,11 +592,8 @@ GENERATE += $(TTF_DIR)/driver_tab.c # # This list must be consistent with PRE_LOADED_MODULES in # erts/preloaded/src/Makefile. -ifeq ($(TARGET),win32) -# On windows the preloaded objects are in a resource object. -PRELOAD_OBJ = $(OBJDIR)/beams.$(RES_EXT) -PRELOAD_SRC = $(TARGET)/beams.rc -$(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \ + +PRELOAD_BEAM = $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \ $(ERL_TOP)/erts/preloaded/ebin/erts_code_purger.beam \ $(ERL_TOP)/erts/preloaded/ebin/init.beam \ $(ERL_TOP)/erts/preloaded/ebin/prim_eval.beam \ @@ -606,23 +604,20 @@ $(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \ $(ERL_TOP)/erts/preloaded/ebin/erl_prim_loader.beam \ $(ERL_TOP)/erts/preloaded/ebin/erlang.beam \ $(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam \ - $(ERL_TOP)/erts/preloaded/ebin/erl_tracer.beam + $(ERL_TOP)/erts/preloaded/ebin/erl_tracer.beam \ + $(ERL_TOP)/erts/preloaded/ebin/erts_literal_area_collector.beam \ + $(ERL_TOP)/erts/preloaded/ebin/erts_dirty_process_code_checker.beam + +ifeq ($(TARGET),win32) +# On windows the preloaded objects are in a resource object. +PRELOAD_OBJ = $(OBJDIR)/beams.$(RES_EXT) +PRELOAD_SRC = $(TARGET)/beams.rc +$(PRELOAD_SRC): $(PRELOAD_BEAM) $(gen_verbose)LANG=C $(PERL) utils/make_preload $(MAKE_PRELOAD_EXTRA) -rc $^ > $@ else PRELOAD_OBJ = $(OBJDIR)/preload.o PRELOAD_SRC = $(TARGET)/preload.c -$(PRELOAD_SRC): $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \ - $(ERL_TOP)/erts/preloaded/ebin/erts_code_purger.beam \ - $(ERL_TOP)/erts/preloaded/ebin/init.beam \ - $(ERL_TOP)/erts/preloaded/ebin/prim_eval.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/prim_zip.beam \ - $(ERL_TOP)/erts/preloaded/ebin/erl_prim_loader.beam \ - $(ERL_TOP)/erts/preloaded/ebin/erlang.beam \ - $(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam \ - $(ERL_TOP)/erts/preloaded/ebin/erl_tracer.beam +$(PRELOAD_SRC): $(PRELOAD_BEAM) $(gen_verbose)LANG=C $(PERL) utils/make_preload -old $^ > $@ endif diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index badd69856e..9dae67cb2d 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -65,6 +65,7 @@ atom undefined_lambda atom DOWN='DOWN' atom UP='UP' atom EXIT='EXIT' +atom abort atom aborted atom abs_path atom absoluteURI @@ -165,6 +166,7 @@ atom commandv atom compact atom compat_rel atom compile +atom complete atom compressed atom config_h atom convert_time_unit @@ -176,6 +178,7 @@ atom const atom context_switches atom control atom copy +atom copy_literals atom counters atom cpu atom cpu_timestamp @@ -195,6 +198,7 @@ atom dgroup_leader atom dictionary atom dirty_cpu atom dirty_cpu_schedulers_online +atom dirty_execution atom dirty_io atom disable_trace atom disabled @@ -235,6 +239,7 @@ atom erlang atom ERROR='ERROR' atom error_handler atom error_logger +atom erts_code_purger atom erts_internal atom ets atom ETS_TRANSFER='ETS-TRANSFER' @@ -384,8 +389,10 @@ atom merge_trap atom meta atom meta_match_spec atom micro_seconds +atom microsecond atom microstate_accounting atom milli_seconds +atom millisecond atom min_heap_size atom min_bin_vheap_size atom minor_version @@ -402,11 +409,13 @@ atom more atom multi_scheduling atom multiline atom nano_seconds +atom nanosecond atom name atom named_table atom namelist atom native atom native_addresses +atom need_gc atom Neq='=/=' atom Neqeq='/=' atom net_kernel @@ -485,6 +494,7 @@ atom pause atom pending atom pending_driver atom pending_process +atom pending_purge_lambda atom pending_reload atom permanent atom pid @@ -494,6 +504,7 @@ atom port_count atom port_limit atom port_op atom positive +atom prepare atom print atom priority atom private @@ -554,6 +565,7 @@ atom schedulers_online atom scheme atom scientific atom scope +atom second atom seconds atom send_to_non_existing_process atom sensitive diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 92815b2bcd..ad107b4861 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -41,13 +41,81 @@ # include "hipe_stack.h" #endif +static struct { + Eterm module; + erts_smp_mtx_t mtx; + Export *pending_purge_lambda; + Eterm *sprocs; + Eterm def_sprocs[10]; + Uint sp_size; + Uint sp_ix; + ErlFunEntry **funs; + ErlFunEntry *def_funs[10]; + Uint fe_size; + Uint fe_ix; +} purge_state; + +Process *erts_code_purger = NULL; + +ErtsLiteralArea *erts_copy_literal_area = NULL; +#ifdef ERTS_DIRTY_SCHEDULERS +Process *erts_dirty_process_code_checker; +#endif +#ifdef ERTS_NEW_PURGE_STRATEGY +Process *erts_literal_area_collector = NULL; + +typedef struct ErtsLiteralAreaRef_ ErtsLiteralAreaRef; +struct ErtsLiteralAreaRef_ { + ErtsLiteralAreaRef *next; + ErtsLiteralArea *literal_area; +}; + +struct { + erts_smp_mtx_t mtx; + ErtsLiteralAreaRef *first; + ErtsLiteralAreaRef *last; +} release_literal_areas; +#endif + static void set_default_trace_pattern(Eterm module); static Eterm check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls); static void delete_code(Module* modp); -static void decrement_refc(BeamCodeHeader*); static int any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size); static int any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size); +static void +init_purge_state(void) +{ + purge_state.module = THE_NON_VALUE; + + erts_smp_mtx_init(&purge_state.mtx, "purge_state"); + + purge_state.pending_purge_lambda = + erts_export_put(am_erts_code_purger, am_pending_purge_lambda, 3); + + purge_state.sprocs = &purge_state.def_sprocs[0]; + purge_state.sp_size = sizeof(purge_state.def_sprocs); + purge_state.sp_size /= sizeof(purge_state.def_sprocs[0]); + purge_state.sp_ix = 0; + + purge_state.funs = &purge_state.def_funs[0]; + purge_state.fe_size = sizeof(purge_state.def_funs); + purge_state.fe_size /= sizeof(purge_state.def_funs[0]); + purge_state.fe_ix = 0; +} + +void +erts_beam_bif_load_init(void) +{ +#ifdef ERTS_NEW_PURGE_STRATEGY + erts_smp_mtx_init(&release_literal_areas.mtx, "release_literal_areas"); + release_literal_areas.first = NULL; + release_literal_areas.last = NULL; +#endif + + init_purge_state(); +} + BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1) { Module* modp; @@ -520,6 +588,43 @@ badarg: BIF_ERROR(BIF_P, BADARG); } +BIF_RETTYPE erts_internal_check_dirty_process_code_2(BIF_ALIST_2) +{ +#if !defined(ERTS_DIRTY_SCHEDULERS) + BIF_ERROR(BIF_P, EXC_NOTSUP); +#else + Process *rp; + int reds = 0; + Eterm res; + + if (BIF_P != erts_dirty_process_code_checker) + BIF_ERROR(BIF_P, EXC_NOTSUP); + + if (is_not_internal_pid(BIF_ARG_1)) + BIF_ERROR(BIF_P, BADARG); + + if (is_not_atom(BIF_ARG_2)) + BIF_ERROR(BIF_P, BADARG); + + rp = erts_pid2proc_not_running(BIF_P, ERTS_PROC_LOCK_MAIN, + BIF_ARG_1, ERTS_PROC_LOCK_MAIN); + if (rp == ERTS_PROC_LOCK_BUSY) + ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_check_dirty_process_code_2], + BIF_P, BIF_ARG_1, BIF_ARG_2); + if (!rp) + BIF_RET(am_false); + + res = erts_check_process_code(rp, BIF_ARG_2, 0, &reds, BIF_P->fcalls); + + if (BIF_P != rp) + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MAIN); + + ASSERT(is_value(res)); + + BIF_RET2(res, reds); +#endif +} + BIF_RETTYPE delete_module_1(BIF_ALIST_1) { ErtsCodeIndex code_ix; @@ -752,6 +857,8 @@ set_default_trace_pattern(Eterm module) } } +#ifndef ERTS_NEW_PURGE_STRATEGY + static ERTS_INLINE int check_mod_funs(Process *p, ErlOffHeap *off_heap, char *area, size_t area_size) { @@ -766,12 +873,244 @@ check_mod_funs(Process *p, ErlOffHeap *off_heap, char *area, size_t area_size) return 0; } +#endif + static Uint hfrag_literal_size(Eterm* start, Eterm* end, char* lit_start, Uint lit_size); static void hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp, Eterm *start, Eterm *end, char *lit_start, Uint lit_size); +#ifdef ERTS_NEW_PURGE_STRATEGY + +Eterm +erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed) +{ + ErtsLiteralArea *la; + ErtsMessage *msgp; + struct erl_off_heap_header* oh; + char *literals; + Uint lit_bsize; + ErlHeapFragment *hfrag; + + la = erts_copy_literal_area; + if (!la) + return am_ok; + + oh = la->off_heap; + literals = (char *) &la->start[0]; + lit_bsize = (char *) la->end - literals; + + /* + * If a literal is in the message queue we make an explicit copy of + * it and attach it to the heap fragment. Each message needs to be + * self contained, we cannot save the literal in the old_heap or + * any other heap than the message it self. + */ + + erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ); + ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p); + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ); + + for (msgp = c_p->msg.first; msgp; msgp = msgp->next) { + ErlHeapFragment *hf; + Uint lit_sz = 0; + + *redsp += 1; + + if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) + hfrag = &msgp->hfrag; + else if (is_value(ERL_MESSAGE_TERM(msgp)) && msgp->data.heap_frag) + hfrag = msgp->data.heap_frag; + else + continue; /* Content on heap or in external term format... */ + + for (hf = hfrag; hf; hf = hf->next) { + lit_sz += hfrag_literal_size(&hf->mem[0], &hf->mem[hf->used_size], + literals, lit_bsize); + *redsp += 1; + } + + *redsp += lit_sz / 16; /* Better value needed... */ + if (lit_sz > 0) { + ErlHeapFragment *bp = new_message_buffer(lit_sz); + Eterm *hp = bp->mem; + + for (hf = hfrag; hf; hf = hf->next) { + hfrag_literal_copy(&hp, &bp->off_heap, + &hf->mem[0], &hf->mem[hf->used_size], + literals, lit_bsize); + hfrag = hf; + } + + /* link new hfrag last */ + ASSERT(hfrag->next == NULL); + hfrag->next = bp; + bp->next = NULL; + } + } + + if (gc_allowed) { + /* + * Current implementation first tests without + * allowing GC, and then restarts the operation + * allowing GC if it is needed. It is therfore + * very likely that we will need the GC (although + * this is not completely certain). We go for + * the GC directly instead of scanning everything + * one more time... + */ + goto literal_gc; + } + + *redsp += 2; + if (any_heap_ref_ptrs(&c_p->fvalue, &c_p->fvalue+1, literals, lit_bsize)) { + c_p->freason = EXC_NULL; + c_p->fvalue = NIL; + c_p->ftrace = NIL; + } + + if (any_heap_ref_ptrs(c_p->stop, c_p->hend, literals, lit_bsize)) + goto literal_gc; + *redsp += 1; + if (any_heap_refs(c_p->heap, c_p->htop, literals, lit_bsize)) + goto literal_gc; + *redsp += 1; + if (any_heap_refs(c_p->old_heap, c_p->old_htop, literals, lit_bsize)) + goto literal_gc; + + /* Check dictionary */ + *redsp += 1; + if (c_p->dictionary) { + Eterm* start = ERTS_PD_START(c_p->dictionary); + Eterm* end = start + ERTS_PD_SIZE(c_p->dictionary); + + if (any_heap_ref_ptrs(start, end, literals, lit_bsize)) + goto literal_gc; + } + + /* Check heap fragments */ + for (hfrag = c_p->mbuf; hfrag; hfrag = hfrag->next) { + Eterm *hp, *hp_end; + + *redsp += 1; + + hp = &hfrag->mem[0]; + hp_end = &hfrag->mem[hfrag->used_size]; + if (any_heap_refs(hp, hp_end, literals, lit_bsize)) + goto literal_gc; + } + + /* + * Message buffer fragments (matched messages) + * - off heap lists should already have been moved into + * process off heap structure. + * - Check for literals + */ + for (msgp = c_p->msg_frag; msgp; msgp = msgp->next) { + hfrag = erts_message_to_heap_frag(msgp); + for (; hfrag; hfrag = hfrag->next) { + Eterm *hp, *hp_end; + + *redsp += 1; + + hp = &hfrag->mem[0]; + hp_end = &hfrag->mem[hfrag->used_size]; + + if (any_heap_refs(hp, hp_end, literals, lit_bsize)) + goto literal_gc; + } + } + + return am_ok; + +literal_gc: + + if (!gc_allowed) + return am_need_gc; + + if (c_p->flags & F_DISABLE_GC) + return THE_NON_VALUE; + + FLAGS(c_p) |= F_NEED_FULLSWEEP; + + *redsp += erts_garbage_collect_nobump(c_p, 0, c_p->arg_reg, c_p->arity, fcalls); + + erts_garbage_collect_literals(c_p, (Eterm *) literals, lit_bsize, oh); + + *redsp += lit_bsize / 64; /* Need, better value... */ + + return am_ok; +} + +static Eterm +check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls) +{ + BeamInstr* start; + char* mod_start; + Uint mod_size; + Eterm* sp; + + *redsp += 1; + + /* + * Pick up limits for the module. + */ + start = (BeamInstr*) modp->old.code_hdr; + mod_start = (char *) start; + mod_size = modp->old.code_length; + + /* + * Check if current instruction or continuation pointer points into module. + */ + if (ErtsInArea(rp->i, mod_start, mod_size) + || ErtsInArea(rp->cp, mod_start, mod_size)) { + return am_true; + } + + *redsp += (STACK_START(rp) - rp->stop) / 32; + + /* + * Check all continuation pointers stored on the stack. + */ + for (sp = rp->stop; sp < STACK_START(rp); sp++) { + if (is_CP(*sp) && ErtsInArea(cp_val(*sp), mod_start, mod_size)) { + return am_true; + } + } + + /* + * Check all continuation pointers stored in stackdump + * and clear exception stackdump if there is a pointer + * to the module. + */ + if (rp->ftrace != NIL) { + struct StackTrace *s; + ASSERT(is_list(rp->ftrace)); + s = (struct StackTrace *) big_val(CDR(list_val(rp->ftrace))); + if ((s->pc && ErtsInArea(s->pc, mod_start, mod_size)) || + (s->current && ErtsInArea(s->current, mod_start, mod_size))) { + rp->freason = EXC_NULL; + rp->fvalue = NIL; + rp->ftrace = NIL; + } else { + int i; + for (i = 0; i < s->depth; i++) { + if (ErtsInArea(s->trace[i], mod_start, mod_size)) { + rp->freason = EXC_NULL; + rp->fvalue = NIL; + rp->ftrace = NIL; + break; + } + } + } + } + + return am_false; +} + +#else /* !ERTS_NEW_PURGE_STRATEGY, i.e, old style purge... */ + static Eterm check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls) { @@ -865,8 +1204,14 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp); erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ); - literals = (char*) modp->old.code_hdr->literals_start; - lit_bsize = (char*) modp->old.code_hdr->literals_end - literals; + if (modp->old.code_hdr->literal_area) { + literals = (char*) modp->old.code_hdr->literal_area->start; + lit_bsize = (char*) modp->old.code_hdr->literal_area->end - literals; + } + else { + literals = NULL; + lit_bsize = 0; + } for (msgp = rp->msg.first; msgp; msgp = msgp->next) { if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) @@ -907,12 +1252,6 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls /* Check heap, stack etc... */ if (check_mod_funs(rp, &rp->off_heap, mod_start, mod_size)) goto try_gc; - if (!(flags & ERTS_CPC_COPY_LITERALS)) { - /* Process ok. May contain old literals but we will be called - * again before module is purged. - */ - return am_false; - } if (any_heap_ref_ptrs(&rp->fvalue, &rp->fvalue+1, literals, lit_bsize)) { rp->freason = EXC_NULL; rp->fvalue = NIL; @@ -1002,7 +1341,7 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls } if (need_gc & ERTS_LITERAL_GC__) { struct erl_off_heap_header* oh; - oh = modp->old.code_hdr->literals_off_heap; + oh = modp->old.code_hdr->literal_area->off_heap; *redsp += lit_bsize / 64; /* Need, better value... */ erts_garbage_collect_literals(rp, (Eterm*)literals, lit_bsize, oh); done_gc |= ERTS_LITERAL_GC__; @@ -1015,6 +1354,8 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls } +#endif /* !ERTS_NEW_PURGE_STRATEGY */ + static int any_heap_ref_ptrs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size) { @@ -1143,200 +1484,438 @@ hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp, } } -#undef in_area +#ifdef ERTS_NEW_PURGE_STRATEGY + +ErtsThrPrgrLaterOp later_literal_area_switch; #ifdef ERTS_SMP -static void copy_literals_commit(void*); +static void +complete_literal_area_switch(void *unused) +{ + Process *p = erts_literal_area_collector; + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); + erts_resume(p, ERTS_PROC_LOCK_STATUS); + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); +} #endif -copy_literals_t erts_clrange = {NULL, 0, THE_NON_VALUE}; +#endif /* ERTS_NEW_PURGE_STRATEGY */ -/* copy literals - * - * copy_literals.ptr = LitPtr - * copy_literals.sz = LitSz - * ------ THR PROG COMMIT ----- - * - * - check process code - * - check process code - * ... - * copy_literals.ptr = NULL - * copy_literals.sz = 0 - * ------ THR PROG COMMIT ----- - * ... - */ +BIF_RETTYPE erts_internal_release_literal_area_switch_0(BIF_ALIST_0) +{ +#ifndef ERTS_NEW_PURGE_STRATEGY + BIF_ERROR(BIF_P, EXC_NOTSUP); +#else + ErtsLiteralAreaRef *la_ref; + if (BIF_P != erts_literal_area_collector) + BIF_ERROR(BIF_P, EXC_NOTSUP); -BIF_RETTYPE erts_internal_copy_literals_2(BIF_ALIST_2) -{ - ErtsCodeIndex code_ix; - Eterm res = am_true; + erts_smp_mtx_lock(&release_literal_areas.mtx); - if (is_not_atom(BIF_ARG_1) || (am_true != BIF_ARG_2 && am_false != BIF_ARG_2)) { - BIF_ERROR(BIF_P, BADARG); + la_ref = release_literal_areas.first; + if (la_ref) { + release_literal_areas.first = la_ref->next; + if (!release_literal_areas.first) + release_literal_areas.last = NULL; } - if (!erts_try_seize_code_write_permission(BIF_P)) { - ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_copy_literals_2], - BIF_P, BIF_ARG_1, BIF_ARG_2); - } + erts_smp_mtx_unlock(&release_literal_areas.mtx); - code_ix = erts_active_code_ix(); + if (erts_copy_literal_area) + erts_release_literal_area(erts_copy_literal_area); - if (BIF_ARG_2 == am_true) { - Module* modp = erts_get_module(BIF_ARG_1, code_ix); - if (!modp || !modp->old.code_hdr) { - res = am_false; - goto done; - } - if (erts_clrange.ptr != NULL - && !(BIF_P->static_flags & ERTS_STC_FLG_SYSTEM_PROC)) { - res = am_aborted; - goto done; - } - erts_clrange.ptr = modp->old.code_hdr->literals_start; - erts_clrange.sz = modp->old.code_hdr->literals_end - erts_clrange.ptr; - erts_clrange.pid = BIF_P->common.id; - } else if (BIF_ARG_2 == am_false) { - if (erts_clrange.pid != BIF_P->common.id) { - res = am_false; - goto done; - } - erts_clrange.ptr = NULL; - erts_clrange.sz = 0; - erts_clrange.pid = THE_NON_VALUE; + if (!la_ref) { + erts_copy_literal_area = NULL; + BIF_RET(am_false); } -#ifdef ERTS_SMP - ASSERT(committer_state.stager == NULL); - committer_state.stager = BIF_P; - erts_schedule_thr_prgr_later_op(copy_literals_commit, NULL, &committer_state.lop); - erts_proc_inc_refc(BIF_P); + erts_copy_literal_area = la_ref->literal_area; + + erts_free(ERTS_ALC_T_LITERAL_REF, la_ref); + +#ifndef ERTS_SMP + BIF_RET(am_true); +#else + erts_schedule_thr_prgr_later_op(complete_literal_area_switch, + NULL, + &later_literal_area_switch); erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL); ERTS_BIF_YIELD_RETURN(BIF_P, am_true); #endif -done: - erts_release_code_write_permission(); - BIF_RET(res); + +#endif /* ERTS_NEW_PURGE_STRATEGY */ +} + +void +erts_purge_state_add_fun(ErlFunEntry *fe) +{ + ASSERT(is_value(purge_state.module)); + if (purge_state.fe_ix >= purge_state.fe_size) { + ErlFunEntry **funs; + purge_state.fe_size += 100; + funs = erts_alloc(ERTS_ALC_T_PURGE_DATA, + sizeof(ErlFunEntry *)*purge_state.fe_size); + sys_memcpy((void *) funs, + (void *) purge_state.funs, + purge_state.fe_ix*sizeof(ErlFunEntry *)); + if (purge_state.funs != &purge_state.def_funs[0]) + erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.funs); + purge_state.funs = funs; + } + purge_state.funs[purge_state.fe_ix++] = fe; +} + +Export * +erts_suspend_process_on_pending_purge_lambda(Process *c_p) +{ + erts_smp_mtx_lock(&purge_state.mtx); + if (is_value(purge_state.module)) { + /* + * The process c_p is about to call a fun in the code + * that we are trying to purge. Suspend it and call + * erts_code_purger:pending_purge_lambda/3. The process + * will be resumed when the purge completes or aborts, + * and will then try to do the call again. + */ + if (purge_state.sp_ix >= purge_state.sp_size) { + Eterm *sprocs; + purge_state.sp_size += 100; + sprocs = erts_alloc(ERTS_ALC_T_PURGE_DATA, + (sizeof(ErlFunEntry *) + * purge_state.sp_size)); + sys_memcpy((void *) sprocs, + (void *) purge_state.sprocs, + purge_state.sp_ix*sizeof(ErlFunEntry *)); + if (purge_state.sprocs != &purge_state.def_sprocs[0]) + erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.sprocs); + purge_state.sprocs = sprocs; + } + purge_state.sprocs[purge_state.sp_ix++] = c_p->common.id; + erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL); + ERTS_VBUMP_ALL_REDS(c_p); + } + erts_smp_mtx_unlock(&purge_state.mtx); + return purge_state.pending_purge_lambda; +} + +static void +finalize_purge_operation(Process *c_p, int succeded) +{ + Uint ix; + + if (c_p) + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN); + + erts_smp_mtx_lock(&purge_state.mtx); + + ASSERT(purge_state.module != THE_NON_VALUE); + + purge_state.module = THE_NON_VALUE; + + /* + * Resume all processes that have tried to call + * funs in this code. + */ + for (ix = 0; ix < purge_state.sp_ix; ix++) { + Process *rp = erts_pid2proc(NULL, 0, + purge_state.sprocs[ix], + ERTS_PROC_LOCK_STATUS); + if (rp) { + erts_resume(rp, ERTS_PROC_LOCK_STATUS); + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS); + } + } + + erts_smp_mtx_unlock(&purge_state.mtx); + + if (c_p) + erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); + + if (purge_state.sprocs != &purge_state.def_sprocs[0]) { + erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.sprocs); + purge_state.sprocs = &purge_state.def_sprocs[0]; + purge_state.sp_size = sizeof(purge_state.def_sprocs); + purge_state.sp_size /= sizeof(purge_state.def_sprocs[0]); + } + purge_state.sp_ix = 0; + + if (purge_state.funs != &purge_state.def_funs[0]) { + erts_free(ERTS_ALC_T_PURGE_DATA, purge_state.funs); + purge_state.funs = &purge_state.def_funs[0]; + purge_state.fe_size = sizeof(purge_state.def_funs); + purge_state.fe_size /= sizeof(purge_state.def_funs[0]); + } + purge_state.fe_ix = 0; } #ifdef ERTS_SMP -static void copy_literals_commit(void* null) { - Process* p = committer_state.stager; -#ifdef DEBUG - committer_state.stager = NULL; -#endif - erts_release_code_write_permission(); + +static ErtsThrPrgrLaterOp purger_lop_data; + +static void +resume_purger(void *unused) +{ + Process *p = erts_code_purger; erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); - if (!ERTS_PROC_IS_EXITING(p)) { - erts_resume(p, ERTS_PROC_LOCK_STATUS); - } + erts_resume(p, ERTS_PROC_LOCK_STATUS); erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); - erts_proc_dec_refc(p); } -#endif /* ERTS_SMP */ +static void +finalize_purge_abort(void *unused) +{ + erts_fun_purge_abort_finalize(purge_state.funs, purge_state.fe_ix); + + finalize_purge_operation(NULL, 0); -/* Do the actualy module purging and return: - * true for success - * false if no such old module - * BADARG if not an atom - */ -BIF_RETTYPE erts_internal_purge_module_1(BIF_ALIST_1) + resume_purger(NULL); +} + +#endif /* ERTS_SMP */ + +BIF_RETTYPE erts_internal_purge_module_2(BIF_ALIST_2) { - ErtsCodeIndex code_ix; - BeamInstr* code; - BeamInstr* end; - Module* modp; - int is_blocking = 0; - Eterm ret; + if (BIF_P != erts_code_purger) + BIF_ERROR(BIF_P, EXC_NOTSUP); - if (is_not_atom(BIF_ARG_1)) { + if (is_not_atom(BIF_ARG_1)) BIF_ERROR(BIF_P, BADARG); - } - if (!erts_try_seize_code_write_permission(BIF_P)) { - ERTS_BIF_YIELD1(bif_export[BIF_erts_internal_purge_module_1], - BIF_P, BIF_ARG_1); - } + switch (BIF_ARG_2) { - code_ix = erts_active_code_ix(); + case am_prepare: { + /* + * Prepare for purge by marking all fun + * entries referring to the code to purge + * with "pending purge" markers. + */ + ErtsCodeIndex code_ix; + Module* modp; + Eterm res; - /* - * Correct module? - */ + if (is_value(purge_state.module)) + BIF_ERROR(BIF_P, BADARG); - if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) { - ERTS_BIF_PREP_RET(ret, am_false); - } - else { - erts_rwlock_old_code(code_ix); + code_ix = erts_active_code_ix(); /* - * Any code to purge? + * Correct module? */ - if (!modp->old.code_hdr) { - ERTS_BIF_PREP_RET(ret, am_false); - } + modp = erts_get_module(BIF_ARG_1, code_ix); + if (!modp) + res = am_false; else { /* - * Unload any NIF library + * Any code to purge? */ - if (modp->old.nif != NULL) { - /* ToDo: Do unload nif without blocking */ - erts_rwunlock_old_code(code_ix); - erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_smp_thr_progress_block(); - is_blocking = 1; - erts_rwlock_old_code(code_ix); - erts_unload_nif(modp->old.nif); - modp->old.nif = NULL; + erts_rlock_old_code(code_ix); + if (!modp->old.code_hdr) + res = am_false; + else { + BeamInstr* code; + BeamInstr* end; + erts_smp_mtx_lock(&purge_state.mtx); + purge_state.module = BIF_ARG_1; + erts_smp_mtx_unlock(&purge_state.mtx); + res = am_true; + code = (BeamInstr*) modp->old.code_hdr; + end = (BeamInstr *)((char *)code + modp->old.code_length); + erts_fun_purge_prepare(code, end); +#if !defined(ERTS_NEW_PURGE_STRATEGY) + ASSERT(!erts_copy_literal_area); + erts_copy_literal_area = modp->old.code_hdr->literal_area; +#endif } - + erts_runlock_old_code(code_ix); + } + +#ifndef ERTS_SMP + BIF_RET(res); +#else + if (res != am_true) + BIF_RET(res); + else { /* - * Remove the old code. + * We'll be resumed when all schedulers are guaranteed + * to see the "pending purge" markers that we've made on + * all fun entries of the code that we are about to purge. + * Processes trying to call these funs will be suspended + * before calling the funs. That is we are guaranteed not + * to get any more direct references into the code while + * checking for such references... */ - ASSERT(erts_total_code_size >= modp->old.code_length); - erts_total_code_size -= modp->old.code_length; - code = (BeamInstr*) modp->old.code_hdr; - end = (BeamInstr *)((char *)code + modp->old.code_length); - erts_cleanup_funs_on_purge(code, end); - beam_catches_delmod(modp->old.catches, code, modp->old.code_length, - code_ix); - decrement_refc(modp->old.code_hdr); - if (modp->old.code_hdr->literals_start) { - erts_free(ERTS_ALC_T_LITERAL, modp->old.code_hdr->literals_start); - } - erts_free(ERTS_ALC_T_CODE, (void *) code); - modp->old.code_hdr = NULL; - modp->old.code_length = 0; - modp->old.catches = BEAM_CATCHES_NIL; - erts_remove_from_ranges(code); - ERTS_BIF_PREP_RET(ret, am_true); + erts_schedule_thr_prgr_later_op(resume_purger, + NULL, + &purger_lop_data); + erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL); + ERTS_BIF_YIELD_RETURN(BIF_P, am_true); } - erts_rwunlock_old_code(code_ix); +#endif } - if (is_blocking) { - erts_smp_thr_progress_unblock(); - erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + + case am_abort: { + /* + * Soft purge that detected direct references into the code + * we set out to purge. Abort the purge. + */ + + if (purge_state.module != BIF_ARG_1) + BIF_ERROR(BIF_P, BADARG); + + erts_fun_purge_abort_prepare(purge_state.funs, purge_state.fe_ix); + +#if !defined(ERTS_NEW_PURGE_STRATEGY) + ASSERT(erts_copy_literal_area); + erts_copy_literal_area = NULL; +#endif +#ifndef ERTS_SMP + erts_fun_purge_abort_finalize(purge_state.funs, purge_state.fe_ix); + finalize_purge_operation(BIF_P, 0); + BIF_RET(am_false); +#else + /* + * We need to restore the code addresses of the funs in + * two stages in order to ensure that we do not get any + * stale suspended processes due to the purge abort. + * Restore address pointer (erts_fun_purge_abort_prepare); + * wait for thread progress; clear pending purge address + * pointer (erts_fun_purge_abort_finalize), and then + * resume processes that got suspended + * (finalize_purge_operation). + */ + erts_schedule_thr_prgr_later_op(finalize_purge_abort, + NULL, + &purger_lop_data); + erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL); + ERTS_BIF_YIELD_RETURN(BIF_P, am_false); +#endif } - erts_release_code_write_permission(); - return ret; -} -static void -decrement_refc(BeamCodeHeader* code_hdr) -{ - struct erl_off_heap_header* oh = code_hdr->literals_off_heap; - - while (oh) { - Binary* bptr; - ASSERT(thing_subtag(oh->thing_word) == REFC_BINARY_SUBTAG); - bptr = ((ProcBin*)oh)->val; - if (erts_refc_dectest(&bptr->refc, 0) == 0) { - erts_bin_free(bptr); + case am_complete: { + ErtsCodeIndex code_ix; + BeamInstr* code; + Module* modp; + int is_blocking = 0; + Eterm ret; + ErtsLiteralArea *literals = NULL; + + + /* + * We have no direct references into the code. + * Complete to purge. + */ + + if (purge_state.module != BIF_ARG_1) + BIF_ERROR(BIF_P, BADARG); + + if (!erts_try_seize_code_write_permission(BIF_P)) { + ERTS_BIF_YIELD2(bif_export[BIF_erts_internal_purge_module_2], + BIF_P, BIF_ARG_1, BIF_ARG_2); + } + + code_ix = erts_active_code_ix(); + + /* + * Correct module? + */ + + if ((modp = erts_get_module(BIF_ARG_1, code_ix)) == NULL) { + ERTS_BIF_PREP_RET(ret, am_false); } - oh = oh->next; + else { + + erts_rwlock_old_code(code_ix); + + /* + * Any code to purge? + */ + if (!modp->old.code_hdr) { + ERTS_BIF_PREP_RET(ret, am_false); + } + else { + /* + * Unload any NIF library + */ + if (modp->old.nif != NULL) { + /* ToDo: Do unload nif without blocking */ + erts_rwunlock_old_code(code_ix); + erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_smp_thr_progress_block(); + is_blocking = 1; + erts_rwlock_old_code(code_ix); + erts_unload_nif(modp->old.nif); + modp->old.nif = NULL; + } + + /* + * Remove the old code. + */ + ASSERT(erts_total_code_size >= modp->old.code_length); + erts_total_code_size -= modp->old.code_length; + code = (BeamInstr*) modp->old.code_hdr; + erts_fun_purge_complete(purge_state.funs, purge_state.fe_ix); + beam_catches_delmod(modp->old.catches, code, modp->old.code_length, + code_ix); + literals = modp->old.code_hdr->literal_area; + modp->old.code_hdr->literal_area = NULL; + erts_free(ERTS_ALC_T_CODE, (void *) code); + modp->old.code_hdr = NULL; + modp->old.code_length = 0; + modp->old.catches = BEAM_CATCHES_NIL; + erts_remove_from_ranges(code); + ERTS_BIF_PREP_RET(ret, am_true); + } + erts_rwunlock_old_code(code_ix); + } + if (is_blocking) { + erts_smp_thr_progress_unblock(); + erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + } + + erts_release_code_write_permission(); + + finalize_purge_operation(BIF_P, ret == am_true); + +#if !defined(ERTS_NEW_PURGE_STRATEGY) + + ASSERT(erts_copy_literal_area == literals); + erts_copy_literal_area = NULL; + erts_release_literal_area(literals); + +#else /* ERTS_NEW_PURGE_STRATEGY */ + + if (literals) { + ErtsLiteralAreaRef *ref; + ref = erts_alloc(ERTS_ALC_T_LITERAL_REF, + sizeof(ErtsLiteralAreaRef)); + ref->literal_area = literals; + ref->next = NULL; + erts_smp_mtx_lock(&release_literal_areas.mtx); + if (release_literal_areas.last) { + release_literal_areas.last->next = ref; + release_literal_areas.last = ref; + } + else { + release_literal_areas.first = ref; + release_literal_areas.last = ref; + } + erts_smp_mtx_unlock(&release_literal_areas.mtx); + erts_queue_message(erts_literal_area_collector, + 0, + erts_alloc_message(0, NULL), + am_copy_literals, + BIF_P->common.id); + } + +#endif /* ERTS_NEW_PURGE_STRATEGY */ + + return ret; + } + + default: + BIF_ERROR(BIF_P, BADARG); + } } diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 8f90a999c4..ef4cdf9d5a 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1960,6 +1960,8 @@ void process_main(void) ErtsMessage* msgp; PROCESS_MAIN_CHK_LOCKS(c_p); + ERTS_CHK_MBUF_SZ(c_p); + PreFetch(0, next); msgp = PEEK_MESSAGE(c_p); @@ -2047,6 +2049,7 @@ void process_main(void) } ERTS_DBG_CHK_REDS(c_p, FCALLS); + ERTS_CHK_MBUF_SZ(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2583,7 +2586,9 @@ do { \ c_p->fcalls = FCALLS; PROCESS_MAIN_CHK_LOCKS(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + ERTS_CHK_MBUF_SZ(c_p); result = (*bf)(c_p, tmp_reg); + ERTS_CHK_MBUF_SZ(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2614,7 +2619,9 @@ do { \ c_p->fcalls = FCALLS; PROCESS_MAIN_CHK_LOCKS(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + ERTS_CHK_MBUF_SZ(c_p); result = (*bf)(c_p, tmp_reg); + ERTS_CHK_MBUF_SZ(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2644,7 +2651,9 @@ do { \ SWAPOUT; PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); + ERTS_CHK_MBUF_SZ(c_p); result = (*bf)(c_p, reg, live); + ERTS_CHK_MBUF_SZ(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2685,7 +2694,9 @@ do { \ SWAPOUT; PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); + ERTS_CHK_MBUF_SZ(c_p); result = (*bf)(c_p, reg, live); + ERTS_CHK_MBUF_SZ(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2728,7 +2739,9 @@ do { \ SWAPOUT; PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); + ERTS_CHK_MBUF_SZ(c_p); result = (*bf)(c_p, reg, live); + ERTS_CHK_MBUF_SZ(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2766,7 +2779,9 @@ do { \ c_p->fcalls = FCALLS; PROCESS_MAIN_CHK_LOCKS(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + ERTS_CHK_MBUF_SZ(c_p); result = (*bf)(c_p, tmp_reg); + ERTS_CHK_MBUF_SZ(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2793,7 +2808,9 @@ do { \ bf = (BifFunction) Arg(0); PROCESS_MAIN_CHK_LOCKS(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + ERTS_CHK_MBUF_SZ(c_p); result = (*bf)(c_p, tmp_reg); + ERTS_CHK_MBUF_SZ(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2843,7 +2860,9 @@ do { \ ASSERT(!ERTS_PROC_IS_EXITING(c_p)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); live_hf_end = c_p->mbuf; + ERTS_CHK_MBUF_SZ(c_p); result = (*bf)(c_p, reg, I); + ERTS_CHK_MBUF_SZ(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); ERTS_HOLE_CHECK(c_p); @@ -3556,11 +3575,13 @@ do { \ ASSERT(c_p->scheduler_data); #endif live_hf_end = c_p->mbuf; + ERTS_CHK_MBUF_SZ(c_p); erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2], NULL); nif_bif_result = (*fp)(&env, bif_nif_arity, reg); if (env.exception_thrown) nif_bif_result = THE_NON_VALUE; erts_post_nif(&env); + ERTS_CHK_MBUF_SZ(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); @@ -3612,7 +3633,9 @@ do { \ Eterm (*bf)(Process*, Eterm*, BeamInstr*) = vbf; ASSERT(!ERTS_PROC_IS_EXITING(c_p)); live_hf_end = c_p->mbuf; + ERTS_CHK_MBUF_SZ(c_p); nif_bif_result = (*bf)(c_p, reg, I); + ERTS_CHK_MBUF_SZ(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(nif_bif_result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); @@ -5252,19 +5275,14 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) else { /* * Dirty CPU scheduler: - * Currently two reductions consumed per - * micro second spent in the dirty NIF. + * Reductions based on time consumed by + * the dirty NIF. */ - ErtsMonotonicTime time; - time = erts_get_monotonic_time(esdp); - time -= start_time; - time = ERTS_MONOTONIC_TO_USEC(time); - time *= (CONTEXT_REDS-1)/1000 + 1; - ASSERT(time >= 0); - if (time == 0) - time = 1; /* At least one reduction */ - time += esdp->virtual_reds; - reds_used = time > INT_MAX ? INT_MAX : (int) time; + Sint64 treds; + treds = erts_time2reds(start_time, + erts_get_monotonic_time(esdp)); + treds += esdp->virtual_reds; + reds_used = treds > INT_MAX ? INT_MAX : (int) treds; } PROCESS_MAIN_CHK_LOCKS(c_p); @@ -5382,7 +5400,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) ASSERT(!c_p->scheduler_data); erts_pre_dirty_nif(esdp, &env, c_p, - (struct erl_module_nif*)I[2], NULL); + (struct erl_module_nif*)I[2]); #ifdef DEBUG result = @@ -5391,7 +5409,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) #endif (*fp)(&env, arity, reg); - erts_post_nif(&env); + erts_post_dirty_nif(&env); ASSERT(!is_value(result)); ASSERT(c_p->freason == TRAP); @@ -6526,34 +6544,49 @@ call_fun(Process* p, /* Current process. */ * representation (the module has never been loaded), * or the module defining the fun has been unloaded. */ + module = fe->module; - if ((modp = erts_get_module(module, code_ix)) != NULL - && modp->curr.code_hdr != NULL) { + + ERTS_SMP_READ_MEMORY_BARRIER; + if (fe->pend_purge_address) { /* - * There is a module loaded, but obviously the fun is not - * defined in it. We must not call the error_handler - * (or we will get into an infinite loop). + * The system is currently trying to purge the + * module containing this fun. Suspend the process + * and let it try again when the purge operation is + * done (may succeed or not). */ - goto badfun; + ep = erts_suspend_process_on_pending_purge_lambda(p); + ASSERT(ep); } + else { + if ((modp = erts_get_module(module, code_ix)) != NULL + && modp->curr.code_hdr != NULL) { + /* + * There is a module loaded, but obviously the fun is not + * defined in it. We must not call the error_handler + * (or we will get into an infinite loop). + */ + goto badfun; + } - /* - * No current code for this module. Call the error_handler module - * to attempt loading the module. - */ + /* + * No current code for this module. Call the error_handler module + * to attempt loading the module. + */ - ep = erts_find_function(erts_proc_get_error_handler(p), - am_undefined_lambda, 3, code_ix); - if (ep == NULL) { /* No error handler */ - p->current = NULL; - p->freason = EXC_UNDEF; - return NULL; + ep = erts_find_function(erts_proc_get_error_handler(p), + am_undefined_lambda, 3, code_ix); + if (ep == NULL) { /* No error handler */ + p->current = NULL; + p->freason = EXC_UNDEF; + return NULL; + } } reg[0] = module; reg[1] = fun; reg[2] = args; reg[3] = NIL; - return ep->addressv[erts_active_code_ix()]; + return ep->addressv[code_ix]; } } } else if (is_export_header(hdr)) { diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 0c2743beb2..b7e802775d 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -105,7 +105,7 @@ typedef struct { */ typedef struct genop { - int op; /* Opcode. */ + unsigned int op; /* Opcode. */ int arity; /* Number of arguments. */ GenOpArg def_args[MAX_OPARGS]; /* Default buffer for arguments. */ GenOpArg* a; /* The arguments. */ @@ -283,8 +283,8 @@ typedef struct LoaderState { byte* code_start; /* Start of code file. */ unsigned code_size; /* Size of code file. */ int specific_op; /* Specific opcode (-1 if not found). */ - int num_functions; /* Number of functions in module. */ - int num_labels; /* Number of labels. */ + unsigned int num_functions; /* Number of functions in module. */ + unsigned int num_labels; /* Number of labels. */ BeamCodeHeader* hdr; /* Loaded code header */ BeamInstr* codev; /* Loaded code buffer */ int codev_size; /* Size of code buffer in words. */ @@ -303,13 +303,13 @@ typedef struct LoaderState { * Atom table. */ - int num_atoms; /* Number of atoms in atom table. */ + unsigned int num_atoms; /* Number of atoms in atom table. */ Eterm* atom; /* Atom table. */ - int num_exps; /* Number of exports. */ + unsigned int num_exps; /* Number of exports. */ ExportEntry* export; /* Pointer to export table. */ - int num_imports; /* Number of imports. */ + unsigned int num_imports; /* Number of imports. */ ImportEntry* import; /* Import entry (translated information). */ /* @@ -323,8 +323,8 @@ typedef struct LoaderState { * Lambda table. */ - int num_lambdas; /* Number of lambdas in table. */ - int lambdas_allocated; /* Size of allocated lambda table. */ + unsigned int num_lambdas; /* Number of lambdas in table. */ + unsigned int lambdas_allocated; /* Size of allocated lambda table. */ Lambda* lambdas; /* Pointer to lambdas. */ Lambda def_lambdas[16]; /* Default storage for lambda table. */ char* lambda_error; /* Delayed missing 'FunT' error. */ @@ -333,8 +333,8 @@ typedef struct LoaderState { * Literals (constant pool). */ - int num_literals; /* Number of literals in table. */ - int allocated_literals; /* Number of literal entries allocated. */ + unsigned int num_literals; /* Number of literals in table. */ + unsigned int allocated_literals; /* Number of literal entries allocated. */ Literal* literals; /* Array of literals. */ LiteralPatch* literal_patches; /* Operands that need to be patched. */ Uint total_literal_size; /* Total heap size for all literals. */ @@ -343,13 +343,13 @@ typedef struct LoaderState { * Line table. */ BeamInstr* line_item; /* Line items from the BEAM file. */ - int num_line_items; /* Number of line items. */ + unsigned int num_line_items;/* Number of line items. */ LineInstr* line_instr; /* Line instructions */ - int num_line_instrs; /* Maximum number of line instructions */ - int current_li; /* Current line instruction */ - int* func_line; /* Mapping from function to first line instr */ + unsigned int num_line_instrs; /* Maximum number of line instructions */ + unsigned int current_li; /* Current line instruction */ + unsigned int* func_line; /* Mapping from function to first line instr */ Eterm* fname; /* List of file names */ - int num_fnames; /* Number of filenames in fname table */ + unsigned int num_fnames; /* Number of filenames in fname table */ int loc_size; /* Size of location info in bytes (2/4) */ } LoaderState; @@ -663,7 +663,7 @@ erts_prepare_loading(Binary* magic, Process *c_p, Eterm group_leader, stp->hdr->compile_ptr = NULL; stp->hdr->compile_size = 0; stp->hdr->compile_size_on_heap = 0; - stp->hdr->literals_start = NULL; + stp->hdr->literal_area = NULL; stp->hdr->md5_ptr = NULL; /* @@ -1005,8 +1005,9 @@ loader_state_dtor(Binary* magic) stp->bin = 0; } if (stp->hdr != 0) { - if (stp->hdr->literals_start) { - erts_free(ERTS_ALC_T_LITERAL, stp->hdr->literals_start); + if (stp->hdr->literal_area) { + erts_release_literal_area(stp->hdr->literal_area); + stp->hdr->literal_area = NULL; } erts_free(ERTS_ALC_T_CODE, stp->hdr); stp->hdr = 0; @@ -1330,7 +1331,7 @@ verify_chunks(LoaderState* stp) static int load_atom_table(LoaderState* stp) { - int i; + unsigned int i; GetInt(stp, 4, stp->num_atoms); stp->num_atoms++; @@ -1375,13 +1376,13 @@ load_atom_table(LoaderState* stp) static int load_import_table(LoaderState* stp) { - int i; + unsigned int i; GetInt(stp, 4, stp->num_imports); stp->import = erts_alloc(ERTS_ALC_T_PREPARED_CODE, stp->num_imports * sizeof(ImportEntry)); for (i = 0; i < stp->num_imports; i++) { - int n; + unsigned int n; Eterm mod; Eterm func; Uint arity; @@ -1389,17 +1390,17 @@ load_import_table(LoaderState* stp) GetInt(stp, 4, n); if (n >= stp->num_atoms) { - LoadError2(stp, "import entry %d: invalid atom number %d", i, n); + LoadError2(stp, "import entry %u: invalid atom number %u", i, n); } mod = stp->import[i].module = stp->atom[n]; GetInt(stp, 4, n); if (n >= stp->num_atoms) { - LoadError2(stp, "import entry %d: invalid atom number %d", i, n); + LoadError2(stp, "import entry %u: invalid atom number %u", i, n); } func = stp->import[i].function = stp->atom[n]; GetInt(stp, 4, arity); if (arity > MAX_REG) { - LoadError2(stp, "import entry %d: invalid arity %d", i, arity); + LoadError2(stp, "import entry %u: invalid arity %d", i, arity); } stp->import[i].arity = arity; stp->import[i].patches = 0; @@ -1427,12 +1428,12 @@ load_import_table(LoaderState* stp) static int read_export_table(LoaderState* stp) { - int i; + unsigned int i; BeamInstr* address; GetInt(stp, 4, stp->num_exps); if (stp->num_exps > stp->num_functions) { - LoadError2(stp, "%d functions exported; only %d functions defined", + LoadError2(stp, "%u functions exported; only %u functions defined", stp->num_exps, stp->num_functions); } stp->export @@ -1450,16 +1451,16 @@ read_export_table(LoaderState* stp) stp->export[i].function = func; GetInt(stp, 4, arity); if (arity > MAX_REG) { - LoadError2(stp, "export table entry %d: absurdly high arity %d", i, arity); + LoadError2(stp, "export table entry %u: absurdly high arity %u", i, arity); } stp->export[i].arity = arity; GetInt(stp, 4, n); if (n >= stp->num_labels) { - LoadError3(stp, "export table entry %d: invalid label %d (highest defined label is %d)", i, n, stp->num_labels); + LoadError3(stp, "export table entry %u: invalid label %u (highest defined label is %u)", i, n, stp->num_labels); } value = stp->labels[n].value; if (value == 0) { - LoadError2(stp, "export table entry %d: label %d not resolved", i, n); + LoadError2(stp, "export table entry %u: label %u not resolved", i, n); } stp->export[i].address = address = stp->codev + value; @@ -1520,7 +1521,7 @@ is_bif(Eterm mod, Eterm func, unsigned arity) static int read_lambda_table(LoaderState* stp) { - int i; + unsigned int i; GetInt(stp, 4, stp->num_lambdas); if (stp->num_lambdas > stp->lambdas_allocated) { @@ -1540,12 +1541,12 @@ read_lambda_table(LoaderState* stp) GetAtom(stp, n, stp->lambdas[i].function); GetInt(stp, 4, arity); if (arity > MAX_REG) { - LoadError2(stp, "lambda entry %d: absurdly high arity %d", i, arity); + LoadError2(stp, "lambda entry %u: absurdly high arity %u", i, arity); } stp->lambdas[i].arity = arity; GetInt(stp, 4, n); if (n >= stp->num_labels) { - LoadError3(stp, "lambda entry %d: invalid label %d (highest defined label is %d)", + LoadError3(stp, "lambda entry %u: invalid label %u (highest defined label is %u)", i, n, stp->num_labels); } stp->lambdas[i].label = n; @@ -1566,7 +1567,7 @@ read_lambda_table(LoaderState* stp) static int read_literal_table(LoaderState* stp) { - int i; + unsigned int i; uLongf uncompressed_sz; byte* uncompressed = 0; @@ -1588,7 +1589,7 @@ read_literal_table(LoaderState* stp) } for (i = 0; i < stp->num_literals; i++) { - int sz; + Uint sz; Sint heap_size; byte* p; Eterm val; @@ -1597,7 +1598,7 @@ read_literal_table(LoaderState* stp) GetInt(stp, 4, sz); /* Size of external term format. */ GetString(stp, p, sz); if ((heap_size = erts_decode_ext_size(p, sz)) < 0) { - LoadError1(stp, "literal %d: bad external format", i); + LoadError1(stp, "literal %u: bad external format", i); } if (heap_size > 0) { @@ -1607,7 +1608,7 @@ read_literal_table(LoaderState* stp) val = erts_decode_ext(&factory, &p, 0); if (is_non_value(val)) { - LoadError1(stp, "literal %d: bad external format", i); + LoadError1(stp, "literal %u: bad external format", i); } erts_factory_close(&factory); stp->literals[i].heap_frags = factory.heap_frags; @@ -1617,7 +1618,7 @@ read_literal_table(LoaderState* stp) erts_factory_dummy_init(&factory); val = erts_decode_ext(&factory, &p, 0); if (is_non_value(val)) { - LoadError1(stp, "literal %d: bad external format", i); + LoadError1(stp, "literal %u: bad external format", i); } ASSERT(is_immed(val)); stp->literals[i].heap_frags = NULL; @@ -1640,9 +1641,9 @@ read_line_table(LoaderState* stp) { unsigned version; ERTS_DECLARE_DUMMY(unsigned flags); - int num_line_items; + unsigned int num_line_items; BeamInstr* lp; - int i; + unsigned int i; BeamInstr fname_index; BeamInstr tag; @@ -1721,7 +1722,7 @@ read_line_table(LoaderState* stp) } } else if (tag == TAG_a) { if (val > stp->num_fnames) { - LoadError2(stp, "file index overflow (%d/%d)", + LoadError2(stp, "file index overflow (%u/%u)", val, stp->num_fnames); } fname_index = val; @@ -1757,9 +1758,9 @@ read_line_table(LoaderState* stp) stp->num_line_instrs * sizeof(LineInstr)); stp->current_li = 0; - stp->func_line = (int *) erts_alloc(ERTS_ALC_T_PREPARED_CODE, - stp->num_functions * - sizeof(int)); + stp->func_line = (unsigned int *) erts_alloc(ERTS_ALC_T_PREPARED_CODE, + stp->num_functions * + sizeof(int)); return 1; @@ -1783,6 +1784,10 @@ read_code_header(LoaderState* stp) */ GetInt(stp, 4, head_size); + if (head_size > stp->file_left) { + LoadError2(stp, "invalid code header size %u; bytes left %u", + head_size, stp->file_left); + } stp->code_start = stp->file_p + head_size; stp->code_size = stp->file_left - head_size; stp->file_left = head_size; @@ -1887,7 +1892,7 @@ load_code(LoaderState* stp) ci = stp->ci; for (;;) { - int new_op; + unsigned int new_op; GenOp* tmp_op; ASSERT(ci <= codev_size); @@ -1895,10 +1900,10 @@ load_code(LoaderState* stp) get_next_instr: GetByte(stp, new_op); if (new_op >= NUM_GENERIC_OPS) { - LoadError1(stp, "invalid opcode %d", new_op); + LoadError1(stp, "invalid opcode %u", new_op); } if (gen_opc[new_op].name[0] == '\0') { - LoadError1(stp, "invalid opcode %d", new_op); + LoadError1(stp, "invalid opcode %u", new_op); } @@ -2368,7 +2373,7 @@ load_code(LoaderState* stp) VerifyTag(stp, tag, TAG_u); last_label = tmp_op->a[arg].val; if (!(0 < last_label && last_label < stp->num_labels)) { - LoadError2(stp, "invalid label num %d (0 < label < %d)", + LoadError2(stp, "invalid label num %u (0 < label < %u)", tmp_op->a[arg].val, stp->num_labels); } if (stp->labels[last_label].value != 0) { @@ -2512,7 +2517,7 @@ load_code(LoaderState* stp) { Sint offset; if (function_number >= stp->num_functions) { - LoadError1(stp, "too many functions in module (header said %d)", + LoadError1(stp, "too many functions in module (header said %u)", stp->num_functions); } @@ -2591,14 +2596,14 @@ load_code(LoaderState* stp) if (stp->line_item) { BeamInstr item = code[ci-1]; BeamInstr loc; - int li; + unsigned int li; if (item >= stp->num_line_items) { - LoadError2(stp, "line instruction index overflow (%d/%d)", + LoadError2(stp, "line instruction index overflow (%u/%u)", item, stp->num_line_items); } li = stp->current_li; if (li >= stp->num_line_instrs) { - LoadError2(stp, "line instruction table overflow (%d/%d)", + LoadError2(stp, "line instruction table overflow (%u/%u)", li, stp->num_line_instrs); } loc = stp->line_item[item]; @@ -4560,13 +4565,16 @@ freeze_code(LoaderState* stp) Eterm* ptr; LiteralPatch* lp; ErlOffHeap code_off_heap; + ErtsLiteralArea *literal_area; + Uint lit_asize; ERTS_INIT_OFF_HEAP(&code_off_heap); - ptr = (Eterm*)erts_alloc(ERTS_ALC_T_LITERAL, - stp->total_literal_size*sizeof(Eterm)); - code_hdr->literals_start = ptr; - code_hdr->literals_end = ptr + stp->total_literal_size; + lit_asize = ERTS_LITERAL_AREA_ALLOC_SIZE(stp->total_literal_size); + literal_area = erts_alloc(ERTS_ALC_T_LITERAL, lit_asize); + ptr = &literal_area->start[0]; + literal_area->end = ptr + stp->total_literal_size; + for (i = 0; i < stp->num_literals; i++) { if (is_not_immed(stp->literals[i].term)) { erts_move_multi_frags(&ptr, &code_off_heap, @@ -4576,7 +4584,7 @@ freeze_code(LoaderState* stp) ptr_val(stp->literals[i].term))); } } - code_hdr->literals_off_heap = code_off_heap.first; + literal_area->off_heap = code_off_heap.first; lp = stp->literal_patches; while (lp != 0) { BeamInstr* op_ptr; @@ -4587,6 +4595,7 @@ freeze_code(LoaderState* stp) op_ptr[0] = lit->term; lp = lp->next; } + code_hdr->literal_area = literal_area; } CHKBLK(ERTS_ALC_T_CODE,code); @@ -4598,8 +4607,8 @@ freeze_code(LoaderState* stp) str_table = (byte *) (codev + stp->ci); } else { BeamCodeLineTab* const line_tab = (BeamCodeLineTab *) (codev+stp->ci); - const int ftab_size = stp->num_functions; - const int num_instrs = stp->current_li; + const unsigned int ftab_size = stp->num_functions; + const unsigned int num_instrs = stp->current_li; const BeamInstr** const line_items = (const BeamInstr**) &line_tab->func_tab[ftab_size + 1]; @@ -4759,7 +4768,7 @@ freeze_code(LoaderState* stp) static void final_touch(LoaderState* stp, struct erl_module_instance* inst_p) { - int i; + unsigned int i; int on_load = stp->on_load; unsigned catches; Uint index; @@ -5431,7 +5440,7 @@ new_genop(LoaderState* stp) static int new_label(LoaderState* stp) { - int num = stp->num_labels; + unsigned int num = stp->num_labels; stp->num_labels++; stp->labels = (Label *) erts_realloc(ERTS_ALC_T_PREPARED_CODE, @@ -5642,6 +5651,28 @@ has_native(BeamCodeHeader *code_hdr) return result; } +void +erts_release_literal_area(ErtsLiteralArea* literal_area) +{ + struct erl_off_heap_header* oh; + + if (!literal_area) + return; + + oh = literal_area->off_heap; + + while (oh) { + Binary* bptr; + ASSERT(thing_subtag(oh->thing_word) == REFC_BINARY_SUBTAG); + bptr = ((ProcBin*)oh)->val; + if (erts_refc_dectest(&bptr->refc, 0) == 0) { + erts_bin_free(bptr); + } + oh = oh->next; + } + erts_free(ERTS_ALC_T_LITERAL, literal_area); +} + int erts_is_module_native(BeamCodeHeader* code_hdr) { @@ -6029,11 +6060,11 @@ stub_copy_info(LoaderState* stp, static int stub_read_export_table(LoaderState* stp) { - int i; + unsigned int i; GetInt(stp, 4, stp->num_exps); if (stp->num_exps > stp->num_functions) { - LoadError2(stp, "%d functions exported; only %d functions defined", + LoadError2(stp, "%u functions exported; only %u functions defined", stp->num_exps, stp->num_functions); } stp->export @@ -6047,7 +6078,7 @@ stub_read_export_table(LoaderState* stp) GetAtom(stp, n, stp->export[i].function); GetInt(stp, 4, n); if (n > MAX_REG) { - LoadError2(stp, "export table entry %d: absurdly high arity %d", i, n); + LoadError2(stp, "export table entry %u: absurdly high arity %u", i, n); } stp->export[i].arity = n; GetInt(stp, 4, n); /* Ignore label */ @@ -6061,8 +6092,8 @@ stub_read_export_table(LoaderState* stp) static void stub_final_touch(LoaderState* stp, BeamInstr* fp) { - int i; - int n = stp->num_exps; + unsigned int i; + unsigned int n = stp->num_exps; Eterm mod = fp[2]; Eterm function = fp[3]; int arity = fp[4]; @@ -6373,9 +6404,7 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info) code_hdr->compile_ptr = NULL; code_hdr->compile_size = 0; code_hdr->compile_size_on_heap = 0; - code_hdr->literals_start = NULL; - code_hdr->literals_end = NULL; - code_hdr->literals_off_heap = 0; + code_hdr->literal_area = NULL; code_hdr->on_load_function_ptr = NULL; code_hdr->line_table = NULL; code_hdr->md5_ptr = NULL; diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h index fd2dd97fee..1200bb9c6f 100644 --- a/erts/emulator/beam/beam_load.h +++ b/erts/emulator/beam/beam_load.h @@ -50,6 +50,7 @@ extern BeamInstr* em_call_error_handler; extern BeamInstr* em_apply_bif; extern BeamInstr* em_call_nif; +struct ErtsLiteralArea_; /* * The following variables keep a sorted list of address ranges for @@ -89,9 +90,7 @@ typedef struct beam_code_header { /* * Literal area (constant pool). */ - Eterm* literals_start; - Eterm* literals_end; - struct erl_off_heap_header* literals_off_heap; + struct ErtsLiteralArea_ *literal_area; /* * Pointer to the on_load function (or NULL if none). @@ -120,7 +119,12 @@ typedef struct beam_code_header { }BeamCodeHeader; +void erts_release_literal_area(struct ErtsLiteralArea_* literal_area); int erts_is_module_native(BeamCodeHeader* code); +void erts_beam_bif_load_init(void); +struct erl_fun_entry; +void erts_purge_state_add_fun(struct erl_fun_entry *fe); +Export *erts_suspend_process_on_pending_purge_lambda(Process *c_p); /* * Layout of the line table. diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 065018514a..80db4eb6ff 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -161,6 +161,7 @@ bif erts_internal:port_close/1 bif erts_internal:port_connect/2 bif erts_internal:request_system_task/3 +bif erts_internal:request_system_task/4 bif erts_internal:check_process_code/2 bif erts_internal:map_to_tuple_keys/1 @@ -174,6 +175,8 @@ bif erts_internal:is_system_process/1 bif erts_internal:system_check/1 +bif erts_internal:release_literal_area_switch/0 + # inet_db support bif erlang:port_set_data/2 bif erlang:port_get_data/1 @@ -642,8 +645,9 @@ bif erts_debug:map_info/1 # New in 19.0 # -bif erts_internal:copy_literals/2 -bif erts_internal:purge_module/1 +bif erts_internal:is_process_executing_dirty/1 +bif erts_internal:check_dirty_process_code/2 +bif erts_internal:purge_module/2 bif binary:split/2 bif binary:split/3 bif erts_debug:size_shared/1 diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 227fedfb69..971052b947 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -279,6 +279,7 @@ type TRACER_NIF LONG_LIVED SYSTEM tracer_nif type TRACE_MSG_QUEUE SHORT_LIVED SYSTEM trace_message_queue type SCHED_ASYNC_JOB SHORT_LIVED SYSTEM async_calls type DIRTY_START STANDARD PROCESSES dirty_start +type DIRTY_SL SHORT_LIVED SYSTEM dirty_short_lived +if threads_no_smp # Need thread safe allocs, but std_alloc and fix_alloc are not; @@ -367,6 +368,8 @@ type MONITOR_LH STANDARD PROCESSES monitor_lh type NLINK_LH STANDARD PROCESSES nlink_lh type CODE LONG_LIVED CODE code type LITERAL LITERAL CODE literal +type LITERAL_REF SHORT_LIVED CODE literal_area_ref +type PURGE_DATA SHORT_LIVED CODE purge_data type DB_HEIR_DATA STANDARD ETS db_heir_data type DB_MS_PSDO_PROC LONG_LIVED ETS db_match_pseudo_proc type SCHDLR_DATA LONG_LIVED SYSTEM scheduler_data diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 6cedf3c8a5..29ba12dfdb 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2883,6 +2883,27 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(AM_tag); #endif } + else if (ERTS_IS_ATOM_STR("check_process_code",BIF_ARG_1)) { + Eterm terms[3]; + Sint length = 1; + Uint sz = 0; + Eterm *hp, res; + DECL_AM(direct_references); + + terms[0] = AM_direct_references; +#if !defined(ERTS_NEW_PURGE_STRATEGY) + { + DECL_AM(indirect_references); + terms[1] = AM_indirect_references; + terms[2] = am_copy_literals; + length = 3; + } +#endif + erts_bld_list(NULL, &sz, length, terms); + hp = HAlloc(BIF_P, sz); + res = erts_bld_list(&hp, NULL, length, terms); + BIF_RET(res); + } BIF_ERROR(BIF_P, BADARG); } diff --git a/erts/emulator/beam/erl_fun.c b/erts/emulator/beam/erl_fun.c index 6ce1376c81..c639ba623f 100644 --- a/erts/emulator/beam/erl_fun.c +++ b/erts/emulator/beam/erl_fun.c @@ -199,14 +199,13 @@ erts_erase_fun_entry(ErlFunEntry* fe) } void -erts_cleanup_funs_on_purge(BeamInstr* start, BeamInstr* end) +erts_fun_purge_prepare(BeamInstr* start, BeamInstr* end) { int limit; HashBucket** bucket; - ErlFunEntry* to_delete = NULL; int i; - erts_fun_write_lock(); + erts_fun_read_lock(); limit = erts_fun_table.size; bucket = erts_fun_table.bucket; for (i = 0; i < limit; i++) { @@ -217,22 +216,51 @@ erts_cleanup_funs_on_purge(BeamInstr* start, BeamInstr* end) BeamInstr* addr = fe->address; if (start <= addr && addr < end) { + fe->pend_purge_address = addr; + ERTS_SMP_WRITE_MEMORY_BARRIER; fe->address = unloaded_fun; - if (erts_refc_dectest(&fe->refc, 0) == 0) { - fe->address = (void *) to_delete; - to_delete = fe; - } + erts_purge_state_add_fun(fe); } b = b->next; } } + erts_fun_read_unlock(); +} + +void +erts_fun_purge_abort_prepare(ErlFunEntry **funs, Uint no) +{ + Uint ix; - while (to_delete != NULL) { - ErlFunEntry* next = (ErlFunEntry *) to_delete->address; - erts_erase_fun_entry_unlocked(to_delete); - to_delete = next; + for (ix = 0; ix < no; ix++) { + ErlFunEntry *fe = funs[ix]; + if (fe->address == unloaded_fun) + fe->address = fe->pend_purge_address; + fe->pend_purge_address = NULL; } - erts_fun_write_unlock(); +} + +void +erts_fun_purge_abort_finalize(ErlFunEntry **funs, Uint no) +{ + Uint ix; + + for (ix = 0; ix < no; ix++) + funs[ix]->pend_purge_address = NULL; +} + +void +erts_fun_purge_complete(ErlFunEntry **funs, Uint no) +{ + Uint ix; + + for (ix = 0; ix < no; ix++) { + ErlFunEntry *fe = funs[ix]; + fe->pend_purge_address = NULL; + if (erts_refc_dectest(&fe->refc, 0) == 0) + erts_erase_fun_entry(fe); + } + ERTS_SMP_WRITE_MEMORY_BARRIER; } void @@ -294,6 +322,7 @@ fun_alloc(ErlFunEntry* template) obj->module = template->module; erts_refc_init(&obj->refc, -1); obj->address = unloaded_fun; + obj->pend_purge_address = NULL; #ifdef HIPE obj->native_address = NULL; #endif diff --git a/erts/emulator/beam/erl_fun.h b/erts/emulator/beam/erl_fun.h index 8c4deea7a0..73c3e19c1c 100644 --- a/erts/emulator/beam/erl_fun.h +++ b/erts/emulator/beam/erl_fun.h @@ -44,6 +44,7 @@ typedef struct erl_fun_entry { Eterm module; /* Tagged atom for module. */ erts_refc_t refc; /* Reference count: One for code + one for each fun object in each process. */ + BeamInstr *pend_purge_address; /* address stored during a pending purge */ } ErlFunEntry; /* @@ -81,7 +82,10 @@ ErlFunEntry* erts_put_fun_entry2(Eterm mod, int old_uniq, int old_index, void erts_erase_fun_entry(ErlFunEntry* fe); void erts_cleanup_funs(ErlFunThing* funp); -void erts_cleanup_funs_on_purge(BeamInstr* start, BeamInstr* end); +void erts_fun_purge_prepare(BeamInstr* start, BeamInstr* end); +void erts_fun_purge_abort_prepare(ErlFunEntry **funs, Uint no); +void erts_fun_purge_abort_finalize(ErlFunEntry **funs, Uint no); +void erts_fun_purge_complete(ErlFunEntry **funs, Uint no); void erts_dump_fun_entries(int, void *); #endif diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 8b0dd9a5a6..75f8ebefbd 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -517,6 +517,8 @@ delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need, int erts_proc_sched_data((p))->virtual_reds += vreds; } + ERTS_CHK_MBUF_SZ(p); + ASSERT(CONTEXT_REDS >= erts_proc_sched_data(p)->virtual_reds); return reds_left; } @@ -527,6 +529,8 @@ young_gen_usage(Process *p) Uint hsz; Eterm *aheap; + ERTS_CHK_MBUF_SZ(p); + hsz = p->mbuf_sz; if (p->flags & F_ON_HEAP_MSGQ) { @@ -590,6 +594,8 @@ garbage_collect(Process* p, ErlHeapFragment *live_hf_end, DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE); #endif + ERTS_CHK_MBUF_SZ(p); + ASSERT(CONTEXT_REDS - ERTS_REDS_LEFT(p, fcalls) >= erts_proc_sched_data(p)->virtual_reds); diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 07cfacf14a..781bf024dd 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -382,6 +382,7 @@ erl_init(int ncpu, erts_init_unicode(); /* after RE to get access to PCRE unicode */ erts_init_external(); erts_init_map(); + erts_beam_bif_load_init(); erts_delay_trap = erts_export_put(am_erlang, am_delay_trap, 2); erts_late_init_process(); #if HAVE_ERTS_MSEG @@ -2248,7 +2249,42 @@ erl_start(int argc, char **argv) otp_ring0_pid = erl_first_process_otp("otp_ring0", NULL, 0, boot_argc, boot_argv); - (void) erl_system_process_otp(otp_ring0_pid, "erts_code_purger"); + { + /* + * The erts_code_purger and the erts_literal_area_collector + * system processes are *always* alive. If they terminate + * they bring the whole VM down. + */ + Eterm pid; + + pid = erl_system_process_otp(otp_ring0_pid, "erts_code_purger"); + erts_code_purger + = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc, + internal_pid_index(pid)); + ASSERT(erts_code_purger && erts_code_purger->common.id == pid); + erts_proc_inc_refc(erts_code_purger); + +#ifdef ERTS_NEW_PURGE_STRATEGY + pid = erl_system_process_otp(otp_ring0_pid, "erts_literal_area_collector"); + erts_literal_area_collector + = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc, + internal_pid_index(pid)); + ASSERT(erts_literal_area_collector + && erts_literal_area_collector->common.id == pid); + erts_proc_inc_refc(erts_literal_area_collector); +#endif + +#ifdef ERTS_DIRTY_SCHEDULERS + pid = erl_system_process_otp(otp_ring0_pid, "erts_dirty_process_code_checker"); + erts_dirty_process_code_checker + = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc, + internal_pid_index(pid)); + ASSERT(erts_dirty_process_code_checker + && erts_dirty_process_code_checker->common.id == pid); + erts_proc_inc_refc(erts_dirty_process_code_checker); +#endif + + } #ifdef ERTS_SMP erts_start_schedulers(); diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 39c0617143..06266363b5 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -96,6 +96,7 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "dist_entry", "address" }, { "dist_entry_links", "address" }, { "code_write_permission", NULL }, + { "purge_state", NULL }, { "proc_status", "pid" }, { "proc_trace", "pid" }, { "ports_snapshot", NULL }, @@ -112,6 +113,9 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "export_tab", NULL }, { "fun_tab", NULL }, { "environ", NULL }, +#ifdef ERTS_NEW_PURGE_STRATEGY + { "release_literal_areas", NULL }, +#endif #endif { "efile_drv", "address" }, { "drv_ev_state_grow", NULL, }, diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 91e06cde2d..e4c696ae3b 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -1842,3 +1842,20 @@ void erts_factory_undo(ErtsHeapFactory* factory) factory->heap_frags = NULL; #endif } + +Uint +erts_mbuf_size(Process *p) +{ + Uint sz = 0; + ErlHeapFragment* bp; + ErtsMessage* mp; + + for (bp = p->mbuf; bp; bp = bp->next) + sz += bp->used_size; + + for (mp = p->msg_frag; mp; mp = mp->next) + for (bp = erts_message_to_heap_frag(mp); bp; bp = bp->next) + sz += bp->used_size; + + return sz; +} diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index 6df969367b..42ed14e69c 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -499,4 +499,15 @@ erts_msgq_replace_msg_ref(ErlMessageQueue *msgq, ErtsMessage *newp, ErtsMessage #endif +Uint erts_mbuf_size(Process *p); +#if defined(DEBUG) || 0 +# define ERTS_CHK_MBUF_SZ(P) \ + do { \ + Uint actual_mbuf_sz__ = erts_mbuf_size((P)); \ + ERTS_ASSERT((P)->mbuf_sz >= actual_mbuf_sz__); \ + } while (0) +#else +# define ERTS_CHK_MBUF_SZ(P) ((void) 1) +#endif + #endif diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index c6127a4967..27392a5996 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -120,8 +120,10 @@ execution_state(ErlNifEnv *env, Process **c_pp, int *schedp) else { Process *c_p = env->proc; - if (!(c_p->static_flags & ERTS_STC_FLG_SHADOW_PROC)) - ASSERT(is_scheduler() > 0); + if (!(c_p->static_flags & ERTS_STC_FLG_SHADOW_PROC)) { + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) + & ERTS_PROC_LOCK_MAIN); + } else { c_p = env->proc->next; ASSERT(is_scheduler() < 0); @@ -154,7 +156,9 @@ static Eterm* alloc_heap_heavy(ErlNifEnv* env, size_t need, Eterm* hp) HEAP_TOP(env->proc) = env->hp; } else { - env->heap_frag->used_size = hp - env->heap_frag->mem; + Uint usz = env->hp - env->heap_frag->mem; + env->proc->mbuf_sz += usz - env->heap_frag->used_size; + env->heap_frag->used_size = usz; ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size); } hp = erts_heap_alloc(env->proc, need, MIN_HEAP_FRAG_SZ); @@ -208,11 +212,16 @@ void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif, #endif } +static void full_cache_env(ErlNifEnv *env); +static void cache_env(ErlNifEnv* env); +static void full_flush_env(ErlNifEnv *env); +static void flush_env(ErlNifEnv* env); + +#ifdef ERTS_DIRTY_SCHEDULERS void erts_pre_dirty_nif(ErtsSchedulerData *esdp, - ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif, - Process* tracee) + ErlNifEnv* env, Process* p, + struct erl_module_nif* mod_nif) { -#ifdef ERTS_DIRTY_SCHEDULERS Process *sproc; #ifdef DEBUG erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state); @@ -223,7 +232,7 @@ void erts_pre_dirty_nif(ErtsSchedulerData *esdp, ASSERT(esdp); #endif - erts_pre_nif(env, p, mod_nif, tracee); + erts_pre_nif(env, p, mod_nif, NULL); sproc = esdp->dirty_shadow_process; ASSERT(sproc); @@ -235,22 +244,10 @@ void erts_pre_dirty_nif(ErtsSchedulerData *esdp, sproc->next = p; sproc->common.id = p->common.id; - sproc->htop = p->htop; - sproc->stop = p->stop; - sproc->hend = p->hend; - sproc->heap = p->heap; - sproc->abandoned_heap = p->abandoned_heap; - sproc->heap_sz = p->heap_sz; - sproc->high_water = p->high_water; - sproc->old_hend = p->old_hend; - sproc->old_htop = p->old_htop; - sproc->old_heap = p->old_heap; - sproc->mbuf = NULL; - sproc->mbuf_sz = 0; - ERTS_INIT_OFF_HEAP(&sproc->off_heap); env->proc = sproc; -#endif + full_cache_env(env); } +#endif /* Temporary object header, auto-deallocated when NIF returns * or when independent environment is cleared. @@ -274,32 +271,37 @@ static ERTS_INLINE void free_tmp_objs(ErlNifEnv* env) void erts_post_nif(ErlNifEnv* env) { erts_unblock_fpe(env->fpe_was_unmasked); + full_flush_env(env); + free_tmp_objs(env); + env->exiting = ERTS_PROC_IS_EXITING(env->proc); +} #ifdef ERTS_DIRTY_SCHEDULERS - if (!(env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC)) +void erts_post_dirty_nif(ErlNifEnv* env) +{ + Process *c_p; + ASSERT(env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC); + ASSERT(env->proc->next); + erts_unblock_fpe(env->fpe_was_unmasked); + full_flush_env(env); + free_tmp_objs(env); + c_p = env->proc->next; + env->exiting = ERTS_PROC_IS_EXITING(c_p); + ERTS_VBUMP_ALL_REDS(c_p); +} #endif - { - ASSERT(is_scheduler() > 0); - if (env->heap_frag == NULL) { - ASSERT(env->hp_end == HEAP_LIMIT(env->proc)); - ASSERT(env->hp >= HEAP_TOP(env->proc)); - ASSERT(env->hp <= HEAP_LIMIT(env->proc)); - HEAP_TOP(env->proc) = env->hp; - } - else { - ASSERT(env->hp_end != HEAP_LIMIT(env->proc)); - ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size); - env->heap_frag->used_size = env->hp - env->heap_frag->mem; - ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size); - } - env->exiting = ERTS_PROC_IS_EXITING(env->proc); - } + +static void full_flush_env(ErlNifEnv* env) +{ #ifdef ERTS_DIRTY_SCHEDULERS - else { /* Dirty nif call using shadow process struct */ + if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) { + /* Dirty nif call using shadow process struct */ Process *c_p = env->proc->next; ASSERT(is_scheduler() < 0); ASSERT(env->proc->common.id == c_p->common.id); + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) + & ERTS_PROC_LOCK_MAIN); if (!env->heap_frag) { ASSERT(env->hp_end == HEAP_LIMIT(c_p)); @@ -308,11 +310,14 @@ void erts_post_nif(ErlNifEnv* env) HEAP_TOP(c_p) = env->hp; } else { + Uint usz; ASSERT(env->hp_end != HEAP_LIMIT(c_p)); ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size); HEAP_TOP(c_p) = HEAP_TOP(env->proc); - env->heap_frag->used_size = env->hp - env->heap_frag->mem; + usz = env->hp - env->heap_frag->mem; + env->proc->mbuf_sz += usz - env->heap_frag->used_size; + env->heap_frag->used_size = usz; ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size); @@ -339,11 +344,48 @@ void erts_post_nif(ErlNifEnv* env) } c_p->off_heap.overhead += env->proc->off_heap.overhead; - env->exiting = ERTS_PROC_IS_EXITING(c_p); - BUMP_ALL_REDS(c_p); + return; } #endif - free_tmp_objs(env); + + flush_env(env); +} + +static void full_cache_env(ErlNifEnv* env) +{ +#ifdef ERTS_DIRTY_SCHEDULERS + if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) { + /* Dirty nif call using shadow process struct */ + Process *sproc = env->proc; + Process *c_p = sproc->next; + ASSERT(c_p); + ASSERT(is_scheduler() < 0); + ASSERT(env->proc->common.id == c_p->common.id); + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) + & ERTS_PROC_LOCK_MAIN); + + sproc->htop = c_p->htop; + sproc->stop = c_p->stop; + sproc->hend = c_p->hend; + sproc->heap = c_p->heap; + sproc->abandoned_heap = c_p->abandoned_heap; + sproc->heap_sz = c_p->heap_sz; + sproc->high_water = c_p->high_water; + sproc->old_hend = c_p->old_hend; + sproc->old_htop = c_p->old_htop; + sproc->old_heap = c_p->old_heap; + sproc->mbuf = NULL; + sproc->mbuf_sz = 0; + ERTS_INIT_OFF_HEAP(&sproc->off_heap); + + env->hp_end = HEAP_LIMIT(c_p); + env->hp = HEAP_TOP(c_p); + env->heap_frag = NULL; + return; + } +#endif + + cache_env(env); } /* Flush out our cached heap pointers to allow an ordinary HAlloc @@ -357,9 +399,12 @@ static void flush_env(ErlNifEnv* env) HEAP_TOP(env->proc) = env->hp; } else { + Uint usz; ASSERT(env->hp_end != HEAP_LIMIT(env->proc)); ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size); - env->heap_frag->used_size = env->hp - env->heap_frag->mem; + usz = env->hp - env->heap_frag->mem; + env->proc->mbuf_sz += usz - env->heap_frag->used_size; + env->heap_frag->used_size = usz; ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size); } } @@ -600,17 +645,32 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, if (scheduler > 0) { /* Normal scheduler */ rp = erts_proc_lookup(receiver); - if (c_p == rp) - rp_locks = ERTS_PROC_LOCK_MAIN; + if (!rp) + return 0; } else { - if (c_p && ERTS_PROC_IS_EXITING(c_p)) - return 0; - rp = erts_pid2proc_opt(c_p, 0, receiver, rp_locks, + if (c_p) { + ASSERT(scheduler < 0); /* Dirty scheduler */ + if (ERTS_PROC_IS_EXITING(c_p)) + return 0; + + if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) { + erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); + } + } + + rp = erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN, + receiver, rp_locks, ERTS_P2P_FLG_INC_REFC); + if (!rp) { + if (c_p && (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC)) + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN); + return 0; + } } - if (rp == NULL) - return 0; + + if (c_p == rp) + rp_locks = ERTS_PROC_LOCK_MAIN; if (menv) { flush_env(msg_env); @@ -631,9 +691,9 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, ErlOffHeap *ohp; Eterm *hp; if (env && !env->tracee) { - flush_env(env); + full_flush_env(env); mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp); - cache_env(env); + full_cache_env(env); } else { erts_aint_t state = erts_smp_atomic32_read_nob(&rp->state); @@ -656,8 +716,11 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, if (!env || !env->tracee) { - if (c_p && IS_TRACED_FL(c_p, F_TRACE_SEND)) + if (c_p && IS_TRACED_FL(c_p, F_TRACE_SEND)) { + full_flush_env(env); trace_send(c_p, receiver, msg); + full_cache_env(env); + } } #ifdef ERTS_SMP else { @@ -690,10 +753,6 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, erts_smp_proc_trylock(rp, ERTS_PROC_LOCK_MSGQ) == EBUSY) { if (!msgq) { -#ifdef ERTS_SMP - ErtsThrPrgrDelayHandle dhndl; -#endif - msgq = erts_alloc(ERTS_ALC_T_TRACE_MSG_QUEUE, sizeof(ErlTraceMessageQueue)); msgq->receiver = receiver; @@ -707,15 +766,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE); -#ifdef ERTS_SMP - if (!scheduler) - dhndl = erts_thr_progress_unmanaged_delay(); -#endif - erts_schedule_flush_trace_messages(t_p->common.id); -#ifdef ERTS_SMP - if (!scheduler) - erts_thr_progress_unmanaged_continue(dhndl); -#endif + erts_schedule_flush_trace_messages(t_p, 0); } else { msgq->len++; *msgq->last = mp; @@ -740,6 +791,8 @@ done: rp_locks &= ~ERTS_PROC_LOCK_MAIN; if (rp_locks & ~lc_locks) erts_smp_proc_unlock(rp, rp_locks & ~lc_locks); + if (c_p && (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC)) + erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN); #endif if (scheduler <= 0) erts_proc_dec_refc(rp); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index aa6af7427c..bc59147c6c 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -448,6 +448,9 @@ int erts_system_profile_ts_type = ERTS_TRACE_FLG_NOW_TIMESTAMP; typedef enum { ERTS_PSTT_GC, /* Garbage Collect */ ERTS_PSTT_CPC, /* Check Process Code */ +#ifdef ERTS_NEW_PURGE_STRATEGY + ERTS_PSTT_CLA, /* Copy Literal Area */ +#endif ERTS_PSTT_COHMQ, /* Change off heap message queue */ ERTS_PSTT_FTMQ /* Flush trace msg queue */ } ErtsProcSysTaskType; @@ -6671,14 +6674,24 @@ erts_schedule_process(Process *p, erts_aint32_t state, ErtsProcLocks locks) } static int -schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st) +schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st, + erts_aint32_t *fail_state_p) { int res; int locked; ErtsProcSysTaskQs *stqs, *free_stqs; - erts_aint32_t state, a, n, enq_prio; + erts_aint32_t fail_state, state, a, n, enq_prio; int enqueue; /* < 0 -> use proxy */ unsigned int prof_runnable_procs; + int strict_fail_state; + + fail_state = *fail_state_p; + /* + * If fail state something other than just exiting process, + * ensure that the task wont be scheduled when the + * receiver is in the failure state. + */ + strict_fail_state = fail_state != ERTS_PSFLG_EXITING; res = 1; /* prepare for success */ st->next = st->prev = st; /* Prep for empty prio queue */ @@ -6705,7 +6718,8 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st) erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); state = erts_smp_atomic32_read_nob(&p->state); - if (state & ERTS_PSFLG_EXITING) { + if (state & fail_state) { + *fail_state_p = (state & fail_state); free_stqs = stqs; res = 0; goto cleanup; @@ -6759,7 +6773,7 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st) /* Status lock prevents out of order "runnable proc" trace msgs */ ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)); - if (!prof_runnable_procs) { + if (!prof_runnable_procs && !strict_fail_state) { erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS); locked = 0; } @@ -6770,6 +6784,11 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st) erts_aint32_t e; n = e = a; + if (strict_fail_state && (a & fail_state)) { + *fail_state_p = (a & fail_state); + goto cleanup; + } + if (a & ERTS_PSFLG_FREE) goto cleanup; /* We don't want to schedule free processes... */ @@ -9092,6 +9111,27 @@ resume_process_1(BIF_ALIST_1) BIF_ERROR(BIF_P, BADARG); } +BIF_RETTYPE +erts_internal_is_process_executing_dirty_1(BIF_ALIST_1) +{ + if (is_not_internal_pid(BIF_ARG_1)) + BIF_ERROR(BIF_P, BADARG); +#ifdef ERTS_DIRTY_SCHEDULERS + else { + Process *rp = erts_proc_lookup(BIF_ARG_1); + if (rp) { + erts_aint32_t state = erts_smp_atomic32_read_nob(&rp->state); + if (state & (ERTS_PSFLG_DIRTY_RUNNING + |ERTS_PSFLG_DIRTY_RUNNING_SYS)) { + BIF_RET(am_true); + } + } + } +#endif + BIF_RET(am_false); +} + + Uint erts_run_queues_len(Uint *qlen, int atomic_queues_read, int incl_active_sched) { @@ -9368,6 +9408,14 @@ erts_set_process_priority(Process *p, Eterm value) } } +#ifdef __WIN32__ +Sint64 +erts_time2reds(ErtsMonotonicTime start, ErtsMonotonicTime end) +{ + return ERTS_TIME2REDS_IMPL__(start, end); +} +#endif + static int scheduler_gc_proc(Process *c_p, int reds_left) { @@ -9498,15 +9546,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) p->reds += actual_reds; -#ifdef ERTS_SMP - erts_smp_proc_lock(p, ERTS_PROC_LOCK_TRACE); - if (p->trace_msg_q) { - erts_smp_proc_unlock(p, ERTS_PROC_LOCK_TRACE); - erts_schedule_flush_trace_messages(p->common.id); - } else - erts_smp_proc_unlock(p, ERTS_PROC_LOCK_TRACE); -#endif - state = erts_smp_atomic32_read_nob(&p->state); if (IS_TRACED(p)) { @@ -9526,7 +9565,15 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) } } - erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS); + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE); + +#ifdef ERTS_SMP + if (p->trace_msg_q) { + erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE); + erts_schedule_flush_trace_messages(p, 1); + erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE); + } +#endif /* have to re-read state after taking lock */ state = erts_smp_atomic32_read_nob(&p->state); @@ -9534,9 +9581,11 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) #ifdef ERTS_SMP if (is_normal_sched && (state & ERTS_PSFLG_PENDING_EXIT)) erts_handle_pending_exit(p, (ERTS_PROC_LOCK_MAIN + | ERTS_PROC_LOCK_TRACE | ERTS_PROC_LOCK_STATUS)); if (p->pending_suspenders) handle_pending_suspend(p, (ERTS_PROC_LOCK_MAIN + | ERTS_PROC_LOCK_TRACE | ERTS_PROC_LOCK_STATUS)); #endif @@ -9556,7 +9605,9 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) p->scheduler_data = NULL; #endif - erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS); + erts_smp_proc_unlock(p, (ERTS_PROC_LOCK_MAIN + | ERTS_PROC_LOCK_STATUS + | ERTS_PROC_LOCK_TRACE)); ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_OTHER); @@ -9984,6 +10035,11 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) if (state & (ERTS_PSFLG_ACTIVE_SYS | ERTS_PSFLG_PENDING_EXIT | ERTS_PSFLG_EXITING)) { + /* + * IMPORTANT! We need to take care of + * scheduled check-process-code requests + * before continuing with dirty execution! + */ /* Migrate to normal scheduler... */ goto sunlock_sched_out_proc; } @@ -10393,6 +10449,27 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds) } break; } +#ifdef ERTS_NEW_PURGE_STRATEGY + case ERTS_PSTT_CLA: { + int fcalls; + int cla_reds = 0; + if (!ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) + fcalls = reds; + else + fcalls = reds - CONTEXT_REDS; + st_res = erts_proc_copy_literal_area(c_p, + &cla_reds, + fcalls, + st->arg[0] == am_true); + reds -= cla_reds; + if (is_non_value(st_res)) { + /* Needed gc, but gc was disabled */ + save_gc_task(c_p, st, st_prio); + st = NULL; + } + break; + } +#endif case ERTS_PSTT_COHMQ: reds -= erts_complete_off_heap_message_queue_change(c_p); st_res = am_true; @@ -10443,14 +10520,15 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds) switch (st->type) { case ERTS_PSTT_GC: - st_res = am_false; - break; case ERTS_PSTT_CPC: - st_res = am_false; - break; case ERTS_PSTT_COHMQ: st_res = am_false; break; +#ifdef ERTS_NEW_PURGE_STRATEGY + case ERTS_PSTT_CLA: + st_res = am_ok; + break; +#endif #ifdef ERTS_SMP case ERTS_PSTT_FTMQ: reds -= erts_flush_trace_messages(c_p, ERTS_PROC_LOCK_MAIN); @@ -10471,22 +10549,83 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds) return reds; } -BIF_RETTYPE -erts_internal_request_system_task_3(BIF_ALIST_3) +#ifdef ERTS_DIRTY_SCHEDULERS + +static BIF_RETTYPE +dispatch_system_task(Process *c_p, erts_aint_t fail_state, + ErtsProcSysTask *st, Eterm target, + Eterm prio, Eterm operation) +{ + Process *rp; + ErtsProcLocks rp_locks = 0; + ErlOffHeap *ohp; + ErtsMessage *mp; + Eterm *hp, msg; + Uint hsz, osz; + BIF_RETTYPE ret; + + switch (st->type) { + case ERTS_PSTT_CPC: + rp = erts_dirty_process_code_checker; + ASSERT(fail_state & (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)); + if (c_p == rp) { + ERTS_BIF_PREP_RET(ret, am_dirty_execution); + return ret; + } + break; + default: + rp = NULL; + ERTS_INTERNAL_ERROR("Non-dispatchable system task"); + break; + } + + ERTS_BIF_PREP_RET(ret, am_ok); + + /* + * Send message on the form: {Requester, Target, Operation} + */ + + ASSERT(is_immed(st->requester)); + ASSERT(is_immed(target)); + ASSERT(is_immed(prio)); + + osz = size_object(operation); + hsz = 5 /* 4-tuple */ + osz; + + mp = erts_alloc_message_heap(rp, &rp_locks, hsz, &hp, &ohp); + + msg = copy_struct(operation, osz, &hp, ohp); + msg = TUPLE4(hp, st->requester, target, prio, msg); + + erts_queue_message(rp, rp_locks, mp, msg, st->requester); + + if (rp_locks) + erts_smp_proc_unlock(rp, rp_locks); + + return ret; +} + +#endif + +static BIF_RETTYPE +request_system_task(Process *c_p, Eterm requester, Eterm target, + Eterm priority, Eterm operation) { - Process *rp = erts_proc_lookup(BIF_ARG_1); + BIF_RETTYPE ret; + Process *rp = erts_proc_lookup(target); ErtsProcSysTask *st = NULL; - erts_aint32_t prio; + erts_aint32_t prio, fail_state = ERTS_PSFLG_EXITING; Eterm noproc_res, req_type; - if (!rp && !is_internal_pid(BIF_ARG_1)) { - if (!is_external_pid(BIF_ARG_1)) + if (!rp && !is_internal_pid(target)) { + if (!is_external_pid(target)) goto badarg; - if (external_pid_dist_entry(BIF_ARG_1) != erts_this_dist_entry) + if (external_pid_dist_entry(target) != erts_this_dist_entry) goto badarg; } - switch (BIF_ARG_2) { + switch (priority) { case am_max: prio = PRIORITY_MAX; break; case am_high: prio = PRIORITY_HIGH; break; case am_normal: prio = PRIORITY_NORMAL; break; @@ -10494,11 +10633,11 @@ erts_internal_request_system_task_3(BIF_ALIST_3) default: goto badarg; } - if (is_not_tuple(BIF_ARG_3)) + if (is_not_tuple(operation)) goto badarg; else { int i; - Eterm *tp = tuple_val(BIF_ARG_3); + Eterm *tp = tuple_val(operation); Uint arity = arityval(*tp); Eterm req_id; Uint req_id_sz; @@ -10536,7 +10675,7 @@ erts_internal_request_system_task_3(BIF_ALIST_3) ERTS_INIT_OFF_HEAP(&st->off_heap); hp = &st->heap[0]; - st->requester = BIF_P->common.id; + st->requester = requester; st->reply_tag = req_type; st->req_id_sz = req_id_sz; st->req_id = req_id_sz == 0 ? req_id : copy_struct(req_id, @@ -10570,26 +10709,85 @@ erts_internal_request_system_task_3(BIF_ALIST_3) st->type = ERTS_PSTT_CPC; if (!rp) goto noproc; +#ifdef ERTS_DIRTY_SCHEDULERS + /* + * If the process should start executing dirty + * code it is important that this task is + * aborted. Therefore this strict fail state... + */ + fail_state |= (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS); +#endif break; +#ifdef ERTS_NEW_PURGE_STRATEGY + case am_copy_literals: + if (st->arg[0] != am_true && st->arg[0] != am_false) + goto badarg; + st->type = ERTS_PSTT_CLA; + noproc_res = am_ok; + if (!rp) + goto noproc; + break; +#endif + default: goto badarg; } - if (!schedule_process_sys_task(rp, prio, st)) { - noproc: - notify_sys_task_executed(BIF_P, st, noproc_res); + if (!schedule_process_sys_task(rp, prio, st, &fail_state)) { + Eterm failure; + if (fail_state & ERTS_PSFLG_EXITING) { + noproc: + failure = noproc_res; + } +#ifdef ERTS_DIRTY_SCHEDULERS + else if (fail_state & (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)) { + ret = dispatch_system_task(c_p, fail_state, st, + target, priority, operation); + goto cleanup_return; + } +#endif + else { + ERTS_INTERNAL_ERROR("Unknown failure schedule_process_sys_task()"); + failure = am_internal_error; + } + notify_sys_task_executed(c_p, st, failure); } - BIF_RET(am_ok); + ERTS_BIF_PREP_RET(ret, am_ok); + + return ret; badarg: + ERTS_BIF_PREP_ERROR(ret, c_p, BADARG); + +#ifdef ERTS_DIRTY_SCHEDULERS +cleanup_return: +#endif + if (st) { erts_cleanup_offheap(&st->off_heap); erts_free(ERTS_ALC_T_PROC_SYS_TSK, st); } - BIF_ERROR(BIF_P, BADARG); + + return ret; +} + +BIF_RETTYPE +erts_internal_request_system_task_3(BIF_ALIST_3) +{ + return request_system_task(BIF_P, BIF_P->common.id, + BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); +} + +BIF_RETTYPE +erts_internal_request_system_task_4(BIF_ALIST_4) +{ + return request_system_task(BIF_P, BIF_ARG_1, + BIF_ARG_2, BIF_ARG_3, BIF_ARG_4); } static void @@ -10598,7 +10796,7 @@ erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type) Process *rp = erts_proc_lookup(pid); if (rp) { ErtsProcSysTask *st; - erts_aint32_t state; + erts_aint32_t state, fail_state; int i; st = erts_alloc(ERTS_ALC_T_PROC_SYS_TSK, @@ -10613,21 +10811,103 @@ erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type) ERTS_INIT_OFF_HEAP(&st->off_heap); state = erts_smp_atomic32_read_nob(&rp->state); - if (!schedule_process_sys_task(rp, ERTS_PSFLGS_GET_USR_PRIO(state), st)) + fail_state = ERTS_PSFLG_EXITING; + + if (!schedule_process_sys_task(rp, ERTS_PSFLGS_GET_USR_PRIO(state), + st, &fail_state)) erts_free(ERTS_ALC_T_PROC_SYS_TSK, st); } } + void erts_schedule_complete_off_heap_message_queue_change(Eterm pid) { erts_schedule_generic_sys_task(pid, ERTS_PSTT_COHMQ); } +#ifdef ERTS_DIRTY_SCHEDULERS + +static void +flush_dirty_trace_messages(void *vpid) +{ + Process *proc; + Eterm pid; +#ifdef ARCH_64 + pid = (Eterm) vpid; +#else + pid = *((Eterm *) vpid); + erts_free(ERTS_ALC_T_DIRTY_SL, vpid); +#endif + + proc = erts_proc_lookup(pid); + if (proc) + (void) erts_flush_trace_messages(proc, 0); +} + +#endif /* ERTS_DIRTY_SCHEDULERS */ + void -erts_schedule_flush_trace_messages(Eterm pid) +erts_schedule_flush_trace_messages(Process *proc, int force_on_proc) { +#ifdef ERTS_SMP + ErtsThrPrgrDelayHandle dhndl; +#endif + Eterm pid = proc->common.id; + +#ifdef ERTS_DIRTY_SCHEDULERS + erts_aint32_t state; + + if (!force_on_proc) { + state = erts_smp_atomic32_read_nob(&proc->state); + if (state & (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)) { + goto sched_flush_dirty; + } + } +#endif + +#ifdef ERTS_SMP + dhndl = erts_thr_progress_unmanaged_delay(); +#endif + erts_schedule_generic_sys_task(pid, ERTS_PSTT_FTMQ); + +#ifdef ERTS_SMP + erts_thr_progress_unmanaged_continue(dhndl); +#endif + +#ifdef ERTS_DIRTY_SCHEDULERS + if (!force_on_proc) { + state = erts_smp_atomic32_read_mb(&proc->state); + if (state & (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)) { + void *vargp; + + sched_flush_dirty: + /* + * We traced 'proc' from another thread than + * it is executing on, and it is executing + * on a dirty scheduler. It might take a + * significant amount of time before it is + * scheduled out (where it gets opportunity + * to flush messages). We therefore schedule + * the flush on the first ordinary scheduler. + */ + +#ifdef ARCH_64 + vargp = (void *) pid; +#else + { + Eterm *argp = erts_alloc(ERTS_ALC_T_DIRTY_SL, sizeof(Eterm)); + *argp = pid; + vargp = (void *) argp; + } +#endif + erts_schedule_misc_aux_work(1, flush_dirty_trace_messages, vargp); + } + } +#endif } static void diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 714e63e986..3347a7a60e 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1328,10 +1328,13 @@ ERTS_GLB_INLINE void erts_heap_frag_shrink(Process* p, Eterm* hp); ERTS_GLB_INLINE void erts_heap_frag_shrink(Process* p, Eterm* hp) { ErlHeapFragment* hf = MBUF(p); + Uint sz; ASSERT(hf!=NULL && (hp - hf->mem < hf->alloc_size)); - hf->used_size = hp - hf->mem; + sz = hp - hf->mem; + p->mbuf_sz -= hf->used_size - sz; + hf->used_size = sz; } #endif /* inline */ @@ -1549,9 +1552,9 @@ extern int erts_system_profile_ts_type; #define ERTS_SCHEDULER_IS_DIRTY(ESDP) \ ((ESDP)->dirty_no.s.num != 0) #define ERTS_SCHEDULER_IS_DIRTY_CPU(ESDP) \ - ((ESDP)->dirty_no.s.type == 0) + (ERTS_SCHEDULER_IS_DIRTY((ESDP)) & ((ESDP)->dirty_no.s.type == 0)) #define ERTS_SCHEDULER_IS_DIRTY_IO(ESDP) \ - ((ESDP)->dirty_no.s.type == 1) + (ERTS_SCHEDULER_IS_DIRTY((ESDP)) & ((ESDP)->dirty_no.s.type == 1)) #else #define ERTS_SCHEDULER_IS_DIRTY(ESDP) 0 #define ERTS_SCHEDULER_IS_DIRTY_CPU(ESDP) 0 @@ -1767,7 +1770,7 @@ void erts_schedule_thr_prgr_later_cleanup_op(void (*)(void *), ErtsThrPrgrLaterOp *, UWord); void erts_schedule_complete_off_heap_message_queue_change(Eterm pid); -void erts_schedule_flush_trace_messages(Eterm pid); +void erts_schedule_flush_trace_messages(Process *proc, int force_on_proc); int erts_flush_trace_messages(Process *c_p, ErtsProcLocks locks); #if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK) @@ -2473,6 +2476,35 @@ erts_get_atom_cache_map(Process *c_p) } #endif +#ifdef __WIN32__ +/* + * Don't want erts_time2reds() inlined in beam_emu.c on windows since + * it is compiled with gcc which fails on it. Implementation is in + * erl_process.c on windows. + */ +# define ERTS_TIME2REDS_IMPL__ erts_time2reds__ +#else +# define ERTS_TIME2REDS_IMPL__ erts_time2reds +#endif + +ERTS_GLB_INLINE Sint64 ERTS_TIME2REDS_IMPL__(ErtsMonotonicTime start, + ErtsMonotonicTime end); + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF +ERTS_GLB_INLINE Sint64 +ERTS_TIME2REDS_IMPL__(ErtsMonotonicTime start, ErtsMonotonicTime end) +{ + ErtsMonotonicTime time = end - start; + ASSERT(time >= 0); + time = ERTS_MONOTONIC_TO_USEC(time); + if (time == 0) + return (Sint64) 1; /* At least one reduction */ + /* Currently two reductions per micro second */ + time *= (CONTEXT_REDS-1)/1000 + 1; + return (Sint64) time; +} +#endif + Process *erts_pid2proc_suspend(Process *, ErtsProcLocks, Eterm, diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c index 21938e7684..26d71f573f 100644 --- a/erts/emulator/beam/erl_thr_progress.c +++ b/erts/emulator/beam/erl_thr_progress.c @@ -969,8 +969,10 @@ erts_thr_progress_unmanaged_continue__(ErtsThrPrgrDelayHandle handle) #ifdef ERTS_ENABLE_LOCK_CHECK ErtsThrPrgrData *tpd = perhaps_thr_prgr_data(NULL); ERTS_LC_ASSERT(tpd && tpd->is_delaying); - tpd->is_delaying = 0; - return_tmp_thr_prgr_data(tpd); + tpd->is_delaying--; + ASSERT(tpd->is_delaying >= 0); + if (!tpd->is_delaying) + return_tmp_thr_prgr_data(tpd); #endif ASSERT(!erts_thr_progress_is_managed_thread()); @@ -995,7 +997,7 @@ erts_thr_progress_unmanaged_delay__(void) #ifdef ERTS_ENABLE_LOCK_CHECK { ErtsThrPrgrData *tpd = tmp_thr_prgr_data(NULL); - tpd->is_delaying = 1; + tpd->is_delaying++; } #endif return (ErtsThrPrgrDelayHandle) umrefc_ix; diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index 9e37106b88..6aa2a7500f 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -2133,22 +2133,26 @@ time_unit_conversion(Process *c_p, Eterm term, ErtsMonotonicTime val, ErtsMonoto /* Convert to common user specified time units */ switch (term) { + case am_second: case am_seconds: case make_small(1): result = ERTS_MONOTONIC_TO_SEC(val) + muloff*ERTS_MONOTONIC_OFFSET_SEC; ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result)); break; + case am_millisecond: case am_milli_seconds: case make_small(1000): result = ERTS_MONOTONIC_TO_MSEC(val) + muloff*ERTS_MONOTONIC_OFFSET_MSEC; ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result)); break; + case am_microsecond: case am_micro_seconds: case make_small(1000*1000): result = ERTS_MONOTONIC_TO_USEC(val) + muloff*ERTS_MONOTONIC_OFFSET_USEC; ERTS_BIF_PREP_RET(ret, make_time_val(c_p, result)); break; #ifdef ARCH_64 + case am_nanosecond: case am_nano_seconds: case make_small(1000*1000*1000): result = ERTS_MONOTONIC_TO_NSEC(val) + muloff*ERTS_MONOTONIC_OFFSET_NSEC; @@ -2159,7 +2163,7 @@ time_unit_conversion(Process *c_p, Eterm term, ErtsMonotonicTime val, ErtsMonoto Eterm value, native_res; #ifndef ARCH_64 Sint user_res; - if (term == am_nano_seconds) + if (term == am_nanosecond || term == am_nano_seconds) goto to_nano_seconds; if (term_to_Sint(term, &user_res)) { if (user_res == 1000*1000*1000) { diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index c4396488f5..f4d92564c1 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -813,6 +813,9 @@ trace_send(Process *p, Eterm to, Eterm msg) ErtsTracerNif *tnif = NULL; ErtsTracingEvent* te; Eterm pam_result; +#ifdef ERTS_SMP + ErtsThrPrgrDelayHandle dhndl; +#endif ASSERT(ARE_TRACE_FLAGS_ON(p, F_TRACE_SEND)); @@ -837,6 +840,10 @@ trace_send(Process *p, Eterm to, Eterm msg) } else pam_result = am_true; +#ifdef ERTS_SMP + dhndl = erts_thr_progress_unmanaged_delay(); +#endif + if (is_internal_pid(to)) { if (!erts_proc_lookup(to)) goto send_to_non_existing_process; @@ -852,6 +859,11 @@ trace_send(Process *p, Eterm to, Eterm msg) send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_SEND, operation, msg, to, pam_result); } + +#ifdef ERTS_SMP + erts_thr_progress_unmanaged_continue(dhndl); +#endif + erts_match_set_release_result_trace(p, pam_result); } @@ -1167,6 +1179,8 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, Eterm transformed_args[MAX_ARG]; ErtsTracer pre_ms_tracer = erts_tracer_nil; + ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(p) & ERTS_PROC_LOCK_MAIN); + ASSERT(tracer); if (ERTS_TRACER_COMPARE(*tracer, erts_tracer_true)) { /* Breakpoint trace enabled without specifying tracer => diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 8933bcad16..e6dc5303a8 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -61,9 +61,12 @@ struct enif_environment_t /* ErlNifEnv */ extern void erts_pre_nif(struct enif_environment_t*, Process*, struct erl_module_nif*, Process* tracee); extern void erts_post_nif(struct enif_environment_t* env); +#ifdef ERTS_DIRTY_SCHEDULERS extern void erts_pre_dirty_nif(ErtsSchedulerData *, struct enif_environment_t*, Process*, - struct erl_module_nif*, Process* tracee); + struct erl_module_nif*); +extern void erts_post_dirty_nif(struct enif_environment_t* env); +#endif extern Eterm erts_nif_taints(Process* p); extern void erts_print_nif_taints(int to, void* to_arg); void erts_unload_nif(struct erl_module_nif* nif); @@ -998,17 +1001,30 @@ Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2); /* beam_bif_load.c */ #define ERTS_CPC_ALLOW_GC (1 << 0) -#define ERTS_CPC_COPY_LITERALS (1 << 1) -#define ERTS_CPC_ALL (ERTS_CPC_ALLOW_GC | ERTS_CPC_COPY_LITERALS) +#define ERTS_CPC_ALL ERTS_CPC_ALLOW_GC Eterm erts_check_process_code(Process *c_p, Eterm module, Uint flags, int *redsp, int fcalls); +#ifdef ERTS_NEW_PURGE_STRATEGY +Eterm erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed); +#endif -typedef struct { - Eterm *ptr; - Uint sz; - Eterm pid; -} copy_literals_t; +typedef struct ErtsLiteralArea_ { + struct erl_off_heap_header *off_heap; + Eterm *end; + Eterm start[1]; /* beginning of area */ +} ErtsLiteralArea; -extern copy_literals_t erts_clrange; +#define ERTS_LITERAL_AREA_ALLOC_SIZE(N) \ + (sizeof(ErtsLiteralArea) + sizeof(Eterm)*((N) - 1)) + +extern ErtsLiteralArea *erts_copy_literal_area; +#ifdef ERTS_NEW_PURGE_STRATEGY +extern Process *erts_literal_area_collector; +#endif +#ifdef ERTS_DIRTY_SCHEDULERS +extern Process *erts_dirty_process_code_checker; +#endif + +extern Process *erts_code_purger; /* beam_load.c */ typedef struct { @@ -1091,12 +1107,19 @@ typedef struct { #define INITIALIZE_SHCOPY(info) \ do { \ + ErtsLiteralArea *larea__ = erts_copy_literal_area; \ info.queue_start = info.queue_default; \ info.bitstore_start = info.bitstore_default; \ info.shtable_start = info.shtable_default; \ info.literal_size = 0; \ - info.range_ptr = erts_clrange.ptr; \ - info.range_sz = erts_clrange.sz; \ + if (larea__) { \ + info.range_ptr = &larea__->start[0]; \ + info.range_sz = larea__->end - info.range_ptr; \ + } \ + else { \ + info.range_ptr = NULL; \ + info.range_sz = 0; \ + } \ } while(0) #define DESTROY_SHCOPY(info) \ diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 85647b8500..6786657faf 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -102,6 +102,7 @@ erts_heap_alloc(Process* p, Uint need, Uint xtra) if (bp != NULL && need <= (bp->alloc_size - bp->used_size)) { Eterm* ret = bp->mem + bp->used_size; bp->used_size += need; + p->mbuf_sz += need; return ret; } #ifdef DEBUG @@ -124,7 +125,7 @@ erts_heap_alloc(Process* p, Uint need, Uint xtra) MBUF(p) = bp; bp->alloc_size = n; bp->used_size = need; - MBUF_SIZE(p) += n; + MBUF_SIZE(p) += need; bp->off_heap.first = NULL; bp->off_heap.overhead = 0; return bp->mem; diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index e394d84f73..b8a28bcc18 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -75,6 +75,7 @@ #include "erl_driver.h" #include "erl_alloc.h" #include "erl_msacc.h" +#include "erl_misc_utils.h" #if !defined(ERTS_POLL_USE_EPOLL) \ && !defined(ERTS_POLL_USE_DEVPOLL) \ @@ -2132,16 +2133,19 @@ get_timeout(ErtsPollSet ps, if (timeout > (ErtsMonotonicTime) INT_MAX) timeout = (ErtsMonotonicTime) INT_MAX; save_timeout_time += ERTS_MSEC_TO_MONOTONIC(timeout); + timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000); break; case 1000000: /* Round up to nearest even micro second */ timeout = ERTS_MONOTONIC_TO_USEC(diff_time - 1) + 1; save_timeout_time += ERTS_USEC_TO_MONOTONIC(timeout); + timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000*1000); break; case 1000000000: /* Round up to nearest even nano second */ timeout = ERTS_MONOTONIC_TO_NSEC(diff_time - 1) + 1; save_timeout_time += ERTS_NSEC_TO_MONOTONIC(timeout); + timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000*1000*1000); break; default: ERTS_INTERNAL_ERROR("Invalid resolution"); @@ -2452,7 +2456,15 @@ ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet ps, } #endif - res = check_fd_events(ps, to, no_fds); + while (1) { + res = check_fd_events(ps, to, no_fds); + if (res != 0) + break; + if (to == ERTS_POLL_NO_TIMEOUT) + break; + if (erts_get_monotonic_time(NULL) >= timeout_time) + break; + } woke_up(ps); diff --git a/erts/emulator/test/after_SUITE.erl b/erts/emulator/test/after_SUITE.erl index 4f20ad3656..b1f7e06bf5 100644 --- a/erts/emulator/test/after_SUITE.erl +++ b/erts/emulator/test/after_SUITE.erl @@ -223,7 +223,7 @@ recv_after_32bit(_, _) -> blaster() -> receive {go, TimeoutTime} -> - Tmo = TimeoutTime - erlang:monotonic_time(milli_seconds), + Tmo = TimeoutTime - erlang:monotonic_time(millisecond), receive after Tmo -> ok end end. @@ -234,7 +234,7 @@ spawn_blasters(N) -> receive_after_blast(Config) when is_list(Config) -> PMs = spawn_blasters(10000), - TimeoutTime = erlang:monotonic_time(milli_seconds) + 5000, + TimeoutTime = erlang:monotonic_time(millisecond) + 5000, lists:foreach(fun ({P, _}) -> P ! {go, TimeoutTime} end, PMs), lists:foreach(fun ({P, M}) -> receive diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 84cf4921d3..3a721095e2 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -342,7 +342,7 @@ start_node_1(Config, Opts) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). diff --git a/erts/emulator/test/busy_port_SUITE.erl b/erts/emulator/test/busy_port_SUITE.erl index bb0632ae08..7094cee992 100644 --- a/erts/emulator/test/busy_port_SUITE.erl +++ b/erts/emulator/test/busy_port_SUITE.erl @@ -469,12 +469,12 @@ hs_busy_pcmd(Prt, Opts, StartFun, EndFun) -> P = spawn_link(fun () -> erlang:yield(), Tester ! {self(), doing_port_command}, - Start = erlang:monotonic_time(micro_seconds), + Start = erlang:monotonic_time(microsecond), Res = try {return, port_command(Prt, [], Opts)} catch Exception:Error -> {Exception, Error} end, - End = erlang:monotonic_time(micro_seconds), + End = erlang:monotonic_time(microsecond), Time = round((End - Start)/1000), Tester ! {self(), port_command_result, Res, Time} end), @@ -717,7 +717,7 @@ run_command(_M,spawn,{Args,Opts}) -> run_command(M,spawn,Args) -> run_command(M,spawn,{Args,[]}); run_command(Mod,Func,Args) -> - erlang:display({{Mod,Func,Args}, erlang:system_time(micro_seconds)}), + erlang:display({{Mod,Func,Args}, erlang:system_time(microsecond)}), apply(Mod,Func,Args). validate_scenario(Data,[{print,Var}|T]) -> diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index 2347a3d4ef..34515efa3d 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -20,7 +20,8 @@ -module(code_SUITE). -export([all/0, suite/0, init_per_suite/1, end_per_suite/1, - versions/1,new_binary_types/1, + versions/1,new_binary_types/1, call_purged_fun_code_gone/1, + call_purged_fun_code_reload/1, call_purged_fun_code_there/1, t_check_process_code/1,t_check_old_code/1, t_check_process_code_ets/1, external_fun/1,get_chunk/1,module_md5/1,make_stub/1, @@ -34,7 +35,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [versions, new_binary_types, t_check_process_code, + [versions, new_binary_types, call_purged_fun_code_gone, + call_purged_fun_code_reload, call_purged_fun_code_there, t_check_process_code, t_check_process_code_ets, t_check_old_code, external_fun, get_chunk, module_md5, make_stub, make_stub_many_funs, constant_pools, constant_refc_binaries, false_dependency, @@ -127,12 +129,169 @@ new_binary_types(Config) when is_list(Config) -> bit_sized_binary(Bin))), ok. +call_purged_fun_code_gone(Config) when is_list(Config) -> + Priv = proplists:get_value(priv_dir, Config), + Data = proplists:get_value(data_dir, Config), + call_purged_fun_test(Priv, Data, code_gone), + ok. + +call_purged_fun_code_reload(Config) when is_list(Config) -> + Priv = proplists:get_value(priv_dir, Config), + Data = proplists:get_value(data_dir, Config), + Path = code:get_path(), + true = code:add_path(Priv), + try + call_purged_fun_test(Priv, Data, code_reload) + after + code:set_path(Path) + end, + ok. + +call_purged_fun_code_there(Config) when is_list(Config) -> + Priv = proplists:get_value(priv_dir, Config), + Data = proplists:get_value(data_dir, Config), + call_purged_fun_test(Priv, Data, code_there), + ok. + +call_purged_fun_test(Priv, Data, Type) -> + File = filename:join(Data, "my_code_test2"), + Code = filename:join(Priv, "my_code_test2"), + + catch erlang:purge_module(my_code_test2), + catch erlang:delete_module(my_code_test2), + catch erlang:purge_module(my_code_test2), + + {ok,my_code_test2} = c:c(File, [{outdir,Priv}]), + + T = ets:new(my_code_test2_fun_table, []), + ets:insert(T, {my_fun,my_code_test2:make_fun(4711)}), + ets:insert(T, {my_fun2,my_code_test2:make_fun2()}), + + spawn(fun () -> + [{my_fun2,F2}] = ets:lookup(T, my_fun2), + F2(fun () -> + receive after infinity -> ok end + end, + fun () -> ok end), + exit(completed) + end), + + PurgeType = case Type of + code_gone -> + ok = file:delete(Code++".beam"), + true; + code_reload -> + true; + code_there -> + false + end, + + true = erlang:delete_module(my_code_test2), + + Purge = start_purge(my_code_test2, PurgeType), + + {P0, M0} = spawn_monitor(fun () -> + [{my_fun,F}] = ets:lookup(T, my_fun), + 4712 = F(1), + exit(completed) + end), + + wait_until(fun () -> + {status, suspended} + == process_info(P0, status) + end), + + ok = continue_purge(Purge), + + {P1, M1} = spawn_monitor(fun () -> + [{my_fun,F}] = ets:lookup(T, my_fun), + 4713 = F(2), + exit(completed) + end), + {P2, M2} = spawn_monitor(fun () -> + [{my_fun,F}] = ets:lookup(T, my_fun), + 4714 = F(3), + exit(completed) + end), + + wait_until(fun () -> + {status, suspended} + == process_info(P1, status) + end), + wait_until(fun () -> + {status, suspended} + == process_info(P2, status) + end), + + {current_function, + {erts_code_purger, + pending_purge_lambda, + 3}} = process_info(P0, current_function), + {current_function, + {erts_code_purger, + pending_purge_lambda, + 3}} = process_info(P1, current_function), + {current_function, + {erts_code_purger, + pending_purge_lambda, + 3}} = process_info(P2, current_function), + + case Type of + code_there -> + false = complete_purge(Purge); + _ -> + {true, true} = complete_purge(Purge) + end, + + case Type of + code_gone -> + receive + {'DOWN', M0, process, P0, Reason0} -> + {undef, _} = Reason0 + end, + receive + {'DOWN', M1, process, P1, Reason1} -> + {undef, _} = Reason1 + end, + receive + {'DOWN', M2, process, P2, Reason2} -> + {undef, _} = Reason2 + end; + _ -> + receive + {'DOWN', M0, process, P0, Reason0} -> + completed = Reason0 + end, + receive + {'DOWN', M1, process, P1, Reason1} -> + completed = Reason1 + end, + receive + {'DOWN', M2, process, P2, Reason2} -> + completed = Reason2 + end, + catch erlang:purge_module(my_code_test2), + catch erlang:delete_module(my_code_test2), + catch erlang:purge_module(my_code_test2) + end, + ok. + t_check_process_code(Config) when is_list(Config) -> + case check_process_code_handle(indirect_references) of + false -> {skipped, "check_process_code() ignores funs"}; + true -> t_check_process_code_test(Config) + end. + +t_check_process_code_test(Config) -> Priv = proplists:get_value(priv_dir, Config), Data = proplists:get_value(data_dir, Config), File = filename:join(Data, "my_code_test"), Code = filename:join(Priv, "my_code_test"), + catch erlang:purge_module(my_code_test), + catch erlang:delete_module(my_code_test), + catch erlang:purge_module(my_code_test), + {ok,my_code_test} = c:c(File, [{outdir,Priv}]), MyFun = fun(X, Y) -> X + Y end, %Confuse things. @@ -231,11 +390,16 @@ gc1() -> ok. %% Test check_process_code/2 in combination with a fun obtained from an ets table. t_check_process_code_ets(Config) when is_list(Config) -> - case test_server:is_native(?MODULE) of - true -> - {skip,"Native code"}; - false -> - do_check_process_code_ets(Config) + case check_process_code_handle(indirect_references) of + false -> + {skipped, "check_process_code() ignores funs"}; + true -> + case test_server:is_native(?MODULE) of + true -> + {skip,"Native code"}; + false -> + do_check_process_code_ets(Config) + end end. do_check_process_code_ets(Config) -> @@ -243,8 +407,9 @@ do_check_process_code_ets(Config) -> Data = proplists:get_value(data_dir, Config), File = filename:join(Data, "my_code_test"), - erlang:purge_module(my_code_test), - erlang:delete_module(my_code_test), + catch erlang:purge_module(my_code_test), + catch erlang:delete_module(my_code_test), + catch erlang:purge_module(my_code_test), {ok,my_code_test} = c:c(File, [{outdir,Priv}]), T = ets:new(my_code_test, []), @@ -295,8 +460,8 @@ t_check_old_code(Config) when is_list(Config) -> Data = proplists:get_value(data_dir, Config), File = filename:join(Data, "my_code_test"), - erlang:purge_module(my_code_test), - erlang:delete_module(my_code_test), + catch erlang:purge_module(my_code_test), + catch erlang:delete_module(my_code_test), catch erlang:purge_module(my_code_test), false = erlang:check_old_code(my_code_test), @@ -971,3 +1136,39 @@ flush() -> receive _ -> flush() after 0 -> ok end. id(I) -> I. + +check_process_code_handle(What) -> + lists:member(What, erlang:system_info(check_process_code)). + +wait_until(Fun) -> + case Fun() of + true -> + ok; + false -> + receive after 100 -> ok end, + wait_until(Fun) + end. + +start_purge(Mod, Type) when is_atom(Mod) + andalso ((Type == true) + orelse (Type == false)) -> + Ref = make_ref(), + erts_code_purger ! {test_purge, Mod, self(), Type, Ref}, + receive + {started, Ref} -> + Ref + end. + +continue_purge(Ref) when is_reference(Ref) -> + erts_code_purger ! {continue, Ref}, + receive + {continued, Ref} -> + ok + end. + +complete_purge(Ref) when is_reference(Ref) -> + erts_code_purger ! {complete, Ref}, + receive + {test_purge, Res, Ref} -> + Res + end. diff --git a/erts/emulator/test/code_SUITE_data/my_code_test.erl b/erts/emulator/test/code_SUITE_data/my_code_test.erl index d2386157d6..9d12aa9897 100644 --- a/erts/emulator/test/code_SUITE_data/my_code_test.erl +++ b/erts/emulator/test/code_SUITE_data/my_code_test.erl @@ -24,5 +24,3 @@ make_fun(A) -> fun(X) -> A + X end. - - diff --git a/erts/emulator/test/code_SUITE_data/my_code_test2.erl b/erts/emulator/test/code_SUITE_data/my_code_test2.erl new file mode 100644 index 0000000000..57973535d4 --- /dev/null +++ b/erts/emulator/test/code_SUITE_data/my_code_test2.erl @@ -0,0 +1,32 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2016. 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(my_code_test2). + +-export([make_fun/1, make_fun2/0]). + +make_fun(A) -> + fun(X) -> A + X end. + +make_fun2() -> + fun (F1,F2) -> + F1(), + F2() + end. diff --git a/erts/emulator/test/dirty_nif_SUITE.erl b/erts/emulator/test/dirty_nif_SUITE.erl index 83b098a704..658bdc41b6 100644 --- a/erts/emulator/test/dirty_nif_SUITE.erl +++ b/erts/emulator/test/dirty_nif_SUITE.erl @@ -33,7 +33,8 @@ dirty_nif_exception/1, call_dirty_nif_exception/1, dirty_scheduler_exit/1, dirty_call_while_terminated/1, dirty_heap_access/1, dirty_process_info/1, - dirty_process_register/1, dirty_process_trace/1]). + dirty_process_register/1, dirty_process_trace/1, + code_purge/1, dirty_nif_send_traced/1]). -define(nif_stub,nif_stub_error(?LINE)). @@ -48,7 +49,9 @@ all() -> dirty_heap_access, dirty_process_info, dirty_process_register, - dirty_process_trace]. + dirty_process_trace, + code_purge, + dirty_nif_send_traced]. init_per_suite(Config) -> try erlang:system_info(dirty_cpu_schedulers) of @@ -145,9 +148,9 @@ dirty_scheduler_exit(Config) when is_list(Config) -> [ok] = mcall(Node, [fun() -> ok = erlang:load_nif(NifLib, []), - Start = erlang:monotonic_time(milli_seconds), + Start = erlang:monotonic_time(millisecond), ok = test_dirty_scheduler_exit(), - End = erlang:monotonic_time(milli_seconds), + End = erlang:monotonic_time(millisecond), io:format("Time=~p ms~n", [End-Start]), ok end]), @@ -230,7 +233,11 @@ dirty_call_while_terminated(Config) when is_list(Config) -> process_info(self(), binary))), process_flag(trap_exit, OT), - ok. + try + blipp:blupp(Bin) + catch + _ : _ -> ok + end. dirty_heap_access(Config) when is_list(Config) -> {ok, Node} = start_node(Config), @@ -349,6 +356,103 @@ dirty_process_trace(Config) when is_list(Config) -> ok end). +dirty_code_test_code() -> + " +-module(dirty_code_test). + +-export([func/1]). + +func(Fun) -> + Fun(), + blipp:blapp(). + +". + +code_purge(Config) when is_list(Config) -> + Path = ?config(data_dir, Config), + File = filename:join(Path, "dirty_code_test.erl"), + ok = file:write_file(File, dirty_code_test_code()), + {ok, dirty_code_test, Bin} = compile:file(File, [binary]), + {module, dirty_code_test} = erlang:load_module(dirty_code_test, Bin), + Start = erlang:monotonic_time(), + {Pid1, Mon1} = spawn_monitor(fun () -> + dirty_code_test:func(fun () -> + %% Sleep for 6 seconds + %% in dirty nif... + dirty_sleeper() + end) + end), + {module, dirty_code_test} = erlang:load_module(dirty_code_test, Bin), + {Pid2, Mon2} = spawn_monitor(fun () -> + dirty_code_test:func(fun () -> + %% Sleep for 6 seconds + %% in dirty nif... + dirty_sleeper() + end) + end), + receive + {'DOWN', Mon1, process, Pid1, _} -> + ct:fail(premature_death) + after 100 -> + ok + end, + true = erlang:purge_module(dirty_code_test), + receive + {'DOWN', Mon1, process, Pid1, Reason1} -> + killed = Reason1 + end, + receive + {'DOWN', Mon2, process, Pid2, _} -> + ct:fail(premature_death) + after 100 -> + ok + end, + true = erlang:delete_module(dirty_code_test), + receive + {'DOWN', Mon2, process, Pid2, _} -> + ct:fail(premature_death) + after 100 -> + ok + end, + true = erlang:purge_module(dirty_code_test), + receive + {'DOWN', Mon2, process, Pid2, Reason2} -> + killed = Reason2 + end, + End = erlang:monotonic_time(), + Time = erlang:convert_time_unit(End-Start, native, milli_seconds), + io:format("Time=~p~n", [Time]), + true = Time =< 1000, + ok. + +dirty_nif_send_traced(Config) when is_list(Config) -> + Parent = self(), + Rcvr = spawn_link(fun() -> + Self = self(), + receive {ok, Self} -> ok end, + Parent ! {Self, received} + end), + Sndr = spawn_link(fun () -> + receive {Parent, go} -> ok end, + {ok, Rcvr} = send_wait_from_dirty_nif(Rcvr), + Parent ! {self(), sent} + end), + 1 = erlang:trace(Sndr, true, [send]), + Start = erlang:monotonic_time(), + Sndr ! {self(), go}, + receive {trace, Sndr, send, {ok, Rcvr}, Rcvr} -> ok end, + receive {Rcvr, received} -> ok end, + End1 = erlang:monotonic_time(), + Time1 = erlang:convert_time_unit(End1-Start, native, 1000), + io:format("Time1: ~p milliseconds~n", [Time1]), + true = Time1 < 500, + receive {Sndr, sent} -> ok end, + End2 = erlang:monotonic_time(), + Time2 = erlang:convert_time_unit(End2-Start, native, 1000), + io:format("Time2: ~p milliseconds~n", [Time2]), + true = Time2 >= 1900, + ok. + %% %% Internal... %% @@ -400,7 +504,7 @@ start_node(Config, Args) when is_list(Config) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). @@ -431,6 +535,7 @@ mcall(Node, Funs) -> lib_loaded() -> false. call_dirty_nif(_,_,_) -> ?nif_stub. send_from_dirty_nif(_) -> ?nif_stub. +send_wait_from_dirty_nif(_) -> ?nif_stub. call_dirty_nif_exception(_) -> ?nif_stub. call_dirty_nif_zero_args() -> ?nif_stub. dirty_call_while_terminated_nif(_) -> ?nif_stub. diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c index d92933a096..a0019e5d95 100644 --- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c +++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c @@ -100,6 +100,32 @@ static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_ return result; } +static ERL_NIF_TERM send_wait_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM result; + ErlNifPid pid; + ErlNifEnv* menv; + int res; + + if (!enif_get_local_pid(env, argv[0], &pid)) + return enif_make_badarg(env); + result = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_pid(env, &pid)); + menv = enif_alloc_env(); + res = enif_send(env, &pid, menv, result); + enif_free_env(menv); + +#ifdef __WIN32__ + Sleep(2000); +#else + sleep(2); +#endif + + if (!res) + return enif_make_badarg(env); + else + return result; +} + static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { switch (argc) { @@ -237,6 +263,7 @@ static ErlNifFunc nif_funcs[] = {"lib_loaded", 0, lib_loaded}, {"call_dirty_nif", 3, call_dirty_nif}, {"send_from_dirty_nif", 1, send_from_dirty_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + {"send_wait_from_dirty_nif", 1, send_wait_from_dirty_nif, ERL_NIF_DIRTY_JOB_IO_BOUND}, {"call_dirty_nif_exception", 1, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND}, {"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND}, {"dirty_sleeper", 0, dirty_sleeper, ERL_NIF_DIRTY_JOB_IO_BOUND}, diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index 159fbc806b..6994bfef83 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -1950,7 +1950,7 @@ port_please(_Name, _Ip) -> %%% Utilities timestamp() -> - erlang:monotonic_time(milli_seconds). + erlang:monotonic_time(millisecond). start_node(X) -> start_node(X, [], []). @@ -1974,7 +1974,7 @@ start_node(Config, Args, Rel) when is_list(Config), is_list(Rel) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive])))), start_node(Name, Args, Rel). @@ -2091,7 +2091,7 @@ node_monitor(Master) -> Master ! {nodeup, node(), Node} end, Nodes0), - io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Nodes0]), + io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Nodes0]), node_monitor_loop(Master); false -> net_kernel:monitor_nodes(false, Opts), @@ -2112,7 +2112,7 @@ node_monitor_loop(Master) -> receive {nodeup, Node, _InfoList} = Msg -> Master ! {nodeup, node(), Node}, - io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]), + io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Msg]), node_monitor_loop(Master); {nodedown, Node, InfoList} = Msg -> Reason = case lists:keysearch(nodedown_reason, 1, InfoList) of @@ -2120,7 +2120,7 @@ node_monitor_loop(Master) -> _ -> undefined end, Master ! {nodedown, node(), Node, Reason}, - io:format("~p ~p: ~p~n", [node(), erlang:system_time(micro_seconds), Msg]), + io:format("~p ~p: ~p~n", [node(), erlang:system_time(microsecond), Msg]), node_monitor_loop(Master) end. diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index f134a197aa..1df72193a6 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -114,7 +114,7 @@ -define(MAX_DATA_SIZE, 16384). % This is the allowed delay when testing the driver timer functionality --define(delay, 100). +-define(delay, 400). -define(heap_binary_size, 64). @@ -401,7 +401,7 @@ try_timeouts(Port, Timeout) -> true -> try_timeouts(Port, Timeout div 2) end - after Timeout + ?delay -> + after Timeout + 100*?delay -> ct:fail("driver failed to timeout") end. @@ -437,7 +437,7 @@ try_cancel(Port, Timeout) -> Timeout == 0 -> ok; true -> try_cancel(Port, Timeout div 2) end - after ?delay -> + after 100*?delay -> ct:fail("No message from driver") end end. @@ -505,7 +505,7 @@ try_change_timer(Port, Timeout) -> true -> try_timeouts(Port, Timeout div 2) end - after Timeout + ?delay -> + after Timeout + 100*?delay -> ct:fail("driver failed to timeout") end. @@ -2427,7 +2427,7 @@ erl_millisecs() -> erl_millisecs(erlang:monotonic_time()). erl_millisecs(MonotonicTime) -> - (1000*MonotonicTime)/erlang:convert_time_unit(1,seconds,native). + (1000*MonotonicTime)/erlang:convert_time_unit(1,second,native). %% Start/stop drivers. start_driver(Config, Name, Binary) -> @@ -2481,7 +2481,7 @@ start_node(Config) when is_list(Config) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), test_server:start_node(Name, slave, [{args, "-pa "++Pa}]). diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl index 93d2065ba3..89e1aefb50 100644 --- a/erts/emulator/test/erl_link_SUITE.erl +++ b/erts/emulator/test/erl_link_SUITE.erl @@ -1011,7 +1011,7 @@ get_names(N, T, Acc) -> ++ "-" ++ atom_to_list(T) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))) | Acc]). diff --git a/erts/emulator/test/estone_SUITE.erl b/erts/emulator/test/estone_SUITE.erl index 1180a45585..3ce849b88e 100644 --- a/erts/emulator/test/estone_SUITE.erl +++ b/erts/emulator/test/estone_SUITE.erl @@ -364,7 +364,7 @@ monotonic_time() -> try erlang:monotonic_time() catch error:undef -> erlang:now() end. subtr(Before, After) when is_integer(Before), is_integer(After) -> - erlang:convert_time_unit(After-Before, native, micro_seconds); + erlang:convert_time_unit(After-Before, native, microsecond); subtr({_,_,_}=Before, {_,_,_}=After) -> timer:now_diff(After, Before). diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl index e85addae3a..36b1f9179f 100644 --- a/erts/emulator/test/float_SUITE.erl +++ b/erts/emulator/test/float_SUITE.erl @@ -276,7 +276,7 @@ start_node(Config) when is_list(Config) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), test_server:start_node(Name, slave, [{args, "-pa "++Pa}]). diff --git a/erts/emulator/test/long_timers_test.erl b/erts/emulator/test/long_timers_test.erl index 7c055a31f9..c9a380a229 100644 --- a/erts/emulator/test/long_timers_test.erl +++ b/erts/emulator/test/long_timers_test.erl @@ -27,11 +27,16 @@ %%% Created : 21 Aug 2006 by Rickard Green <[email protected]> %%%------------------------------------------------------------------- +-define(HIGH_CPU_INFO, "Ignored due to high CPU utilization."). +-define(MISSING_CPU_INFO, "Ignored due to missing CPU utilization information."). -define(MAX_TIMEOUT, 60). % Minutes --define(MAX_LATE_MS, 15*1000). % Milliseconds +-define(MAX_LATE_MS, 1000). % Milliseconds -define(REG_NAME, '___LONG___TIMERS___TEST___SERVER___'). +-define(HIGH_UTIL, 96.0). +-define(UTIL_INTERVAL, 10000). + -define(DRV_NAME, timer_driver). % First byte in communication with the timer driver @@ -72,52 +77,149 @@ check_result() -> receive {'DOWN', Mon, process, _, Reason} -> {?REG_NAME, 'DOWN', Reason}; - {result, ?REG_NAME, TORs, Start, End} -> + {result, ?REG_NAME, TORs, Start, End, UtilData} -> erlang:demonitor(Mon), receive {'DOWN', Mon, _, _, _} -> ok after 0 -> ok end, stop_node(Node), - check(TORs, ms((End - Start) - max_late()), ok) + Res = check(TORs, Start, End, UtilData, ms((End - Start) - max_late()), ok), + io:format("Start = ~p~n End = ~p~n UtilData = ~p~n", [Start, End, UtilData]), + Res end. +res(New, Old) when New == failed; Old == failed -> + failed; +res(New, Old) when New == missing_cpu_info; Old == missing_cpu_info -> + missing_cpu_info; +res(New, Old) when New == high_cpu; Old == high_cpu -> + high_cpu; +res(New, _Old) -> + New. + check([#timeout_rec{timeout = Timeout, type = Type, timeout_diff = undefined} | TORs], + Start, + End, + UtilData, NeedRes, - _Ok) when Timeout < NeedRes -> - io:format("~p timeout = ~p ms failed! No timeout.~n", - [Type, Timeout]), - check(TORs, NeedRes, failed); + Ok) when Timeout < NeedRes -> + {NewOk, HCPU} = case had_high_cpu_util(Start, + Timeout, + End - Timeout*1000, + UtilData) of + yes -> {res(high_cpu, Ok), ?HIGH_CPU_INFO}; + no -> {res(failed, Ok), ""}; + missing -> {res(missing_cpu_info, Ok), "FAILED", ?MISSING_CPU_INFO} + end, + io:format("~p timeout = ~p ms FAILED! No timeout. ~s~n", + [Type, Timeout, HCPU]), + check(TORs, Start, End, UtilData, NeedRes, NewOk); check([#timeout_rec{timeout_diff = undefined} | TORs], + Start, + End, + UtilData, NeedRes, Ok) -> - check(TORs, NeedRes, Ok); + check(TORs, Start, End, UtilData, NeedRes, Ok); check([#timeout_rec{timeout = Timeout, type = Type, timeout_diff = {error, Reason}} | TORs], + Start, + End, + UtilData, NeedRes, _Ok) -> - io:format("~p timeout = ~p ms failed! exit reason ~p~n", + io:format("~p timeout = ~p ms FAILED! exit reason ~p~n", [Type, Timeout, Reason]), - check(TORs, NeedRes, failed); + check(TORs, Start, End, UtilData, NeedRes, failed); check([#timeout_rec{timeout = Timeout, type = Type, timeout_diff = TimeoutDiff} | TORs], + Start, + End, + UtilData, NeedRes, Ok) -> - {NewOk, SuccessStr} = case ((0 =< TimeoutDiff) - andalso (TimeoutDiff =< max_late())) of - true -> {Ok, "succeeded"}; - false -> {failed, "FAILED"} + {NewOk, SuccessStr, HCPU} = case {(0 =< TimeoutDiff), + (TimeoutDiff =< max_late())} of + {true, true} -> + {res(ok, Ok), "succeeded", ""}; + {false, _} -> + {res(failed, Ok), "FAILED", ""}; + _ -> + case had_high_cpu_util(Start, + Timeout, + TimeoutDiff, + UtilData) of + yes -> {res(high_cpu, Ok), "FAILED", ?HIGH_CPU_INFO}; + no -> {res(failed, Ok), "FAILED", ""}; + missing -> {res(missing_cpu_info, Ok), "FAILED", ?MISSING_CPU_INFO} + end end, - io:format("~s timeout = ~s ms ~s! timeout diff = ~s.~n", + io:format("~s timeout = ~s ms ~s! timeout diff = ~s. ~s~n", [type_str(Type), time_str(Timeout), SuccessStr, - time_str(TimeoutDiff, erlang:convert_time_unit(1, seconds, native))]), - check(TORs, NeedRes, NewOk); -check([], _NeedRes, Ok) -> + time_str(TimeoutDiff, 1000000), + HCPU]), + check(TORs, Start, End, UtilData, NeedRes, NewOk); +check([],_Start,_End,_UtilData,_NeedRes, Ok) -> Ok. +% TargetTimeout in ms, other in us. +had_high_cpu_util(StartTime, + TargetTimeout, + TimeoutDiff, + UtilData) -> + TargetTo = StartTime + TargetTimeout*1000, + ActTo = TargetTo + TimeoutDiff, + hcpu(ActTo, TargetTo, UtilData). + +hcpu(_ActTo, _TargetTo, [{UT, 0} | _] = UD) -> + missing; %% Util is the integer zero when not supported... +%% UT2 =:= UT1 +hcpu(ActTo, TargetTo, [{UT, _}, {UT, _} | _] = UD) -> + hcpu(ActTo, TargetTo, tl(UD)); +%% UT2 > UT1 > ActTo > TargetTo +hcpu(ActTo, TargetTo, [{_UT2, _}, {UT1, _} | _] = UD) when UT1 > ActTo -> + hcpu(ActTo, TargetTo, tl(UD)); +%% UT2 >= ActTo > TargetTo >= UT1 +hcpu(ActTo, TargetTo, + [{UT2, U}, {UT1, _} | _]) when UT2 >= ActTo, + TargetTo >= UT1 -> + case U >= (((ActTo - TargetTo) / (UT2 - UT1)) + * (?HIGH_UTIL/100.0)) of + true -> yes; + false -> no + end; +%% UT2 >= ActTo >= UT1 > TargetTo +hcpu(ActTo, TargetTo, + [{UT2, U}, {UT1, _} | _] = UD) when UT2 >= ActTo, + ActTo >= UT1, + UT1 > TargetTo -> + case U >= (((ActTo - UT1) / (UT2 - UT1)) + * (?HIGH_UTIL/100.0)) of + true -> hcpu(ActTo, TargetTo, tl(UD)); + false -> no + end; +%% ActTo > UT2 >= TargetTo >= UT1 +hcpu(ActTo, TargetTo, + [{UT2, U}, {UT1, _} | _]) when ActTo > UT2, + TargetTo >= UT1 -> + case U >= (((UT2 - TargetTo) / (UT2 - UT1)) + * (?HIGH_UTIL/100.0)) of + true -> yes; + false -> no + end; +%% ActTo > UT2 > UT1 > TargetTo +hcpu(ActTo, TargetTo, + [{UT2, U}, {UT1, _} | _] = UD) when ActTo > UT2, + UT1 > TargetTo -> + case U >= ?HIGH_UTIL of + true -> hcpu(ActTo, TargetTo, tl(UD)); + false -> no + end. + type_str(receive_after) -> "receive ... after"; type_str(bif_timer) -> "BIF timer"; type_str(driver) -> "driver". @@ -142,24 +244,24 @@ unit_str(Res) -> Res. to_diff(Timeout, Start, Stop) -> %% 'Timeout' in milli seconds - %% 'Start', 'Stop', and result in native unit - (Stop - Start) - erlang:convert_time_unit(Timeout, milli_seconds, native). + %% 'Start', 'Stop', and result in micro seconds + (Stop - Start) - Timeout*1000. ms(Time) -> - erlang:convert_time_unit(Time, native, milli_seconds). + erlang:convert_time_unit(Time, microsecond, millisecond). max_late() -> - erlang:convert_time_unit(?MAX_LATE_MS, milli_seconds, native). + erlang:convert_time_unit(?MAX_LATE_MS, millisecond, microsecond). receive_after(Timeout) -> - Start = erlang:monotonic_time(), + Start = erlang:monotonic_time(microsecond), receive {get_result, ?REG_NAME} -> ?REG_NAME ! #timeout_rec{pid = self(), type = receive_after, timeout = Timeout} after Timeout -> - Stop = erlang:monotonic_time(), + Stop = erlang:monotonic_time(microsecond), receive {get_result, ?REG_NAME} -> ?REG_NAME ! #timeout_rec{pid = self(), @@ -174,7 +276,7 @@ receive_after(Timeout) -> driver(Timeout) -> Port = open_port({spawn, ?DRV_NAME},[]), link(Port), - Start = erlang:monotonic_time(), + Start = erlang:monotonic_time(microsecond), erlang:port_command(Port, <<?START_TIMER, Timeout:32>>), receive {get_result, ?REG_NAME} -> @@ -182,7 +284,7 @@ driver(Timeout) -> type = driver, timeout = Timeout}; {Port,{data,[?TIMER]}} -> - Stop = erlang:monotonic_time(), + Stop = erlang:monotonic_time(microsecond), unlink(Port), true = erlang:port_close(Port), receive @@ -197,7 +299,7 @@ driver(Timeout) -> end. bif_timer(Timeout) -> - Start = erlang:monotonic_time(), + Start = erlang:monotonic_time(microsecond), Tmr = erlang:start_timer(Timeout, self(), ok), receive {get_result, ?REG_NAME} -> @@ -205,7 +307,7 @@ bif_timer(Timeout) -> type = bif_timer, timeout = Timeout}; {timeout, Tmr, ok} -> - Stop = erlang:monotonic_time(), + Stop = erlang:monotonic_time(microsecond), receive {get_result, ?REG_NAME} -> ?REG_NAME ! #timeout_rec{pid = self(), @@ -218,13 +320,22 @@ bif_timer(Timeout) -> end. test(Starter, DrvDir, StartDone) -> + process_flag(priority, high), erl_ddll:start(), ok = load_driver(DrvDir, ?DRV_NAME), process_flag(trap_exit, true), register(?REG_NAME, self()), {group_leader, GL} = process_info(whereis(net_kernel),group_leader), group_leader(GL, self()), - Start = erlang:monotonic_time(), + try + application:start(sasl), + application:start(os_mon) + catch + _ : _ -> + ok + end, + UtilData = new_util(), + Start = erlang:monotonic_time(microsecond), TORs = lists:map(fun (Min) -> TO = Min*60*1000, [#timeout_rec{pid = spawn_opt( @@ -252,16 +363,27 @@ test(Starter, DrvDir, StartDone) -> lists:seq(1, ?MAX_TIMEOUT)), FlatTORs = lists:flatten(TORs), Starter ! StartDone, - test_loop(FlatTORs, Start). + test_loop(FlatTORs, Start, UtilData). + +new_util() -> + new_util([]). + +new_util(UtilData) -> + Util = cpu_sup:util(), + Time = erlang:monotonic_time(microsecond), + [{Time, Util} | UtilData]. -test_loop(TORs, Start) -> +test_loop(TORs, Start, UtilData) -> receive {get_result, ?REG_NAME, Pid} -> - End = erlang:monotonic_time(), - Pid ! {result, ?REG_NAME, get_test_results(TORs), Start, End}, + End = erlang:monotonic_time(microsecond), + EndUtilData = new_util(UtilData), + Pid ! {result, ?REG_NAME, get_test_results(TORs), Start, End, EndUtilData}, erl_ddll:unload_driver(?DRV_NAME), erl_ddll:stop(), exit(bye) + after ?UTIL_INTERVAL -> + test_loop(TORs, Start, new_util(UtilData)) end. get_test_results(TORs) -> diff --git a/erts/emulator/test/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl index 44e77dfad0..e084b9482d 100644 --- a/erts/emulator/test/message_queue_data_SUITE.erl +++ b/erts/emulator/test/message_queue_data_SUITE.erl @@ -200,7 +200,7 @@ start_node(Config, Opts) when is_list(Config), is_list(Opts) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), test_server:start_node(Name, slave, [{args, Opts++" -pa "++Pa}]). diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index 90d2bd8c5d..827ed817cc 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -697,7 +697,7 @@ mixer(Config) when is_list(Config) -> named_down(Config) when is_list(Config) -> Name = list_to_atom(atom_to_list(?MODULE) ++ "-named_down-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), Prio = process_flag(priority,high), %% Spawn a bunch of high prio cpu bound processes to prevent diff --git a/erts/emulator/test/mtx_SUITE.erl b/erts/emulator/test/mtx_SUITE.erl index 1493e52655..12928ed6d8 100644 --- a/erts/emulator/test/mtx_SUITE.erl +++ b/erts/emulator/test/mtx_SUITE.erl @@ -443,7 +443,7 @@ hammer_ets_rwlock_test(XOpts, UW, C, N, NP, SC) -> {'DOWN', M, process, P, _} -> ok end end, Ps), - Res = (Stop-Start)/erlang:convert_time_unit(1,seconds,native), + Res = (Stop-Start)/erlang:convert_time_unit(1,second,native), Caller ! {?MODULE, self(), Res} end, TP = spawn_link(T), diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index a0e9f1bad6..8df2733fac 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1443,6 +1443,17 @@ otp_9828_loop(Bin, Val) -> consume_timeslice(Config) when is_list(Config) -> + case {erlang:system_info(debug_compiled), + erlang:system_info(lock_checking)} of + {false, false} -> + consume_timeslice_test(Config); + {false, true} -> + {skipped, "Lock checking enabled"}; + _ -> + {skipped, "Debug compiled"} + end. + +consume_timeslice_test(Config) when is_list(Config) -> CONTEXT_REDS = 2000, Me = self(), Go = make_ref(), @@ -1692,7 +1703,7 @@ nif_raise_exceptions(NifFunc) -> end, ok, ExcTerms). -define(ERL_NIF_TIME_ERROR, -9223372036854775808). --define(TIME_UNITS, [seconds, milli_seconds, micro_seconds, nano_seconds]). +-define(TIME_UNITS, [second, millisecond, microsecond, nanosecond]). nif_monotonic_time(Config) -> ?ERL_NIF_TIME_ERROR = monotonic_time(invalid_time_unit), @@ -1758,8 +1769,8 @@ chk_toffs([TU|TUs]) -> chk_toffs(TUs). nif_convert_time_unit(Config) -> - ?ERL_NIF_TIME_ERROR = convert_time_unit(0, seconds, invalid_time_unit), - ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, seconds), + ?ERL_NIF_TIME_ERROR = convert_time_unit(0, second, invalid_time_unit), + ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, second), ?ERL_NIF_TIME_ERROR = convert_time_unit(0, invalid_time_unit, invalid_time_unit), lists:foreach(fun (Offset) -> lists:foreach(fun (Diff) -> @@ -1808,7 +1819,7 @@ nif_convert_time_unit(Config) -> ctu_loop(0) -> ok; ctu_loop(N) -> - chk_ctu(erlang:monotonic_time(nano_seconds)), + chk_ctu(erlang:monotonic_time(nanosecond)), ctu_loop(N-1). chk_ctu(Time) -> @@ -1823,7 +1834,7 @@ chk_ctu(Time, [FromTU|FromTUs]) -> chk_ctu(_Time, _FromTU, []) -> ok; chk_ctu(Time, FromTU, [ToTU|ToTUs]) -> - T = erlang:convert_time_unit(Time, nano_seconds, FromTU), + T = erlang:convert_time_unit(Time, nanosecond, FromTU), TE = erlang:convert_time_unit(T, FromTU, ToTU), TN = convert_time_unit(T, FromTU, ToTU), case TE =:= TN of diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 13846244d4..f2b1ef9d24 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -38,10 +38,10 @@ static ERL_NIF_TERM atom_self; static ERL_NIF_TERM atom_ok; static ERL_NIF_TERM atom_join; static ERL_NIF_TERM atom_binary_resource_type; -static ERL_NIF_TERM atom_seconds; -static ERL_NIF_TERM atom_milli_seconds; -static ERL_NIF_TERM atom_micro_seconds; -static ERL_NIF_TERM atom_nano_seconds; +static ERL_NIF_TERM atom_second; +static ERL_NIF_TERM atom_millisecond; +static ERL_NIF_TERM atom_microsecond; +static ERL_NIF_TERM atom_nanosecond; typedef struct @@ -147,10 +147,10 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_ok = enif_make_atom(env,"ok"); atom_join = enif_make_atom(env,"join"); atom_binary_resource_type = enif_make_atom(env,"binary_resource_type"); - atom_seconds = enif_make_atom(env,"seconds"); - atom_milli_seconds = enif_make_atom(env,"milli_seconds"); - atom_micro_seconds = enif_make_atom(env,"micro_seconds"); - atom_nano_seconds = enif_make_atom(env,"nano_seconds"); + atom_second = enif_make_atom(env,"second"); + atom_millisecond = enif_make_atom(env,"millisecond"); + atom_microsecond = enif_make_atom(env,"microsecond"); + atom_nanosecond = enif_make_atom(env,"nanosecond"); *priv_data = data; return 0; @@ -1808,13 +1808,13 @@ static ERL_NIF_TERM monotonic_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM if (argc != 1) return atom_false; - if (enif_compare(argv[0], atom_seconds) == 0) + if (enif_compare(argv[0], atom_second) == 0) time_unit = ERL_NIF_SEC; - else if (enif_compare(argv[0], atom_milli_seconds) == 0) + else if (enif_compare(argv[0], atom_millisecond) == 0) time_unit = ERL_NIF_MSEC; - else if (enif_compare(argv[0], atom_micro_seconds) == 0) + else if (enif_compare(argv[0], atom_microsecond) == 0) time_unit = ERL_NIF_USEC; - else if (enif_compare(argv[0], atom_nano_seconds) == 0) + else if (enif_compare(argv[0], atom_nanosecond) == 0) time_unit = ERL_NIF_NSEC; else time_unit = 4711; /* invalid time unit */ @@ -1829,13 +1829,13 @@ static ERL_NIF_TERM time_offset(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg if (argc != 1) return atom_false; - if (enif_compare(argv[0], atom_seconds) == 0) + if (enif_compare(argv[0], atom_second) == 0) time_unit = ERL_NIF_SEC; - else if (enif_compare(argv[0], atom_milli_seconds) == 0) + else if (enif_compare(argv[0], atom_millisecond) == 0) time_unit = ERL_NIF_MSEC; - else if (enif_compare(argv[0], atom_micro_seconds) == 0) + else if (enif_compare(argv[0], atom_microsecond) == 0) time_unit = ERL_NIF_USEC; - else if (enif_compare(argv[0], atom_nano_seconds) == 0) + else if (enif_compare(argv[0], atom_nanosecond) == 0) time_unit = ERL_NIF_NSEC; else time_unit = 4711; /* invalid time unit */ @@ -1856,24 +1856,24 @@ static ERL_NIF_TERM convert_time_unit(ErlNifEnv* env, int argc, const ERL_NIF_TE val = (ErlNifTime) i64; - if (enif_compare(argv[1], atom_seconds) == 0) + if (enif_compare(argv[1], atom_second) == 0) from = ERL_NIF_SEC; - else if (enif_compare(argv[1], atom_milli_seconds) == 0) + else if (enif_compare(argv[1], atom_millisecond) == 0) from = ERL_NIF_MSEC; - else if (enif_compare(argv[1], atom_micro_seconds) == 0) + else if (enif_compare(argv[1], atom_microsecond) == 0) from = ERL_NIF_USEC; - else if (enif_compare(argv[1], atom_nano_seconds) == 0) + else if (enif_compare(argv[1], atom_nanosecond) == 0) from = ERL_NIF_NSEC; else from = 4711; /* invalid time unit */ - if (enif_compare(argv[2], atom_seconds) == 0) + if (enif_compare(argv[2], atom_second) == 0) to = ERL_NIF_SEC; - else if (enif_compare(argv[2], atom_milli_seconds) == 0) + else if (enif_compare(argv[2], atom_millisecond) == 0) to = ERL_NIF_MSEC; - else if (enif_compare(argv[2], atom_micro_seconds) == 0) + else if (enif_compare(argv[2], atom_microsecond) == 0) to = ERL_NIF_USEC; - else if (enif_compare(argv[2], atom_nano_seconds) == 0) + else if (enif_compare(argv[2], atom_nanosecond) == 0) to = ERL_NIF_NSEC; else to = 4711; /* invalid time unit */ diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl index 536c91d4ae..af18545bff 100644 --- a/erts/emulator/test/node_container_SUITE.erl +++ b/erts/emulator/test/node_container_SUITE.erl @@ -1092,7 +1092,7 @@ wait_until(Pred) -> get_nodefirstname_string() -> atom_to_list(?MODULE) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive])). diff --git a/erts/emulator/test/old_scheduler_SUITE.erl b/erts/emulator/test/old_scheduler_SUITE.erl index f91d84beea..ffe7d40139 100644 --- a/erts/emulator/test/old_scheduler_SUITE.erl +++ b/erts/emulator/test/old_scheduler_SUITE.erl @@ -309,13 +309,13 @@ receiver(T0, TimeSec, Main, {P1,P1N}, {P2,P2N}) -> %% uncomment lines below to get life sign (debug) receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 0) -> - % T = erlang:convert_time_unit(erlang:monotonic_time() - T0, native, milli_seconds), + % T = erlang:convert_time_unit(erlang:monotonic_time() - T0, native, millisecond), % erlang:display({round(T/1000),P1Rs,P2Rs}), receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, 100000); receiver(T0, Time, Main, P1,P1N,P1Rs, P2,P2N,P2Rs, C) -> Remain = Time - erlang:convert_time_unit(erlang:monotonic_time() - T0, - native, milli_seconds), % test time remaining + native, millisecond), % test time remaining Remain1 = if Remain < 0 -> 0; true -> diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index a9814d7df9..4323849465 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -1986,7 +1986,7 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> Parent = self(), io:format("SleepSecs = ~p~n", [SleepSecs]), PortProg = "sleep " ++ integer_to_list(SleepSecs), - Start = erlang:monotonic_time(micro_seconds), + Start = erlang:monotonic_time(microsecond), NoProcs = case NoSchedsOnln of NProcs when NProcs < ?EXIT_STATUS_MSB_MAX_PROCS -> NProcs; @@ -2060,12 +2060,12 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> receive {P, started, SIds} -> SIds end end, Procs), - StartedTime = (erlang:monotonic_time(micro_seconds) - Start)/1000000, + StartedTime = (erlang:monotonic_time(microsecond) - Start)/1000000, io:format("StartedTime = ~p~n", [StartedTime]), true = StartedTime < SleepSecs, erlang:system_flag(multi_scheduling, block), lists:foreach(fun (P) -> receive {P, done} -> ok end end, Procs), - DoneTime = (erlang:monotonic_time(micro_seconds) - Start)/1000000, + DoneTime = (erlang:monotonic_time(microsecond) - Start)/1000000, io:format("DoneTime = ~p~n", [DoneTime]), true = DoneTime > SleepSecs, ok = verify_multi_scheduling_blocked(), diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index dae8990f56..0f999e0efe 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -372,7 +372,7 @@ eat_high(Low) -> process_flag(priority, high), receive after 1000 -> ok end, exit(Low, {you, are, dead}), - loop(erlang:monotonic_time() + erlang:convert_time_unit(5,seconds,native)). + loop(erlang:monotonic_time() + erlang:convert_time_unit(5,second,native)). %% Busy loop for 5 seconds. @@ -2376,7 +2376,7 @@ no_priority_inversion2(Config) when is_list(Config) -> [{priority, max}, monitor, link]) end, lists:seq(1, 2*erlang:system_info(schedulers))), - receive after 500 -> ok end, + receive after 2000 -> ok end, {PL, ML} = spawn_opt(fun () -> tok_loop() end, @@ -2567,7 +2567,7 @@ start_node(Config, Args) when is_list(Config) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index f18d79d770..3aee15a8fc 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -793,13 +793,13 @@ update_cpu_info(Config) when is_list(Config) -> io:format("START - Affinity mask: ~p - Schedulers online: ~p - Scheduler bindings: ~p~n", [OldAff, OldOnline, erlang:system_info(scheduler_bindings)]), case {erlang:system_info(logical_processors_available), OldAff} of - {Avail, _} when Avail == unknown; OldAff == unknown -> + {Avail, _} when Avail == unknown; OldAff == unknown; OldAff == 1 -> %% Nothing much to test; just a smoke test case erlang:system_info(update_cpu_info) of unchanged -> ok; changed -> ok end; - _ -> + {Avail, _} -> try adjust_schedulers_online(), case erlang:system_info(schedulers_online) of @@ -810,7 +810,7 @@ update_cpu_info(Config) when is_list(Config) -> %% unset least significant bit Aff = (OldAff band (OldAff - 1)), set_affinity_mask(Aff), - Onln1 = Onln0 - 1, + Onln1 = Avail - 1, case adjust_schedulers_online() of {Onln0, Onln1} -> Onln1 = erlang:system_info(schedulers_online), @@ -1072,20 +1072,27 @@ scheduler_threads(Config) when is_list(Config) -> {Sched, HalfSchedOnln, _} = get_sstate(Config, "+SP:50"), %% Configure 2x scheduler threads only {TwiceSched, SchedOnln, _} = get_sstate(Config, "+SP 200"), - %% Test resetting the scheduler counts - ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0", - {Sched, SchedOnln, _} = get_sstate(Config, ResetCmd), - %% Test negative +S settings, but only for SMP-enabled emulators - case SmpSupport of - false -> ok; - true -> - SchedMinus1 = Sched-1, - SchedOnlnMinus1 = SchedOnln-1, - {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"), - {Sched, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"), - {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1") - end, - ok. + case {erlang:system_info(logical_processors), + erlang:system_info(logical_processors_available)} of + {LProc, LProcAvail} when is_integer(LProc), is_integer(LProcAvail) -> + %% Test resetting the scheduler counts + ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0", + {LProc, LProcAvail, _} = get_sstate(Config, ResetCmd), + %% Test negative +S settings, but only for SMP-enabled emulators + case {SmpSupport, LProc > 1, LProcAvail > 1} of + {true, true, true} -> + SchedMinus1 = LProc-1, + SchedOnlnMinus1 = LProcAvail-1, + {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"), + {LProc, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"), + {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1"), + ok; + _ -> + {comment, "Skipped reduced amount of schedulers test due to too few logical processors"} + end; + _ -> %% Skipped when missing info about logical processors... + {comment, "Skipped reset amount of schedulers test, and reduced amount of schedulers test due to too unknown amount of logical processors"} + end. dirty_scheduler_threads(Config) when is_list(Config) -> SmpSupport = erlang:system_info(smp_support), @@ -1312,11 +1319,33 @@ scheduler_suspend_test(Config, Schedulers) -> true -> ok end, - erlang:system_info(schedulers_state) + until(fun () -> + {_A, B, C} = erlang:system_info( + schedulers_state), + B == C + end, + erlang:monotonic_time() + + erlang:convert_time_unit(1, + seconds, + native)), + erlang:system_info(schedulers_state) end]), stop_node(Node), ok. - + +until(Pred, MaxTime) -> + case Pred() of + true -> + true; + false -> + case erlang:monotonic_time() > MaxTime of + true -> + false; + false -> + receive after 100 -> ok end, + until(Pred, MaxTime) + end + end. sst0_loop(0) -> ok; @@ -1978,10 +2007,10 @@ do_it(Tracer, Low, Normal, High, Max) -> do_it(Tracer, Low, Normal, High, Max, RedsPerSchedLimit) -> OldPrio = process_flag(priority, max), go_work(Low, Normal, High, Max), - StartWait = erlang:monotonic_time(milli_seconds), + StartWait = erlang:monotonic_time(millisecond), %% Give the emulator a chance to balance the load... wait_balance(5), - EndWait = erlang:monotonic_time(milli_seconds), + EndWait = erlang:monotonic_time(millisecond), BalanceWait = EndWait-StartWait, erlang:display({balance_wait, BalanceWait}), Timeout = (15 - 4)*60*1000 - BalanceWait, @@ -2181,7 +2210,7 @@ start_node(Config, Args) when is_list(Config) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]). diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl index 0b11fa13f5..7e516176f7 100644 --- a/erts/emulator/test/signal_SUITE.erl +++ b/erts/emulator/test/signal_SUITE.erl @@ -484,7 +484,7 @@ repeat(Fun, N) when is_integer(N) -> start_node(Config) -> Name = list_to_atom(atom_to_list(?MODULE) ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) - ++ "-" ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), Pa = filename:dirname(code:which(?MODULE)), test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]). diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl index 042c7225d5..5eccdc562b 100644 --- a/erts/emulator/test/smoke_test_SUITE.erl +++ b/erts/emulator/test/smoke_test_SUITE.erl @@ -152,7 +152,7 @@ start_node(Config, Args) when is_list(Config) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), Opts = [{args, "-pa "++Pa++" "++Args}], diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl index 71ef003b25..a1f12ba93c 100644 --- a/erts/emulator/test/statistics_SUITE.erl +++ b/erts/emulator/test/statistics_SUITE.erl @@ -129,11 +129,15 @@ do_runtime_update(0) -> {comment,"Never close enough"}; do_runtime_update(N) -> {T1,Diff0} = statistics(runtime), - spawn_link(fun cpu_heavy/0), + {CPUHog, CPUHogMon} = spawn_opt(fun cpu_heavy/0,[link,monitor]), receive after 1000 -> ok end, {T2,Diff} = statistics(runtime), + unlink(CPUHog), + exit(CPUHog, kill), + true = is_integer(T1+T2+Diff0+Diff), io:format("T1 = ~p, T2 = ~p, Diff = ~p, T2-T1 = ~p", [T1,T2,Diff,T2-T1]), + receive {'DOWN',CPUHogMon,process,CPUHog,_} -> ok end, if T2 - T1 =:= Diff, 900 =< Diff, Diff =< 1500 -> ok; true -> do_runtime_update(N-1) @@ -311,8 +315,17 @@ scheduler_wall_time(Config) when is_list(Config) -> true -> exit({fullload, FullLoad}) end, - [exit(Pid, kill) || Pid <- [P1|HalfHogs++LastHogs]], + KillHog = fun (HP) -> + HPM = erlang:monitor(process, HP), + exit(HP, kill), + receive + {'DOWN', HPM, process, HP, killed} -> + ok + end + end, + [KillHog(Pid) || Pid <- [P1|HalfHogs++LastHogs]], AfterLoad = get_load(), + io:format("AfterLoad=~p~n", [AfterLoad]), {false,_} = {lists:any(fun(Load) -> Load > 25 end, AfterLoad),AfterLoad}, true = erlang:system_flag(scheduler_wall_time, false) after diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index f31d474c20..a4aedb31f6 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -508,7 +508,7 @@ start_node(Config, Envs) when is_list(Config) -> ++ "-" ++ atom_to_list(proplists:get_value(testcase, Config)) ++ "-" - ++ integer_to_list(erlang:system_time(seconds)) + ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive]))), test_server:start_node(Name, peer, [{args, "-pa "++Pa}, {env, Envs}]). diff --git a/erts/emulator/test/time_SUITE.erl b/erts/emulator/test/time_SUITE.erl index 87b8c62cfa..9501569814 100644 --- a/erts/emulator/test/time_SUITE.erl +++ b/erts/emulator/test/time_SUITE.erl @@ -295,7 +295,7 @@ timestamp(Config) when is_list(Config) -> os_system_time_offset() -> erlang:convert_time_unit(os:system_time() - erlang:monotonic_time(), - native, micro_seconds). + native, microsecond). had_time_warp(Secs) -> had_time_warp(os_system_time_offset(), Secs). @@ -488,12 +488,12 @@ check_time_warp_mode(Config, TimeCorrection, TimeWarpMode) -> MonotonicTimeUnit = rpc:call(Node, erlang, convert_time_unit, - [1, seconds, native]), + [1, second, native]), UpMilliSeconds = erlang:convert_time_unit(MonotonicTime - StartTime, MonotonicTimeUnit, - milli_seconds), + millisecond), io:format("UpMilliSeconds=~p~n", [UpMilliSeconds]), - End = erlang:monotonic_time(milli_seconds), + End = erlang:monotonic_time(millisecond), stop_node(Node), try true = (UpMilliSeconds > (98*MonotonicityTimeout) div 100), @@ -810,10 +810,10 @@ do_check_erlang_timestamp(Done, Mon, TO) -> MaxMon = erlang:monotonic_time(), TsMin = erlang:convert_time_unit(MinMon+TO, native, - micro_seconds), + microsecond), TsMax = erlang:convert_time_unit(MaxMon+TO, native, - micro_seconds), + microsecond), TsTime = (MegaSec*1000000+Sec)*1000000+MicroSec, case (TsMin =< TsTime) andalso (TsTime =< TsMax) of true -> diff --git a/erts/emulator/test/timer_bif_SUITE.erl b/erts/emulator/test/timer_bif_SUITE.erl index a5f11bd959..7cbd93a0f3 100644 --- a/erts/emulator/test/timer_bif_SUITE.erl +++ b/erts/emulator/test/timer_bif_SUITE.erl @@ -74,7 +74,7 @@ all() -> %% Basic start_timer/3 functionality start_timer_1(Config) when is_list(Config) -> Ref1 = erlang:start_timer(1000, self(), plopp), - ok = get(1100, {timeout, Ref1, plopp}), + ok = get(1400, {timeout, Ref1, plopp}), false = erlang:read_timer(Ref1), false = erlang:cancel_timer(Ref1), @@ -83,12 +83,12 @@ start_timer_1(Config) when is_list(Config) -> Ref2 = erlang:start_timer(1000, self(), plapp), Left2 = erlang:cancel_timer(Ref2), UpperLimit = 1000, - true = (Left2 > 900) and (Left2 =< UpperLimit), + true = (Left2 > 600) and (Left2 =< UpperLimit), empty = get_msg(), false = erlang:cancel_timer(Ref2), Ref3 = erlang:start_timer(1000, self(), plopp), - no_message = get(900, {timeout, Ref3, plopp}), + no_message = get(600, {timeout, Ref3, plopp}), ok. %% Basic send_after/3 functionality @@ -489,7 +489,7 @@ registered_process(Config) when is_list(Config) -> same_time_yielding(Config) when is_list(Config) -> Mem = mem(), SchdlrsOnln = erlang:system_info(schedulers_online), - Tmo = erlang:monotonic_time(milli_seconds) + 3000, + Tmo = erlang:monotonic_time(millisecond) + 3000, Tmrs = lists:map(fun (I) -> process_flag(scheduler, (I rem SchdlrsOnln) + 1), erlang:start_timer(Tmo, self(), hej, [{abs, true}]) @@ -497,7 +497,7 @@ same_time_yielding(Config) when is_list(Config) -> lists:seq(1, (?TIMEOUT_YIELD_LIMIT*3+1)*SchdlrsOnln)), true = mem_larger_than(Mem), lists:foreach(fun (Tmr) -> receive {timeout, Tmr, hej} -> ok end end, Tmrs), - Done = erlang:monotonic_time(milli_seconds), + Done = erlang:monotonic_time(millisecond), true = Done >= Tmo, case erlang:system_info(build_type) of opt -> true = Done < Tmo + 200; @@ -517,10 +517,10 @@ same_time_yielding_with_cancel_other(Config) when is_list(Config) -> do_cancel_tmrs(Tmo, Tmrs, Tester) -> BeginCancel = erlang:convert_time_unit(Tmo, - milli_seconds, - micro_seconds) - 100, + millisecond, + microsecond) - 100, busy_wait_until(fun () -> - erlang:monotonic_time(micro_seconds) >= BeginCancel + erlang:monotonic_time(microsecond) >= BeginCancel end), lists:foreach(fun (Tmr) -> erlang:cancel_timer(Tmr, @@ -535,7 +535,7 @@ do_cancel_tmrs(Tmo, Tmrs, Tester) -> same_time_yielding_with_cancel_test(Other, Accessor) -> Mem = mem(), SchdlrsOnln = erlang:system_info(schedulers_online), - Tmo = erlang:monotonic_time(milli_seconds) + 3000, + Tmo = erlang:monotonic_time(millisecond) + 3000, Tester = self(), Cancelor = case Other of false -> @@ -656,7 +656,7 @@ get_msg() -> start_slave() -> Pa = filename:dirname(code:which(?MODULE)), Name = atom_to_list(?MODULE) - ++ "-" ++ integer_to_list(erlang:system_time(seconds)) + ++ "-" ++ integer_to_list(erlang:system_time(second)) ++ "-" ++ integer_to_list(erlang:unique_integer([positive])), {ok, Node} = test_server:start_node(Name, slave, [{args, "-pa " ++ Pa}]), Node. diff --git a/erts/emulator/test/trace_bif_SUITE.erl b/erts/emulator/test/trace_bif_SUITE.erl index 491b37ae46..f60c777ba1 100644 --- a/erts/emulator/test/trace_bif_SUITE.erl +++ b/erts/emulator/test/trace_bif_SUITE.erl @@ -289,9 +289,9 @@ receive_trace_msg_ts_return_to({trace_ts, Pid, return_to, {M,F,A}}, PrevTs, TsTy make_ts(timestamp) -> erlang:now(); make_ts(monotonic_timestamp) -> - erlang:monotonic_time(nano_seconds); + erlang:monotonic_time(nanosecond); make_ts(strict_monotonic_timestamp) -> - MT = erlang:monotonic_time(nano_seconds), + MT = erlang:monotonic_time(nanosecond), UMI = erlang:unique_integer([monotonic]), {MT, UMI}. diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl index 6582ad134b..26f96a1766 100644 --- a/erts/emulator/test/trace_call_time_SUITE.erl +++ b/erts/emulator/test/trace_call_time_SUITE.erl @@ -294,7 +294,7 @@ combo(Config) when is_list(Config) -> T0 = erlang:monotonic_time(), with_bif(Nbc), T1 = erlang:monotonic_time(), - TimeB = erlang:convert_time_unit(T1-T0, native, micro_seconds), + TimeB = erlang:convert_time_unit(T1-T0, native, microsecond), %% List = collect(100), @@ -655,13 +655,13 @@ execute(Pids, Mfa) when is_list(Pids) -> [P ! {self(), execute, Mfa} || P <- Pids], As = [receive {P, answer, Answer} -> Answer end || P <- Pids], T1 = erlang:monotonic_time(), - {As, erlang:convert_time_unit(T1-T0, native, micro_seconds)}; + {As, erlang:convert_time_unit(T1-T0, native, microsecond)}; execute(P, Mfa) -> T0 = erlang:monotonic_time(), P ! {self(), execute, Mfa}, A = receive {P, answer, Answer} -> Answer end, T1 = erlang:monotonic_time(), - {A, erlang:convert_time_unit(T1-T0, native, micro_seconds)}. + {A, erlang:convert_time_unit(T1-T0, native, microsecond)}. diff --git a/erts/emulator/test/z_SUITE.erl b/erts/emulator/test/z_SUITE.erl index d1085c1958..ab56018373 100644 --- a/erts/emulator/test/z_SUITE.erl +++ b/erts/emulator/test/z_SUITE.erl @@ -191,7 +191,13 @@ node_container_refc_check(Config) when is_list(Config) -> ok. long_timers(Config) when is_list(Config) -> - ok = long_timers_test:check_result(). + case long_timers_test:check_result() of + ok -> ok; + high_cpu -> {comment, "Ignored failures due to high CPU utilization"}; + missing_cpu_info -> {comment, "Ignored failures due to missing CPU utilization information"}; + Fail -> ct:fail(Fail) + end. + pollset_size(Config) when is_list(Config) -> Name = pollset_size_testcase_initial_state_holder, diff --git a/erts/example/time_compat.erl b/erts/example/time_compat.erl index b87c6cc550..589781c8e8 100644 --- a/erts/example/time_compat.erl +++ b/erts/example/time_compat.erl @@ -272,6 +272,10 @@ system_flag(Flag, Value) -> %% integer_time_unit(native) -> 1000*1000; +integer_time_unit(nanosecond) -> 1000*1000*1000; +integer_time_unit(microsecond) -> 1000*1000; +integer_time_unit(millisecond) -> 1000; +integer_time_unit(second) -> 1; integer_time_unit(nano_seconds) -> 1000*1000*1000; integer_time_unit(micro_seconds) -> 1000*1000; integer_time_unit(milli_seconds) -> 1000; diff --git a/erts/include/internal/erl_misc_utils.h b/erts/include/internal/erl_misc_utils.h index a4a5d1d510..55566ddf74 100644 --- a/erts/include/internal/erl_misc_utils.h +++ b/erts/include/internal/erl_misc_utils.h @@ -56,4 +56,33 @@ int erts_map_win_error_to_errno(DWORD win_error); int erts_get_last_win_errno(void); #endif +#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) +#define __DARWIN__ 1 +#endif + +/* + * ERTS_PREMATURE_TIMEOUT() expects time units + * 1000 (millisec), 1000000 (microsec), or + * 1000000000 (nanosec). Might not work properly + * otherwise. + */ +#undef ERTS_USE_PREMATURE_TIMEOUT +#undef ERTS_PREMATURE_TIMEOUT + +#if defined(__DARWIN__) +#define ERTS_USE_PREMATURE_TIMEOUT 1 +#define ERTS_PREMATURE_TIMEOUT(TMO, TU) \ + ((TMO) >= 1 * ((TU) / 1000) \ + ? ((TMO) >= 20 * ((TU) / 1000) \ + ? 15 * ((TU) / 1000) \ + : ((TMO) >= 5 * ((TU) / 1000) \ + ? 3 * ((TU) / 1000) \ + : 5 * ((TU) / 10000))) \ + : 0) + +#else +#define ERTS_USE_PREMATURE_TIMEOUT 0 +#define ERTS_PREMATURE_TIMEOUT(TMO, TU) (0) +#endif + #endif /* #ifndef ERL_MISC_UTILS_H_ */ diff --git a/erts/lib_src/pthread/ethr_event.c b/erts/lib_src/pthread/ethr_event.c index eef88d5002..464875570a 100644 --- a/erts/lib_src/pthread/ethr_event.c +++ b/erts/lib_src/pthread/ethr_event.c @@ -184,6 +184,8 @@ return_event_on: #include <errno.h> #include <string.h> +#include "erl_misc_utils.h" + #ifdef __DARWIN__ struct ethr_event_fdsets___ { @@ -346,12 +348,9 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) ethr_sint32_t val; int res, ulres; int until_yield = ETHR_YIELD_AFTER_BUSY_LOOPS; - ethr_sint64_t time = 0; /* SHUT UP annoying faulty warning... */ + ethr_sint64_t time = 0; #ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME - ethr_sint64_t start = 0; /* SHUT UP annoying faulty warning... */ -#endif -#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC - struct timespec cond_timeout; + ethr_sint64_t timeout_time = 0; /* SHUT UP annoying faulty warning... */ #endif val = ethr_atomic32_read(&e->state); @@ -365,30 +364,27 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) if (timeout == 0) return ETIMEDOUT; else { - time = timeout; - switch (e->fd[0]) { - case ETHR_EVENT_INVALID_FD__: #ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME - start = ethr_get_monotonic_time(); + timeout_time = ethr_get_monotonic_time(); + timeout_time += timeout; #endif + switch (e->fd[0]) { + case ETHR_EVENT_INVALID_FD__: + time = timeout; setup_nonblocking_pipe(e); break; #ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC case ETHR_EVENT_COND_TIMEDWAIT__: - time += ethr_get_monotonic_time(); - cond_timeout.tv_sec = time / (1000*1000*1000); - cond_timeout.tv_nsec = time % (1000*1000*1000); + time = -1; if (spincount == 0) goto set_event_off_waiter; break; #endif default: + time = timeout; /* Already initialized pipe... */ if (spincount == 0) goto set_select_timeout; -#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME - start = ethr_get_monotonic_time(); -#endif break; } } @@ -418,6 +414,9 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) || e->fd[0] == ETHR_EVENT_COND_TIMEDWAIT__ #endif ) { +#ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC + struct timespec cond_timeout; +#endif set_event_off_waiter: @@ -446,9 +445,35 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) #ifdef ETHR_HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC if (timeout > 0) { + if (time != timeout_time) { + time = timeout_time; + +#if ERTS_USE_PREMATURE_TIMEOUT + { + ethr_sint64_t rtmo; + + rtmo = timeout_time - ethr_get_monotonic_time(); + if (rtmo <= 0) { + res = ETIMEDOUT; + break; + } + time = timeout_time; + time -= ERTS_PREMATURE_TIMEOUT(rtmo, 1000*1000*1000); + } +#endif + + cond_timeout.tv_sec = time / (1000*1000*1000); + cond_timeout.tv_nsec = time % (1000*1000*1000); + } res = pthread_cond_timedwait(&e->cnd, &e->mtx, &cond_timeout); - if (res == EINTR || res == ETIMEDOUT) + if (res == EINTR + || (res == ETIMEDOUT +#if ERTS_USE_PREMATURE_TIMEOUT + && time == timeout_time +#endif + )) { break; + } } else #endif @@ -477,7 +502,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) struct timeval select_timeout; #ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME - time -= ethr_get_monotonic_time() - start; +#if ERTS_USE_PREMATURE_TIMEOUT + restart_select: +#endif + + time = timeout_time - ethr_get_monotonic_time(); if (time <= 0) return ETIMEDOUT; #endif @@ -492,6 +521,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) */ time = ((time - 1) / 1000) + 1; +#if defined(ETHR_HAVE_ETHR_GET_MONOTONIC_TIME) \ + && ERTS_USE_PREMATURE_TIMEOUT + time -= ERTS_PREMATURE_TIMEOUT(time, 1000*1000); +#endif + select_timeout.tv_sec = time / (1000*1000); select_timeout.tv_usec = time % (1000*1000); @@ -559,7 +593,11 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout) val = ethr_atomic32_read(&e->state); if (val == ETHR_EVENT_ON__) goto return_event_on; - +#if defined(ETHR_HAVE_ETHR_GET_MONOTONIC_TIME) \ + && ERTS_USE_PREMATURE_TIMEOUT + if (res == ETIMEDOUT) + goto restart_select; /* Verify timeout */ +#endif } return res; diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam Binary files differindex b62da04bfd..c68debeabc 100644 --- a/erts/preloaded/ebin/erlang.beam +++ b/erts/preloaded/ebin/erlang.beam diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam Binary files differindex a0da864824..a1eb126098 100644 --- a/erts/preloaded/ebin/erts_code_purger.beam +++ b/erts/preloaded/ebin/erts_code_purger.beam diff --git a/erts/preloaded/ebin/erts_dirty_process_code_checker.beam b/erts/preloaded/ebin/erts_dirty_process_code_checker.beam Binary files differnew file mode 100644 index 0000000000..a7ac116c05 --- /dev/null +++ b/erts/preloaded/ebin/erts_dirty_process_code_checker.beam diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam Binary files differindex d897c8e92f..22817be8f4 100644 --- a/erts/preloaded/ebin/erts_internal.beam +++ b/erts/preloaded/ebin/erts_internal.beam diff --git a/erts/preloaded/ebin/erts_literal_area_collector.beam b/erts/preloaded/ebin/erts_literal_area_collector.beam Binary files differnew file mode 100644 index 0000000000..71f3c2ec8c --- /dev/null +++ b/erts/preloaded/ebin/erts_literal_area_collector.beam diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam Binary files differindex f0344fd6ba..849273f746 100644 --- a/erts/preloaded/ebin/init.beam +++ b/erts/preloaded/ebin/init.beam diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index 4a447d3a09..2ab9edaf5e 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -44,7 +44,9 @@ PRE_LOADED_ERL_MODULES = \ erts_code_purger \ erlang \ erts_internal \ - erl_tracer + erl_tracer \ + erts_literal_area_collector \ + erts_dirty_process_code_checker PRE_LOADED_BEAM_MODULES = \ prim_eval diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index edf79b8f75..652a954807 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -59,6 +59,7 @@ -export_type([timestamp/0]). -export_type([time_unit/0]). +-export_type([deprecated_time_unit/0]). -type ext_binary() :: binary(). -type timestamp() :: {MegaSecs :: non_neg_integer(), @@ -67,12 +68,20 @@ -type time_unit() :: pos_integer() - | 'seconds' + | 'second' + | 'millisecond' + | 'microsecond' + | 'nanosecond' + | 'native' + | 'perf_counter' + | deprecated_time_unit(). + +%% Deprecated symbolic units... +-type deprecated_time_unit() :: + 'seconds' | 'milli_seconds' | 'micro_seconds' - | 'nano_seconds' - | 'native' - | 'perf_counter'. + | 'nano_seconds'. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Native code BIF stubs and their types @@ -1365,19 +1374,33 @@ convert_time_unit(Time, FromUnit, ToUnit) -> FU = case FromUnit of native -> erts_internal:time_unit(); perf_counter -> erts_internal:perf_counter_unit(); + nanosecond -> 1000*1000*1000; + microsecond -> 1000*1000; + millisecond -> 1000; + second -> 1; + + %% Deprecated symbolic units... nano_seconds -> 1000*1000*1000; micro_seconds -> 1000*1000; milli_seconds -> 1000; seconds -> 1; + _ when FromUnit > 0 -> FromUnit end, TU = case ToUnit of native -> erts_internal:time_unit(); perf_counter -> erts_internal:perf_counter_unit(); + nanosecond -> 1000*1000*1000; + microsecond -> 1000*1000; + millisecond -> 1000; + second -> 1; + + %% Deprecated symbolic units... nano_seconds -> 1000*1000*1000; micro_seconds -> 1000*1000; milli_seconds -> 1000; seconds -> 1; + _ when ToUnit > 0 -> ToUnit end, case Time < 0 of diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src index e18da28905..7ab06164b4 100644 --- a/erts/preloaded/src/erts.app.src +++ b/erts/preloaded/src/erts.app.src @@ -37,7 +37,7 @@ {registered, []}, {applications, []}, {env, []}, - {runtime_dependencies, ["stdlib-3.0", "kernel-5.0", "sasl-3.0"]} + {runtime_dependencies, ["stdlib-3.0", "kernel-5.0", "sasl-3.0.1"]} ]}. %% vim: ft=erlang diff --git a/erts/preloaded/src/erts_code_purger.erl b/erts/preloaded/src/erts_code_purger.erl index d1e64342e0..ee4fcedd2d 100644 --- a/erts/preloaded/src/erts_code_purger.erl +++ b/erts/preloaded/src/erts_code_purger.erl @@ -22,7 +22,7 @@ %% Purpose : Implement system process erts_code_purger %% to handle code module purging. --export([start/0, purge/1, soft_purge/1]). +-export([start/0, purge/1, soft_purge/1, pending_purge_lambda/3]). -spec start() -> term(). start() -> @@ -40,10 +40,43 @@ loop() -> Res = do_soft_purge(Mod), From ! {reply, soft_purge, Res, Ref}; + {test_purge, Mod, From, Type, Ref} when is_atom(Mod), is_pid(From) -> + do_test_purge(Mod, From, Type, Ref); + _Other -> ignore end, loop(). +%% +%% Processes that tries to call a fun that belongs to +%% a module that currently is being purged will end +%% up here (pending_purge_lambda) in a suspended state. +%% When the purge operation completes or aborts (soft +%% purge that failed) these processes will be resumed. +%% +pending_purge_lambda(_Module, Fun, Args) -> + %% + %% When the process is resumed, the following + %% scenarios exist: + %% * The code that the fun refers to is still + %% there due to a failed soft purge. The + %% call to the fun will succeed via apply/2. + %% * The code was purged, and a current version + %% of the module is loaded which does not + %% contain this fun. The call will result + %% in an exception being raised. + %% * The code was purged, and no current + %% version of the module is loaded. An attempt + %% to load the module (via the error_handler) + %% will be made. This may or may not succeed. + %% If the module is loaded, it may or may + %% not contain the fun. The call will + %% succeed if the error_handler was able + %% to load the module and loaded module + %% contains this fun; otherwise, an exception + %% will be raised. + %% + apply(Fun, Args). %% purge(Module) %% Kill all processes running code from *old* Module, and then purge the @@ -60,16 +93,14 @@ purge(Mod) when is_atom(Mod) -> Result end. - do_purge(Mod) -> - case erts_internal:copy_literals(Mod, true) of - false -> - {false, false}; - true -> - DidKill = check_proc_code(erlang:processes(), Mod, true), - true = erts_internal:copy_literals(Mod, false), - WasPurged = erts_internal:purge_module(Mod), - {WasPurged, DidKill} + case erts_internal:purge_module(Mod, prepare) of + false -> + {false, false}; + true -> + DidKill = check_proc_code(erlang:processes(), Mod, true), + true = erts_internal:purge_module(Mod, complete), + {true, DidKill} end. %% soft_purge(Module) @@ -85,21 +116,17 @@ soft_purge(Mod) -> Result end. - do_soft_purge(Mod) -> - case erts_internal:copy_literals(Mod, true) of + case erts_internal:purge_module(Mod, prepare) of false -> true; true -> - DoPurge = check_proc_code(erlang:processes(), Mod, false), - true = erts_internal:copy_literals(Mod, false), - case DoPurge of - false -> - false; - true -> - erts_internal:purge_module(Mod), - true - end + Res = check_proc_code(erlang:processes(), Mod, false), + erts_internal:purge_module(Mod, + case Res of + false -> abort; + true -> complete + end) end. %% @@ -283,8 +310,7 @@ cpc_sched_kill(Pid, cpc_request(#cpc_static{tag = Tag, module = Mod}, Pid, AllowGc) -> erts_internal:check_process_code(Pid, Mod, [{async, {Tag, Pid, AllowGc}}, - {allow_gc, AllowGc}, - {copy_literals, true}]). + {allow_gc, AllowGc}]). cpc_request_gc(CpcS, [Pid|Pids]) -> cpc_request(CpcS, Pid, true), @@ -297,3 +323,72 @@ cpc_init(CpcS, [Pid|Pids], NoReqs) -> cpc_init(CpcS, Pids, NoReqs+1). % end of check_proc_code() implementation. + +%% +%% FOR TESTING ONLY +%% +%% do_test_purge() is for testing only. The purge is done +%% as usual, but the tester can control when to enter the +%% specific phases. +%% +do_test_purge(Mod, From, Type, Ref) when Type == true; Type == false -> + Mon = erlang:monitor(process, From), + Res = case Type of + true -> do_test_hard_purge(Mod, From, Ref, Mon); + false -> do_test_soft_purge(Mod, From, Ref, Mon) + end, + From ! {test_purge, Res, Ref}, + erlang:demonitor(Mon, [flush]), + ok; +do_test_purge(_, _, _, _) -> + ok. + +do_test_soft_purge(Mod, From, Ref, Mon) -> + PrepRes = erts_internal:purge_module(Mod, prepare), + TestRes = test_progress(started, From, Mon, Ref, ok), + case PrepRes of + false -> + _ = test_progress(continued, From, Mon, Ref, TestRes), + true; + true -> + Res = check_proc_code(erlang:processes(), Mod, false), + _ = test_progress(continued, From, Mon, Ref, TestRes), + erts_internal:purge_module(Mod, + case Res of + false -> abort; + true -> complete + end) + end. + +do_test_hard_purge(Mod, From, Ref, Mon) -> + PrepRes = erts_internal:purge_module(Mod, prepare), + TestRes = test_progress(started, From, Mon, Ref, ok), + case PrepRes of + false -> + _ = test_progress(continued, From, Mon, Ref, TestRes), + {false, false}; + true -> + DidKill = check_proc_code(erlang:processes(), Mod, true), + _ = test_progress(continued, From, Mon, Ref, TestRes), + true = erts_internal:purge_module(Mod, complete), + {true, DidKill} + end. + +test_progress(_State, _From, _Mon, _Ref, died) -> + %% Test process died; continue so we wont + %% leave the system in an inconsistent + %% state... + died; +test_progress(started, From, Mon, Ref, ok) -> + From ! {started, Ref}, + receive + {'DOWN', Mon, process, From, _} -> died; + {continue, Ref} -> ok + end; +test_progress(continued, From, Mon, Ref, ok) -> + From ! {continued, Ref}, + receive + {'DOWN', Mon, process, From, _} -> died; + {complete, Ref} -> ok + end. + diff --git a/erts/preloaded/src/erts_dirty_process_code_checker.erl b/erts/preloaded/src/erts_dirty_process_code_checker.erl new file mode 100644 index 0000000000..911642082c --- /dev/null +++ b/erts/preloaded/src/erts_dirty_process_code_checker.erl @@ -0,0 +1,82 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2016. 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(erts_dirty_process_code_checker). + +-export([start/0]). + +%% +%% The erts_dirty_process_code_checker is started at +%% VM boot by the VM. It is a spawned as a system +%% process, i.e, the whole VM will terminate if +%% this process terminates. +%% +start() -> + process_flag(trap_exit, true), + msg_loop(). + +msg_loop() -> + _ = receive + Request -> + handle_request(Request) + end, + msg_loop(). + +check_process(Requester, Target, ReqId, Module) -> + Result = erts_internal:check_dirty_process_code(Target, Module), + Requester ! {check_process_code, ReqId, Result}. + +handle_request({Requester, + Target, + Prio, + {check_process_code, + ReqId, + Module, + _Flags} = Op}) -> + %% + %% Target may have stopped executing dirty since the + %% initial request was made. Check its current state + %% and try to send the request if possible; otherwise, + %% check the dirty executing process and send the result... + %% + try + case erts_internal:is_process_executing_dirty(Target) of + true -> + check_process(Requester, Target, ReqId, Module); + false -> + case erts_internal:request_system_task(Requester, + Target, + Prio, + Op) of + ok -> + ok; + dirty_execution -> + check_process(Requester, Target, ReqId, Module) + end + end + catch + _ : _ -> + ok %% Ignore all failures; someone passed us garbage... + end; +handle_request(_Garbage) -> + ignore. + + + diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 2459ea2a2c..6229754c8c 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -38,11 +38,13 @@ -export([system_check/1, gather_system_check_result/1]). --export([request_system_task/3]). +-export([request_system_task/3, request_system_task/4]). -export([check_process_code/3]). --export([copy_literals/2]). --export([purge_module/1]). +-export([check_dirty_process_code/2]). +-export([is_process_executing_dirty/1]). +-export([release_literal_area_switch/0]). +-export([purge_module/2]). -export([flush_monitor_messages/3]). @@ -204,14 +206,25 @@ port_info(_Result, _Item) -> -spec request_system_task(Pid, Prio, Request) -> 'ok' when Prio :: 'max' | 'high' | 'normal' | 'low', Request :: {'garbage_collect', term()} - | {'check_process_code', term(), module(), non_neg_integer()}, + | {'check_process_code', term(), module(), non_neg_integer()} + | {'copy_literals', term(), boolean()}, Pid :: pid(). request_system_task(_Pid, _Prio, _Request) -> erlang:nif_error(undefined). +-spec request_system_task(RequesterPid, TargetPid, Prio, Request) -> 'ok' | 'dirty_execution' when + Prio :: 'max' | 'high' | 'normal' | 'low', + Request :: {'garbage_collect', term()} + | {'check_process_code', term(), module(), non_neg_integer()} + | {'copy_literals', term(), boolean()}, + RequesterPid :: pid(), + TargetPid :: pid(). + +request_system_task(_RequesterPid, _TargetPid, _Prio, _Request) -> + erlang:nif_error(undefined). + -define(ERTS_CPC_ALLOW_GC, (1 bsl 0)). --define(ERTS_CPC_COPY_LITERALS, (1 bsl 1)). -spec check_process_code(Module, Flags) -> boolean() when Module :: module(), @@ -223,7 +236,7 @@ check_process_code(_Module, _Flags) -> Pid :: pid(), Module :: module(), RequestId :: term(), - Option :: {async, RequestId} | {allow_gc, boolean()} | {copy_literals, boolean()}, + Option :: {async, RequestId} | {allow_gc, boolean()}, OptionList :: [Option], CheckResult :: boolean() | aborted. check_process_code(Pid, Module, OptionList) -> @@ -265,8 +278,6 @@ get_cpc_opts([{async, _ReqId} = AsyncTuple | Options], _OldAsync, Flags) -> get_cpc_opts(Options, AsyncTuple, Flags); get_cpc_opts([{allow_gc, AllowGC} | Options], Async, Flags) -> get_cpc_opts(Options, Async, cpc_flags(Flags, ?ERTS_CPC_ALLOW_GC, AllowGC)); -get_cpc_opts([{copy_literals, CopyLit} | Options], Async, Flags) -> - get_cpc_opts(Options, Async, cpc_flags(Flags, ?ERTS_CPC_COPY_LITERALS, CopyLit)); get_cpc_opts([], Async, Flags) -> {Async, Flags}. @@ -275,15 +286,26 @@ cpc_flags(OldFlags, Bit, true) -> cpc_flags(OldFlags, Bit, false) -> OldFlags band (bnot Bit). --spec copy_literals(Module,Bool) -> 'true' | 'false' | 'aborted' when - Module :: module(), - Bool :: boolean(). -copy_literals(_Mod, _Bool) -> +-spec check_dirty_process_code(Pid,Module) -> 'true' | 'false' when + Pid :: pid(), + Module :: module(). +check_dirty_process_code(_Pid,_Module) -> erlang:nif_error(undefined). --spec purge_module(Module) -> boolean() when - Module :: module(). -purge_module(_Module) -> +-spec is_process_executing_dirty(Pid) -> 'true' | 'false' when + Pid :: pid(). +is_process_executing_dirty(_Pid) -> + erlang:nif_error(undefined). + +-spec release_literal_area_switch() -> 'true' | 'false'. + +release_literal_area_switch() -> + erlang:nif_error(undefined). + +-spec purge_module(Module, Op) -> boolean() when + Module :: module(), + Op :: 'prepare' | 'abort' | 'complete'. +purge_module(_Module, _Op) -> erlang:nif_error(undefined). -spec system_check(Type) -> 'ok' when diff --git a/erts/preloaded/src/erts_literal_area_collector.erl b/erts/preloaded/src/erts_literal_area_collector.erl new file mode 100644 index 0000000000..3befad8dfb --- /dev/null +++ b/erts/preloaded/src/erts_literal_area_collector.erl @@ -0,0 +1,113 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2016. 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(erts_literal_area_collector). + +-export([start/0]). + +%% Currently we only allow two outstanding literal +%% copying jobs that garbage collect in order to +%% copy the literals. Maybe we could allow more +%% than two outstanding processes, but for now we +%% play it safe... +-define(MAX_GC_OUTSTND, 2). + +%% +%% The erts_literal_area_collector is started at +%% VM boot by the VM. It is a spawned as a system +%% process, i.e, the whole VM will terminate if +%% this process terminates. +%% +start() -> + process_flag(trap_exit, true), + msg_loop(undefined, 0, 0, []). + +%% +%% The VM will send us a 'copy_literals' message +%% when it has a new literal area that needs to +%% be handled is added. We will also be informed +%% about more areas when we call +%% erts_internal:release_literal_area_switch(). +%% +msg_loop(Area, Outstnd, GcOutstnd, NeedGC) -> + receive + + %% A new area to handle has arrived... + copy_literals when Outstnd == 0 -> + switch_area(); + + %% Process (_Pid) has completed the request... + {copy_literals, {Area, _GcAllowed, _Pid}, ok} when Outstnd == 1 -> + switch_area(); %% Last process completed... + {copy_literals, {Area, false, _Pid}, ok} -> + msg_loop(Area, Outstnd-1, GcOutstnd, NeedGC); + {copy_literals, {Area, true, _Pid}, ok} when NeedGC == [] -> + msg_loop(Area, Outstnd-1, GcOutstnd-1, []); + {copy_literals, {Area, true, _Pid}, ok} -> + send_copy_req(hd(NeedGC), Area, true), + msg_loop(Area, Outstnd-1, GcOutstnd, tl(NeedGC)); + + %% Process (Pid) failed to complete the request + %% since it needs to garbage collect in order to + %% complete the request... + {copy_literals, {Area, false, Pid}, need_gc} when GcOutstnd < ?MAX_GC_OUTSTND -> + send_copy_req(Pid, Area, true), + msg_loop(Area, Outstnd, GcOutstnd+1, NeedGC); + {copy_literals, {Area, false, Pid}, need_gc} -> + msg_loop(Area, Outstnd, GcOutstnd, [Pid|NeedGC]); + + %% Not handled message regarding the area that we + %% currently are working with. Crash the VM so + %% we notice this bug... + {copy_literals, {Area, _, _}, _} = Msg when erlang:is_reference(Area) -> + exit({not_handled_message, Msg}); + + %% Unexpected garbage message. Get rid of it... + _Ignore -> + msg_loop(Area, Outstnd, GcOutstnd, NeedGC) + + end. + +switch_area() -> + Res = erts_internal:release_literal_area_switch(), + erlang:garbage_collect(), %% Almost no live data now... + case Res of + false -> + %% No more areas to handle... + msg_loop(undefined, 0, 0, []); + true -> + %% Send requests to all processes to copy + %% all live data they have referring to the + %% literal area that is to be released... + Area = make_ref(), + Outstnd = send_copy_reqs(erlang:processes(), Area, false), + msg_loop(Area, Outstnd, 0, []) + end. + +send_copy_reqs(Ps, Area, GC) -> + send_copy_reqs(Ps, Area, GC, 0). + +send_copy_reqs([], _Area, _GC, N) -> + N; +send_copy_reqs([P|Ps], Area, GC, N) -> + send_copy_req(P, Area, GC), + send_copy_reqs(Ps, Area, GC, N+1). + +send_copy_req(P, Area, GC) -> + erts_internal:request_system_task(P, normal, {copy_literals, {Area, GC, P}, GC}). diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 67929c53c2..962528f7ab 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -669,9 +669,9 @@ unload(_) -> do_unload(sub([heart|erlang:pre_loaded()],erlang:loaded())). do_unload([M|Mods]) -> - catch erts_internal:purge_module(M), + catch erlang:purge_module(M), catch erlang:delete_module(M), - catch erts_internal:purge_module(M), + catch erlang:purge_module(M), do_unload(Mods); do_unload([]) -> purge_all_hipe_refs(), diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl index 281a47134f..d474c71c4f 100644 --- a/erts/test/z_SUITE.erl +++ b/erts/test/z_SUITE.erl @@ -214,7 +214,19 @@ dump_core(#core_search_conf{ cerl = Cerl }, Core) -> format_core(Conf, {ignore, Core}) -> format_core(Conf, Core, "[ignored] "); format_core(Conf, Core) -> - format_core(Conf, Core, ""). + format_core(Conf, Core, ""), + + %% Try print (log dir) name of offending application + CoreDir = filename:dirname(Core), + lists:foreach(fun(TestDir) -> + case filelib:is_dir(filename:join(CoreDir,TestDir)) of + true -> + io:format(" from ~s~n", [TestDir]); + false -> + no + end + end, + filelib:wildcard("*.logs", CoreDir)). format_core(#core_search_conf{file = false}, Core, Ignore) -> io:format(" ~s~s " ++ time_fstr() ++ "~s~n", diff --git a/lib/common_test/src/ct_make.erl b/lib/common_test/src/ct_make.erl index e7a9cfa843..f22959d457 100644 --- a/lib/common_test/src/ct_make.erl +++ b/lib/common_test/src/ct_make.erl @@ -342,5 +342,7 @@ check_includes2(Epp, File, ObjMTime) -> epp:close(Epp), false; {error, _Error} -> + check_includes2(Epp, File, ObjMTime); + {warning, _Warning} -> check_includes2(Epp, File, ObjMTime) end. diff --git a/lib/common_test/src/erl2html2.erl b/lib/common_test/src/erl2html2.erl index e819f345de..7f3eb699de 100644 --- a/lib/common_test/src/erl2html2.erl +++ b/lib/common_test/src/erl2html2.erl @@ -130,6 +130,8 @@ parse_preprocessed_file(Epp, File, InCorrectFile) -> throw({error,Reason,InCorrectFile}); {error,_Reason} -> parse_preprocessed_file(Epp, File, InCorrectFile); + {warning,_} -> + parse_preprocessed_file(Epp, File, InCorrectFile); {eof,_Location} -> [] end. diff --git a/lib/common_test/test/ct_auto_compile_SUITE.erl b/lib/common_test/test/ct_auto_compile_SUITE.erl index e6939d23c0..dface99b8f 100644 --- a/lib/common_test/test/ct_auto_compile_SUITE.erl +++ b/lib/common_test/test/ct_auto_compile_SUITE.erl @@ -27,6 +27,7 @@ %%% The suites used for the test are located in the data directory. %%%------------------------------------------------------------------- -module(ct_auto_compile_SUITE). +-warning("Ignore me -- testing that the debugger can handle warnings"). -compile(export_all). diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c index 6dc51adee1..624100ad49 100644 --- a/lib/erl_interface/src/connect/ei_connect.c +++ b/lib/erl_interface/src/connect/ei_connect.c @@ -1708,28 +1708,36 @@ error: static int get_home(char *buf, int size) { - char* homedrive; - char* homepath; - #ifdef __WIN32__ - homedrive = getenv("HOMEDRIVE"); - homepath = getenv("HOMEPATH"); -#else - homedrive = ""; - homepath = getenv("HOME"); -#endif + char* homedrive = getenv("HOMEDRIVE"); + char* homepath = getenv("HOMEPATH"); - if (!homedrive || !homepath) { - buf[0] = '.'; - buf[1] = '\0'; - return 1; - } else if (strlen(homedrive)+strlen(homepath) < size-1) { + if (homedrive && homepath) { + if (strlen(homedrive)+strlen(homepath) >= size) + return 0; strcpy(buf, homedrive); strcat(buf, homepath); return 1; } - - return 0; + else { + int len = GetWindowsDirectory(buf, size); + if (len) { + return (len < size); + } + } +#else + char* homepath = getenv("HOME"); + if (homepath) { + if (strlen(homepath) >= size) + return 0; + strcpy(buf, homepath); + return 1; + } +#endif + + buf[0] = '.'; + buf[1] = '\0'; + return 1; } diff --git a/lib/erl_interface/src/misc/ei_portio.h b/lib/erl_interface/src/misc/ei_portio.h index fbb61b0ccf..bded811a35 100644 --- a/lib/erl_interface/src/misc/ei_portio.h +++ b/lib/erl_interface/src/misc/ei_portio.h @@ -21,7 +21,7 @@ */ #ifndef _EI_PORTIO_H #define _EI_PORTIO_H -#if !defined(__WIN32__) || !defined(VXWORKS) +#if !defined(__WIN32__) && !defined(VXWORKS) #ifdef HAVE_WRITEV /* Declaration of struct iovec *iov should be visible in this scope. */ #include <sys/uio.h> diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml index d3611d6a03..21f8a2d54a 100644 --- a/lib/kernel/doc/src/code.xml +++ b/lib/kernel/doc/src/code.xml @@ -651,6 +651,11 @@ ok = code:finish_loading(Prepared), <p>Purges the code for <c><anno>Module</anno></c>, that is, removes code marked as old. If some processes still linger in the old code, these processes are killed before the code is removed.</p> + <note><p>As of ERTS version 9.0, a process is only considered + to be lingering in the code if it has direct references to the code. + For more information see documentation of + <seealso marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>, + which is used in order to determine this.</p></note> <p>Returns <c>true</c> if successful and any process is needed to be killed, otherwise <c>false</c>.</p> </desc> @@ -661,6 +666,11 @@ ok = code:finish_loading(Prepared), <desc> <p>Purges the code for <c><anno>Module</anno></c>, that is, removes code marked as old, but only if no processes linger in it.</p> + <note><p>As of ERTS version 9.0, a process is only considered + to be lingering in the code if it has direct references to the code. + For more information see documentation of + <seealso marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>, + which is used in order to determine this.</p></note> <p>Returns <c>false</c> if the module cannot be purged because of processes lingering in old code, otherwise <c>true</c>.</p> </desc> diff --git a/lib/kernel/doc/src/heart.xml b/lib/kernel/doc/src/heart.xml index 864f8facac..59a046bf4d 100644 --- a/lib/kernel/doc/src/heart.xml +++ b/lib/kernel/doc/src/heart.xml @@ -83,7 +83,7 @@ <c><![CDATA[SIGKILL]]></c>:</p> <pre> % <input>erl -heart -env HEART_KILL_SIGNAL SIGABRT ...</input></pre> - <p> If heart should <b>not</b> kill the Erlang runtime system, this can be indicated + <p> If heart should <em>not</em> kill the Erlang runtime system, this can be indicated using the environment variable <c><![CDATA[HEART_NO_KILL=TRUE]]></c>. This can be useful if the command executed by heart takes care of this, for example as part of a specific cleanup sequence. diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index dc0291babe..fb6421e1c5 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -215,16 +215,16 @@ <p>Defines the <c>First..Last</c> port range for the listener socket of a distributed Erlang node.</p> </item> - <marker id="inet_dist_listen_options"></marker> <tag><c>{inet_dist_listen_options, Opts}</c></tag> <item> + <marker id="inet_dist_listen_options"></marker> <p>Defines a list of extra socket options to be used when opening the listening socket for a distributed Erlang node. See <seealso marker="gen_tcp#listen/2"><c>gen_tcp:listen/2</c></seealso>.</p> </item> - <marker id="inet_dist_connect_options"></marker> <tag><c>{inet_dist_connect_options, Opts}</c></tag> <item> + <marker id="inet_dist_connect_options"></marker> <p>Defines a list of extra socket options to be used when connecting to other distributed Erlang nodes. See <seealso marker="gen_tcp#connect/4"><c>gen_tcp:connect/4</c></seealso>.</p> diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index 1370e23195..2b59eb2bfe 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -27,7 +27,8 @@ -export([get_arguments/1, get_argument/1, boot_var/1, restart/1, many_restarts/0, many_restarts/1, get_plain_arguments/1, - reboot/1, stop_status/1, stop/1, get_status/1, script_id/1]). + reboot/1, stop_status/1, stop/1, get_status/1, script_id/1, + find_system_processes/0]). -export([boot1/1, boot2/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -355,12 +356,16 @@ wait_for(N,Node,EHPid) -> restart(Config) when is_list(Config) -> Args = args(), + Pa = " -pa " ++ filename:dirname(code:which(?MODULE)), + %% Currently test_server:start_node cannot be used. The restarted %% node immediately halts due to the implementation of %% test_server:start_node. - {ok, Node} = loose_node:start(init_test, Args, ?DEFAULT_TIMEOUT_SEC), + {ok, Node} = loose_node:start(init_test, Args ++ Pa, ?DEFAULT_TIMEOUT_SEC), %% Ok, the node is up, now the real test test begins. erlang:monitor_node(Node, true), + SysProcs0 = rpc:call(Node, ?MODULE, find_system_processes, []), + [InitPid, PurgerPid, LitCollectorPid, DirtyCodePid] = SysProcs0, InitPid = rpc:call(Node, erlang, whereis, [init]), PurgerPid = rpc:call(Node, erlang, whereis, [erts_code_purger]), Procs = rpc:call(Node, erlang, processes, []), @@ -375,6 +380,9 @@ restart(Config) when is_list(Config) -> end, ok = wait_restart(30, Node), + SysProcs1 = rpc:call(Node, ?MODULE, find_system_processes, []), + [InitPid1, PurgerPid1, LitCollectorPid1, DirtyCodePid1] = SysProcs1, + %% Still the same init process! InitPid1 = rpc:call(Node, erlang, whereis, [init]), InitP = pid_to_list(InitPid), @@ -385,8 +393,24 @@ restart(Config) when is_list(Config) -> PurgerP = pid_to_list(PurgerPid), PurgerP = pid_to_list(PurgerPid1), + %% and same literal area collector process! + case LitCollectorPid of + undefined -> undefined = LitCollectorPid1; + _ -> + LitCollectorP = pid_to_list(LitCollectorPid), + LitCollectorP = pid_to_list(LitCollectorPid1) + end, + + %% and same dirty process code checker process! + case DirtyCodePid of + undefined -> undefined = DirtyCodePid1; + _ -> + DirtyCodeP = pid_to_list(DirtyCodePid), + DirtyCodeP = pid_to_list(DirtyCodePid1) + end, + NewProcs0 = rpc:call(Node, erlang, processes, []), - NewProcs = NewProcs0 -- [InitPid1, PurgerPid1], + NewProcs = NewProcs0 -- SysProcs1, case check_processes(NewProcs, MaxPid) of true -> ok; @@ -406,6 +430,37 @@ restart(Config) when is_list(Config) -> loose_node:stop(Node), ok. +-record(sys_procs, {init, + code_purger, + literal_collector, + dirty_proc_checker}). + +find_system_processes() -> + find_system_procs(processes(), #sys_procs{}). + +find_system_procs([], SysProcs) -> + [SysProcs#sys_procs.init, + SysProcs#sys_procs.code_purger, + SysProcs#sys_procs.literal_collector, + SysProcs#sys_procs.dirty_proc_checker]; +find_system_procs([P|Ps], SysProcs) -> + case process_info(P, initial_call) of + {initial_call,{otp_ring0,start,2}} -> + undefined = SysProcs#sys_procs.init, + find_system_procs(Ps, SysProcs#sys_procs{init = P}); + {initial_call,{erts_code_purger,start,0}} -> + undefined = SysProcs#sys_procs.code_purger, + find_system_procs(Ps, SysProcs#sys_procs{code_purger = P}); + {initial_call,{erts_literal_area_collector,start,0}} -> + undefined = SysProcs#sys_procs.literal_collector, + find_system_procs(Ps, SysProcs#sys_procs{literal_collector = P}); + {initial_call,{erts_dirty_process_code_checker,start,0}} -> + undefined = SysProcs#sys_procs.dirty_proc_checker, + find_system_procs(Ps, SysProcs#sys_procs{dirty_proc_checker = P}); + _ -> + find_system_procs(Ps, SysProcs) + end. + wait_restart(0, _Node) -> ct:fail(not_restarted); wait_restart(N, Node) -> diff --git a/lib/sasl/src/sasl.app.src b/lib/sasl/src/sasl.app.src index 4ee8a7d6c8..633cdfa070 100644 --- a/lib/sasl/src/sasl.app.src +++ b/lib/sasl/src/sasl.app.src @@ -46,5 +46,5 @@ {errlog_type, all}]}, {mod, {sasl, []}}, {runtime_dependencies, ["tools-2.6.14","stdlib-3.0","kernel-5.0", - "erts-8.0"]}]}. + "erts-8.1"]}]}. diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl index 352e4984df..efe6cc9eb4 100644 --- a/lib/sasl/src/systools_make.erl +++ b/lib/sasl/src/systools_make.erl @@ -1491,8 +1491,9 @@ mandatory_modules() -> preloaded() -> %% Sorted [erl_prim_loader,erl_tracer,erlang, - erts_code_purger, - erts_internal,init,otp_ring0,prim_eval,prim_file, + erts_code_purger,erts_dirty_process_code_checker, + erts_internal,erts_literal_area_collector, + init,otp_ring0,prim_eval,prim_file, prim_inet,prim_zip,zlib]. %%______________________________________________________________________ diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src index 3245ba5197..76b7d8cd55 100644 --- a/lib/ssh/src/ssh.app.src +++ b/lib/ssh/src/ssh.app.src @@ -45,7 +45,7 @@ "erts-6.0", "kernel-3.0", "public_key-1.1", - "stdlib-3.0" + "stdlib-3.1" ]}]}. diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 0570853a9b..1d7be3547b 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -52,16 +52,15 @@ %% is temporary. see application(3) %%-------------------------------------------------------------------- start() -> - application:start(crypto), - application:start(asn1), - application:start(public_key), - application:start(ssh). + start(temporary). start(Type) -> - application:start(crypto, Type), - application:start(asn1), - application:start(public_key, Type), - application:start(ssh, Type). + case application:ensure_all_started(ssh, Type) of + {ok, _} -> + ok; + Other -> + Other + end. %%-------------------------------------------------------------------- -spec stop() -> ok | {error, term()}. @@ -90,7 +89,7 @@ connect(Socket, Options, Timeout) when is_port(Socket) -> {error, Error}; {_SocketOptions, SshOptions} -> case valid_socket_to_use(Socket, Options) of - ok -> + ok -> {ok, {Host,_Port}} = inet:sockname(Socket), Opts = [{user_pid,self()}, {host,fmt_host(Host)} | SshOptions], ssh_connection_handler:start_connection(client, Socket, Opts, Timeout); @@ -128,23 +127,23 @@ connect(Host, Port, Options, Timeout) -> -spec close(pid()) -> ok. %% %% Description: Closes an ssh connection. -%%-------------------------------------------------------------------- +%%-------------------------------------------------------------------- close(ConnectionRef) -> ssh_connection_handler:stop(ConnectionRef). %%-------------------------------------------------------------------- -spec connection_info(pid(), [atom()]) -> [{atom(), term()}]. %% -%% Description: Retrieves information about a connection. -%%-------------------------------------------------------------------- +%% Description: Retrieves information about a connection. +%%-------------------------------------------------------------------- connection_info(ConnectionRef, Options) -> ssh_connection_handler:connection_info(ConnectionRef, Options). %%-------------------------------------------------------------------- -spec channel_info(pid(), channel_id(), [atom()]) -> [{atom(), term()}]. %% -%% Description: Retrieves information about a connection. -%%-------------------------------------------------------------------- +%% Description: Retrieves information about a connection. +%%-------------------------------------------------------------------- channel_info(ConnectionRef, ChannelId, Options) -> ssh_connection_handler:channel_info(ConnectionRef, ChannelId, Options). @@ -153,9 +152,9 @@ channel_info(ConnectionRef, ChannelId, Options) -> -spec daemon(integer()|port(), proplists:proplist()) -> {ok, pid()} | {error, term()}. -spec daemon(any | inet:ip_address(), integer(), proplists:proplist()) -> {ok, pid()} | {error, term()}. -%% Description: Starts a server listening for SSH connections +%% Description: Starts a server listening for SSH connections %% on the given port. -%%-------------------------------------------------------------------- +%%-------------------------------------------------------------------- daemon(Port) -> daemon(Port, []). @@ -188,9 +187,9 @@ daemon_info(Pid) -> -spec stop_listener(pid()) -> ok. -spec stop_listener(inet:ip_address(), integer()) -> ok. %% -%% Description: Stops the listener, but leaves +%% Description: Stops the listener, but leaves %% existing connections started by the listener up and running. -%%-------------------------------------------------------------------- +%%-------------------------------------------------------------------- stop_listener(SysSup) -> ssh_system_sup:stop_listener(SysSup). stop_listener(Address, Port) -> @@ -202,9 +201,9 @@ stop_listener(Address, Port, Profile) -> -spec stop_daemon(pid()) -> ok. -spec stop_daemon(inet:ip_address(), integer()) -> ok. %% -%% Description: Stops the listener and all connections started by +%% Description: Stops the listener and all connections started by %% the listener. -%%-------------------------------------------------------------------- +%%-------------------------------------------------------------------- stop_daemon(SysSup) -> ssh_system_sup:stop_system(SysSup). stop_daemon(Address, Port) -> @@ -243,7 +242,7 @@ start_shell({ok, ConnectionRef}) -> case ssh_connection:session_channel(ConnectionRef, infinity) of {ok,ChannelId} -> success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId, []), - Args = [{channel_cb, ssh_shell}, + Args = [{channel_cb, ssh_shell}, {init_args,[ConnectionRef, ChannelId]}, {cm, ConnectionRef}, {channel_id, ChannelId}], {ok, State} = ssh_channel:init([Args]), @@ -256,7 +255,7 @@ start_shell(Error) -> %%-------------------------------------------------------------------- %%-------------------------------------------------------------------- -default_algorithms() -> +default_algorithms() -> ssh_transport:default_algorithms(). %%-------------------------------------------------------------------- @@ -296,13 +295,13 @@ daemon_shell_opt(Options) -> daemon_host_inet_opt(HostAddr, Options1) -> case HostAddr of any -> - {ok, Host0} = inet:gethostname(), + {ok, Host0} = inet:gethostname(), {Host0, proplists:get_value(inet, Options1, inet), Options1}; {_,_,_,_} -> - {HostAddr, inet, + {HostAddr, inet, [{ip, HostAddr} | Options1]}; {_,_,_,_,_,_,_,_} -> - {HostAddr, inet6, + {HostAddr, inet6, [{ip, HostAddr} | Options1]} end. @@ -313,8 +312,8 @@ start_daemon(Socket, Options) -> {error, Error}; {SocketOptions, SshOptions} -> case valid_socket_to_use(Socket, Options) of - ok -> - try + ok -> + try do_start_daemon(Socket, [{role,server}|SshOptions], SocketOptions) catch throw:bad_fd -> {error,bad_fd}; @@ -330,16 +329,16 @@ start_daemon(Host, Port, Options, Inet) -> {error, _Reason} = Error -> Error; {SocketOptions, SshOptions}-> - try + try do_start_daemon(Host, Port, [{role,server}|SshOptions] , [Inet|SocketOptions]) catch throw:bad_fd -> {error,bad_fd}; _C:_E -> {error,{cannot_start_daemon,_C,_E}} end end. - + do_start_daemon(Socket, SshOptions, SocketOptions) -> - {ok, {IP,Port}} = + {ok, {IP,Port}} = try {ok,_} = inet:sockname(Socket) catch _:_ -> throw(bad_socket) @@ -351,7 +350,7 @@ do_start_daemon(Socket, SshOptions, SocketOptions) -> {address, Host}, {port, Port}, {role, server}, - {socket_opts, SocketOptions}, + {socket_opts, SocketOptions}, {ssh_opts, SshOptions}], {_, Callback, _} = proplists:get_value(transport, SshOptions, {tcp, gen_tcp, tcp_closed}), case ssh_system_sup:system_supervisor(Host, Port, Profile) of @@ -385,7 +384,7 @@ do_start_daemon(Socket, SshOptions, SocketOptions) -> end. do_start_daemon(Host0, Port0, SshOptions, SocketOptions) -> - {Host,Port1} = + {Host,Port1} = try case proplists:get_value(fd, SocketOptions) of undefined -> @@ -402,21 +401,21 @@ do_start_daemon(Host0, Port0, SshOptions, SocketOptions) -> {Port, WaitRequestControl, Opts0} = case Port1 of 0 -> %% Allocate the socket here to get the port number... - {_, Callback, _} = + {_, Callback, _} = proplists:get_value(transport, SshOptions, {tcp, gen_tcp, tcp_closed}), {ok,LSock} = ssh_acceptor:callback_listen(Callback, 0, SocketOptions), {ok,{_,LPort}} = inet:sockname(LSock), - {LPort, - {LSock,Callback}, + {LPort, + {LSock,Callback}, [{lsocket,LSock},{lsock_owner,self()}] }; _ -> {Port1, false, []} end, - Opts = [{address, Host}, + Opts = [{address, Host}, {port, Port}, {role, server}, - {socket_opts, SocketOptions}, + {socket_opts, SocketOptions}, {ssh_opts, SshOptions} | Opts0], case ssh_system_sup:system_supervisor(Host, Port, Profile) of undefined -> @@ -465,7 +464,7 @@ find_hostport(Fd) -> {ok, HostPort} = inet:sockname(S), ok = inet:close(S), HostPort. - + handle_options(Opts) -> try handle_option(algs_compatibility(proplists:unfold(Opts)), [], []) of @@ -480,9 +479,9 @@ handle_options(Opts) -> algs_compatibility(Os0) -> %% Take care of old options 'public_key_alg' and 'pref_public_key_algs' case proplists:get_value(public_key_alg, Os0) of - undefined -> + undefined -> Os0; - A when is_atom(A) -> + A when is_atom(A) -> %% Skip public_key_alg if pref_public_key_algs is defined: Os = lists:keydelete(public_key_alg, 1, Os0), case proplists:get_value(pref_public_key_algs,Os) of @@ -492,7 +491,7 @@ algs_compatibility(Os0) -> [{pref_public_key_algs,['ssh-dss','ssh-rsa']} | Os]; undefined -> throw({error, {eoptions, {public_key_alg,A} }}); - _ -> + _ -> Os end; V -> @@ -620,7 +619,7 @@ handle_ssh_option({silently_accept_hosts, Value} = Opt) when is_boolean(Value) - Opt; handle_ssh_option({user_interaction, Value} = Opt) when is_boolean(Value) -> Opt; -handle_ssh_option({preferred_algorithms,[_|_]} = Opt) -> +handle_ssh_option({preferred_algorithms,[_|_]} = Opt) -> handle_pref_algs(Opt); handle_ssh_option({dh_gex_groups,L0}) when is_list(L0) -> @@ -629,7 +628,7 @@ handle_ssh_option({dh_gex_groups,L0}) when is_list(L0) -> lists:foldl( fun({N,G,P}, Acc) when is_integer(N),N>0, is_integer(G),G>0, - is_integer(P),P>0 -> + is_integer(P),P>0 -> [{N,{G,P}} | Acc]; ({N,{G,P}}, Acc) when is_integer(N),N>0, is_integer(G),G>0, @@ -637,7 +636,7 @@ handle_ssh_option({dh_gex_groups,L0}) when is_list(L0) -> [{N,{G,P}} | Acc]; ({N,GPs}, Acc) when is_list(GPs) -> lists:foldr(fun({Gi,Pi}, Acci) when is_integer(Gi),Gi>0, - is_integer(Pi),Pi>0 -> + is_integer(Pi),Pi>0 -> [{N,{Gi,Pi}} | Acci] end, Acc, GPs) end, [], L0))}; @@ -647,7 +646,7 @@ handle_ssh_option({dh_gex_groups,{Tag,File=[C|_]}}=Opt) when is_integer(C), C>0, Tag == ssh_moduli_file -> {ok,GroupDefs} = case Tag of - file -> + file -> file:consult(File); ssh_moduli_file -> case file:open(File,[read]) of @@ -672,14 +671,14 @@ handle_ssh_option({dh_gex_groups,{Tag,File=[C|_]}}=Opt) when is_integer(C), C>0, catch _:_ -> throw({error, {{eoptions, Opt}, "Bad format in file: "++File}}) - end; - + end; + -handle_ssh_option({dh_gex_limits,{Min,Max}} = Opt) when is_integer(Min), Min>0, +handle_ssh_option({dh_gex_limits,{Min,Max}} = Opt) when is_integer(Min), Min>0, is_integer(Max), Max>=Min -> %% Server Opt; -handle_ssh_option({dh_gex_limits,{Min,I,Max}} = Opt) when is_integer(Min), Min>0, +handle_ssh_option({dh_gex_limits,{Min,I,Max}} = Opt) when is_integer(Min), Min>0, is_integer(I), I>=Min, is_integer(Max), Max>=I -> %% Client @@ -724,7 +723,7 @@ handle_ssh_option({keyboard_interact_fun, Value} = Opt) when is_function(Value,3 Opt; handle_ssh_option({compression, Value} = Opt) when is_atom(Value) -> Opt; -handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module), +handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module), is_atom(Function) -> Opt; handle_ssh_option({exec, Function} = Opt) when is_function(Function) -> @@ -772,7 +771,7 @@ handle_ssh_option({quiet_mode, Value} = Opt) when is_boolean(Value) -> Opt; handle_ssh_option({idle_time, Value} = Opt) when is_integer(Value), Value > 0 -> Opt; -handle_ssh_option({rekey_limit, Value} = Opt) when is_integer(Value) -> +handle_ssh_option({rekey_limit, Value} = Opt) when is_integer(Value) -> Opt; handle_ssh_option({id_string, random}) -> {id_string, {random,2,5}}; %% 2 - 5 random characters @@ -814,11 +813,11 @@ handle_pref_algs({preferred_algorithms,Algs}) -> of DefAlgs -> handle_pref_alg(Key,Vals,DefAlgs) catch - _:_ -> throw({error, {{eoptions, {preferred_algorithms,Key}}, + _:_ -> throw({error, {{eoptions, {preferred_algorithms,Key}}, "Bad preferred_algorithms key"}}) end || {Key,Vals} <- Algs] }; - + Dups -> throw({error, {{eoptions, {preferred_algorithms,Dups}}, "Duplicates found"}}) catch @@ -857,13 +856,13 @@ handle_pref_alg(Key, ) -> handle_pref_alg(Key, lists:reverse(Vs), Sup); -handle_pref_alg(Key, +handle_pref_alg(Key, Vs=[V|_], Sup=[{client2server,_},{server2client,_}] ) when is_atom(V) -> handle_pref_alg(Key, [{client2server,Vs},{server2client,Vs}], Sup); -handle_pref_alg(Key, +handle_pref_alg(Key, Vs=[V|_], Sup=[S|_] ) when is_atom(V), is_atom(S) -> @@ -878,14 +877,14 @@ chk_alg_vs(OptKey, Values, SupportedValues) -> [] -> Values; Bad -> throw({error, {{eoptions, {OptKey,Bad}}, "Unsupported value(s) found"}}) end. - + handle_ip(Inet) -> %% Default to ipv4 case lists:member(inet, Inet) of true -> Inet; false -> case lists:member(inet6, Inet) of - true -> + true -> Inet; false -> [inet | Inet] @@ -916,8 +915,8 @@ directory_exist_readable(Dir) -> {error, Error} -> {error, Error} end. - - + + collect_per_size(L) -> lists:foldr( @@ -948,7 +947,7 @@ read_moduli_file(D, I, Acc) -> read_moduli_file(D, I+1, Acc) end end. - + handle_user_pref_pubkey_algs([], Acc) -> {true, lists:reverse(Acc)}; handle_user_pref_pubkey_algs([H|T], Acc) -> @@ -963,7 +962,7 @@ handle_user_pref_pubkey_algs([H|T], Acc) -> false end. -fmt_host({A,B,C,D}) -> +fmt_host({A,B,C,D}) -> lists:concat([A,".",B,".",C,".",D]); -fmt_host(T={_,_,_,_,_,_,_,_}) -> +fmt_host(T={_,_,_,_,_,_,_,_}) -> lists:flatten(string:join([io_lib:format("~.16B",[A]) || A <- tuple_to_list(T)], ":")). diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl index 868f3a9181..4cd91177f6 100644 --- a/lib/ssh/src/ssh.hrl +++ b/lib/ssh/src/ssh.hrl @@ -127,7 +127,7 @@ recv_sequence = 0, keyex_key, keyex_info, - random_length_padding = 255, % From RFC 4253 section 6. + random_length_padding = 15, % From RFC 4253 section 6. %% User auth user, diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index fb5e086656..1dcf5d0708 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -264,12 +264,23 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, SessionId, #ssh{opts = Opts, userauth_supported_methods = Methods} = Ssh) -> - <<?BYTE(HaveSig), ?UINT32(ALen), BAlg:ALen/binary, - ?UINT32(KLen), KeyBlob:KLen/binary, SigWLen/binary>> = Data, - Alg = binary_to_list(BAlg), + + <<?BYTE(HaveSig), + ?UINT32(ALen), BAlg:ALen/binary, + Rest/binary>> = Data, + + {KeyBlob, SigWLen} = + case Rest of + <<?UINT32(KLen0), KeyBlob0:KLen0/binary, SigWLen0/binary>> -> + {KeyBlob0, SigWLen0}; + <<>> -> + {<<>>, <<>>} + end, + case HaveSig of ?TRUE -> - case verify_sig(SessionId, User, "ssh-connection", Alg, + case verify_sig(SessionId, User, "ssh-connection", + binary_to_list(BAlg), KeyBlob, SigWLen, Opts) of true -> {authorized, User, @@ -284,7 +295,7 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, ?FALSE -> {not_authorized, {User, undefined}, ssh_transport:ssh_packet( - #ssh_msg_userauth_pk_ok{algorithm_name = Alg, + #ssh_msg_userauth_pk_ok{algorithm_name = binary_to_list(BAlg), key_blob = KeyBlob}, Ssh)} end; diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index dcb6ff9343..2eb29c9b32 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -339,6 +339,7 @@ renegotiate_data(ConnectionHandler) -> ssh_params :: #ssh{} | undefined, socket :: inet:socket(), + sender :: pid() | undefined, decrypted_data_buffer = <<>> :: binary(), encrypted_data_buffer = <<>> :: binary(), undecrypted_packet_length :: undefined | non_neg_integer(), @@ -367,9 +368,10 @@ init_connection_handler(Role, Socket, Opts) -> {Protocol, Callback, CloseTag} = proplists:get_value(transport, Opts, ?DefaultTransport), S0#data{ssh_params = init_ssh_record(Role, Socket, Opts), - transport_protocol = Protocol, - transport_cb = Callback, - transport_close_tag = CloseTag + sender = spawn_link(fun() -> nonblocking_sender(Socket, Callback) end), + transport_protocol = Protocol, + transport_cb = Callback, + transport_close_tag = CloseTag } of S -> @@ -525,7 +527,7 @@ handle_event(_, _Event, {init_error,Error}, _) -> %% The very first event that is sent when the we are set as controlling process of Socket handle_event(_, socket_control, {hello,_}, D) -> VsnMsg = ssh_transport:hello_version_msg(string_version(D#data.ssh_params)), - ok = send_bytes(VsnMsg, D), + send_bytes(VsnMsg, D), case inet:getopts(Socket=D#data.socket, [recbuf]) of {ok, [{recbuf,Size}]} -> %% Set the socket to the hello text line handling mode: @@ -550,7 +552,7 @@ handle_event(_, {info_line,_Line}, {hello,Role}, D) -> server -> %% But the client may NOT send them to the server. Openssh answers with cleartext, %% and so do we - ok = send_bytes("Protocol mismatch.", D), + send_bytes("Protocol mismatch.", D), {stop, {shutdown,"Protocol mismatch in version exchange. Client sent info lines."}} end; @@ -565,7 +567,7 @@ handle_event(_, {version_exchange,Version}, {hello,Role}, D) -> {active, once}, {recbuf, D#data.inet_initial_recbuf_size}]), {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh1), - ok = send_bytes(SshPacket, D), + send_bytes(SshPacket, D), {next_state, {kexinit,Role,init}, D#data{ssh_params = Ssh, key_exchange_init_msg = KeyInitMsg}}; not_supported -> @@ -583,7 +585,7 @@ handle_event(_, {#ssh_msg_kexinit{}=Kex, Payload}, {kexinit,Role,ReNeg}, Ssh1 = ssh_transport:key_init(peer_role(Role), D#data.ssh_params, Payload), Ssh = case ssh_transport:handle_kexinit_msg(Kex, OwnKex, Ssh1) of {ok, NextKexMsg, Ssh2} when Role==client -> - ok = send_bytes(NextKexMsg, D), + send_bytes(NextKexMsg, D), Ssh2; {ok, Ssh2} when Role==server -> Ssh2 @@ -596,43 +598,43 @@ handle_event(_, {#ssh_msg_kexinit{}=Kex, Payload}, {kexinit,Role,ReNeg}, %%%---- diffie-hellman handle_event(_, #ssh_msg_kexdh_init{} = Msg, {key_exchange,server,ReNeg}, D) -> {ok, KexdhReply, Ssh1} = ssh_transport:handle_kexdh_init(Msg, D#data.ssh_params), - ok = send_bytes(KexdhReply, D), + send_bytes(KexdhReply, D), {ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1), - ok = send_bytes(NewKeys, D), + send_bytes(NewKeys, D), {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}}; handle_event(_, #ssh_msg_kexdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) -> {ok, NewKeys, Ssh} = ssh_transport:handle_kexdh_reply(Msg, D#data.ssh_params), - ok = send_bytes(NewKeys, D), + send_bytes(NewKeys, D), {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}}; %%%---- diffie-hellman group exchange handle_event(_, #ssh_msg_kex_dh_gex_request{} = Msg, {key_exchange,server,ReNeg}, D) -> {ok, GexGroup, Ssh} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params), - ok = send_bytes(GexGroup, D), + send_bytes(GexGroup, D), {next_state, {key_exchange_dh_gex_init,server,ReNeg}, D#data{ssh_params=Ssh}}; handle_event(_, #ssh_msg_kex_dh_gex_request_old{} = Msg, {key_exchange,server,ReNeg}, D) -> {ok, GexGroup, Ssh} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params), - ok = send_bytes(GexGroup, D), + send_bytes(GexGroup, D), {next_state, {key_exchange_dh_gex_init,server,ReNeg}, D#data{ssh_params=Ssh}}; handle_event(_, #ssh_msg_kex_dh_gex_group{} = Msg, {key_exchange,client,ReNeg}, D) -> {ok, KexGexInit, Ssh} = ssh_transport:handle_kex_dh_gex_group(Msg, D#data.ssh_params), - ok = send_bytes(KexGexInit, D), + send_bytes(KexGexInit, D), {next_state, {key_exchange_dh_gex_reply,client,ReNeg}, D#data{ssh_params=Ssh}}; %%%---- elliptic curve diffie-hellman handle_event(_, #ssh_msg_kex_ecdh_init{} = Msg, {key_exchange,server,ReNeg}, D) -> {ok, KexEcdhReply, Ssh1} = ssh_transport:handle_kex_ecdh_init(Msg, D#data.ssh_params), - ok = send_bytes(KexEcdhReply, D), + send_bytes(KexEcdhReply, D), {ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1), - ok = send_bytes(NewKeys, D), + send_bytes(NewKeys, D), {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}}; handle_event(_, #ssh_msg_kex_ecdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) -> {ok, NewKeys, Ssh} = ssh_transport:handle_kex_ecdh_reply(Msg, D#data.ssh_params), - ok = send_bytes(NewKeys, D), + send_bytes(NewKeys, D), {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}}; @@ -640,9 +642,9 @@ handle_event(_, #ssh_msg_kex_ecdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) handle_event(_, #ssh_msg_kex_dh_gex_init{} = Msg, {key_exchange_dh_gex_init,server,ReNeg}, D) -> {ok, KexGexReply, Ssh1} = ssh_transport:handle_kex_dh_gex_init(Msg, D#data.ssh_params), - ok = send_bytes(KexGexReply, D), + send_bytes(KexGexReply, D), {ok, NewKeys, Ssh} = ssh_transport:new_keys_message(Ssh1), - ok = send_bytes(NewKeys, D), + send_bytes(NewKeys, D), {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}}; @@ -650,7 +652,7 @@ handle_event(_, #ssh_msg_kex_dh_gex_init{} = Msg, {key_exchange_dh_gex_init,serv handle_event(_, #ssh_msg_kex_dh_gex_reply{} = Msg, {key_exchange_dh_gex_reply,client,ReNeg}, D) -> {ok, NewKeys, Ssh1} = ssh_transport:handle_kex_dh_gex_reply(Msg, D#data.ssh_params), - ok = send_bytes(NewKeys, D), + send_bytes(NewKeys, D), {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh1}}; @@ -662,7 +664,7 @@ handle_event(_, #ssh_msg_newkeys{} = Msg, {new_keys,Role,init}, D) -> Ssh = case Role of client -> {MsgReq, Ssh2} = ssh_auth:service_request_msg(Ssh1), - ok = send_bytes(MsgReq, D), + send_bytes(MsgReq, D), Ssh2; server -> Ssh1 @@ -680,7 +682,7 @@ handle_event(_, Msg = #ssh_msg_service_request{name=ServiceName}, StateName = {s "ssh-userauth" -> Ssh0 = #ssh{session_id=SessionId} = D#data.ssh_params, {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0), - ok = send_bytes(Reply, D), + send_bytes(Reply, D), {next_state, {userauth,server}, D#data{ssh_params = Ssh}}; _ -> @@ -692,7 +694,7 @@ handle_event(_, Msg = #ssh_msg_service_request{name=ServiceName}, StateName = {s handle_event(_, #ssh_msg_service_accept{name = "ssh-userauth"}, {service_request,client}, #data{ssh_params = #ssh{service="ssh-userauth"} = Ssh0} = State) -> {Msg, Ssh} = ssh_auth:init_userauth_request_msg(Ssh0), - ok = send_bytes(Msg, State), + send_bytes(Msg, State), {next_state, {userauth,client}, State#data{auth_user = Ssh#ssh.user, ssh_params = Ssh}}; @@ -709,7 +711,7 @@ handle_event(_, %% Probably the very first userauth_request but we deny unauthorized login {not_authorized, _, {Reply,Ssh}} = ssh_auth:handle_userauth_request(Msg, Ssh0#ssh.session_id, Ssh0), - ok = send_bytes(Reply, D), + send_bytes(Reply, D), {keep_state, D#data{ssh_params = Ssh}}; {"ssh-connection", "ssh-connection", Method} -> @@ -719,7 +721,7 @@ handle_event(_, %% Yepp! we support this method case ssh_auth:handle_userauth_request(Msg, Ssh0#ssh.session_id, Ssh0) of {authorized, User, {Reply, Ssh}} -> - ok = send_bytes(Reply, D), + send_bytes(Reply, D), D#data.starter ! ssh_connected, connected_fun(User, Method, D), {next_state, {connected,server}, @@ -727,11 +729,11 @@ handle_event(_, ssh_params = Ssh#ssh{authenticated = true}}}; {not_authorized, {User, Reason}, {Reply, Ssh}} when Method == "keyboard-interactive" -> retry_fun(User, Reason, D), - ok = send_bytes(Reply, D), + send_bytes(Reply, D), {next_state, {userauth_keyboard_interactive,server}, D#data{ssh_params = Ssh}}; {not_authorized, {User, Reason}, {Reply, Ssh}} -> retry_fun(User, Reason, D), - ok = send_bytes(Reply, D), + send_bytes(Reply, D), {keep_state, D#data{ssh_params = Ssh}} end; false -> @@ -1430,18 +1432,15 @@ start_the_connection_child(UserPid, Role, Socket, Options) -> %% Stopping -type finalize_termination_result() :: ok . -finalize_termination(_StateName, #data{transport_cb = Transport, - connection_state = Connection, - socket = Socket}) -> - case Connection of +finalize_termination(_StateName, D) -> + case D#data.connection_state of #connection{system_supervisor = SysSup, sub_system_supervisor = SubSysSup} when is_pid(SubSysSup) -> ssh_system_sup:stop_subsystem(SysSup, SubSysSup); _ -> do_nothing end, - (catch Transport:close(Socket)), - ok. + close_transport(D). %%-------------------------------------------------------------------- %% "Invert" the Role @@ -1496,8 +1495,33 @@ send_msg(Msg, State=#data{ssh_params=Ssh0}) when is_tuple(Msg) -> send_bytes(Bytes, State), State#data{ssh_params=Ssh}. -send_bytes(Bytes, #data{socket = Socket, transport_cb = Transport}) -> - Transport:send(Socket, Bytes). +send_bytes(Bytes, #data{sender = Sender}) -> + Sender ! {send,Bytes}, + ok. + +close_transport(D) -> + D#data.sender ! close, + ok. + + +nonblocking_sender(Socket, Callback) -> + receive + {send, Bytes} -> + case Callback:send(Socket, Bytes) of + ok -> + nonblocking_sender(Socket, Callback); + E = {error,_} -> + exit({shutdown,E}) + end; + + close -> + case Callback:close(Socket) of + ok -> + ok; + E = {error,_} -> + exit({shutdown,E}) + end + end. handle_version({2, 0} = NumVsn, StrVsn, Ssh0) -> Ssh = counterpart_versions(NumVsn, StrVsn, Ssh0), diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl index 0f68130a05..8b2db0e1a8 100644 --- a/lib/ssh/test/ssh_algorithms_SUITE.erl +++ b/lib/ssh/test/ssh_algorithms_SUITE.erl @@ -180,21 +180,19 @@ simple_exec(Config) -> %%-------------------------------------------------------------------- %% Testing if no group matches simple_exec_groups_no_match_too_small(Config) -> - try simple_exec_group({400,500,600}, Config) - of - _ -> ct:fail("Exec though no group available") - catch - error:{badmatch,{error,"No possible diffie-hellman-group-exchange group found"}} -> - ok - end. + try_exec_simple_group({400,500,600}, Config). simple_exec_groups_no_match_too_large(Config) -> - try simple_exec_group({9200,9500,9700}, Config) + try_exec_simple_group({9200,9500,9700}, Config). + + +try_exec_simple_group(Group, Config) -> + try simple_exec_group(Group, Config) of _ -> ct:fail("Exec though no group available") catch - error:{badmatch,{error,"No possible diffie-hellman-group-exchange group found"}} -> - ok + error:{badmatch,{error,"No possible diffie-hellman-group-exchange group found"}} -> ok; + error:{badmatch,{error,"Connection closed"}} -> ok end. %%-------------------------------------------------------------------- diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index 575c1af3a9..212b99c695 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,5 +1,5 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 4.3.1 +SSH_VSN = 4.3.2 APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src index b26efbd88f..00b0513891 100644 --- a/lib/ssl/src/ssl.app.src +++ b/lib/ssl/src/ssl.app.src @@ -55,7 +55,7 @@ {applications, [crypto, public_key, kernel, stdlib]}, {env, []}, {mod, {ssl_app, []}}, - {runtime_dependencies, ["stdlib-3.0","public_key-1.2","kernel-3.0", + {runtime_dependencies, ["stdlib-3.1","public_key-1.2","kernel-3.0", "erts-7.0","crypto-3.3", "inets-5.10.7"]}]}. diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index fadc67ef80..22e24af0a8 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,24 +1,11 @@ %% -*- erlang -*- {"%VSN%", [ - {<<"^8[.]0$">>, - [{load_module, ssl_handshake, soft_purge, soft_purge, []} - ]}, - {<<"^7[.][^.].*">>, [{restart_application, ssl}]}, - {<<"6\\..*">>, [{restart_application, ssl}]}, - {<<"5\\..*">>, [{restart_application, ssl}]}, - {<<"4\\..*">>, [{restart_application, ssl}]}, - {<<"3\\..*">>, [{restart_application, ssl}]} + {<<"^8[.]0([.][0-9]+)?$">>, [{restart_application, ssl}]}, + {<<"^[3-7][.][^.].*">>, [{restart_application, ssl}]} ], [ - {<<"^8[.]0$">>, - [{load_module, ssl_handshake, soft_purge, soft_purge, []} - ]}, - {<<"^7[.][^.].*">>, [{restart_application, ssl}]}, - {<<"6\\..*">>, [{restart_application, ssl}]}, - {<<"5\\..*">>, [{restart_application, ssl}]}, - {<<"4\\..*">>, [{restart_application, ssl}]}, - {<<"3\\..*">>, [{restart_application, ssl}]} + {<<"^8[.]0([.][0-9]+)?$">>, [{restart_application, ssl}]}, + {<<"^[3-7][.][^.].*">>, [{restart_application, ssl}]} ] }. - diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index adee59393e..8a990870e8 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -824,8 +824,6 @@ handle_common_event(internal, #change_cipher_spec{type = <<1>>}, StateName, #state{negotiated_version = Version} = State, Connection) -> Connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Version, StateName, State); -handle_common_event(internal, _, _, _, _) -> - {keep_state_and_data, [postpone]}; handle_common_event(_Type, Msg, StateName, #state{negotiated_version = Version} = State, Connection) -> Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index eaf866c339..8b828f3421 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -214,7 +214,7 @@ init({call, From}, {start, Timeout}, {Record, State} = next_record(State1), next_event(hello, Record, State); init(Type, Event, State) -> - ssl_connection:init(Type, Event, State, ?MODULE). + gen_handshake(ssl_connection, init, Type, Event, State). %%-------------------------------------------------------------------- -spec error(gen_statem:event_type(), @@ -258,13 +258,13 @@ hello(internal, #client_hello{client_version = ClientVersion, _ -> Protocol0 end, - ssl_connection:hello(internal, {common_client_hello, Type, ServerHelloExt}, + gen_handshake(ssl_connection, hello, internal, {common_client_hello, Type, ServerHelloExt}, State#state{connection_states = ConnectionStates, negotiated_version = Version, hashsign_algorithm = HashSign, session = Session, client_ecc = {EllipticCurves, EcPointFormats}, - negotiated_protocol = Protocol}, ?MODULE) + negotiated_protocol = Protocol}) end; hello(internal, #server_hello{} = Hello, #state{connection_states = ConnectionStates0, @@ -280,36 +280,36 @@ hello(internal, #server_hello{} = Hello, Version, NewId, ConnectionStates, ProtoExt, Protocol, State) end; hello(info, Event, State) -> - handle_info(Event, hello, State); + gen_info(Event, hello, State); hello(Type, Event, State) -> - ssl_connection:hello(Type, Event, State, ?MODULE). + gen_handshake(ssl_connection, hello, Type, Event, State). %%-------------------------------------------------------------------- -spec abbreviated(gen_statem:event_type(), term(), #state{}) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- abbreviated(info, Event, State) -> - handle_info(Event, abbreviated, State); + gen_info(Event, abbreviated, State); abbreviated(Type, Event, State) -> - ssl_connection:abbreviated(Type, Event, State, ?MODULE). + gen_handshake(ssl_connection, abbreviated, Type, Event, State). %%-------------------------------------------------------------------- -spec certify(gen_statem:event_type(), term(), #state{}) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- certify(info, Event, State) -> - handle_info(Event, certify, State); + gen_info(Event, certify, State); certify(Type, Event, State) -> - ssl_connection:certify(Type, Event, State, ?MODULE). + gen_handshake(ssl_connection, certify, Type, Event, State). %%-------------------------------------------------------------------- -spec cipher(gen_statem:event_type(), term(), #state{}) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- cipher(info, Event, State) -> - handle_info(Event, cipher, State); + gen_info(Event, cipher, State); cipher(Type, Event, State) -> - ssl_connection:cipher(Type, Event, State, ?MODULE). + gen_handshake(ssl_connection, cipher, Type, Event, State). %%-------------------------------------------------------------------- -spec connection(gen_statem:event_type(), @@ -317,7 +317,7 @@ cipher(Type, Event, State) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- connection(info, Event, State) -> - handle_info(Event, connection, State); + gen_info(Event, connection, State); connection(internal, #hello_request{}, #state{role = client, host = Host, port = Port, session = #session{own_certificate = Cert} = Session0, @@ -433,11 +433,16 @@ handle_common_event(internal, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Da %%% TLS record protocol level Alert messages handle_common_event(internal, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName, #state{negotiated_version = Version} = State) -> - case decode_alerts(EncAlerts) of + try decode_alerts(EncAlerts) of Alerts = [_|_] -> handle_alerts(Alerts, {next_state, StateName, State}); + [] -> + handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, empty_alert), Version, StateName, State); #alert{} = Alert -> handle_own_alert(Alert, Version, StateName, State) + catch + _:_ -> + handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, alert_decode_error), Version, StateName, State) end; %% Ignore unknown TLS record level protocol messages handle_common_event(internal, #ssl_tls{type = _Unknown}, StateName, State) -> @@ -1040,3 +1045,31 @@ handle_sni_extension(#client_hello{extensions = HelloExtensions}, State0) -> end; handle_sni_extension(_, State) -> State. + +gen_handshake(GenConnection, StateName, Type, Event, #state{negotiated_version = Version} = State) -> + try GenConnection:StateName(Type, Event, State, ?MODULE) of + Result -> + Result + catch + _:_ -> + handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake_data), Version, StateName, State) + end. + +gen_info(Event, connection = StateName, #state{negotiated_version = Version} = State) -> + try handle_info(Event, StateName, State) of + Result -> + Result + catch + _:_ -> + handle_own_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR, malformed_data), Version, StateName, State) + end; + +gen_info(Event, StateName, #state{negotiated_version = Version} = State) -> + try handle_info(Event, StateName, State) of + Result -> + Result + catch + _:_ -> + handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake_data), Version, StateName, State) + end. + diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 566b7db332..6e593950d9 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -109,19 +109,25 @@ hello(#client_hello{client_version = ClientVersion, cipher_suites = CipherSuites} = Hello, #ssl_options{versions = Versions} = SslOpts, Info, Renegotiation) -> - Version = ssl_handshake:select_version(tls_record, ClientVersion, Versions), - case ssl_cipher:is_fallback(CipherSuites) of + try + Version = ssl_handshake:select_version(tls_record, ClientVersion, Versions), + case ssl_cipher:is_fallback(CipherSuites) of true -> - Highest = tls_record:highest_protocol_version(Versions), - case tls_record:is_higher(Highest, Version) of - true -> - ?ALERT_REC(?FATAL, ?INAPPROPRIATE_FALLBACK); - false -> - handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation) - end; - false -> - handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation) - end. + Highest = tls_record:highest_protocol_version(Versions), + case tls_record:is_higher(Highest, Version) of + true -> + ?ALERT_REC(?FATAL, ?INAPPROPRIATE_FALLBACK); + false -> + handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation) + end; + false -> + handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation) + end + catch + _:_ -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake_data) + end. + %%-------------------------------------------------------------------- -spec encode_handshake(tls_handshake(), tls_record:tls_version()) -> iolist(). %% @@ -187,8 +193,13 @@ handle_client_hello(Version, #client_hello{session_id = SugesstedId, get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length), Body:Length/binary,Rest/binary>>, #ssl_options{v2_hello_compatible = V2Hello} = Opts, Acc) -> Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>, - Handshake = decode_handshake(Version, Type, Body, V2Hello), - get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc]); + try decode_handshake(Version, Type, Body, V2Hello) of + Handshake -> + get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc]) + catch + _:_ -> + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, handshake_decode_error)) + end; get_tls_handshake_aux(_Version, Data, _, Acc) -> {lists:reverse(Acc), Data}. diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 665dbb1df3..38341f77aa 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -340,7 +340,7 @@ init_per_testcase(TestCase, Config) when TestCase == client_renegotiate; TestCase == renegotiate_dos_mitigate_passive; TestCase == renegotiate_dos_mitigate_absolute -> ssl_test_lib:ct_log_supported_protocol_versions(Config), - ct:timetrap({seconds, 30}), + ct:timetrap({seconds, 90}), Config; init_per_testcase(TestCase, Config) when TestCase == psk_cipher_suites; @@ -350,6 +350,11 @@ init_per_testcase(TestCase, Config) when TestCase == psk_cipher_suites; TestCase == ciphers_dsa_signed_certs; TestCase == ciphers_dsa_signed_certs_openssl_names; TestCase == anonymous_cipher_suites; + TestCase == ciphers_ecdsa_signed_certs; + TestCase == ciphers_ecdsa_signed_certs_openssl_names; + TestCase == anonymous_cipher_suites; + TestCase == psk_anon_cipher_suites; + TestCase == psk_anon_with_hint_cipher_suites; TestCase == versions_option, TestCase == tls_tcp_connect_big -> ssl_test_lib:ct_log_supported_protocol_versions(Config), diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl index 00636e5660..e37e127440 100644 --- a/lib/ssl/test/ssl_crl_SUITE.erl +++ b/lib/ssl/test/ssl_crl_SUITE.erl @@ -72,7 +72,7 @@ init_per_suite(Config) -> false -> {skip, io_lib:format("Bad openssl version: ~p",[OpenSSL_version])}; _ -> - catch crypto:stop(), + end_per_suite(Config), try crypto:start() of ok -> {ok, Hostname0} = inet:gethostname(), diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index e49d432c21..17237118a0 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -41,7 +41,7 @@ -define(MANY, 1000). -define(SOME, 50). --define(BASE_TIMEOUT_SECONDS, 15). +-define(BASE_TIMEOUT_SECONDS, 30). -define(SOME_SCALE, 20). -define(MANY_SCALE, 20). @@ -140,6 +140,7 @@ init_per_suite(Config) -> catch crypto:stop(), try crypto:start() of ok -> + ssl:stop(), ssl:start(), {ok, _} = make_certs:all(proplists:get_value(data_dir, Config), proplists:get_value(priv_dir, Config)), @@ -162,6 +163,7 @@ init_per_group(GroupName, Config) -> {skip, "Missing crypto support"} end; _ -> + ssl:stop(), ssl:start(), Config end. diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl index cb0571d0a7..c0b762760d 100644 --- a/lib/ssl/test/ssl_payload_SUITE.erl +++ b/lib/ssl/test/ssl_payload_SUITE.erl @@ -104,8 +104,13 @@ init_per_testcase(TestCase, Config) when TestCase == server_echos_passive_huge; TestCase == client_echos_passive_huge; TestCase == client_echos_active_once_huge; TestCase == client_echos_active_huge -> - ct:timetrap({seconds, 90}), - Config; + case erlang:system_info(system_architecture) of + "sparc-sun-solaris2.10" -> + {skip,"Will take to long time on an old Sparc"}; + _ -> + ct:timetrap({seconds, 90}), + Config + end; init_per_testcase(TestCase, Config) when TestCase == server_echos_passive_big; TestCase == server_echos_active_once_big; diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index b3109b5de9..83a4dae0a1 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -116,6 +116,7 @@ init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of ok -> + ssl:stop(), ssl:start(), {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), proplists:get_value(priv_dir, Config0)), @@ -1264,7 +1265,7 @@ client_check_result(Port, DataExpected, DataReceived) -> _ -> client_check_result(Port, DataExpected, NewData) end - after 3000 -> + after 20000 -> ct:fail({"Time out on openSSL Client", {expected, DataExpected}, {got, DataReceived}}) end. diff --git a/lib/ssl/test/ssl_upgrade_SUITE.erl b/lib/ssl/test/ssl_upgrade_SUITE.erl index 113b3b4158..f6af1e6182 100644 --- a/lib/ssl/test/ssl_upgrade_SUITE.erl +++ b/lib/ssl/test/ssl_upgrade_SUITE.erl @@ -29,7 +29,8 @@ server, client, soft, - result_proxy + result_proxy, + skip }). all() -> @@ -73,8 +74,15 @@ major_upgrade(Config) when is_list(Config) -> minor_upgrade(Config) when is_list(Config) -> ct_release_test:upgrade(ssl, minor,{?MODULE, #state{config = Config}}, Config). -upgrade_init(CTData, #state{config = Config} = State) -> - {ok, {_, _, Up, _Down}} = ct_release_test:get_appup(CTData, ssl), +upgrade_init(CtData, State) -> + {ok,{FromVsn,ToVsn}} = ct_release_test:get_app_vsns(CtData, ssl), + upgrade_init(FromVsn, ToVsn, CtData, State). + +upgrade_init(_, "8.0.2", _, State) -> + %% Requires stdlib upgrade so it will be a node upgrade! + State#state{skip = true}; +upgrade_init(_, _, CtData, #state{config = Config} = State) -> + {ok, {_, _, Up, _Down}} = ct_release_test:get_appup(CtData, ssl), ct:pal("Up: ~p", [Up]), Soft = is_soft(Up), %% It is symmetrical, if upgrade is soft so is downgrade Pid = spawn(?MODULE, result_proxy_init, [[]]), @@ -88,6 +96,8 @@ upgrade_init(CTData, #state{config = Config} = State) -> State#state{soft = Soft, result_proxy = Pid} end. +upgrade_upgraded(_, #state{skip = true} = State) -> + State; upgrade_upgraded(_, #state{soft = false, config = Config, result_proxy = Pid} = State) -> ct:pal("Restart upgrade ~n", []), {Server, Client} = restart_start_connection(Config, Pid), @@ -96,7 +106,6 @@ upgrade_upgraded(_, #state{soft = false, config = Config, result_proxy = Pid} = ssl_test_lib:close(Client), ok = Result, State; - upgrade_upgraded(_, #state{server = Server0, client = Client0, config = Config, soft = true, result_proxy = Pid} = State) -> @@ -110,6 +119,8 @@ upgrade_upgraded(_, #state{server = Server0, client = Client0, {Server, Client} = soft_start_connection(Config, Pid), State#state{server = Server, client = Client}. +upgrade_downgraded(_, #state{skip = true} = State) -> + State; upgrade_downgraded(_, #state{soft = false, config = Config, result_proxy = Pid} = State) -> ct:pal("Restart downgrade: ~n", []), {Server, Client} = restart_start_connection(Config, Pid), @@ -119,7 +130,6 @@ upgrade_downgraded(_, #state{soft = false, config = Config, result_proxy = Pid} Pid ! stop, ok = Result, State; - upgrade_downgraded(_, #state{server = Server, client = Client, soft = true, result_proxy = Pid} = State) -> ct:pal("Soft downgrade: ~n", []), Server ! changed_version, diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 6afac59109..914eb43505 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 8.0.1 +SSL_VSN = 8.0.2 diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml index b8e262208d..3653c6a632 100644 --- a/lib/stdlib/doc/src/ets.xml +++ b/lib/stdlib/doc/src/ets.xml @@ -1458,7 +1458,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> specification returned <c>true</c>.</fsummary> <desc> <p>Matches the objects in table <c><anno>Tab</anno></c> using a - <seealso marker="#match_spec">match specificationc</seealso>. If the + <seealso marker="#match_spec">match specification</seealso>. If the match specification returns <c>true</c> for an object, that object considered a match and is counted. For any other result from the match specification the object is not considered a match and is diff --git a/lib/stdlib/doc/src/queue.xml b/lib/stdlib/doc/src/queue.xml index a46ca47033..9f3aff03a3 100644 --- a/lib/stdlib/doc/src/queue.xml +++ b/lib/stdlib/doc/src/queue.xml @@ -401,7 +401,7 @@ </func> <func> - <name name="liat" arity="1"/>v + <name name="liat" arity="1"/> <fsummary>Remove the tail item from a queue.</fsummary> <desc> <p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk index 41037b8f53..c74343d9ca 100644 --- a/lib/stdlib/vsn.mk +++ b/lib/stdlib/vsn.mk @@ -1 +1 @@ -STDLIB_VSN = 3.0.1 +STDLIB_VSN = 3.1 diff --git a/lib/tools/emacs/erlang-test.el b/lib/tools/emacs/erlang-test.el index 9a146632c5..ba6190d194 100644 --- a/lib/tools/emacs/erlang-test.el +++ b/lib/tools/emacs/erlang-test.el @@ -52,28 +52,42 @@ concatenated to form an erlang file to test on.") (ert-deftest erlang-test-tags () (let* ((dir (make-temp-file "erlang-test" t)) - (erlang-file (expand-file-name "erlang_test.erl" dir)) - (tags-file (expand-file-name "TAGS" dir)) - tags-file-name tags-table-list erlang-buffer) - (unwind-protect - (progn - (erlang-test-create-erlang-file erlang-file) - (erlang-test-compile-tags erlang-file tags-file) - (setq erlang-buffer (find-file-noselect erlang-file)) - (with-current-buffer erlang-buffer - (setq-local tags-file-name tags-file)) - ;; Setting global tags-file-name is a workaround for - ;; GNU Emacs bug#23164. - (setq tags-file-name tags-file) - (erlang-test-completion-table) - (erlang-test-xref-find-definitions erlang-file erlang-buffer)) - (when (buffer-live-p erlang-buffer) - (kill-buffer erlang-buffer)) - (let ((tags-buffer (find-buffer-visiting tags-file))) - (when (buffer-live-p tags-buffer) - (kill-buffer tags-buffer))) - (when (file-exists-p dir) - (delete-directory dir t))))) + (erlang-file (expand-file-name "erlang_test.erl" dir)) + (tags-file (expand-file-name "TAGS" dir)) + (old-tags-file-name (default-value 'tags-file-name)) + (old-tags-table-list (default-value 'tags-table-list)) + tags-file-name + tags-table-list + tags-table-set-list + erlang-buffer + erlang-mode-hook + prog-mode-hook + erlang-shell-mode-hook + tags-add-tables) + (unwind-protect + (progn + (setq-default tags-file-name nil) + (setq-default tags-table-list nil) + (erlang-test-create-erlang-file erlang-file) + (erlang-test-compile-tags erlang-file tags-file) + (setq erlang-buffer (find-file-noselect erlang-file)) + (with-current-buffer erlang-buffer + (setq-local tags-file-name tags-file)) + ;; Setting global tags-file-name is a workaround for + ;; GNU Emacs bug#23164. + (setq tags-file-name tags-file) + (erlang-test-complete-at-point tags-file) + (erlang-test-completion-table) + (erlang-test-xref-find-definitions erlang-file erlang-buffer)) + (when (buffer-live-p erlang-buffer) + (kill-buffer erlang-buffer)) + (let ((tags-buffer (find-buffer-visiting tags-file))) + (when (buffer-live-p tags-buffer) + (kill-buffer tags-buffer))) + (when (file-exists-p dir) + (delete-directory dir t)) + (setq-default tags-file-name old-tags-file-name) + (setq-default tags-table-list old-tags-table-list)))) (defun erlang-test-create-erlang-file (erlang-file) (with-temp-file erlang-file @@ -116,6 +130,54 @@ concatenated to form an erlang file to test on.") (should (eq expected-line (line-number-at-pos))) (should (= (point-at-bol) (point)))) +(defun erlang-test-complete-at-point (tags-file) + (with-temp-buffer + (erlang-mode) + (setq-local tags-file-name tags-file) + (insert "\nerlang_test:fun") + (erlang-complete-tag) + (should (looking-back "erlang_test:function")) + (insert "\nfun") + (erlang-complete-tag) + (should (looking-back "function")) + (insert "\nerlang_") + (erlang-complete-tag) + (should (looking-back "erlang_test:")))) + + +(ert-deftest erlang-test-compile-options () + (erlang-test-format-opt t + "t") + (erlang-test-format-opt nil + "nil") + (erlang-test-format-opt (cons 1 2) + "{1, 2}") + (erlang-test-format-opt (list 1) + "[1]") + (erlang-test-format-opt (list 1 2) + "[1, 2]") + (erlang-test-format-opt (list 1 2 3) + "[1, 2, 3]") + (erlang-test-format-opt 'symbol + "symbol") + (erlang-test-format-opt "string" + "\"string\"") + (erlang-test-format-opt [] + "{}") + (erlang-test-format-opt [1] + "{1}") + (erlang-test-format-opt [1 2] + "{1, 2}") + (erlang-test-format-opt [1 2 (3 [4 5 6] 7)] + "{1, 2, [3, {4, 5, 6}, 7]}")) + +(defun erlang-test-format-opt (elisp &optional expected-erlang) + (let ((erlang (inferior-erlang-format-opt elisp))) + (message "%s -> %s" elisp erlang) + (when expected-erlang + (should (equal erlang expected-erlang))) + erlang)) + (provide 'erlang-test) diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el index a2062180f3..73c6b8d768 100644 --- a/lib/tools/emacs/erlang.el +++ b/lib/tools/emacs/erlang.el @@ -1436,8 +1436,6 @@ Other commands: (erlang-skel-init) (when (fboundp 'tempo-use-tag-list) (tempo-use-tag-list 'erlang-tempo-tags)) - (when (boundp 'xref-backend-functions) - (add-hook 'xref-backend-functions #'erlang-etags--xref-backend nil t)) (run-hooks 'erlang-mode-hook) (if (zerop (buffer-size)) (run-hooks 'erlang-new-file-hook))) @@ -1548,9 +1546,7 @@ Other commands: (set (make-local-variable 'outline-regexp) "[[:lower:]0-9_]+ *(.*) *-> *$") (set (make-local-variable 'outline-level) (lambda () 1)) (set (make-local-variable 'add-log-current-defun-function) - 'erlang-current-defun) - (set (make-local-variable 'find-tag-default-function) - 'erlang-find-tag-for-completion)) + 'erlang-current-defun)) (defun erlang-font-lock-init () "Initialize Font Lock for Erlang mode." @@ -3233,18 +3229,16 @@ With argument, do this that many times." (interactive "p") (or arg (setq arg 1)) (while (and (looking-at "[ \t]*[%\n]") - (zerop (forward-line 1)))) + (zerop (forward-line 1)))) ;; Move to the next clause. (erlang-beginning-of-clause (- arg)) (beginning-of-line);; Just to be sure... (let ((continue t)) (while (and (not (bobp)) continue) (forward-line -1) - (skip-chars-forward " \t") - (if (looking-at "[%\n]") - nil - (end-of-line) - (setq continue nil))))) + (unless (looking-at "[ \t]*[%\n]") + (end-of-line) + (setq continue nil))))) (defun erlang-mark-clause () "Put mark at end of clause, point at beginning." @@ -4352,11 +4346,6 @@ as on the old form `tag'. In the completion list, `module:tag' and `module:' shows up. -Call this function from an appropriate init file, or add it to -Erlang mode hook with the commands: - (add-hook 'erlang-mode-hook 'erlang-tags-init) - (add-hook 'erlang-shell-mode-hook 'erlang-tags-init) - This function only works under Emacs 18 and Emacs 19. Currently, It is not implemented under XEmacs. (Hint: The Emacs 19 etags module works under XEmacs.)" @@ -4367,11 +4356,16 @@ works under XEmacs.)" (setq erlang-tags-installed t)) (t (require 'etags) - ;; Test on a function available in the Emacs 19 version - ;; of tags but not in the XEmacs version. - (when (fboundp 'find-tag-noselect) - (erlang-tags-define-keys (current-local-map)) - (setq erlang-tags-installed t))))) + (set (make-local-variable 'find-tag-default-function) + 'erlang-find-tag-for-completion) + (if (>= emacs-major-version 25) + (add-hook 'xref-backend-functions + #'erlang-etags--xref-backend nil t) + ;; Test on a function available in the Emacs 19 version + ;; of tags but not in the XEmacs version. + (when (fboundp 'find-tag-noselect) + (erlang-tags-define-keys (current-local-map)) + (setq erlang-tags-installed t)))))) @@ -4775,8 +4769,6 @@ for a tag on the form `module:tag'." ;;; completion-table' containing all normal tags plus tags on the form ;;; `module:tag' and `module:'. -;; PENDING - Should probably make use of the -;; `completion-at-point-functions' hook instead of this advice. (when (and (locate-library "etags") (require 'etags) (fboundp 'etags-tags-completion-table) @@ -4784,11 +4776,7 @@ for a tag on the form `module:tag'." (if (fboundp 'advice-add) ;; Emacs 24.4+ (advice-add 'etags-tags-completion-table :around - (lambda (oldfun) - (if erlang-replace-etags-tags-completion-table - (erlang-etags-tags-completion-table) - (funcall oldfun))) - (list :name 'erlang-replace-tags-table)) + #'erlang-etags-tags-completion-table-advice) ;; Emacs 23.1-24.3 (defadvice etags-tags-completion-table (around erlang-replace-tags-table @@ -4797,6 +4785,11 @@ for a tag on the form `module:tag'." (setq ad-return-value (erlang-etags-tags-completion-table)) ad-do-it)))) +(defun erlang-etags-tags-completion-table-advice (oldfun) + (if erlang-replace-etags-tags-completion-table + (erlang-etags-tags-completion-table) + (funcall oldfun))) + (defun erlang-complete-tag () "Perform tags completion on the text around point. Completes to the set of names listed in the current tags table. @@ -4807,8 +4800,7 @@ about Erlang modules." (condition-case nil (require 'etags) (error nil)) - (cond ((and erlang-tags-installed - (fboundp 'etags-tags-completion-table) + (cond ((and (fboundp 'etags-tags-completion-table) (fboundp 'tags-lazy-completion-table)) ; Emacs 23.1+ (let ((erlang-replace-etags-tags-completion-table t)) (complete-tag))) @@ -5213,19 +5205,10 @@ The following special commands are available: (setq comint-input-ignoredups t) (setq comint-scroll-show-maximum-output t) (setq comint-scroll-to-bottom-on-output t) - ;; In Emacs 19.30, `add-hook' has got a `local' flag, use it. If - ;; the call fails, just call the normal `add-hook'. - (condition-case nil - (progn - (add-hook 'comint-output-filter-functions - 'inferior-erlang-strip-delete nil t) - (add-hook 'comint-output-filter-functions - 'inferior-erlang-strip-ctrl-m nil t)) - (error - (funcall (symbol-function 'make-local-hook) - 'comint-output-filter-functions) ; obsolete as of Emacs 21.1 - (add-hook 'comint-output-filter-functions 'inferior-erlang-strip-delete) - (add-hook 'comint-output-filter-functions 'inferior-erlang-strip-ctrl-m))) + (add-hook 'comint-output-filter-functions + 'inferior-erlang-strip-delete nil t) + (add-hook 'comint-output-filter-functions + 'inferior-erlang-strip-ctrl-m nil t) ;; Some older versions of comint don't have an input ring. (if (fboundp 'comint-read-input-ring) (progn @@ -5251,6 +5234,7 @@ The following special commands are available: (define-key map [menu-bar compilation] (cons "Errors" compilation-menu-map))) map))))) + (erlang-tags-init) (run-hooks 'erlang-shell-mode-hook)) @@ -5730,31 +5714,29 @@ unless the optional NO-DISPLAY is non-nil." (defun inferior-erlang-format-comma-opts (opts) (if (null opts) "" - (concat ", " (inferior-erlang-format-opts opts)))) - -(defun inferior-erlang-format-opts (opts) - (concat "[" (inferior-erlang-string-join (mapcar 'inferior-erlang-format-opt - opts) - ", ") - "]")) + (concat ", " (inferior-erlang-format-opt opts)))) (defun inferior-erlang-format-opt (opt) (cond ((stringp opt) (concat "\"" opt "\"")) - ((atom opt) (format "%s" opt)) - ((consp opt) (concat "{" (inferior-erlang-string-join - (mapcar 'inferior-erlang-format-opt - (list (car opt) (cdr opt))) - ", ") - "}")) - (t (error (format "Unexpected opt %s" opt))))) - -(defun inferior-erlang-string-join (strs sep) - (let ((result (or (car strs) ""))) - (setq strs (cdr strs)) - (while strs - (setq result (concat result sep (car strs))) - (setq strs (cdr strs))) - result)) + ((vectorp opt) (inferior-erlang-tuple (append opt nil))) + ((atom opt) (format "%s" opt)) + ((consp opt) (if (listp (cdr opt)) + (inferior-erlang-list opt) + (inferior-erlang-tuple (list (car opt) (cdr opt))))) + (t (error "Unexpected erlang compile option %s" opt)))) + +(defun inferior-erlang-tuple (opts) + (concat "{" (mapconcat 'inferior-erlang-format-opt + opts + ", ") + "}")) + +(defun inferior-erlang-list (opts) + (concat "[" (mapconcat 'inferior-erlang-format-opt + opts + ", ") + "]")) + (defun erlang-local-buffer-file-name () ;; When editing a file remotely via tramp, diff --git a/lib/tools/src/make.erl b/lib/tools/src/make.erl index 26378f28a0..37e67cbe34 100644 --- a/lib/tools/src/make.erl +++ b/lib/tools/src/make.erl @@ -317,5 +317,7 @@ check_includes2(Epp, File, ObjMTime) -> epp:close(Epp), false; {error, _Error} -> + check_includes2(Epp, File, ObjMTime); + {warning, _Warning} -> check_includes2(Epp, File, ObjMTime) end. diff --git a/lib/tools/src/tools.app.src b/lib/tools/src/tools.app.src index a00969eabe..18166cceb0 100644 --- a/lib/tools/src/tools.app.src +++ b/lib/tools/src/tools.app.src @@ -40,7 +40,7 @@ {env, [{file_util_search_methods,[{"", ""}, {"ebin", "esrc"}, {"ebin", "src"}]} ] }, - {runtime_dependencies, ["stdlib-2.5","runtime_tools-1.8.14", + {runtime_dependencies, ["stdlib-3.1","runtime_tools-1.8.14", "kernel-3.0","inets-5.10","erts-7.0", "compiler-5.0"]} ] diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl index b4c9264b30..90e113c178 100644 --- a/lib/tools/test/cover_SUITE.erl +++ b/lib/tools/test/cover_SUITE.erl @@ -1001,41 +1001,40 @@ otp_6115(Config) when is_list(Config) -> %% Cover compile f1, but not f2 {ok, f1} = cover:compile(f1), + %% This test used to ensure that a process containing a + %% fun refering to cover compiled code was killed. + %% check_process_code may however ignore funs as of ERTS + %% version 8.1. The test has therefore been rewritten to + %% check that a process with a direct reference (in this + %% case a return address) to the code is killed. + %% %% If f1 is cover compiled, a process P is started with a - %% reference to the fun created in start_fail/0, and cover:stop() is - %% called, then P should be killed. - %% This is because (the fun held by P) references the cover + %% direct reference to the f1, and cover:stop() is called, + %% then P should be killed. + %% This is because of the return address to the cover %% compiled code which should be *unloaded* when cover:stop() is %% called -- running cover compiled code when there is no cover %% server and thus no ets tables to bump counters in, makes no %% sense. - Pid1 = f1:start_a(), - Pid2 = f1:start_b(), + Pid = spawn(fun () -> f1:non_tail_call_f2_wait() end), %% Now stop cover cover:stop(), %% Ensure that f1 is loaded (and not cover compiled), and that - %% both Pid1 and Pid2 are dead. + %% both Pid is dead. case code:which(f1) of Beam when is_list(Beam) -> ok; Other -> ct:fail({"f1 is not reloaded", Other}) end, - case process_info(Pid1) of + case process_info(Pid) of undefined -> ok; - _PI1 -> - RefToOldP1 = erlang:check_process_code(Pid1, f1), - ct:fail({"Pid1 still alive", RefToOldP1}) - end, - case process_info(Pid2) of - undefined -> - ok; - _PI2 -> - RefToOldP2 = erlang:check_process_code(Pid1, f2), - ct:fail({"Pid2 still alive", RefToOldP2}) + _PI -> + RefToOldP = erlang:check_process_code(Pid, f1), + ct:fail({"Pid still alive", RefToOldP}) end, file:set_cwd(CWD), diff --git a/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl b/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl index 5399b33f19..fc4a62e70e 100644 --- a/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl +++ b/lib/tools/test/cover_SUITE_data/otp_6115/f1.erl @@ -1,13 +1,6 @@ -module(f1). --export([start_a/0, start_b/0]). +-export([non_tail_call_f2_wait/0]). -start_a() -> - f2:start(fun() -> - ok - end). - -start_b() -> - f2:start(fun fun1/0). - -fun1() -> - ok. +non_tail_call_f2_wait() -> + f2:wait(), + im_back. diff --git a/lib/tools/test/cover_SUITE_data/otp_6115/f2.erl b/lib/tools/test/cover_SUITE_data/otp_6115/f2.erl index 72a6a64c4d..4bc88035c7 100644 --- a/lib/tools/test/cover_SUITE_data/otp_6115/f2.erl +++ b/lib/tools/test/cover_SUITE_data/otp_6115/f2.erl @@ -1,13 +1,5 @@ -module(f2). --export([start/1]). +-export([wait/0]). -start(Fun) -> - spawn(fun() -> - wait(Fun) - end). - -wait(Fun) -> - receive - go -> - Fun() - end. +wait() -> + receive after infinity -> ok end. diff --git a/lib/tools/test/make_SUITE_data/test1.erl b/lib/tools/test/make_SUITE_data/test1.erl index f4a133008e..9e21bdc767 100644 --- a/lib/tools/test/make_SUITE_data/test1.erl +++ b/lib/tools/test/make_SUITE_data/test1.erl @@ -3,6 +3,8 @@ -vsn('$Revision: /main/release/2 $'). -compile(export_all). +-warning("a warning"). + f1() -> true. diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk index e6287b0430..e066dbf5e9 100644 --- a/lib/tools/vsn.mk +++ b/lib/tools/vsn.mk @@ -1 +1 @@ -TOOLS_VSN = 2.8.5 +TOOLS_VSN = 2.8.6 |