diff options
43 files changed, 814 insertions, 425 deletions
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index b39d0e5e23..29b1d106cd 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -6170,7 +6170,7 @@ true</pre> <p>Monitors the new process (like <seealso marker="#monitor/2"><c>monitor/2</c></seealso> does).</p> </item> - <tag><c>{priority, <anno>Level</anno></c></tag> + <tag><c>{priority, <anno>Level</anno>}</c></tag> <item> <p>Sets the priority of the new process. Equivalent to executing <seealso marker="#process_flag_priority"> @@ -7581,7 +7581,7 @@ ok </func> <func> - <name name="system_info" arity="1" clause_i="75"/> + <name name="system_info" arity="1" clause_i="76"/> <fsummary>System info overview.</fsummary> <desc> <p>Returns information about the current system. @@ -7629,6 +7629,7 @@ ok <p> <seealso marker="#system_info_atom_count"><c>atom_count</c></seealso>, <seealso marker="#system_info_atom_limit"><c>atom_limit</c></seealso>, + <seealso marker="#system_info_ets_count"><c>ets_count</c></seealso>, <seealso marker="#system_info_ets_limit"><c>ets_limit</c></seealso>, <seealso marker="#system_info_port_count"><c>port_count</c></seealso>, <seealso marker="#system_info_port_limit"><c>port_limit</c></seealso>, @@ -7872,8 +7873,8 @@ ok <name name="system_info" arity="1" clause_i="12" anchor="system_info_cpu_topology"/> <!-- cpu_topology --> <name name="system_info" arity="1" clause_i="13"/> <!-- {cpu_topology, _} --> - <name name="system_info" arity="1" clause_i="37"/> <!-- logical_processors --> - <name name="system_info" arity="1" clause_i="72"/> <!-- update_cpu_info --> + <name name="system_info" arity="1" clause_i="38"/> <!-- logical_processors --> + <name name="system_info" arity="1" clause_i="73"/> <!-- update_cpu_info --> <fsummary>Information about the CPU topology of the system.</fsummary> <type name="cpu_topology"/> <type name="level_entry"/> @@ -8024,16 +8025,16 @@ ok </func> <func> - <name name="system_info" arity="1" clause_i="30" + <name name="system_info" arity="1" clause_i="31" anchor="system_info_process"/> <!-- fullsweep_after --> - <name name="system_info" arity="1" clause_i="31"/> <!-- garbage_collection --> - <name name="system_info" arity="1" clause_i="32"/> <!-- heap_sizes --> - <name name="system_info" arity="1" clause_i="33"/> <!-- heap_type --> - <name name="system_info" arity="1" clause_i="39"/> <!-- max_heap_size --> - <name name="system_info" arity="1" clause_i="40"/> <!-- message_queue_data --> - <name name="system_info" arity="1" clause_i="41"/> <!-- min_heap_size --> - <name name="system_info" arity="1" clause_i="42"/> <!-- min_bin_vheap_size --> - <name name="system_info" arity="1" clause_i="56"/> <!-- procs --> + <name name="system_info" arity="1" clause_i="32"/> <!-- garbage_collection --> + <name name="system_info" arity="1" clause_i="33"/> <!-- heap_sizes --> + <name name="system_info" arity="1" clause_i="34"/> <!-- heap_type --> + <name name="system_info" arity="1" clause_i="40"/> <!-- max_heap_size --> + <name name="system_info" arity="1" clause_i="41"/> <!-- message_queue_data --> + <name name="system_info" arity="1" clause_i="42"/> <!-- min_heap_size --> + <name name="system_info" arity="1" clause_i="43"/> <!-- min_bin_vheap_size --> + <name name="system_info" arity="1" clause_i="57"/> <!-- procs --> <fsummary>Information about the default process heap settings.</fsummary> <type name="message_queue_data"/> <type name="max_heap_size"/> @@ -8143,14 +8144,14 @@ ok </func> <func> - <name name="system_info" arity="1" clause_i="6" - anchor="system_info_limits"/> <!-- atom_count --> + <name name="system_info" arity="1" clause_i="6" anchor="system_info_limits"/> <!-- atom_count --> <name name="system_info" arity="1" clause_i="7"/> <!-- atom_limit --> - <name name="system_info" arity="1" clause_i="29"/> <!-- ets_limit --> - <name name="system_info" arity="1" clause_i="52"/> <!-- port_count --> - <name name="system_info" arity="1" clause_i="53"/> <!-- port_limit --> - <name name="system_info" arity="1" clause_i="54"/> <!-- process_count --> - <name name="system_info" arity="1" clause_i="55"/> <!-- process_limit --> + <name name="system_info" arity="1" clause_i="29"/> <!-- ets_count --> + <name name="system_info" arity="1" clause_i="30"/> <!-- ets_limit --> + <name name="system_info" arity="1" clause_i="53"/> <!-- port_count --> + <name name="system_info" arity="1" clause_i="54"/> <!-- port_limit --> + <name name="system_info" arity="1" clause_i="55"/> <!-- process_count --> + <name name="system_info" arity="1" clause_i="56"/> <!-- process_limit --> <fsummary>Information about various system limits.</fsummary> <desc> <marker id="system_info_limits"/> @@ -8173,7 +8174,7 @@ ok <c>erl(1)</c>. </p> </item> - <tag><marker id="system_info_ets_count"/> + <tag><marker id="system_info_ets_count"/> <c>ets_count</c></tag> <item> <p>Returns the number of ETS tables currently existing at the @@ -8225,13 +8226,13 @@ ok <func> <name name="system_info" arity="1" clause_i="26" anchor="system_info_time"/> <!-- end_time --> - <name name="system_info" arity="1" clause_i="49"/> <!-- os_monotonic_time_source --> - <name name="system_info" arity="1" clause_i="50"/> <!-- os_system_time_source --> - <name name="system_info" arity="1" clause_i="62"/> <!-- start_time --> - <name name="system_info" arity="1" clause_i="67"/> <!-- time_correction --> - <name name="system_info" arity="1" clause_i="68"/> <!-- time_offset --> - <name name="system_info" arity="1" clause_i="69"/> <!-- time_warp_mode --> - <name name="system_info" arity="1" clause_i="70"/> <!-- tolerant_timeofday --> + <name name="system_info" arity="1" clause_i="50"/> <!-- os_monotonic_time_source --> + <name name="system_info" arity="1" clause_i="51"/> <!-- os_system_time_source --> + <name name="system_info" arity="1" clause_i="63"/> <!-- start_time --> + <name name="system_info" arity="1" clause_i="68"/> <!-- time_correction --> + <name name="system_info" arity="1" clause_i="69"/> <!-- time_offset --> + <name name="system_info" arity="1" clause_i="70"/> <!-- time_warp_mode --> + <name name="system_info" arity="1" clause_i="71"/> <!-- tolerant_timeofday --> <fsummary>Information about system time.</fsummary> <desc> <marker id="system_info_time_tags"/> @@ -8455,16 +8456,16 @@ ok anchor="system_info_scheduler"/> <!-- dirty_cpu_schedulers --> <name name="system_info" arity="1" clause_i="18"/> <!-- dirty_cpu_schedulers_online --> <name name="system_info" arity="1" clause_i="19"/> <!-- dirty_io_schedulers --> - <name name="system_info" arity="1" clause_i="44"/> <!-- multi_scheduling --> - <name name="system_info" arity="1" clause_i="45"/> <!-- multi_scheduling_blockers --> - <name name="system_info" arity="1" clause_i="47"/> <!-- normal_multi_scheduling_blockers --> - <name name="system_info" arity="1" clause_i="57"/> <!-- scheduler_bind_type --> - <name name="system_info" arity="1" clause_i="58"/> <!-- scheduler_bindings --> - <name name="system_info" arity="1" clause_i="59"/> <!-- scheduler_id --> - <name name="system_info" arity="1" clause_i="60"/> <!-- schedulers --> - <name name="system_info" arity="1" clause_i="61"/> <!-- smp_support --> - <name name="system_info" arity="1" clause_i="65"/> <!-- threads --> - <name name="system_info" arity="1" clause_i="66"/> <!-- thread_pool_size --> + <name name="system_info" arity="1" clause_i="45"/> <!-- multi_scheduling --> + <name name="system_info" arity="1" clause_i="46"/> <!-- multi_scheduling_blockers --> + <name name="system_info" arity="1" clause_i="49"/> <!-- normal_multi_scheduling_blockers --> + <name name="system_info" arity="1" clause_i="58"/> <!-- scheduler_bind_type --> + <name name="system_info" arity="1" clause_i="59"/> <!-- scheduler_bindings --> + <name name="system_info" arity="1" clause_i="60"/> <!-- scheduler_id --> + <name name="system_info" arity="1" clause_i="61"/> <!-- schedulers --> + <name name="system_info" arity="1" clause_i="62"/> <!-- smp_support --> + <name name="system_info" arity="1" clause_i="66"/> <!-- threads --> + <name name="system_info" arity="1" clause_i="67"/> <!-- thread_pool_size --> <fsummary>Information about system schedulers.</fsummary> <desc> <marker id="system_info_scheduler_tags"/> @@ -8851,53 +8852,54 @@ ok <!-- <name name="system_info" arity="1" clause_i="26"/> end_time --> <!-- <name name="system_info" arity="1" clause_i="27"/> elib_malloc --> <!-- <name name="system_info" arity="1" clause_i="28"/> eager_check_io, removed --> - <!-- <name name="system_info" arity="1" clause_i="29"/> ets_limit --> - <!-- <name name="system_info" arity="1" clause_i="30"/> fullsweep_after --> - <!-- <name name="system_info" arity="1" clause_i="31"/> garbage_collection --> - <!-- <name name="system_info" arity="1" clause_i="32"/> heap_sizes --> - <!-- <name name="system_info" arity="1" clause_i="33"/> heap_type --> - <name name="system_info" arity="1" clause_i="34"/> <!-- info --> - <name name="system_info" arity="1" clause_i="35"/> <!-- kernel_poll --> - <name name="system_info" arity="1" clause_i="36"/> <!-- loaded --> - <!-- <name name="system_info" arity="1" clause_i="37"/> logical_processors --> - <name name="system_info" arity="1" clause_i="38"/> <!-- machine --> - <!-- <name name="system_info" arity="1" clause_i="39"/> max_heap_size --> - <!-- <name name="system_info" arity="1" clause_i="40"/> message_queue_data --> - <!-- <name name="system_info" arity="1" clause_i="41"/> min_heap_size --> - <!-- <name name="system_info" arity="1" clause_i="42"/> min_bin_vheap_size --> - <name name="system_info" arity="1" clause_i="43"/> <!-- modified_timing_level --> - <!-- <name name="system_info" arity="1" clause_i="44"/> multi_scheduling --> - <!-- <name name="system_info" arity="1" clause_i="45"/> multi_scheduling_blockers --> - <name name="system_info" arity="1" clause_i="46"/> <!-- nif_version --> - <!-- n<name name="system_info" arity="1" clause_i="47"/> ormal_multi_scheduling_blockers --> - <name name="system_info" arity="1" clause_i="48"/> <!-- otp_release --> - <!-- <name name="system_info" arity="1" clause_i="49"/> os_monotonic_time_source --> - <!-- <name name="system_info" arity="1" clause_i="50"/> os_system_time_source --> - <name name="system_info" arity="1" clause_i="51"/> <!-- port_parallelism --> - <!-- <name name="system_info" arity="1" clause_i="52"/> port_count --> - <!-- <name name="system_info" arity="1" clause_i="53"/> port_limit --> - <!-- <name name="system_info" arity="1" clause_i="54"/> process_count --> - <!-- <name name="system_info" arity="1" clause_i="55"/> process_limit --> - <!-- <name name="system_info" arity="1" clause_i="56"/> procs --> - <!-- <name name="system_info" arity="1" clause_i="57"/> scheduler_bind_type --> - <!-- <name name="system_info" arity="1" clause_i="58"/> scheduler_bindings --> - <!-- <name name="system_info" arity="1" clause_i="59"/> scheduler_id --> - <!-- <name name="system_info" arity="1" clause_i="60"/> schedulers --> - <!-- <name name="system_info" arity="1" clause_i="61"/> smp_support --> - <!-- <name name="system_info" arity="1" clause_i="62"/> start_time --> - <name name="system_info" arity="1" clause_i="63"/> <!-- system_version --> - <name name="system_info" arity="1" clause_i="64"/> <!-- system_architecture --> - <!-- <name name="system_info" arity="1" clause_i="65"/> threads --> - <!-- <name name="system_info" arity="1" clause_i="66"/> thread_pool_size --> - <!-- <name name="system_info" arity="1" clause_i="67"/> time_correction --> - <!-- <name name="system_info" arity="1" clause_i="68"/> time_offset --> - <!-- <name name="system_info" arity="1" clause_i="69"/> time_warp_mode --> - <!-- <name name="system_info" arity="1" clause_i="70"/> tolerant_timeofday --> - <name name="system_info" arity="1" clause_i="71"/> <!-- trace_control_word --> - <!-- <name name="system_info" arity="1" clause_i="72"/> update_cpu_info --> - <name name="system_info" arity="1" clause_i="73"/> <!-- version --> - <name name="system_info" arity="1" clause_i="74"/> <!-- wordsize --> - <!-- <name name="system_info" arity="1" clause_i="75"/> overview --> + <!-- <name name="system_info" arity="1" clause_i="29"/> ets_count --> + <!-- <name name="system_info" arity="1" clause_i="30"/> ets_limit --> + <!-- <name name="system_info" arity="1" clause_i="31"/> fullsweep_after --> + <!-- <name name="system_info" arity="1" clause_i="32"/> garbage_collection --> + <!-- <name name="system_info" arity="1" clause_i="33"/> heap_sizes --> + <!-- <name name="system_info" arity="1" clause_i="34"/> heap_type --> + <name name="system_info" arity="1" clause_i="35"/> <!-- info --> + <name name="system_info" arity="1" clause_i="36"/> <!-- kernel_poll --> + <name name="system_info" arity="1" clause_i="37"/> <!-- loaded --> + <!-- <name name="system_info" arity="1" clause_i="38"/> logical_processors --> + <name name="system_info" arity="1" clause_i="39"/> <!-- machine --> + <!-- <name name="system_info" arity="1" clause_i="40"/> max_heap_size --> + <!-- <name name="system_info" arity="1" clause_i="41"/> message_queue_data --> + <!-- <name name="system_info" arity="1" clause_i="42"/> min_heap_size --> + <!-- <name name="system_info" arity="1" clause_i="43"/> min_bin_vheap_size --> + <name name="system_info" arity="1" clause_i="44"/> <!-- modified_timing_level --> + <!-- <name name="system_info" arity="1" clause_i="45"/> multi_scheduling --> + <!-- <name name="system_info" arity="1" clause_i="46"/> multi_scheduling_blockers --> + <name name="system_info" arity="1" clause_i="47"/> <!-- nif_version --> + <!-- n<name name="system_info" arity="1" clause_i="48"/> ormal_multi_scheduling_blockers --> + <name name="system_info" arity="1" clause_i="49"/> <!-- otp_release --> + <!-- <name name="system_info" arity="1" clause_i="50"/> os_monotonic_time_source --> + <!-- <name name="system_info" arity="1" clause_i="51"/> os_system_time_source --> + <name name="system_info" arity="1" clause_i="52"/> <!-- port_parallelism --> + <!-- <name name="system_info" arity="1" clause_i="53"/> port_count --> + <!-- <name name="system_info" arity="1" clause_i="54"/> port_limit --> + <!-- <name name="system_info" arity="1" clause_i="55"/> process_count --> + <!-- <name name="system_info" arity="1" clause_i="56"/> process_limit --> + <!-- <name name="system_info" arity="1" clause_i="57"/> procs --> + <!-- <name name="system_info" arity="1" clause_i="58"/> scheduler_bind_type --> + <!-- <name name="system_info" arity="1" clause_i="59"/> scheduler_bindings --> + <!-- <name name="system_info" arity="1" clause_i="60"/> scheduler_id --> + <!-- <name name="system_info" arity="1" clause_i="61"/> schedulers --> + <!-- <name name="system_info" arity="1" clause_i="62"/> smp_support --> + <!-- <name name="system_info" arity="1" clause_i="63"/> start_time --> + <name name="system_info" arity="1" clause_i="64"/> <!-- system_version --> + <name name="system_info" arity="1" clause_i="65"/> <!-- system_architecture --> + <!-- <name name="system_info" arity="1" clause_i="66"/> threads --> + <!-- <name name="system_info" arity="1" clause_i="67"/> thread_pool_size --> + <!-- <name name="system_info" arity="1" clause_i="68"/> time_correction --> + <!-- <name name="system_info" arity="1" clause_i="69"/> time_offset --> + <!-- <name name="system_info" arity="1" clause_i="70"/> time_warp_mode --> + <!-- <name name="system_info" arity="1" clause_i="71"/> tolerant_timeofday --> + <name name="system_info" arity="1" clause_i="72"/> <!-- trace_control_word --> + <!-- <name name="system_info" arity="1" clause_i="73"/> update_cpu_info --> + <name name="system_info" arity="1" clause_i="74"/> <!-- version --> + <name name="system_info" arity="1" clause_i="75"/> <!-- wordsize --> + <!-- <name name="system_info" arity="1" clause_i="76"/> overview --> <fsummary>Information about the system.</fsummary> <desc> <marker id="system_info_misc_tags"/> diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index ed0946d6ba..3b300c8c72 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -31,6 +31,21 @@ </header> <p>This document describes the changes made to the ERTS application.</p> +<section><title>Erts 10.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>The keys used in <c>os:getenv</c> and <c>os:putenv</c> + are case-insensitive again on Windows.</p> + <p> + Own Id: OTP-15147 Aux Id: ERL-644 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 10.0</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -967,6 +982,21 @@ </section> +<section><title>Erts 9.3.3.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Fixed a rare bug that could cause processes to be + scheduled after they had been freed.</p> + <p> + Own Id: OTP-15067 Aux Id: ERL-573 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 9.3.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 8253ec4f40..11b52526d5 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -3761,6 +3761,8 @@ dequeue_process(ErtsRunQueue *runq, int prio_q, erts_aint32_t *statep) ERTS_THR_DATA_DEPENDENCY_READ_MEMORY_BARRIER; state = erts_atomic32_read_nob(&p->state); + ASSERT(state & ERTS_PSFLG_IN_RUNQ); + if (statep) *statep = state; @@ -3768,8 +3770,7 @@ dequeue_process(ErtsRunQueue *runq, int prio_q, erts_aint32_t *statep) rqi = &runq->procs.prio_info[prio]; - if (p) - unqueue_process(runq, rpq, rqi, prio, NULL, p); + unqueue_process(runq, rpq, rqi, prio, NULL, p); return p; } @@ -4088,7 +4089,7 @@ evacuate_run_queue(ErtsRunQueue *rq, erts_runq_unlock(to_rq); smp_notify_inc_runq(to_rq); - erts_runq_lock(to_rq); + erts_runq_lock(rq); } if (rq->ports.start) { @@ -4157,22 +4158,17 @@ evacuate_run_queue(ErtsRunQueue *rq, free_proxy_proc(proc); else { erts_aint32_t clr_bits; -#ifdef DEBUG - erts_aint32_t old; -#endif clr_bits = ERTS_PSFLG_IN_RUNQ; clr_bits |= qbit << ERTS_PSFLGS_IN_PRQ_MASK_OFFSET; -#ifdef DEBUG - old = -#else - (void) -#endif - erts_atomic32_read_band_mb(&proc->state, - ~clr_bits); - ASSERT((old & clr_bits) == clr_bits); + state = erts_atomic32_read_band_mb(&proc->state, ~clr_bits); + ASSERT((state & clr_bits) == clr_bits); + if (state & ERTS_PSFLG_FREE) { + /* free and not queued by proxy */ + erts_proc_dec_refc(proc); + } } goto handle_next_proc; @@ -6208,13 +6204,14 @@ fin_dirty_enq_s_change(Process *p, /* Already enqueue by someone else... */ if (pstruct_reserved) { /* We reserved process struct for enqueue; clear it... */ -#ifdef DEBUG - erts_aint32_t old = -#else - (void) -#endif - erts_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_IN_RUNQ); - ASSERT(old & ERTS_PSFLG_IN_RUNQ); + erts_aint32_t state; + + state = erts_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_IN_RUNQ); + ASSERT(state & ERTS_PSFLG_IN_RUNQ); + + if (state & ERTS_PSFLG_FREE) { + erts_proc_dec_refc(p); + } } return 0; } @@ -6407,8 +6404,9 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, == ERTS_PSFLG_ACTIVE)); n &= ~running_flgs; - if ((a & (ERTS_PSFLG_ACTIVE_SYS|ERTS_PSFLG_DIRTY_ACTIVE_SYS)) - || (a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE) { + if ((!!(a & (ERTS_PSFLG_ACTIVE_SYS|ERTS_PSFLG_DIRTY_ACTIVE_SYS)) + | ((a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE)) + & !(a & ERTS_PSFLG_FREE)) { enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a); } a = erts_atomic32_cmpxchg_mb(&p->state, n, e); @@ -6655,62 +6653,72 @@ erts_schedule_process(Process *p, erts_aint32_t state, ErtsProcLocks locks) schedule_process(p, state, locks); } +/* Enqueues the given sys task on the process and schedules it. The task may be + * NULL if only scheduling is desired. */ static ERTS_INLINE erts_aint32_t -active_sys_enqueue(Process *p, erts_aint32_t state, - erts_aint32_t enable_flags, int status_locked) -{ - /* - * This function may or may not be called with status locke held. - * It always returns without the status lock held! - */ - unsigned int prof_runnable_procs = erts_system_profile_flags.runnable_procs; - erts_aint32_t n, a = state, enq_prio = -1; - int slocked = status_locked; +active_sys_enqueue(Process *p, ErtsProcSysTask *sys_task, + erts_aint32_t task_prio, erts_aint32_t enable_flags, + erts_aint32_t state, erts_aint32_t *fail_state_p) +{ + int runnable_procs = erts_system_profile_flags.runnable_procs; + erts_aint32_t n, a, enq_prio, fail_state; + int already_scheduled; + int status_locked; int enqueue; /* < 0 -> use proxy */ - /* Status lock prevents out of order "runnable proc" trace msgs */ - ERTS_LC_ASSERT(slocked || !(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p))); - ERTS_LC_ASSERT(!slocked || (ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p))); - - if (!prof_runnable_procs) { - if (slocked) { - erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); - slocked = 0; - } - } - else { - if (!slocked) { - erts_proc_lock(p, ERTS_PROC_LOCK_STATUS); - slocked = !0; - } - } + enable_flags |= ERTS_PSFLG_ACTIVE_SYS; + fail_state = *fail_state_p; + already_scheduled = 0; + status_locked = 0; + enq_prio = -1; + a = state; + ERTS_LC_ASSERT(!(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p))); + ASSERT(fail_state & (ERTS_PSFLG_EXITING | ERTS_PSFLG_FREE)); + ASSERT(!(fail_state & enable_flags)); ASSERT(!(state & ERTS_PSFLG_PROXY)); + /* When runnable_procs is enabled, we need to take the status lock to + * prevent trace messages from being sent in the wrong order. The lock must + * be held over the call to add2runq. + * + * Otherwise, we only need to take it when we're enqueuing a task and can + * safely release it before add2runq. */ + if (sys_task || runnable_procs) { + erts_proc_lock(p, ERTS_PROC_LOCK_STATUS); + status_locked = 1; + } + while (1) { erts_aint32_t e; n = e = a; - if (a & ERTS_PSFLG_FREE) - goto cleanup; /* We don't want to schedule free processes... */ + if (a & fail_state) { + *fail_state_p = a & fail_state; + goto cleanup; + } enqueue = ERTS_ENQUEUE_NOT; - n |= enable_flags; - n |= ERTS_PSFLG_ACTIVE_SYS; + n |= enable_flags; + if (!(a & (ERTS_PSFLG_RUNNING | ERTS_PSFLG_RUNNING_SYS | ERTS_PSFLG_DIRTY_RUNNING - | ERTS_PSFLG_DIRTY_RUNNING_SYS))) + | ERTS_PSFLG_DIRTY_RUNNING_SYS))) { enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a); + } + a = erts_atomic32_cmpxchg_mb(&p->state, n, e); - if (a == e) + if (a == e) { break; - if (a == n && enqueue == ERTS_ENQUEUE_NOT) - goto cleanup; + } + else if (a == n && enqueue == ERTS_ENQUEUE_NOT) { + already_scheduled = 1; + break; + } } - if (prof_runnable_procs) { - + if (!already_scheduled && runnable_procs) { if (!(a & (ERTS_PSFLG_ACTIVE_SYS | ERTS_PSFLG_RUNNING | ERTS_PSFLG_RUNNING_SYS @@ -6720,19 +6728,56 @@ active_sys_enqueue(Process *p, erts_aint32_t state, /* We activated a prevously inactive process */ profile_runnable_proc(p, am_active); } - - erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); - slocked = 0; } - add2runq(enqueue, enq_prio, p, n, NULL); + if (sys_task) { + ErtsProcSysTaskQs *stqs = p->sys_task_qs; -cleanup: + if (!stqs) { + sys_task->next = sys_task->prev = sys_task; - if (slocked) - erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + stqs = proc_sys_task_queues_alloc(); - ERTS_LC_ASSERT(!(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p))); + stqs->qmask = 1 << task_prio; + stqs->ncount = 0; + stqs->q[PRIORITY_MAX] = NULL; + stqs->q[PRIORITY_HIGH] = NULL; + stqs->q[PRIORITY_NORMAL] = NULL; + stqs->q[PRIORITY_LOW] = NULL; + stqs->q[task_prio] = sys_task; + + p->sys_task_qs = stqs; + } + else { + if (!stqs->q[task_prio]) { + sys_task->next = sys_task->prev = sys_task; + + stqs->q[task_prio] = sys_task; + stqs->qmask |= 1 << task_prio; + } + else { + sys_task->next = stqs->q[task_prio]; + sys_task->prev = stqs->q[task_prio]->prev; + sys_task->next->prev = sys_task; + sys_task->prev->next = sys_task; + ASSERT(stqs->qmask & (1 << task_prio)); + } + } + } + + if (status_locked && !runnable_procs) { + erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + status_locked = 0; + } + + if (!already_scheduled) { + add2runq(enqueue, enq_prio, p, n, NULL); + } + +cleanup: + if (status_locked) { + erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + } return n; } @@ -6740,103 +6785,41 @@ cleanup: erts_aint32_t erts_proc_sys_schedule(Process *p, erts_aint32_t state, erts_aint32_t enable_flag) { - /* We are not allowed to call this function with status lock held... */ - return active_sys_enqueue(p, state, enable_flag, 0); + erts_aint32_t fail_state = ERTS_PSFLG_FREE; + + return active_sys_enqueue(p, NULL, 0, enable_flag, state, &fail_state); } static int 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 fail_state, state; - fail_state = *fail_state_p; - - res = 1; /* prepare for success */ - st->next = st->prev = st; /* Prep for empty prio queue */ + /* Elevate priority if needed. */ state = erts_atomic32_read_nob(&p->state); - locked = 0; - free_stqs = NULL; - if (state & ERTS_PSFLG_SYS_TASKS) - stqs = NULL; - else { - alloc_qs: - stqs = proc_sys_task_queues_alloc(); - stqs->qmask = 1 << prio; - stqs->ncount = 0; - stqs->q[PRIORITY_MAX] = NULL; - stqs->q[PRIORITY_HIGH] = NULL; - stqs->q[PRIORITY_NORMAL] = NULL; - stqs->q[PRIORITY_LOW] = NULL; - stqs->q[prio] = st; - } - - if (!locked) { - locked = 1; - erts_proc_lock(p, ERTS_PROC_LOCK_STATUS); - - state = erts_atomic32_read_nob(&p->state); - if (state & fail_state) { - erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); - *fail_state_p = (state & fail_state); - free_stqs = stqs; - res = 0; - goto cleanup; - } - } - - if (!p->sys_task_qs) { - if (stqs) - p->sys_task_qs = stqs; - else - goto alloc_qs; - } - else { - free_stqs = stqs; - stqs = p->sys_task_qs; - if (!stqs->q[prio]) { - stqs->q[prio] = st; - stqs->qmask |= 1 << prio; - } - else { - st->next = stqs->q[prio]; - st->prev = stqs->q[prio]->prev; - st->next->prev = st; - st->prev->next = st; - ASSERT(stqs->qmask & (1 << prio)); - } - } - if (ERTS_PSFLGS_GET_ACT_PRIO(state) > prio) { - erts_aint32_t n, a, e; - /* Need to elevate actual prio */ + erts_aint32_t n, a, e; - a = state; - do { - if (ERTS_PSFLGS_GET_ACT_PRIO(a) <= prio) { - n = a; - break; - } - n = e = a; - n &= ~ERTS_PSFLGS_ACT_PRIO_MASK; - n |= (prio << ERTS_PSFLGS_ACT_PRIO_OFFSET); - a = erts_atomic32_cmpxchg_nob(&p->state, n, e); - } while (a != e); - state = n; - } - - /* active_sys_enqueue() always return with status lock unlocked */ - (void) active_sys_enqueue(p, state, ERTS_PSFLG_SYS_TASKS, locked); + a = state; + do { + if (ERTS_PSFLGS_GET_ACT_PRIO(a) <= prio) { + n = a; + break; + } + n = e = a; + n &= ~ERTS_PSFLGS_ACT_PRIO_MASK; + n |= (prio << ERTS_PSFLGS_ACT_PRIO_OFFSET); + a = erts_atomic32_cmpxchg_nob(&p->state, n, e); + } while (a != e); -cleanup: + state = n; + } - if (free_stqs) - proc_sys_task_queues_free(free_stqs); + fail_state = *fail_state_p; - return res; + return !(active_sys_enqueue(p, st, prio, ERTS_PSFLG_SYS_TASKS, + state, fail_state_p) & fail_state); } static ERTS_INLINE int @@ -9563,6 +9546,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) } else if (state & ERTS_PSFLG_FREE) { /* free and not queued by proxy */ + ASSERT(state & ERTS_PSFLG_IN_RUNQ); erts_proc_dec_refc(p); } if (!is_normal_sched) diff --git a/erts/emulator/sys/common/erl_osenv.c b/erts/emulator/sys/common/erl_osenv.c index 9f54d1dff0..487ff87116 100644 --- a/erts/emulator/sys/common/erl_osenv.c +++ b/erts/emulator/sys/common/erl_osenv.c @@ -75,7 +75,15 @@ static int compare_env_keys(const erts_osenv_data_t a, const erts_osenv_data_t b #include "erl_rbtree.h" static int compare_env_keys(const erts_osenv_data_t a, const erts_osenv_data_t b) { - int relation = sys_memcmp(a.data, b.data, MIN(a.length, b.length)); + int relation; + +#ifdef __WIN32__ + /* Environment variables are case-insensitive on Windows. */ + relation = _wcsnicmp((const WCHAR*)a.data, (const WCHAR*)b.data, + MIN(a.length, b.length) / sizeof(WCHAR)); +#else + relation = sys_memcmp(a.data, b.data, MIN(a.length, b.length)); +#endif if(relation != 0) { return relation; diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index 32bfcd5520..9e7bcd5255 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -36,7 +36,8 @@ error_stacktrace_during_call_trace/1, group_leader_prio/1, group_leader_prio_dirty/1, is_process_alive/1, - process_info_blast/1]). + process_info_blast/1, + os_env_case_sensitivity/1]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -51,7 +52,7 @@ all() -> erl_crash_dump_bytes, min_max, erlang_halt, is_builtin, error_stacktrace, error_stacktrace_during_call_trace, group_leader_prio, group_leader_prio_dirty, - is_process_alive, process_info_blast]. + is_process_alive, process_info_blast, os_env_case_sensitivity]. %% Uses erlang:display to test that erts_printf does not do deep recursion display(Config) when is_list(Config) -> @@ -443,6 +444,17 @@ os_env_long(Min, Max, Value) -> true = os:unsetenv(EnvVar), os_env_long(Min+1, Max, Value). +os_env_case_sensitivity(Config) when is_list(Config) -> + %% The keys in os:getenv/putenv must be case-insensitive on Windows, and + %% case-sensitive elsewhere. + true = os:putenv("os_env_gurka", "gaffel"), + Expected = case os:type() of + {win32, _} -> "gaffel"; + _ -> false + end, + Expected = os:getenv("OS_ENV_GURKA"), + ok. + %% Test that string:to_integer does not Halloc in wrong order. otp_7526(Config) when is_list(Config) -> ok = test_7526(256). diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 6775d06b4b..f4b1d885fe 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -59,6 +59,7 @@ no_priority_inversion2/1, system_task_blast/1, system_task_on_suspended/1, + system_task_failed_enqueue/1, gc_request_when_gc_disabled/1, gc_request_blast_when_gc_disabled/1]). -export([prio_server/2, prio_client/2, init/1, handle_event/2]). @@ -106,7 +107,7 @@ groups() -> otp_7738_resume]}, {system_task, [], [no_priority_inversion, no_priority_inversion2, - system_task_blast, system_task_on_suspended, + system_task_blast, system_task_on_suspended, system_task_failed_enqueue, gc_request_when_gc_disabled, gc_request_blast_when_gc_disabled]}]. init_per_suite(Config) -> @@ -2627,6 +2628,57 @@ system_task_on_suspended(Config) when is_list(Config) -> ok end. +%% When a system task couldn't be enqueued due to the process being in an +%% incompatible state, it would linger in the system task list and get executed +%% anyway the next time the process was scheduled. This would result in a +%% double-free at best. +%% +%% This test continuously purges modules while other processes run dirty code, +%% which will provoke this error as ERTS_PSTT_CPC can't be enqueued while a +%% process is running dirty code. +system_task_failed_enqueue(Config) when is_list(Config) -> + case erlang:system_info(dirty_cpu_schedulers) of + N when N > 0 -> + system_task_failed_enqueue_1(Config); + _ -> + {skipped, "No dirty scheduler support"} + end. + +system_task_failed_enqueue_1(Config) -> + Priv = proplists:get_value(priv_dir, Config), + + Purgers = [spawn_link(fun() -> purge_loop(Priv, Id) end) + || Id <- lists:seq(1, erlang:system_info(schedulers))], + Hogs = [spawn_link(fun() -> dirty_loop() end) + || _ <- lists:seq(1, erlang:system_info(dirty_cpu_schedulers))], + + ct:sleep(5000), + + [begin + unlink(Pid), + exit(Pid, kill) + end || Pid <- (Purgers ++ Hogs)], + + ok. + +purge_loop(PrivDir, Id) -> + Mod = "failed_enq_" ++ integer_to_list(Id), + Path = PrivDir ++ "/" ++ Mod, + file:write_file(Path ++ ".erl", + "-module('" ++ Mod ++ "').\n" ++ + "-export([t/0]).\n" ++ + "t() -> ok."), + purge_loop_1(Path). +purge_loop_1(Path) -> + {ok, Mod} = compile:file(Path, []), + erlang:delete_module(Mod), + erts_code_purger:purge(Mod), + purge_loop_1(Path). + +dirty_loop() -> + ok = erts_debug:dirty_cpu(reschedule, 10000), + dirty_loop(). + gc_request_when_gc_disabled(Config) when is_list(Config) -> AIS = erts_debug:set_internal_state(available_internal_state, true), gc_request_when_gc_disabled_do(ref), diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index 3572fdd954..896e4c8e45 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -69,15 +69,6 @@ cxargs_add() { done } -eeargs= -eeargs_add() { - while [ $# -gt 0 ]; do - cargs="$cargs $1" - eeargs="$eeargs $1" - shift - done -} - core= GDB= @@ -97,8 +88,6 @@ TARGET=%TARGET% PROGNAME=$ROOTDIR/bin/cerl EMU=beam -PRELOADED=$ROOTDIR/erts/preloaded/ebin - while [ $# -gt 0 ]; do case "$1" in @@ -255,7 +244,7 @@ EXEC=$BINDIR/erlexec PROGNAME="$PROGNAME$cargs" EMU="$EMU$TYPE" -EMU_NAME=`$EXEC -emu_name_exit $eeargs` +EMU_NAME=`$EXEC -emu_name_exit` if [ $skip_erlexec = yes ]; then emu_xargs=`echo $xargs | sed "s|+|-|g"` @@ -269,8 +258,6 @@ if [ $skip_erlexec = yes ]; then ' set -- $beam_args IFS="$SAVE_IFS" -else - xargs="$xargs -pz $PRELOADED --" fi if [ "x$GDB" = "x" ]; then if [ $run_valgrind = yes ]; then @@ -316,12 +303,12 @@ if [ "x$GDB" = "x" ]; then sched_arg= fi - exec $taskset1 valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU_NAME $sched_arg $emu_xargs "$@" -pz $PRELOADED + exec $taskset1 valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU_NAME $sched_arg $emu_xargs "$@" elif [ $run_rr = yes ]; then - exec rr record --ignore-nested $BINDIR/$EMU_NAME $emu_xargs "$@" -pz $PRELOADED + exec rr record --ignore-nested $BINDIR/$EMU_NAME $emu_xargs "$@" else - exec $EXEC $eeargs $xargs ${1+"$@"} + exec $EXEC $xargs ${1+"$@"} fi elif [ "x$GDB" = "xgdb" ]; then case "x$core" in diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 71a7d24331..fd8a35aabf 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2681,11 +2681,12 @@ tuple_to_list(_Tuple) -> (dist_ctrl) -> {Node :: node(), ControllingEntity :: port() | pid()}; (driver_version) -> string(); - (dynamic_trace) -> none | dtrace | systemtap; + (dynamic_trace) -> none | dtrace | systemtap; (dynamic_trace_probes) -> boolean(); (end_time) -> non_neg_integer(); (elib_malloc) -> false; (eager_check_io) -> boolean(); + (ets_count) -> pos_integer(); (ets_limit) -> pos_integer(); (fullsweep_after) -> {fullsweep_after, non_neg_integer()}; (garbage_collection) -> [{atom(), integer()}]; diff --git a/erts/vsn.mk b/erts/vsn.mk index 519556119d..e60405215a 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 10.0 +VSN = 10.0.1 # Port number 4365 in 4.2 # Port number 4366 in 4.3 diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml index 997a506181..fcfa7d38a7 100644 --- a/lib/compiler/doc/src/notes.xml +++ b/lib/compiler/doc/src/notes.xml @@ -32,6 +32,21 @@ <p>This document describes the changes made to the Compiler application.</p> +<section><title>Compiler 7.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>The could could crash when compiling a complicated + function that used the binary syntax.</p> + <p> + Own Id: OTP-15150 Aux Id: ERL-650 </p> + </item> + </list> + </section> + +</section> + <section><title>Compiler 7.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl index 9eee56d604..43084ad588 100644 --- a/lib/compiler/src/beam_jump.erl +++ b/lib/compiler/src/beam_jump.erl @@ -156,41 +156,51 @@ function({function,Name,Arity,CLabel,Asm0}) -> %%% share(Is0) -> - %% We will get more sharing if we never fall through to a label. - Is = eliminate_fallthroughs(Is0, []), - share_1(Is, #{}, [], []). + Is1 = eliminate_fallthroughs(Is0, []), + Is2 = find_fixpoint(fun(Is) -> + share_1(Is, #{}, #{}, [], []) + end, Is1), + reverse(Is2). -share_1([{label,L}=Lbl|Is], Dict0, [_|_]=Seq, Acc) -> +share_1([{label,L}=Lbl|Is], Dict0, Lbls0, [_|_]=Seq, Acc) -> case maps:find(Seq, Dict0) of - error -> - Dict = maps:put(Seq, L, Dict0), - share_1(Is, Dict, [], [Lbl|Seq ++ Acc]); - {ok,Label} -> - share_1(Is, Dict0, [], [Lbl,{jump,{f,Label}}|Acc]) + error -> + Dict = maps:put(Seq, L, Dict0), + share_1(Is, Dict, Lbls0, [], [[Lbl|Seq]|Acc]); + {ok,Label} -> + Lbls = maps:put(L, Label, Lbls0), + share_1(Is, Dict0, Lbls, [], [[Lbl,{jump,{f,Label}}]|Acc]) end; -share_1([{func_info,_,_,_}=I|Is], _, [], Acc) -> - reverse(Is, [I|Acc]); -share_1([{'catch',_,_}=I|Is], Dict0, Seq, Acc) -> - Dict = clean_non_sharable(Dict0), - share_1(Is, Dict, [I|Seq], Acc); -share_1([{'try',_,_}=I|Is], Dict0, Seq, Acc) -> - Dict = clean_non_sharable(Dict0), - share_1(Is, Dict, [I|Seq], Acc); -share_1([{try_case,_}=I|Is], Dict0, Seq, Acc) -> - Dict = clean_non_sharable(Dict0), - share_1(Is, Dict, [I|Seq], Acc); -share_1([{catch_end,_}=I|Is], Dict0, Seq, Acc) -> - Dict = clean_non_sharable(Dict0), - share_1(Is, Dict, [I|Seq], Acc); -share_1([I|Is], Dict, Seq, Acc) -> +share_1([{func_info,_,_,_}|_]=Is0, _, Lbls, [], Acc0) when Lbls =/= #{} -> + lists:foldl(fun(Is, Acc) -> + beam_utils:replace_labels(Is, Acc, Lbls, fun(Old) -> Old end) + end, Is0, Acc0); +share_1([{func_info,_,_,_}|_]=Is, _, Lbls, [], Acc) when Lbls =:= #{} -> + lists:foldl(fun lists:reverse/2, Is, Acc); +share_1([{'catch',_,_}=I|Is], Dict0, Lbls0, Seq, Acc) -> + {Dict,Lbls} = clean_non_sharable(Dict0, Lbls0), + share_1(Is, Dict, Lbls, [I|Seq], Acc); +share_1([{'try',_,_}=I|Is], Dict0, Lbls0, Seq, Acc) -> + {Dict,Lbls} = clean_non_sharable(Dict0, Lbls0), + share_1(Is, Dict, Lbls, [I|Seq], Acc); +share_1([{try_case,_}=I|Is], Dict0, Lbls0, Seq, Acc) -> + {Dict,Lbls} = clean_non_sharable(Dict0, Lbls0), + share_1(Is, Dict, Lbls, [I|Seq], Acc); +share_1([{catch_end,_}=I|Is], Dict0, Lbls0, Seq, Acc) -> + {Dict,Lbls} = clean_non_sharable(Dict0, Lbls0), + share_1(Is, Dict, Lbls, [I|Seq], Acc); +share_1([{jump,{f,To}}=I,{label,L}=Lbl|Is], Dict0, Lbls0, _Seq, Acc) -> + Lbls = maps:put(L, To, Lbls0), + share_1(Is, Dict0, Lbls, [], [[Lbl,I]|Acc]); +share_1([I|Is], Dict, Lbls, Seq, Acc) -> case is_unreachable_after(I) of false -> - share_1(Is, Dict, [I|Seq], Acc); + share_1(Is, Dict, Lbls, [I|Seq], Acc); true -> - share_1(Is, Dict, [I], Acc) + share_1(Is, Dict, Lbls, [I], Acc) end. -clean_non_sharable(Dict) -> +clean_non_sharable(Dict0, Lbls0) -> %% We are passing in or out of a 'catch' or 'try' block. Remove %% sequences that should not be shared over the boundaries of the %% block. Since the end of the sequence must match, the only @@ -198,7 +208,17 @@ clean_non_sharable(Dict) -> %% the 'catch'/'try' block is a sequence that ends with an %% instruction that causes an exception. Any sequence that causes %% an exception must contain a line/1 instruction. - maps:filter(fun(K, _V) -> sharable_with_try(K) end, Dict). + Dict1 = maps:to_list(Dict0), + Lbls1 = maps:to_list(Lbls0), + {Dict2,Lbls2} = foldl(fun({K, V}, {Dict,Lbls}) -> + case sharable_with_try(K) of + true -> + {[{K,V}|Dict],lists:keydelete(V, 2, Lbls)}; + false -> + {Dict,Lbls} + end + end, {[],Lbls1}, Dict1), + {maps:from_list(Dict2),maps:from_list(Lbls2)}. sharable_with_try([{line,_}|_]) -> %% This sequence may cause an exception and may potentially diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl index 5510624b2d..ff587c4982 100644 --- a/lib/compiler/src/beam_utils.erl +++ b/lib/compiler/src/beam_utils.erl @@ -1105,8 +1105,12 @@ defs([{bif,_,{f,Fail},_Src,Dst}=I|Is], Regs0, D) -> defs([{block,Block0}|Is], Regs0, D0) -> {Block,Regs,D} = defs_list(Block0, Regs0, D0), [{block,[make_anno({def,Regs0})|Block]}|defs(Is, Regs, D)]; -defs([{bs_init,{f,L},_,_,_,Dst}=I|Is], Regs0, D) -> - Regs = def_regs([Dst], Regs0), +defs([{bs_init,{f,L},_,Live,_,Dst}=I|Is], Regs0, D) -> + Regs1 = case Live of + none -> Regs0; + _ -> init_def_regs(Live) + end, + Regs = def_regs([Dst], Regs1), [I|defs(Is, Regs, update_regs(L, Regs, D))]; defs([{bs_put,{f,L},_,_}=I|Is], Regs, D) -> [I|defs(Is, Regs, update_regs(L, Regs, D))]; diff --git a/lib/compiler/src/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl index 68489a0122..71ab0e872a 100644 --- a/lib/compiler/src/erl_bifs.erl +++ b/lib/compiler/src/erl_bifs.erl @@ -91,6 +91,7 @@ is_pure(erlang, is_bitstring, 1) -> true; %% erlang:is_builtin/3 depends on the state (i.e. the version of the emulator). is_pure(erlang, is_float, 1) -> true; is_pure(erlang, is_function, 1) -> true; +is_pure(erlang, is_function, 2) -> true; is_pure(erlang, is_integer, 1) -> true; is_pure(erlang, is_list, 1) -> true; is_pure(erlang, is_map, 1) -> true; diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index ceb7d56221..0aa58a46e4 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -99,7 +99,7 @@ t=#{} :: map(), %Types in_guard=false}). %In guard or not. --type type_info() :: cerl:cerl() | 'bool' | 'integer'. +-type type_info() :: cerl:cerl() | 'bool' | 'integer' | {'fun', pos_integer()}. -type yes_no_maybe() :: 'yes' | 'no' | 'maybe'. -type sub() :: #sub{}. @@ -883,6 +883,10 @@ fold_non_lit_args(Call, erlang, setelement, [Arg1,Arg2,Arg3], _) -> eval_setelement(Call, Arg1, Arg2, Arg3); fold_non_lit_args(Call, erlang, is_record, [Arg1,Arg2,Arg3], Sub) -> eval_is_record(Call, Arg1, Arg2, Arg3, Sub); +fold_non_lit_args(Call, erlang, is_function, [Arg1], Sub) -> + eval_is_function_1(Call, Arg1, Sub); +fold_non_lit_args(Call, erlang, is_function, [Arg1,Arg2], Sub) -> + eval_is_function_2(Call, Arg1, Arg2, Sub); fold_non_lit_args(Call, erlang, N, Args, Sub) -> NumArgs = length(Args), case erl_internal:comp_op(N, NumArgs) of @@ -898,6 +902,22 @@ fold_non_lit_args(Call, erlang, N, Args, Sub) -> end; fold_non_lit_args(Call, _, _, _, _) -> Call. +eval_is_function_1(Call, Arg1, Sub) -> + case get_type(Arg1, Sub) of + none -> Call; + {'fun',_} -> #c_literal{anno=cerl:get_ann(Call),val=true}; + _ -> #c_literal{anno=cerl:get_ann(Call),val=false} + end. + +eval_is_function_2(Call, Arg1, #c_literal{val=Arity}, Sub) + when is_integer(Arity), Arity > 0 -> + case get_type(Arg1, Sub) of + none -> Call; + {'fun',Arity} -> #c_literal{anno=cerl:get_ann(Call),val=true}; + _ -> #c_literal{anno=cerl:get_ann(Call),val=false} + end; +eval_is_function_2(Call, _Arg1, _Arg2, _Sub) -> Call. + %% Evaluate a relational operation using type information. eval_rel_op(Call, Op, [#c_var{name=V},#c_var{name=V}], _) -> Bool = erlang:Op(same, same), @@ -3105,6 +3125,10 @@ update_types_2(V, [#c_tuple{}=P], Types) -> Types#{V=>P}; update_types_2(V, [#c_literal{val=Bool}], Types) when is_boolean(Bool) -> Types#{V=>bool}; +update_types_2(V, [#c_fun{vars=Vars}], Types) -> + Types#{V=>{'fun',length(Vars)}}; +update_types_2(V, [#c_var{name={_,Arity}}], Types) -> + Types#{V=>{'fun',Arity}}; update_types_2(V, [Type], Types) when is_atom(Type) -> Types#{V=>Type}; update_types_2(_, _, Types) -> Types. @@ -3123,6 +3147,8 @@ kill_types2(V, [{_,#c_tuple{}=Tuple}=Entry|Tdb]) -> false -> [Entry|kill_types2(V, Tdb)]; true -> kill_types2(V, Tdb) end; +kill_types2(V, [{_, {'fun',_}}=Entry|Tdb]) -> + [Entry|kill_types2(V, Tdb)]; kill_types2(V, [{_,Atom}=Entry|Tdb]) when is_atom(Atom) -> [Entry|kill_types2(V, Tdb)]; kill_types2(_, []) -> []. diff --git a/lib/compiler/test/beam_utils_SUITE.erl b/lib/compiler/test/beam_utils_SUITE.erl index 3d35b546fc..360dcc1e84 100644 --- a/lib/compiler/test/beam_utils_SUITE.erl +++ b/lib/compiler/test/beam_utils_SUITE.erl @@ -132,6 +132,15 @@ bs_init(_Config) -> <<"foo/foo">> = do_bs_init_4(<<"foo">>, true), error = do_bs_init_4([], not_boolean), + Id = 17575, + Domain = -8798798, + [<<10,1:16,Id:16/signed>>,<<8,2:16,Domain:32/signed>>] = + do_bs_init_5(#{tag=>value,id=>Id,domain=>Domain}), + {'EXIT',{{required,id},[_|_]}} = + (catch do_bs_init_5(#{tag=>value,id=>nil,domain=>Domain})), + {'EXIT',{{required,domain},[_|_]}} = + (catch do_bs_init_5(#{tag=>value,id=>Id,domain=>nil})), + ok. do_bs_init_1([?MODULE], Sz) -> @@ -189,6 +198,20 @@ do_bs_init_4(Arg1, Arg2) -> error end. +do_bs_init_5(#{tag := value, id := Id, domain := Domain}) -> + [case Id of + nil -> + error(id({required, id})); + _ -> + <<10, 1:16/signed, Id:16/signed>> + end, + case Domain of + nil -> + error(id({required, domain})); + _ -> + <<8, 2:16/signed, Domain:32/signed>> + end]. + bs_save(_Config) -> {a,30,<<>>} = do_bs_save(<<1:1,30:5>>), {b,127,<<>>} = do_bs_save(<<1:1,31:5,0:1,127:7>>), diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl index dfff8236e8..5654edae79 100644 --- a/lib/compiler/test/core_fold_SUITE.erl +++ b/lib/compiler/test/core_fold_SUITE.erl @@ -278,6 +278,8 @@ coverage(Config) when is_list(Config) -> a = cover_remove_non_vars_alias({a,b,c}), error = cover_will_match_lit_list(), {ok,[a]} = cover_is_safe_bool_expr(a), + false = cover_is_safe_bool_expr2(a), + ok = cover_eval_is_function(fun id/1), ok = cover_opt_guard_try(#cover_opt_guard_try{list=[a]}), error = cover_opt_guard_try(#cover_opt_guard_try{list=[]}), @@ -341,6 +343,15 @@ cover_is_safe_bool_expr(X) -> false end. +cover_is_safe_bool_expr2(X) -> + try + V = [X], + is_function(V, 1) + catch + _:_ -> + false + end. + cover_opt_guard_try(Msg) -> if length(Msg#cover_opt_guard_try.list) =/= 1 -> @@ -349,6 +360,12 @@ cover_opt_guard_try(Msg) -> ok end. +cover_eval_is_function(X) -> + case X of + {a,_} -> is_function(X); + _ -> ok + end. + bsm_an_inlined(<<_:8>>, _) -> ok; bsm_an_inlined(_, _) -> error. diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk index a26cb09dff..261c908513 100644 --- a/lib/compiler/vsn.mk +++ b/lib/compiler/vsn.mk @@ -1 +1 @@ -COMPILER_VSN = 7.2 +COMPILER_VSN = 7.2.1 diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml index 5b81f795b2..1438317d8d 100644 --- a/lib/erl_interface/doc/src/notes.xml +++ b/lib/erl_interface/doc/src/notes.xml @@ -69,6 +69,24 @@ </section> +<section><title>Erl_Interface 3.10.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Make <c>ei_connect</c> and friends also accept state + <c>ok_simultaneous</c> during handshake, which means the + other node has initiated a connection setup that will be + cancelled in favor of this connection.</p> + <p> + Own Id: OTP-15161 Aux Id: ERIERL-191 </p> + </item> + </list> + </section> + +</section> + <section><title>Erl_Interface 3.10.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c index f5034ca3e8..34f4620866 100644 --- a/lib/erl_interface/src/connect/ei_connect.c +++ b/lib/erl_interface/src/connect/ei_connect.c @@ -1357,11 +1357,14 @@ static int recv_status(int fd, unsigned ms) "<- RECV_STATUS socket read failed (%d)", rlen); goto error; } - if (rlen == 3 && buf[0] == 's' && buf[1] == 'o' && - buf[2] == 'k') { + + EI_TRACE_CONN2("recv_status", + "<- RECV_STATUS (%.*s)", (rlen>20 ? 20 : rlen), buf); + + if (rlen >= 3 && buf[0] == 's' && buf[1] == 'o' && buf[2] == 'k') { + /* Expecting "sok" or "sok_simultaneous" */ if (!is_static) free(buf); - EI_TRACE_CONN0("recv_status","<- RECV_STATUS (ok)"); return 0; } error: diff --git a/lib/ftp/doc/src/ftp.xml b/lib/ftp/doc/src/ftp.xml index 3dbd3ed403..23a9e3fec7 100644 --- a/lib/ftp/doc/src/ftp.xml +++ b/lib/ftp/doc/src/ftp.xml @@ -546,11 +546,12 @@ <v>start_option() = {verbose, verbose()} | {debug, debug()}</v> <v>verbose() = boolean() (default is false)</v> <v>debug() = disable | debug | trace (default is disable)</v> - <v>open_option() = {ipfamily, ipfamily()} | {port, port()} | {mode, mode()} | {tls, tls_options()} | {timeout, timeout()} | {dtimeout, dtimeout()} | {progress, progress()}</v> + <v>open_option() = {ipfamily, ipfamily()} | {port, port()} | {mode, mode()} | {tls, tls_options()} | {timeout, timeout()} | {dtimeout, dtimeout()} | {progress, progress() | {sock_ctrl, sock_opts()} | {sock_data_act, sock_opts()} | {sock_data_pass, sock_opts()} }</v> <v>ipfamily() = inet | inet6 | inet6fb4 (default is inet)</v> <v>port() = integer() > 0 (default is 21)</v> <v>mode() = active | passive (default is passive)</v> <v>tls_options() = [<seealso marker="ssl:ssl#type-ssloption">ssl:ssloption()</seealso>]</v> + <v>sock_opts() = [<seealso marker="kernel:gen_tcp#type-option">gen_tcp:option()</seealso> except for ipv6_v6only, active, packet, mode, packet_size and header</v> <v>timeout() = integer() > 0 (default is 60000 milliseconds)</v> <v>dtimeout() = integer() > 0 | infinity (default is infinity)</v> <v>pogress() = ignore | {module(), function(), initial_data()} (default is ignore)</v> @@ -573,6 +574,11 @@ is used for securing both the control connection and the data sessions. </p> + <p>The options <c>sock_ctrl</c>, <c>sock_data_act</c> and <c>sock_data_pass</c> passes options down to + the underlying transport layer (tcp). The default value for <c>sock_ctrl</c> is <c>[]</c>. Both + <c>sock_data_act</c> and <c>sock_data_pass</c> uses the value of <c>sock_ctrl</c> as default value. + </p> + <p>A session opened in this way is closed using function <seealso marker="#close">close</seealso>.</p> diff --git a/lib/ftp/src/ftp.erl b/lib/ftp/src/ftp.erl index 8790bfec13..40f6b53fa3 100644 --- a/lib/ftp/src/ftp.erl +++ b/lib/ftp/src/ftp.erl @@ -101,6 +101,9 @@ %% data needed further on. caller = undefined, % term() ipfamily, % inet | inet6 | inet6fb4 + sockopts_ctrl = [], + sockopts_data_passive = [], + sockopts_data_active = [], progress = ignore, % ignore | pid() dtimeout = ?DATA_ACCEPT_TIMEOUT, % non_neg_integer() | infinity tls_upgrading_data_connection = false, @@ -135,9 +138,10 @@ start_standalone(Options) -> try {ok, StartOptions} = start_options(Options), {ok, OpenOptions} = open_options(Options), + {ok, SocketOptions} = socket_options(Options), case start_link(StartOptions, []) of {ok, Pid} -> - call(Pid, {open, ip_comm, OpenOptions}, plain); + call(Pid, {open, ip_comm, OpenOptions, SocketOptions}, plain); Error1 -> Error1 end @@ -149,10 +153,11 @@ start_standalone(Options) -> start_service(Options) -> try {ok, StartOptions} = start_options(Options), - {ok, OpenOptions} = open_options(Options), + {ok, OpenOptions} = open_options(Options), + {ok, SocketOptions} = socket_options(Options), case ftp_sup:start_child([[[{client, self()} | StartOptions], []]]) of {ok, Pid} -> - call(Pid, {open, ip_comm, OpenOptions}, plain); + call(Pid, {open, ip_comm, OpenOptions, SocketOptions}, plain); Error1 -> Error1 end @@ -200,9 +205,10 @@ open({option_list, Options}) when is_list(Options) -> try {ok, StartOptions} = start_options(Options), {ok, OpenOptions} = open_options(Options), + {ok, SockOpts} = socket_options(Options), case ftp_sup:start_child([[[{client, self()} | StartOptions], []]]) of {ok, Pid} -> - call(Pid, {open, ip_comm, OpenOptions}, plain); + call(Pid, {open, ip_comm, OpenOptions, SockOpts}, plain); Error1 -> Error1 end @@ -227,9 +233,10 @@ open(Host, Opts) when is_list(Opts) -> try {ok, StartOptions} = start_options(Opts), {ok, OpenOptions} = open_options([{host, Host}|Opts]), + {ok, SocketOptions} = socket_options(Opts), case start_link(StartOptions, []) of {ok, Pid} -> - do_open(Pid, OpenOptions, tls_options(Opts)); + do_open(Pid, OpenOptions, SocketOptions, tls_options(Opts)); Error1 -> Error1 end @@ -238,8 +245,8 @@ open(Host, Opts) when is_list(Opts) -> Error2 end. -do_open(Pid, OpenOptions, TLSOpts) -> - case call(Pid, {open, ip_comm, OpenOptions}, plain) of +do_open(Pid, OpenOptions, SocketOptions, TLSOpts) -> + case call(Pid, {open, ip_comm, OpenOptions, SocketOptions}, plain) of {ok, Pid} -> maybe_tls_upgrade(Pid, TLSOpts); Error -> @@ -1026,7 +1033,7 @@ handle_call({_,latest_ctrl_response}, _, #state{latest_ctrl_response=Resp} = Sta handle_call({Pid, _}, _, #state{owner = Owner} = State) when Owner =/= Pid -> {reply, {error, not_connection_owner}, State}; -handle_call({_, {open, ip_comm, Opts}}, From, State) -> +handle_call({_, {open, ip_comm, Opts, {CtrlOpts, DataPassOpts, DataActOpts}}}, From, State) -> case key_search(host, Opts, undefined) of undefined -> {stop, normal, {error, ehost}, State}; @@ -1043,6 +1050,9 @@ handle_call({_, {open, ip_comm, Opts}}, From, State) -> mode = Mode, progress = progress(Progress), ipfamily = IpFamily, + sockopts_ctrl = CtrlOpts, + sockopts_data_passive = DataPassOpts, + sockopts_data_active = DataActOpts, dtimeout = DTimeout, ftp_extension = FtpExt}, @@ -1055,28 +1065,6 @@ handle_call({_, {open, ip_comm, Opts}}, From, State) -> end end; -handle_call({_, {open, ip_comm, Host, Opts}}, From, State) -> - Mode = key_search(mode, Opts, ?DEFAULT_MODE), - Port = key_search(port, Opts, ?FTP_PORT), - Timeout = key_search(timeout, Opts, ?CONNECTION_TIMEOUT), - DTimeout = key_search(dtimeout, Opts, ?DATA_ACCEPT_TIMEOUT), - Progress = key_search(progress, Opts, ignore), - FtpExt = key_search(ftp_extension, Opts, ?FTP_EXT_DEFAULT), - - State2 = State#state{client = From, - mode = Mode, - progress = progress(Progress), - dtimeout = DTimeout, - ftp_extension = FtpExt}, - - case setup_ctrl_connection(Host, Port, Timeout, State2) of - {ok, State3, WaitTimeout} -> - {noreply, State3, WaitTimeout}; - {error, _Reason} -> - gen_server:reply(From, {error, ehost}), - {stop, normal, State2#state{client = undefined}} - end; - handle_call({_, {open, tls_upgrade, TLSOptions}}, From, State) -> _ = send_ctrl_message(State, mk_cmd("AUTH TLS", [])), activate_ctrl_connection(State), @@ -1659,11 +1647,12 @@ handle_ctrl_result({pos_compl, Lines}, client = From, caller = {setup_data_connection, Caller}, csock = CSock, + sockopts_data_passive = SockOpts, timeout = Timeout} = State) -> [_, PortStr | _] = lists:reverse(string:tokens(Lines, "|")), {ok, {IP, _}} = peername(CSock), - case connect(IP, list_to_integer(PortStr), Timeout, State) of + case connect(IP, list_to_integer(PortStr), SockOpts, Timeout, State) of {ok, _, Socket} -> handle_caller(State#state{caller = Caller, dsock = {tcp, Socket}}); {error, _Reason} = Error -> @@ -1676,7 +1665,8 @@ handle_ctrl_result({pos_compl, Lines}, ipfamily = inet, client = From, caller = {setup_data_connection, Caller}, - timeout = Timeout, + timeout = Timeout, + sockopts_data_passive = SockOpts, ftp_extension = false} = State) -> {_, [?LEFT_PAREN | Rest]} = @@ -1690,7 +1680,7 @@ handle_ctrl_result({pos_compl, Lines}, Port = (P1 * 256) + P2, ?DBG('<--data tcp connect to ~p:~p, Caller=~p~n',[IP,Port,Caller]), - case connect(IP, Port, Timeout, State) of + case connect(IP, Port, SockOpts, Timeout, State) of {ok, _, Socket} -> handle_caller(State#state{caller = Caller, dsock = {tcp,Socket}}); {error, _Reason} = Error -> @@ -1705,13 +1695,14 @@ handle_ctrl_result({pos_compl, Lines}, caller = {setup_data_connection, Caller}, csock = CSock, timeout = Timeout, + sockopts_data_passive = SockOpts, ftp_extension = true} = State) -> [_, PortStr | _] = lists:reverse(string:tokens(Lines, "|")), {ok, {IP, _}} = peername(CSock), ?DBG('<--data tcp connect to ~p:~p, Caller=~p~n',[IP,PortStr,Caller]), - case connect(IP, list_to_integer(PortStr), Timeout, State) of + case connect(IP, list_to_integer(PortStr), SockOpts, Timeout, State) of {ok, _, Socket} -> handle_caller(State#state{caller = Caller, dsock = {tcp, Socket}}); {error, _Reason} = Error -> @@ -2075,9 +2066,9 @@ handle_caller(#state{caller = {transfer_data, {Cmd, Bin, RemoteFile}}} = %% Connect to FTP server at Host (default is TCP port 21) %% in order to establish a control connection. -setup_ctrl_connection(Host, Port, Timeout, State) -> +setup_ctrl_connection(Host, Port, Timeout, #state{sockopts_ctrl = SockOpts} = State) -> MsTime = erlang:monotonic_time(), - case connect(Host, Port, Timeout, State) of + case connect(Host, Port, SockOpts, Timeout, State) of {ok, IpFam, CSock} -> NewState = State#state{csock = {tcp, CSock}, ipfamily = IpFam}, activate_ctrl_connection(NewState), @@ -2095,12 +2086,15 @@ setup_ctrl_connection(Host, Port, Timeout, State) -> setup_data_connection(#state{mode = active, caller = Caller, csock = CSock, + sockopts_data_active = SockOpts, ftp_extension = FtpExt} = State) -> case (catch sockname(CSock)) of - {ok, {{_, _, _, _, _, _, _, _} = IP, _}} -> + {ok, {{_, _, _, _, _, _, _, _} = IP0, _}} -> + IP = proplists:get_value(ip, SockOpts, IP0), {ok, LSock} = gen_tcp:listen(0, [{ip, IP}, {active, false}, - inet6, binary, {packet, 0}]), + inet6, binary, {packet, 0} | + lists:keydelete(ip,1,SockOpts)]), {ok, {_, Port}} = sockname({tcp,LSock}), IpAddress = inet_parse:ntoa(IP), Cmd = mk_cmd("EPRT |2|~s|~p|", [IpAddress, Port]), @@ -2108,9 +2102,11 @@ setup_data_connection(#state{mode = active, activate_ctrl_connection(State), {noreply, State#state{caller = {setup_data_connection, {LSock, Caller}}}}; - {ok, {{_,_,_,_} = IP, _}} -> + {ok, {{_,_,_,_} = IP0, _}} -> + IP = proplists:get_value(ip, SockOpts, IP0), {ok, LSock} = gen_tcp:listen(0, [{ip, IP}, {active, false}, - binary, {packet, 0}]), + binary, {packet, 0} | + lists:keydelete(ip,1,SockOpts)]), {ok, Port} = inet:port(LSock), _ = case FtpExt of false -> @@ -2149,41 +2145,41 @@ setup_data_connection(#state{mode = passive, ipfamily = inet, activate_ctrl_connection(State), {noreply, State#state{caller = {setup_data_connection, Caller}}}. -connect(Host, Port, Timeout, #state{ipfamily = inet = IpFam}) -> - connect2(Host, Port, IpFam, Timeout); +connect(Host, Port, SockOpts, Timeout, #state{ipfamily = inet = IpFam}) -> + connect2(Host, Port, IpFam, SockOpts, Timeout); -connect(Host, Port, Timeout, #state{ipfamily = inet6 = IpFam}) -> - connect2(Host, Port, IpFam, Timeout); +connect(Host, Port, SockOpts, Timeout, #state{ipfamily = inet6 = IpFam}) -> + connect2(Host, Port, IpFam, SockOpts, Timeout); -connect(Host, Port, Timeout, #state{ipfamily = inet6fb4}) -> +connect(Host, Port, SockOpts, Timeout, #state{ipfamily = inet6fb4}) -> case inet:getaddr(Host, inet6) of {ok, {0, 0, 0, 0, 0, 16#ffff, _, _} = IPv6} -> case inet:getaddr(Host, inet) of {ok, IPv4} -> IpFam = inet, - connect2(IPv4, Port, IpFam, Timeout); + connect2(IPv4, Port, IpFam, SockOpts, Timeout); _ -> IpFam = inet6, - connect2(IPv6, Port, IpFam, Timeout) + connect2(IPv6, Port, IpFam, SockOpts, Timeout) end; {ok, IPv6} -> IpFam = inet6, - connect2(IPv6, Port, IpFam, Timeout); + connect2(IPv6, Port, IpFam, SockOpts, Timeout); _ -> case inet:getaddr(Host, inet) of {ok, IPv4} -> IpFam = inet, - connect2(IPv4, Port, IpFam, Timeout); + connect2(IPv4, Port, IpFam, SockOpts, Timeout); Error -> Error end end. -connect2(Host, Port, IpFam, Timeout) -> - Opts = [IpFam, binary, {packet, 0}, {active, false}], +connect2(Host, Port, IpFam, SockOpts, Timeout) -> + Opts = [IpFam, binary, {packet, 0}, {active, false} | SockOpts], case gen_tcp:connect(Host, Port, Opts, Timeout) of {ok, Sock} -> {ok, IpFam, Sock}; @@ -2553,6 +2549,32 @@ open_options(Options) -> {ftp_extension, ValidateFtpExtension, false, ?FTP_EXT_DEFAULT}], validate_options(Options, ValidOptions, []). +socket_options(Options) -> + CtrlOpts = proplists:get_value(sock_ctrl, Options, []), + DataActOpts = proplists:get_value(sock_data_act, Options, CtrlOpts), + DataPassOpts = proplists:get_value(sock_data_pass, Options, CtrlOpts), + case [O || O <- lists:usort(CtrlOpts++DataPassOpts++DataActOpts), + not valid_socket_option(O)] of + [] -> + {ok, {CtrlOpts, DataPassOpts, DataActOpts}}; + Invalid -> + throw({error,{sock_opts,Invalid}}) + end. + + +valid_socket_option(inet ) -> false; +valid_socket_option(inet6 ) -> false; +valid_socket_option({ipv6_v6only, _}) -> false; +valid_socket_option({active,_} ) -> false; +valid_socket_option({packet,_} ) -> false; +valid_socket_option({mode,_} ) -> false; +valid_socket_option(binary ) -> false; +valid_socket_option(list ) -> false; +valid_socket_option({header,_} ) -> false; +valid_socket_option({packet_size,_} ) -> false; +valid_socket_option(_) -> true. + + tls_options(Options) -> %% Options will be validated by ssl application proplists:get_value(tls, Options, undefined). diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index f4bf4b1e1f..d967f56576 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -79,7 +79,6 @@ </list> </section> - <section><title>Improvements and New Features</title> <list> <item> @@ -91,7 +90,26 @@ </list> </section> -</section> + </section> + + + <section><title>Inets 6.5.2.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Options added for setting low-level properties on the + underlying TCP connections. The options are: + <c>sock_ctrl</c>, <c>sock_data_act</c> and + <c>sock_data_pass</c>. See the manual for details.</p> + <p> + Own Id: OTP-15120 Aux Id: ERIERL-192 </p> + </item> + </list> + </section> + + </section> <section><title>Inets 6.5.2</title> diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index e1ef8ab387..6e88e98c6d 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -314,6 +314,26 @@ </section> +<section><title>Kernel 5.4.3.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix some potential buggy behavior in how ticks are sent + on inter node distribution connections. Tick is now sent + to c-node even if there are unsent buffered data, as + c-nodes need ticks in order to send reply ticks. The + amount of sent data was calculated wrongly when ticks + where suppressed due to unsent buffered data.</p> + <p> + Own Id: OTP-15162 Aux Id: ERIERL-191 </p> + </item> + </list> + </section> + +</section> + <section><title>Kernel 5.4.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl index 063b19e533..3a24986381 100644 --- a/lib/runtime_tools/src/observer_backend.erl +++ b/lib/runtime_tools/src/observer_backend.erl @@ -94,7 +94,7 @@ sys_info() -> {port_limit, erlang:system_info(port_limit)}, {port_count, erlang:system_info(port_count)}, {ets_limit, erlang:system_info(ets_limit)}, - {ets_count, length(ets:all())}, + {ets_count, erlang:system_info(ets_count)}, {dist_buf_busy_limit, erlang:system_info(dist_buf_busy_limit)} | MemInfo]. diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index c9aa877a7f..2478a8950b 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -31,7 +31,6 @@ </header> <section><title>Ssh 4.7</title> - <section><title>Fixed Bugs and Malfunctions</title> <list> <item> @@ -190,6 +189,33 @@ </item> </list> </section> +</section> + +<section><title>Ssh 4.6.9.1</title> + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + SFTP clients reported the error reason <c>""</c> if a + non-OTP sftp server was killed during a long file + transmission.</p> + <p> + Now the signal name (for example <c>"KILL"</c>) will be + the error reason if the server's reason is empty.</p> + <p> + The documentation also lacked type information about this + class of errors.</p> + <p> + Own Id: OTP-15148 Aux Id: ERIERL-194 </p> + </item> + <item> + <p> + Fix ssh_sftp decode error for sftp protocol version 4</p> + <p> + Own Id: OTP-15149 Aux Id: ERIERL-199 </p> + </item> + </list> + </section> </section> diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml index 4d2337c30a..ea55126cb3 100644 --- a/lib/ssh/doc/src/ssh_sftp.xml +++ b/lib/ssh/doc/src/ssh_sftp.xml @@ -46,9 +46,9 @@ <taglist> <tag><c>reason()</c></tag> <item> - <p>= <c>atom()</c> A description of the reason why an operation failed.</p> + <p>= <c>atom() | string() | tuple() </c>A description of the reason why an operation failed.</p> <p> - The value is formed from the sftp error codes in the protocol-level responses as defined in + The <c>atom()</c> value is formed from the sftp error codes in the protocol-level responses as defined in <url href="https://tools.ietf.org/id/draft-ietf-secsh-filexfer-13.txt">draft-ietf-secsh-filexfer-13.txt</url> section 9.1. </p> @@ -57,6 +57,10 @@ E.g. the error code <c>SSH_FX_NO_SUCH_FILE</c> will cause the <c>reason()</c> to be <c>no_such_file</c>. </p> + <p>The <c>string()</c> reason is the error information from the server in case of an exit-signal. If that information is empty, the reason is the exit signal name. + </p> + <p>The <c>tuple()</c> reason are other errors like the <c>{exit_status,integer()}</c> if the exit status is not 0. + </p> </item> <tag><c>connection_ref() =</c></tag> diff --git a/lib/ssh/src/ssh_sftp.erl b/lib/ssh/src/ssh_sftp.erl index 6e720a47b7..1b2ba5a50b 100644 --- a/lib/ssh/src/ssh_sftp.erl +++ b/lib/ssh/src/ssh_sftp.erl @@ -798,13 +798,22 @@ handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) -> %% Ignore signals according to RFC 4254 section 6.9. {ok, State}; -handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, Error, _}}, +handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, Signal, Error0, _}}, State0) -> + Error = + case Error0 of + "" -> Signal; + _ -> Error0 + end, State = reply_all(State0, {error, Error}), {stop, ChannelId, State}; handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, Status}}, State0) -> - State = reply_all(State0, {error, {exit_status, Status}}), + State = + case State0 of + 0 -> State0; + _ -> reply_all(State0, {error, {exit_status, Status}}) + end, {stop, ChannelId, State}. %%-------------------------------------------------------------------- diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl index 7ee762dcee..278f6a9780 100644 --- a/lib/ssh/src/ssh_sftpd.erl +++ b/lib/ssh/src/ssh_sftpd.erl @@ -125,9 +125,9 @@ handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) -> %% Ignore signals according to RFC 4254 section 6.9. {ok, State}; -handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, Error, _}}, State) -> - Report = io_lib:format("Connection closed by peer ~n Error ~p~n", - [Error]), +handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, Signal, Error, _}}, State) -> + Report = io_lib:format("Connection closed by peer signal ~p~n Error ~p~n", + [Signal,Error]), error_logger:error_report(Report), {stop, ChannelId, State}; diff --git a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl index e1680c120e..7bb9c2d101 100644 --- a/lib/ssh/src/ssh_xfer.erl +++ b/lib/ssh/src/ssh_xfer.erl @@ -734,7 +734,7 @@ decode_ATTR(Vsn, <<?UINT32(Flags), Tail/binary>>) -> {Type,Tail2} = if Vsn =< 3 -> {?SSH_FILEXFER_TYPE_UNKNOWN, Tail}; - Vsn >= 5 -> + true -> <<?BYTE(T), TL/binary>> = Tail, {T, TL} end, diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index 6060e50d31..c3572b5b70 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,5 +1,4 @@ #-*-makefile-*- ; force emacs to enter makefile-mode SSH_VSN = 4.7 - APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl index ef4c7d255c..01181b1097 100644 --- a/lib/stdlib/src/beam_lib.erl +++ b/lib/stdlib/src/beam_lib.erl @@ -690,30 +690,31 @@ chunk_to_data(debug_info=Id, Chunk, File, _Cs, AtomTable, Mod) -> <<0:8,N:8,Mode0:N/binary,Rest/binary>> -> Mode = binary_to_atom(Mode0, utf8), Term = decrypt_chunk(Mode, Mod, File, Id, Rest), - {AtomTable, {Id, Term}}; + {AtomTable, {Id, anno_from_term(Term)}}; _ -> case catch binary_to_term(Chunk) of {'EXIT', _} -> error({invalid_chunk, File, chunk_name_to_id(Id, File)}); Term -> - {AtomTable, {Id, Term}} + {AtomTable, {Id, anno_from_term(Term)}} end end; chunk_to_data(abstract_code=Id, Chunk, File, _Cs, AtomTable, Mod) -> + %% Before Erlang/OTP 20.0. case Chunk of <<>> -> {AtomTable, {Id, no_abstract_code}}; <<0:8,N:8,Mode0:N/binary,Rest/binary>> -> Mode = binary_to_atom(Mode0, utf8), Term = decrypt_chunk(Mode, Mod, File, Id, Rest), - {AtomTable, {Id, anno_from_term(Term)}}; + {AtomTable, {Id, old_anno_from_term(Term)}}; _ -> case catch binary_to_term(Chunk) of {'EXIT', _} -> error({invalid_chunk, File, chunk_name_to_id(Id, File)}); Term -> try - {AtomTable, {Id, anno_from_term(Term)}} + {AtomTable, {Id, old_anno_from_term(Term)}} catch _:_ -> error({invalid_chunk, File, @@ -947,14 +948,24 @@ decrypt_chunk(Type, Module, File, Id, Bin) -> error({key_missing_or_invalid, File, Id}) end. -anno_from_term({raw_abstract_v1, Forms}) -> +old_anno_from_term({raw_abstract_v1, Forms}) -> {raw_abstract_v1, anno_from_forms(Forms)}; -anno_from_term({Tag, Forms}) when Tag =:= abstract_v1; Tag =:= abstract_v2 -> +old_anno_from_term({Tag, Forms}) when Tag =:= abstract_v1; + Tag =:= abstract_v2 -> try {Tag, anno_from_forms(Forms)} catch _:_ -> {Tag, Forms} end; +old_anno_from_term(T) -> + T. + +anno_from_term({debug_info_v1=Tag1, erl_abstract_code=Tag2, {Forms, Opts}}) -> + try {Tag1, Tag2, {anno_from_forms(Forms), Opts}} + catch + _:_ -> + {Tag1, Tag2, {Forms, Opts}} + end; anno_from_term(T) -> T. diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl index 31c0e60fe1..2066b2f60f 100644 --- a/lib/stdlib/src/erl_eval.erl +++ b/lib/stdlib/src/erl_eval.erl @@ -329,7 +329,8 @@ expr({'fun',Line,{clauses,Cs}} = Ex, Bs, Lf, Ef, RBs) -> 20 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) -> eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T], Info) end; _Other -> - erlang:raise(error, {'argument_limit',{'fun',Line,Cs}}, + L = erl_anno:location(Line), + erlang:raise(error, {'argument_limit',{'fun',L,to_terms(Cs)}}, ?STACKTRACE) end, ret_expr(F, Bs, RBs); @@ -381,7 +382,9 @@ expr({named_fun,Line,Name,Cs} = Ex, Bs, Lf, Ef, RBs) -> eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T], RF, Info) end; _Other -> - erlang:raise(error, {'argument_limit',{named_fun,Line,Name,Cs}}, + L = erl_anno:location(Line), + erlang:raise(error, {'argument_limit', + {named_fun,L,Name,to_terms(Cs)}}, ?STACKTRACE) end, ret_expr(F, Bs, RBs); @@ -1092,7 +1095,7 @@ match(Pat, Term, Bs) -> match(Pat, Term, Bs, BBs) -> case catch match1(Pat, Term, Bs, BBs) of invalid -> - erlang:raise(error, {illegal_pattern,Pat}, ?STACKTRACE); + erlang:raise(error, {illegal_pattern,to_term(Pat)}, ?STACKTRACE); Other -> Other end. @@ -1288,6 +1291,12 @@ merge_bindings(Bs1, Bs2) -> %% end %% end, Bs2, Bs1). +to_terms(Abstrs) -> + [to_term(Abstr) || Abstr <- Abstrs]. + +to_term(Abstr) -> + erl_parse:anno_to_term(Abstr). + %% Substitute {value, A, Item} for {var, A, Var}, preserving A. %% {value, A, Item} is a shell/erl_eval convention, and for example %% the linter cannot handle it. diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 3390cee8ae..9602f0bcd9 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -980,7 +980,7 @@ Erlang code. -type af_unary_op(T) :: {'op', anno(), unary_op(), T}. --type unary_op() :: '+' | '*' | 'bnot' | 'not'. +-type unary_op() :: '+' | '-' | 'bnot' | 'not'. %% See also lib/stdlib/{src/erl_bits.erl,include/erl_bits.hrl}. -type type_specifier_list() :: 'default' | [type_specifier(), ...]. diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index 367dbefb82..dd302a2880 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-2018. 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. @@ -59,7 +59,7 @@ _ -> ?TEST(T) end). -define(EXPRS_TEST(L), - [?TEST(E) || E <- L]). + _ = [?TEST(E) || E <- L]). -define(TEST(T), %% Assumes that erl_anno has been compiled with DEBUG=true. %% erl_pp does not use the annoations, but test it anyway. diff --git a/lib/stdlib/src/uri_string.erl b/lib/stdlib/src/uri_string.erl index f07307c039..d33dc89af8 100644 --- a/lib/stdlib/src/uri_string.erl +++ b/lib/stdlib/src/uri_string.erl @@ -415,7 +415,7 @@ transcode(URIString, Options) when is_list(URIString) -> %% (application/x-www-form-urlencoded encoding algorithm) %%------------------------------------------------------------------------- -spec compose_query(QueryList) -> QueryString when - QueryList :: [{unicode:chardata(), unicode:chardata()}], + QueryList :: [{unicode:chardata(), unicode:chardata() | true}], QueryString :: uri_string() | error(). compose_query(List) -> @@ -423,7 +423,7 @@ compose_query(List) -> -spec compose_query(QueryList, Options) -> QueryString when - QueryList :: [{unicode:chardata(), unicode:chardata()}], + QueryList :: [{unicode:chardata(), unicode:chardata() | true}], Options :: [{encoding, atom()}], QueryString :: uri_string() | error(). @@ -435,6 +435,11 @@ compose_query(List, Options) -> throw:{error, Atom, RestData} -> {error, Atom, RestData} end. %% +compose_query([{Key,true}|Rest], Options, IsList, Acc) -> + Separator = get_separator(Rest), + K = form_urlencode(Key, Options), + IsListNew = IsList orelse is_list(Key), + compose_query(Rest, Options, IsListNew, <<Acc/binary,K/binary,Separator/binary>>); compose_query([{Key,Value}|Rest], Options, IsList, Acc) -> Separator = get_separator(Rest), K = form_urlencode(Key, Options), @@ -454,7 +459,7 @@ compose_query([], _Options, IsList, Acc) -> %%------------------------------------------------------------------------- -spec dissect_query(QueryString) -> QueryList when QueryString :: uri_string(), - QueryList :: [{unicode:chardata(), unicode:chardata()}] + QueryList :: [{unicode:chardata(), unicode:chardata() | true}] | error(). dissect_query(<<>>) -> []; @@ -1889,13 +1894,12 @@ dissect_query_key(<<$=,T/binary>>, IsList, Acc, Key, Value) -> dissect_query_value(T, IsList, Acc, Key, Value); dissect_query_key(<<"&#",T/binary>>, IsList, Acc, Key, Value) -> dissect_query_key(T, IsList, Acc, <<Key/binary,"&#">>, Value); -dissect_query_key(<<$&,_T/binary>>, _IsList, _Acc, _Key, _Value) -> - throw({error, missing_value, "&"}); +dissect_query_key(T = <<$&,_/binary>>, IsList, Acc, Key, <<>>) -> + dissect_query_value(T, IsList, Acc, Key, true); dissect_query_key(<<H,T/binary>>, IsList, Acc, Key, Value) -> dissect_query_key(T, IsList, Acc, <<Key/binary,H>>, Value); -dissect_query_key(B, _, _, _, _) -> - throw({error, missing_value, B}). - +dissect_query_key(T = <<>>, IsList, Acc, Key, <<>>) -> + dissect_query_value(T, IsList, Acc, Key, true). dissect_query_value(<<$&,T/binary>>, IsList, Acc, Key, Value) -> K = form_urldecode(IsList, Key), @@ -1908,9 +1912,10 @@ dissect_query_value(<<>>, IsList, Acc, Key, Value) -> V = form_urldecode(IsList, Value), lists:reverse([{K,V}|Acc]). - %% HTML 5.2 - 4.10.21.6 URL-encoded form data - WHATWG URL (10 Jan 2018) - UTF-8 %% HTML 5.0 - 4.10.22.6 URL-encoded form data - decoding (non UTF-8) +form_urldecode(_, true) -> + true; form_urldecode(true, B) -> Result = base10_decode(form_urldecode(B, <<>>)), convert_to_list(Result, utf8); diff --git a/lib/stdlib/test/beam_lib_SUITE.erl b/lib/stdlib/test/beam_lib_SUITE.erl index 73219f8fd8..3597d6d94b 100644 --- a/lib/stdlib/test/beam_lib_SUITE.erl +++ b/lib/stdlib/test/beam_lib_SUITE.erl @@ -78,7 +78,7 @@ normal(Conf) when is_list(Conf) -> BeamFile = Simple ++ ".beam", simple_file(Source), - NoOfTables = length(ets:all()), + NoOfTables = erlang:system_info(ets_count), P0 = pps(), do_normal(Source, PrivDir, BeamFile, []), @@ -95,7 +95,7 @@ normal(Conf) when is_list(Conf) -> file:delete(BeamFile), file:delete(Source), - NoOfTables = length(ets:all()), + NoOfTables = erlang:system_info(ets_count), true = (P0 == pps()), ok. @@ -173,7 +173,7 @@ error(Conf) when is_list(Conf) -> WrongFile = Simple ++ "foo.beam", simple_file(Source), - NoOfTables = length(ets:all()), + NoOfTables = erlang:system_info(ets_count), P0 = pps(), {ok,_} = compile:file(Source, [{outdir,PrivDir},debug_info]), ACopy = filename:join(PrivDir, "a_copy.beam"), @@ -213,7 +213,7 @@ error(Conf) when is_list(Conf) -> %% we have eliminated them. ok = file:write_file(BeamFile, <<"FOR1",5:32,"BEAMfel">>), - NoOfTables = length(ets:all()), + NoOfTables = erlang:system_info(ets_count), true = (P0 == pps()), file:delete(Source), file:delete(WrongFile), @@ -273,7 +273,7 @@ cmp(Conf) when is_list(Conf) -> {Source2D1, BeamFile2D1} = make_beam(Dir1, simple2, concat), {SourceD2, BeamFileD2} = make_beam(Dir2, simple, concat), - NoOfTables = length(ets:all()), + NoOfTables = erlang:system_info(ets_count), P0 = pps(), %% cmp @@ -300,7 +300,7 @@ cmp(Conf) when is_list(Conf) -> ver(not_a_directory, beam_lib:diff_dirs(foo, bar)), true = (P0 == pps()), - NoOfTables = length(ets:all()), + NoOfTables = erlang:system_info(ets_count), delete_files([SourceD1, BeamFileD1, Source2D1, BeamFile2D1, SourceD2, BeamFileD2]), @@ -321,7 +321,7 @@ cmp_literals(Conf) when is_list(Conf) -> {SourceD1, BeamFileD1} = make_beam(Dir1, simple, constant), {SourceD2, BeamFileD2} = make_beam(Dir2, simple, constant2), - NoOfTables = length(ets:all()), + NoOfTables = erlang:system_info(ets_count), P0 = pps(), %% cmp @@ -334,7 +334,7 @@ cmp_literals(Conf) when is_list(Conf) -> ver(chunks_different, beam_lib:cmp(B1, B2)), true = (P0 == pps()), - NoOfTables = length(ets:all()), + NoOfTables = erlang:system_info(ets_count), delete_files([SourceD1, BeamFileD1, SourceD2, BeamFileD2]), @@ -351,7 +351,7 @@ strip(Conf) when is_list(Conf) -> {Source4D1, BeamFile4D1} = make_beam(PrivDir, constant, constant), {Source5D1, BeamFile5D1} = make_beam(PrivDir, lines, lines), - NoOfTables = length(ets:all()), + NoOfTables = erlang:system_info(ets_count), P0 = pps(), %% strip binary @@ -392,7 +392,7 @@ strip(Conf) when is_list(Conf) -> (catch lines:t(atom)), true = (P0 == pps()), - NoOfTables = length(ets:all()), + NoOfTables = erlang:system_info(ets_count), delete_files([SourceD1, BeamFileD1, Source2D1, BeamFile2D1, @@ -457,7 +457,7 @@ building(Conf) when is_list(Conf) -> {SourceD1, BeamFileD1} = make_beam(Dir1, building, member), - NoOfTables = length(ets:all()), + NoOfTables = erlang:system_info(ets_count), P0 = pps(), %% read all chunks @@ -487,7 +487,7 @@ building(Conf) when is_list(Conf) -> end, ChunkIds), true = (P0 == pps()), - NoOfTables = length(ets:all()), + NoOfTables = erlang:system_info(ets_count), delete_files([SourceD1, BeamFileD1, BeamFileD2]), file:del_dir(Dir1), @@ -535,7 +535,7 @@ encrypted_abstr_1(Conf) -> %% Avoid getting an extra port when crypto starts erl_ddll. erl_ddll:start(), - NoOfTables = length(ets:all()), + NoOfTables = erlang:system_info(ets_count), P0 = pps(), Key = "#a_crypto_key", @@ -549,7 +549,7 @@ encrypted_abstr_1(Conf) -> ok = crypto:stop(), %To get rid of extra ets tables. file:delete(BeamFile), file:delete(Source), - NoOfTables = length(ets:all()), + NoOfTables = erlang:system_info(ets_count), true = (P0 == pps()), ok. @@ -658,7 +658,7 @@ encrypted_abstr_file_1(Conf) -> %% Avoid getting an extra port when crypto starts erl_ddll. erl_ddll:start(), - NoOfTables = length(ets:all()), + NoOfTables = erlang:system_info(ets_count), P0 = pps(), Key = "Long And niCe 99Krypto Key", @@ -676,7 +676,7 @@ encrypted_abstr_file_1(Conf) -> file:delete(filename:join(PrivDir, ".erlang.crypt")), file:delete(BeamFile), file:delete(Source), - NoOfTables = length(ets:all()), + NoOfTables = erlang:system_info(ets_count), true = (P0 == pps()), ok. diff --git a/lib/stdlib/test/uri_string_SUITE.erl b/lib/stdlib/test/uri_string_SUITE.erl index 4fc0d76be8..ddaead9c7c 100644 --- a/lib/stdlib/test/uri_string_SUITE.erl +++ b/lib/stdlib/test/uri_string_SUITE.erl @@ -862,9 +862,11 @@ transcode_negative(_Config) -> compose_query(_Config) -> [] = uri_string:compose_query([]), "foo=1&bar=2" = uri_string:compose_query([{<<"foo">>,"1"}, {"bar", "2"}]), + "foo=1&bar" = uri_string:compose_query([{<<"foo">>,"1"}, {"bar", true}]), "foo=1&b%C3%A4r=2" = uri_string:compose_query([{"foo","1"}, {"bär", "2"}],[{encoding,utf8}]), "foo=1&b%C3%A4r=2" = uri_string:compose_query([{"foo","1"}, {"bär", "2"}],[{encoding,unicode}]), "foo=1&b%E4r=2" = uri_string:compose_query([{"foo","1"}, {"bär", "2"}],[{encoding,latin1}]), + "foo&b%E4r=2" = uri_string:compose_query([{"foo",true}, {"bär", "2"}],[{encoding,latin1}]), "foo+bar=1&%E5%90%88=2" = uri_string:compose_query([{"foo bar","1"}, {"合", "2"}]), "foo+bar=1&%26%2321512%3B=2" = uri_string:compose_query([{"foo bar","1"}, {"合", "2"}],[{encoding,latin1}]), @@ -906,11 +908,13 @@ dissect_query(_Config) -> [{"föo bar","1"},{"ö","2"}] = uri_string:dissect_query("föo+bar=1&%C3%B6=2"), [{<<"föo bar"/utf8>>,<<"1">>},{<<"ö"/utf8>>,<<"2">>}] = - uri_string:dissect_query(<<"föo+bar=1&%C3%B6=2"/utf8>>). + uri_string:dissect_query(<<"föo+bar=1&%C3%B6=2"/utf8>>), + [{"foo1",true},{"bar","2"}] = + uri_string:dissect_query("foo1&bar=2"), + [{<<"foo1">>,<<"1">>},{<<"bar">>,true}] = + uri_string:dissect_query(<<"foo1=1&bar">>). dissect_query_negative(_Config) -> - {error,missing_value,"&"} = - uri_string:dissect_query("foo1&bar=2"), {error,invalid_percent_encoding,"%XX%B6"} = uri_string:dissect_query("foo=%XX%B6&bar=2"), {error,invalid_input,[153]} = uri_string:dissect_query("foo=%99%B6&bar=2"), diff --git a/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml index 7ba90a6495..76c2d6ecbd 100644 --- a/lib/syntax_tools/doc/src/notes.xml +++ b/lib/syntax_tools/doc/src/notes.xml @@ -33,29 +33,28 @@ application.</p> <section><title>Syntax_Tools 2.1.5</title> - - <section><title>Fixed Bugs and Malfunctions</title> + <section><title>Improvements and New Features</title> <list> <item> - <p> Fix a bug regarding reverting map types. </p> <p> - Own Id: OTP-15098 Aux Id: ERIERL-177 </p> + Update to use the new string api instead of the old.</p> + <p> + Own Id: OTP-15036</p> </item> </list> </section> +</section> - - <section><title>Improvements and New Features</title> +<section><title>Syntax_Tools 2.1.4.1</title> + <section><title>Fixed Bugs and Malfunctions</title> <list> <item> + <p> Fix a bug regarding reverting map types. </p> <p> - Update to use the new string api instead of the old.</p> - <p> - Own Id: OTP-15036</p> + Own Id: OTP-15098 Aux Id: ERIERL-177 </p> </item> </list> </section> - </section> <section><title>Syntax_Tools 2.1.4</title> diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl index 029f1e88ac..758aff32fd 100644 --- a/lib/syntax_tools/src/erl_syntax.erl +++ b/lib/syntax_tools/src/erl_syntax.erl @@ -7223,7 +7223,7 @@ macro_arguments(Node) -> %% @doc Returns the syntax tree corresponding to an Erlang term. %% `Term' must be a literal term, i.e., one that can be %% represented as a source code literal. Thus, it may not contain a -%% process identifier, port, reference, binary or function value as a +%% process identifier, port, reference or function value as a %% subterm. The function recognises printable strings, in order to get a %% compact and readable representation. Evaluation fails with reason %% `badarg' if `Term' is not a literal term. @@ -7257,6 +7257,13 @@ abstract(T) when is_map(T) -> || {Key,Value} <- maps:to_list(T)]); abstract(T) when is_binary(T) -> binary([binary_field(integer(B)) || B <- binary_to_list(T)]); +abstract(T) when is_bitstring(T) -> + S = bit_size(T), + ByteS = S div 8, + BitS = S rem 8, + <<Bin:ByteS/binary, I:BitS>> = T, + binary([binary_field(integer(B)) || B <- binary_to_list(Bin)] + ++ [binary_field(integer(I), integer(BitS), [])]); abstract(T) -> erlang:error({badarg, T}). @@ -7332,15 +7339,20 @@ concrete(Node) -> Node0 -> maps:merge(concrete(Node0),M0) end; binary -> - Fs = [revert_binary_field( - binary_field(binary_field_body(F), - case binary_field_size(F) of - none -> none; - S -> - revert(S) - end, - binary_field_types(F))) - || F <- binary_fields(Node)], + Fs = [begin + B = binary_field_body(F), + {Body, Size} = + case type(B) of + size_qualifier -> + {size_qualifier_body(B), + size_qualifier_argument(B)}; + _ -> + {B, none} + end, + revert_binary_field( + binary_field(Body, Size, binary_field_types(F))) + end + || F <- binary_fields(Node)], {value, B, _} = eval_bits:expr_grp(Fs, [], fun(F, _) -> @@ -7413,7 +7425,14 @@ is_literal(T) -> is_literal_binary_field(F) -> case binary_field_types(F) of - [] -> is_literal(binary_field_body(F)); + [] -> B = binary_field_body(F), + case type(B) of + size_qualifier -> + is_literal(size_qualifier_body(B)) andalso + is_literal(size_qualifier_argument(B)); + _ -> + is_literal(B) + end; _ -> false end. diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl index c8e6448d37..4cddf8f0c3 100644 --- a/lib/syntax_tools/test/syntax_tools_SUITE.erl +++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl @@ -157,6 +157,7 @@ t_abstract_type(Config) when is_list(Config) -> {[$a,$b,$c],string}, {"hello world",string}, {<<1,2,3>>,binary}, + {<<1,2,3:4>>,binary}, {#{a=>1,"b"=>2},map_expr}, {#{#{i=>1}=>1,"b"=>#{v=>2}},map_expr}, {{a,b,c},tuple}]), diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl index 018f632948..4ec75f7962 100644 --- a/lib/tools/test/xref_SUITE.erl +++ b/lib/tools/test/xref_SUITE.erl @@ -2233,18 +2233,18 @@ variables(Conf) when is_list(Conf) -> {{error, _, _}, _} = xref_base:variables(S108, [{verbose,false}]), {ok, S109} = xref_base:set_library_path(S108, [], [{verbose,false}]), - Tabs = length(ets:all()), + NoOfTables = erlang:system_info(ets_count), {ok, S110} = eval("Eplus := closure E, TT := Eplus", 'closure()', S109), {{ok, [{user, ['Eplus','TT']}]}, S111} = xref_base:variables(S110), {ok, S112} = xref_base:forget(S111, ['TT','Eplus']), - true = Tabs =:= length(ets:all()), + true = NoOfTables =:= erlang:system_info(ets_count), {ok, NS0} = eval("Eplus := closure E", 'closure()', S112), {{ok, [{user, ['Eplus']}]}, NS} = xref_base:variables(NS0), ok = xref_base:delete(NS), - true = Tabs =:= length(ets:all()), + true = NoOfTables =:= erlang:system_info(ets_count), ok = file:delete(Beam), ok. diff --git a/otp_versions.table b/otp_versions.table index 07a0416a98..c6e5b5db00 100644 --- a/otp_versions.table +++ b/otp_versions.table @@ -1,4 +1,7 @@ +OTP-21.0.1 : compiler-7.2.1 erts-10.0.1 # asn1-5.0.6 common_test-1.16 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 megaco-3.18.3 mnesia-4.15.4 observer-2.8 odbc-2.12.1 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 public_key-1.6 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 snmp-5.2.11 ssh-4.7 ssl-9.0 stdlib-3.5 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 : OTP-21.0 : asn1-5.0.6 common_test-1.16 compiler-7.2 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 erts-10.0 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 mnesia-4.15.4 observer-2.8 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 public_key-1.6 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 ssh-4.7 ssl-9.0 stdlib-3.5 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 # megaco-3.18.3 odbc-2.12.1 snmp-5.2.11 : +OTP-20.3.8.2 : erl_interface-3.10.2.1 erts-9.3.3.1 ic-4.4.4.1 kernel-5.4.3.1 # asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.3 et-1.6.1 eunit-2.3.5 hipe-3.17.1 inets-6.5.2.1 jinterface-1.8.1 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssh-4.6.9.1 ssl-8.2.6 stdlib-3.4.5 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : +OTP-20.3.8.1 : inets-6.5.2.1 ssh-4.6.9.1 syntax_tools-2.1.4.1 # asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.3 erl_interface-3.10.2 erts-9.3.3 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4 jinterface-1.8.1 kernel-5.4.3 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssl-8.2.6 stdlib-3.4.5 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : OTP-20.3.8 : erts-9.3.3 snmp-5.2.11 # asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.3 erl_interface-3.10.2 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4 inets-6.5.2 jinterface-1.8.1 kernel-5.4.3 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 ssh-4.6.9 ssl-8.2.6 stdlib-3.4.5 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : OTP-20.3.7 : erl_docgen-0.7.3 erts-9.3.2 inets-6.5.2 # asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_interface-3.10.2 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4 jinterface-1.8.1 kernel-5.4.3 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.10 ssh-4.6.9 ssl-8.2.6 stdlib-3.4.5 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : OTP-20.3.6 : crypto-4.2.2 ssh-4.6.9 # asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.2 erts-9.3.1 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4 inets-6.5.1 jinterface-1.8.1 kernel-5.4.3 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.10 ssl-8.2.6 stdlib-3.4.5 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : diff --git a/scripts/pre-push b/scripts/pre-push index 0349378056..71e9fd1e75 100755 --- a/scripts/pre-push +++ b/scripts/pre-push @@ -22,15 +22,30 @@ # <local ref> <local sha1> <remote ref> <remote sha1> # -RELEASES="20 19 18 17 r16 r15 r14 r13" +NEW_RELEASES="21 20 19 18 17" +OLD_RELEASES="r16 r15 r14 r13" +RELEASES="$NEW_RELEASES $OLD_RELEASES" # First commit on master, not allowed in other branches -MASTER_ONLY=f52748254f17ba42e344798e8c787a1e3361fa33 +MASTER_ONLY=aea2a053e28a11497796879715be29ab0c3cd1a0 # Number of commits and files allowed in one push by this script NCOMMITS_MAX=100 NFILES_MAX=100 + +# Example testing this script for "git push upstream OTP-20.3.8.2": +# +#> null=0000000000000000000000000000000000000000 +#> echo "refs/tags/OTP-20.3.8.2 dummysha refs/tags/OTP-20.3.8.2 $null" | scripts/pre-push upstream https://github.com/erlang/otp.git + +# Example to test "git push upstream master" +# +#> local_sha=`git rev-parse master` +#> remote_sha=`git rev-parse upstream/master` +#> echo "refs/heads/master $local_sha refs/heads/master $remote_sha" | scripts/pre-push upstream https://github.com/erlang/otp.git + + remote="$1" url="$2" @@ -158,8 +173,24 @@ then exit 1 fi ;; - refs/tags/OTP-20.* | refs/tags/OTP-19.* | refs/tags/OTP-18.* | refs/tags/OTP-17.*) + refs/tags/OTP-*) tag=${remote_ref#refs/tags/} + REL="UNKNOWN" + for x in $NEW_RELEASES; do + if [ ${tag#OTP-$x.} != $tag ] + then + REL=$x + break + fi + done + if [ $REL = "UNKNOWN" ] + then + echo "$0 says:" + echo "***" + echo "*** Unknown OTP release number in tag '$tag'" + echo "***" + exit 1 + fi if [ "$remote_sha" != $null ] then echo "$0 says:" |